agentbox-app 0.1.13 → 0.1.16
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 +24 -9
- package/bin/agentbox.mjs +23 -1
- package/briefs/message-rules.md +36 -8
- package/briefs/worker.md +46 -23
- package/main/account-discovery.mjs +122 -30
- package/main/account-tooling.mjs +29 -1
- package/main/account-usage.mjs +46 -0
- package/main/agent-sessions.mjs +282 -268
- package/main/agents.mjs +179 -466
- package/main/analytics.mjs +12 -2
- package/main/answer-modes.mjs +103 -37
- package/main/approval-prompt-core.mjs +197 -0
- package/main/approval-prompt-server.mjs +4 -138
- package/main/approvals.mjs +101 -297
- package/main/artifact-path.mjs +47 -18
- package/main/claude-input.mjs +48 -4
- package/main/claude-plan.mjs +48 -0
- package/main/claude-session-open.mjs +28 -0
- package/main/claude-usage-process.mjs +40 -0
- package/main/claude-usage.mjs +18 -40
- package/main/cloud/account.mjs +179 -0
- package/main/cloud/aws.mjs +127 -0
- package/main/cloud/browser-return.mjs +58 -0
- package/main/cloud/clients.mjs +15 -0
- package/main/cloud/codex-cloud.mjs +53 -0
- package/main/cloud/config.mjs +107 -0
- package/main/cloud/docs.mjs +219 -0
- package/main/cloud/exec.mjs +27 -0
- package/main/cloud/host.mjs +165 -0
- package/main/cloud/kit.mjs +80 -0
- package/main/cloud/logins.mjs +543 -0
- package/main/cloud/managed.mjs +115 -0
- package/main/cloud/marker.mjs +104 -0
- package/main/cloud/org.mjs +53 -0
- package/main/cloud/reuse.mjs +208 -0
- package/main/cloud/run-cli.mjs +177 -0
- package/main/cloud/run.mjs +1934 -0
- package/main/cloud/usage.mjs +76 -0
- package/main/cloud-permission.mjs +51 -0
- package/main/codex-account.mjs +78 -0
- package/main/codex-app-server.mjs +276 -409
- package/main/codex-approvals.mjs +148 -433
- package/main/codex-models.mjs +20 -2
- package/main/codex-session.mjs +212 -507
- package/main/codex-threads-worker.mjs +12 -0
- package/main/codex-threads.mjs +71 -3
- package/main/codex-usage-file.mjs +5 -3
- package/main/codex-usage.mjs +56 -7
- package/main/codex-watch.mjs +17 -3
- package/main/codex.mjs +95 -363
- package/main/config.mjs +11 -1
- package/main/corner-tag.mjs +340 -19
- package/main/dev-window.mjs +1 -1
- package/main/engine-bin.mjs +232 -282
- package/main/engine-setup.mjs +21 -9
- package/main/feedback.mjs +3 -25
- package/main/folder-pictures.mjs +2 -2
- package/main/freeze-recorder-thread.mjs +86 -0
- package/main/freeze-recorder.mjs +146 -0
- package/main/fresh-user.mjs +20 -9
- package/main/git-change-offthread.mjs +17 -3
- package/main/git-change-thread.mjs +5 -2
- package/main/harnesses/claude.mjs +32 -11
- package/main/harnesses/codex.mjs +9 -2
- package/main/importable-threads.mjs +64 -6
- package/main/ipc.mjs +362 -111
- package/main/leftovers.mjs +117 -25
- package/main/liquid-glass.mjs +26 -0
- package/main/live-replies.mjs +10 -4
- package/main/main-went-red.mjs +330 -0
- package/main/main.mjs +199 -84
- package/main/memory-gate-history.mjs +36 -10
- package/main/memory-gate-server.mjs +62 -0
- package/main/memory-gate.mjs +14 -1
- package/main/menu.mjs +4 -2
- package/main/message-priority.mjs +2 -2
- package/main/mic.mjs +38 -0
- package/main/native/liquid-glass/build/liquid-glass.node +0 -0
- package/main/new-chats-watch.mjs +110 -0
- package/main/new-user-window.mjs +25 -0
- package/main/notify.mjs +24 -8
- package/main/phone-alerts.mjs +117 -0
- package/main/phone-inbox.mjs +625 -0
- package/main/phone-link.mjs +29 -9
- package/main/phone-uploads.mjs +476 -0
- package/main/picture-shape.mjs +114 -0
- package/main/quit-sequence.mjs +143 -0
- package/main/refresh-agent-models.mjs +15 -2
- package/main/relay-client.mjs +69 -1
- package/main/repeats.mjs +10 -4
- package/main/row-label.mjs +12 -41
- package/main/run-change.mjs +13 -0
- package/main/run-ledger.mjs +135 -0
- package/main/save-attachments.mjs +41 -0
- package/main/serve.mjs +45 -3
- package/main/settings.mjs +220 -44
- package/main/shell-path.mjs +185 -37
- package/main/ship-conflict-brief.mjs +119 -0
- package/main/ship-queue.mjs +115 -19
- package/main/source-updater.mjs +18 -10
- package/main/stage-attachment.mjs +68 -0
- package/main/stall-watch.mjs +50 -0
- package/main/store/home.mjs +87 -232
- package/main/store/paths.mjs +11 -0
- package/main/store/teammates-files.mjs +99 -0
- package/main/store/work-items.mjs +231 -13
- package/main/store.mjs +226 -52
- package/main/supervisor.mjs +3103 -341
- package/main/task-folders.mjs +430 -30
- package/main/task-terminals.mjs +110 -34
- package/main/team/index.mjs +114 -10
- package/main/team/memory-cloud.mjs +183 -1
- package/main/team/room-hours.mjs +48 -0
- package/main/team/room-setup.mjs +31 -0
- package/main/team/rooms-memory.mjs +454 -0
- package/main/team/rooms-shape.mjs +42 -0
- package/main/team/rooms-supabase.mjs +324 -0
- package/main/team/rooms.mjs +575 -0
- package/main/team/session.mjs +96 -26
- package/main/team/supabase-backend.mjs +180 -0
- package/main/team/sync.mjs +109 -1
- package/main/teammates.mjs +580 -0
- package/main/terminal-host.mjs +143 -0
- package/main/terminal-hosts.mjs +261 -0
- package/main/transcript-search.mjs +80 -0
- package/main/webview-guard.mjs +45 -0
- package/main/worktree-include.mjs +90 -219
- package/mcp/core/documents.mjs +3 -1
- package/mcp/core/repeats.mjs +88 -0
- package/mcp/core/teammates.mjs +222 -0
- package/mcp/core/work.mjs +76 -4
- package/mcp/stdio.mjs +24 -11
- package/mcp/tools.mjs +116 -8
- package/package.json +21 -5
- package/renderer/dist/assets/{abnfDiagram-YUKMZFIV-DPPuMWt-.js → abnfDiagram-YUKMZFIV-CoWB6YUK.js} +1 -1
- package/renderer/dist/assets/{arc-HmFfdseH.js → arc-Ch0NrCul.js} +1 -1
- package/renderer/dist/assets/{architectureDiagram-47P4ROYG-DxJ3zHrH.js → architectureDiagram-47P4ROYG-CXz9M5tQ.js} +1 -1
- package/renderer/dist/assets/{blockDiagram-E7TT5TSB-CbTXzOqQ.js → blockDiagram-E7TT5TSB-DchGUuHH.js} +1 -1
- package/renderer/dist/assets/{c4Diagram-CRA5TL53-X5ItFyLn.js → c4Diagram-CRA5TL53-Cip-zNyr.js} +1 -1
- package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
- package/renderer/dist/assets/{chunk-2Q5K7J3B-DC12Ho_L.js → chunk-2Q5K7J3B-BoXid_2r.js} +1 -1
- package/renderer/dist/assets/{chunk-5VM5RSS4-ClVtfPd0.js → chunk-5VM5RSS4-Cpez1rRz.js} +1 -1
- package/renderer/dist/assets/{chunk-7TKQ45FW-DgcOm0HT.js → chunk-7TKQ45FW-DKA84YkE.js} +1 -1
- package/renderer/dist/assets/{chunk-AW2ZBBNX-C01s4yoF.js → chunk-AW2ZBBNX-Dg-SumPG.js} +1 -1
- package/renderer/dist/assets/{chunk-FVRAUYC3-Bj0zxhR4.js → chunk-FVRAUYC3-06TCvPm1.js} +2 -2
- package/renderer/dist/assets/{chunk-HTAEGDNF-DWDeEhLU.js → chunk-HTAEGDNF-CMEBy1Td.js} +1 -1
- package/renderer/dist/assets/{chunk-JWPE2WC7-Dsqwv3eW.js → chunk-JWPE2WC7-BJ2ObmV-.js} +1 -1
- package/renderer/dist/assets/{chunk-KQW6MTUR-CE86ANL2.js → chunk-KQW6MTUR-CGWOYXUE.js} +1 -1
- package/renderer/dist/assets/{chunk-XXDRQBXY-CoNTASfs.js → chunk-XXDRQBXY-CiZp07XC.js} +1 -1
- package/renderer/dist/assets/{classDiagram-v2-K4WV4PDN-2wprLqtw.js → classDiagram-v2-K4WV4PDN-DJNIX719.js} +1 -1
- package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
- package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
- package/renderer/dist/assets/{cose-bilkent-JH36ORCC-enqMiYbn.js → cose-bilkent-JH36ORCC-CbkyZOUH.js} +1 -1
- package/renderer/dist/assets/{cynefin-EF2NZ3EQ-DHngLxvQ.js → cynefin-EF2NZ3EQ-78iOroZO.js} +1 -1
- package/renderer/dist/assets/{cynefinDiagram-3GCD6N5R-C0rZZIl3.js → cynefinDiagram-3GCD6N5R-GcCopMs7.js} +1 -1
- package/renderer/dist/assets/{dagre-W4DXFKR2-DQnaEWO7.js → dagre-W4DXFKR2-DHLlXLkz.js} +1 -1
- package/renderer/dist/assets/{diagram-2UJZ2QOL-CS8AlGPx.js → diagram-2UJZ2QOL-DuZSUHcH.js} +1 -1
- package/renderer/dist/assets/{diagram-OVF4WLC6-yiDMHIkY.js → diagram-OVF4WLC6-Ckxu3cqH.js} +1 -1
- package/renderer/dist/assets/{diagram-PFPMY2P6-XbQKzbBA.js → diagram-PFPMY2P6-CsTw9Ycn.js} +1 -1
- package/renderer/dist/assets/{diagram-UMYRVEAY-dhVvlfl_.js → diagram-UMYRVEAY-CTXd8qVg.js} +1 -1
- package/renderer/dist/assets/{diagram-YEKJPTXX-DZHe8SCf.js → diagram-YEKJPTXX-CJs1T_9m.js} +1 -1
- package/renderer/dist/assets/{diagram-ZIFT7M5P-B5h6aXVR.js → diagram-ZIFT7M5P-CvjI23mf.js} +1 -1
- package/renderer/dist/assets/{ebnfDiagram-VR2GEFS7-PR9ScDco.js → ebnfDiagram-VR2GEFS7-d_IeAzOJ.js} +1 -1
- package/renderer/dist/assets/{elk-IJKZMXRS-Cg-yQXPd.js → elk-IJKZMXRS-zo9KFP3N.js} +1 -1
- package/renderer/dist/assets/{erDiagram-O2IAPWRE-omOrJMxu.js → erDiagram-O2IAPWRE-DqfYf5Dt.js} +1 -1
- package/renderer/dist/assets/{flowDiagram-OXPTDLAJ-aOX2hzvT.js → flowDiagram-OXPTDLAJ-DAWsMLmo.js} +1 -1
- package/renderer/dist/assets/{ganttDiagram-R7TSEDQI-B1H3pWyI.js → ganttDiagram-R7TSEDQI-4ml_bhwR.js} +1 -1
- package/renderer/dist/assets/{gitGraphDiagram-XJZIOB7I-Cvw304HC.js → gitGraphDiagram-XJZIOB7I-B_WcqSId.js} +1 -1
- package/renderer/dist/assets/{infoDiagram-5W2HQ5XZ-_ZXcNViH.js → infoDiagram-5W2HQ5XZ-CI-jYqzc.js} +1 -1
- package/renderer/dist/assets/{ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js → ishikawaDiagram-K3B6WC7H-B56VWRlV.js} +1 -1
- package/renderer/dist/assets/{journeyDiagram-COXZFDF6-fr62QoAt.js → journeyDiagram-COXZFDF6-D1-p09Lr.js} +1 -1
- package/renderer/dist/assets/{kanban-definition-P3RFRI5V-J8zsabCe.js → kanban-definition-P3RFRI5V-Cv3w7JoN.js} +1 -1
- package/renderer/dist/assets/{linear-BmNdEtIi.js → linear-C35V2Spi.js} +1 -1
- package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
- package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
- package/renderer/dist/assets/{mermaid.core-CnYzvFkz.js → mermaid.core-BzGT5Vo9.js} +4 -4
- package/renderer/dist/assets/{mindmap-definition-LZFPQGKD-Bfcxc7PM.js → mindmap-definition-LZFPQGKD-BZUfuymC.js} +1 -1
- package/renderer/dist/assets/{pegDiagram-BGZESJAR-CwUJOq6X.js → pegDiagram-BGZESJAR-DFh9XNGe.js} +1 -1
- package/renderer/dist/assets/{pieDiagram-CAPJLFHJ-BrwMmqfC.js → pieDiagram-CAPJLFHJ-C65tPVsy.js} +1 -1
- package/renderer/dist/assets/{product-name-CTKLYNWY.js → product-name-CSNW6d7s.js} +1 -1
- package/renderer/dist/assets/{quadrantDiagram-GDMTTRAM-BN4Stoj-.js → quadrantDiagram-GDMTTRAM-Cushj_nk.js} +1 -1
- package/renderer/dist/assets/{railroadDiagram-SM67HX2B-CPZ5bbeR.js → railroadDiagram-SM67HX2B-DZGebGx5.js} +1 -1
- package/renderer/dist/assets/{requirementDiagram-X7JNWC4A-CgRnimVe.js → requirementDiagram-X7JNWC4A-DpAB0MNo.js} +1 -1
- package/renderer/dist/assets/{sankeyDiagram-UM26HJRW-Bt2rej__.js → sankeyDiagram-UM26HJRW-D-29rHra.js} +1 -1
- package/renderer/dist/assets/{sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js → sequenceDiagram-ZO4K6R2Y-FfOQAZol.js} +1 -1
- package/renderer/dist/assets/{sizeCapture-INFHLROL-usyNyXyh.js → sizeCapture-INFHLROL-C_n6fDBY.js} +1 -1
- package/renderer/dist/assets/{stateDiagram-v2-TBUQTH76-CRzUBb0u.js → stateDiagram-v2-TBUQTH76-BZjenzJG.js} +1 -1
- package/renderer/dist/assets/{swimlanes-N4OXWK64-C1GJvMS0.js → swimlanes-N4OXWK64-DxaMU9vX.js} +1 -1
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
- package/renderer/dist/assets/{timeline-definition-YOQAKGHF-caeyPlcc.js → timeline-definition-YOQAKGHF-DTg5HJKz.js} +1 -1
- package/renderer/dist/assets/{usecaseDiagram-VIAY4XPW-BN_SV6PC.js → usecaseDiagram-VIAY4XPW-Bc3MZDsl.js} +1 -1
- package/renderer/dist/assets/{vennDiagram-BLWOH2XV-BLGuaUX-.js → vennDiagram-BLWOH2XV-GIVGk9Id.js} +1 -1
- package/renderer/dist/assets/{wardleyDiagram-YMQ3BMBF-Da0cJeNc.js → wardleyDiagram-YMQ3BMBF-NmJnbQOg.js} +1 -1
- package/renderer/dist/assets/{xychartDiagram-TAQBALBS-cGfUffMk.js → xychartDiagram-TAQBALBS-p-kfGfnW.js} +1 -1
- package/renderer/dist/corner-tag.html +3 -3
- package/renderer/dist/index.html +3 -3
- package/scripts/after-install.mjs +30 -8
- package/scripts/cloud-shim.pl +57 -0
- package/shared/account-names.d.mts +6 -0
- package/shared/account-names.mjs +15 -0
- package/shared/agent-hours.mjs +136 -0
- package/shared/agent-import.mjs +60 -34
- package/shared/agents.mjs +126 -522
- package/shared/analytics-events.mjs +12 -0
- package/shared/approval-card.mjs +36 -0
- package/shared/bridge-map.mjs +27 -1
- package/shared/claude-code-shape.mjs +6 -0
- package/shared/claude-usage.d.mts +1 -0
- package/shared/claude-usage.mjs +42 -1
- package/shared/cloud-hand-back.d.mts +7 -0
- package/shared/cloud-hand-back.mjs +20 -0
- package/shared/cloud-kit.d.mts +219 -0
- package/shared/cloud-kit.mjs +341 -0
- package/shared/cloud-login-name.mjs +4 -0
- package/shared/cloud-start.mjs +17 -0
- package/shared/cloud-versions.mjs +23 -0
- package/shared/codex-import.mjs +2 -2
- package/shared/codex-modes.d.mts +22 -9
- package/shared/codex-modes.mjs +67 -11
- package/shared/codex-usage.mjs +108 -2
- package/shared/corner-tag.d.mts +23 -1
- package/shared/corner-tag.mjs +284 -29
- package/shared/dead-run-trace.mjs +19 -2
- package/shared/engines.mjs +70 -309
- package/shared/feedback.d.mts +7 -0
- package/shared/feedback.mjs +25 -0
- package/shared/first-run-practice.mjs +17 -24
- package/shared/harness-definitions.mjs +8 -0
- package/shared/instruction-defaults.json +2 -2
- package/shared/key-caps.d.mts +14 -0
- package/shared/key-caps.mjs +68 -0
- package/shared/message-edits.d.mts +7 -0
- package/shared/message-edits.mjs +35 -0
- package/shared/move-thread.d.mts +24 -0
- package/shared/move-thread.mjs +112 -0
- package/shared/named-threads.mjs +72 -0
- package/shared/not-shipped.d.mts +22 -0
- package/shared/not-shipped.mjs +89 -0
- package/shared/notify-rules.d.mts +6 -0
- package/shared/notify-rules.mjs +36 -8
- package/shared/phone-files.mjs +28 -0
- package/shared/phone-settings.mjs +118 -0
- package/shared/phone-snapshot.d.mts +16 -0
- package/shared/phone-snapshot.mjs +348 -0
- package/shared/picture-formats.d.mts +1 -0
- package/shared/picture-formats.mjs +13 -0
- package/shared/rank.d.mts +2 -1
- package/shared/rank.mjs +32 -19
- package/shared/repeats.d.mts +8 -0
- package/shared/repeats.mjs +33 -2
- package/shared/room-cards.mjs +167 -0
- package/shared/room-links.d.mts +4 -0
- package/shared/room-links.mjs +21 -0
- package/shared/runs-on.d.mts +3 -3
- package/shared/runs-on.mjs +8 -5
- package/shared/shipped-note.d.mts +4 -0
- package/shared/shipped-note.mjs +6 -0
- package/shared/shortcuts.mjs +14 -6
- package/shared/spawn-trouble.d.mts +4 -0
- package/shared/spawn-trouble.mjs +443 -40
- package/shared/team-rules.mjs +9 -2
- package/shared/teammates.d.mts +19 -0
- package/shared/teammates.mjs +756 -0
- package/shared/terminal-focus.d.mts +1 -0
- package/shared/terminal-focus.mjs +13 -0
- package/shared/thread-cards.mjs +4 -1
- package/shared/usage.d.mts +9 -0
- package/shared/usage.mjs +49 -3
- package/shared/websites.d.mts +15 -0
- package/shared/websites.mjs +61 -0
- package/shared/work-items.mjs +127 -30
- package/shared/work-lines.mjs +92 -244
- package/renderer/dist/assets/channel-BlT2g9Sz.js +0 -1
- package/renderer/dist/assets/cornerTag-BLrxiDE5.css +0 -1
- package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +0 -1
- package/renderer/dist/assets/main-ChNdnUvt.js +0 -1132
- package/renderer/dist/assets/main-nj0QR5kg.css +0 -32
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +0 -8
package/shared/work-lines.mjs
CHANGED
|
@@ -1,54 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// 535 work lines in 162 runs 328 of them (61%) ran past the end of their box
|
|
7
|
-
// and were cut by the ellipsis, so they could not be read at all 368 of them
|
|
8
|
-
// (69%) were over sixty characters the six worst showed 2% to 4% of
|
|
9
|
-
// themselves; every one of those was `cd /Users/you/Desktop/dev/... &&
|
|
10
|
-
// python3 - <<'PY' ...` 18 distinct raw tool names were printed as verbs, the
|
|
11
|
-
// worst being `mcp__plugin_paper-desktop_paper__write_html`, 31 times in one
|
|
12
|
-
// thread the longest unbroken run was 20 lines, and that happened three times
|
|
13
|
-
//
|
|
14
|
-
// So there are three separate defects wearing one word, and this file is the
|
|
15
|
-
// three answers.
|
|
16
|
-
//
|
|
17
|
-
// ONE. THE INTERESTING PART OF A COMMAND IS NEVER AT THE FRONT. Every one of
|
|
18
|
-
// the six worst lines was a real command hiding behind a `cd` into an absolute
|
|
19
|
-
// path, or an `export PATH=` prelude, or both. Truncating from the right, which
|
|
20
|
-
// is what an ellipsis does, therefore threw away the only part that mattered and
|
|
21
|
-
// kept the part every line shares. `plainCommand` drops the prelude instead, so
|
|
22
|
-
// the line STARTS at the thing that ran.
|
|
23
|
-
//
|
|
24
|
-
// TWO. A PATH IS SHOWN FROM THE END. `/Users/you/Desktop/dev/zero/
|
|
25
|
-
// renderer/src/components/Tweaks.tsx` is 63 characters of which the last 11 are
|
|
26
|
-
// the answer. Under the session's own folder it becomes
|
|
27
|
-
// `renderer/src/components/Tweaks.tsx`; deeper than that it keeps the last
|
|
28
|
-
// three segments behind a leading ellipsis. Nothing is invented and nothing is
|
|
29
|
-
// silently dropped: the whole string is one press away on the line itself.
|
|
30
|
-
//
|
|
31
|
-
// THREE. A RUN AGGREGATES. Consecutive tool calls fold into one line that says
|
|
32
|
-
// how many and what kind. The threshold is three and it is measured, not
|
|
33
|
-
// chosen: the run-length histogram is
|
|
34
|
-
// {1:38, 2:42, 3:36, 4:18, 5:6, 6:6, 7:8, 8:2, 11:2, 13:1, 20:3}. Folding at
|
|
35
|
-
// two takes the screen down 70% and folding at three takes it down 62%, so the
|
|
36
|
-
// last eight points cost the 42 pairs, and a pair read as two short clean
|
|
37
|
-
// lines says more than "2 things it ran" does. Three is where a wall starts.
|
|
38
|
-
//
|
|
39
|
-
// NOTHING HERE HIDES A FAILURE. A folded run says on its own line that
|
|
40
|
-
// something in it failed, in the same words and the same colour a single line
|
|
41
|
-
// would, because a fold she has to open to find bad news in is worse than the
|
|
42
|
-
// wall it replaced.
|
|
43
|
-
|
|
44
|
-
/* ------------------------------ WHAT IT DID ------------------------------- */
|
|
45
|
-
// PLAIN WORDS AND NOT OUR TOOL NAMES. The design law forbids jargon in UI copy,
|
|
46
|
-
// so "Bash" and "Glob" are ours, not the reader's.
|
|
47
|
-
//
|
|
48
|
-
// `changed` RATHER THAN `edited` (2026-10-01). A tester who is not a programmer
|
|
49
|
-
// reads "edited" as a document being revised; the thing that happened is that a
|
|
50
|
-
// file in the code is now different, and the line next to it already offers the
|
|
51
|
-
// file as a chip into the change. One word for one event.
|
|
1
|
+
// Pure. Turns a tool call into the line a thread shows for it. The full string
|
|
2
|
+
// is always one press away on the line, so shortening here never loses it.
|
|
3
|
+
|
|
4
|
+
// Plain words, not tool names. Edit is "changed" because a reader who is not a
|
|
5
|
+
// programmer takes "edited" to mean a document was revised.
|
|
52
6
|
const VERBS = {
|
|
53
7
|
Bash: 'ran',
|
|
54
8
|
Read: 'read',
|
|
@@ -65,8 +19,6 @@ const VERBS = {
|
|
|
65
19
|
ListAgents: 'listed the other sessions',
|
|
66
20
|
SendMessage: 'messaged',
|
|
67
21
|
ToolSearch: 'looked up a tool',
|
|
68
|
-
// Measured in her own threads on 2026-08-20 and printed raw until now:
|
|
69
|
-
// `skill` 3 times and `askuserquestion` twice, both as bare lowercase names.
|
|
70
22
|
Skill: 'used a skill',
|
|
71
23
|
AskUserQuestion: 'asked a question',
|
|
72
24
|
BashOutput: 'checked on a command',
|
|
@@ -74,48 +26,32 @@ const VERBS = {
|
|
|
74
26
|
ExitPlanMode: 'finished planning',
|
|
75
27
|
};
|
|
76
28
|
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// untouched and she read that string 31 times in one afternoon. An MCP name is
|
|
81
|
-
// `mcp__<server>__<tool>` and a server may contain anything, so the tool is
|
|
82
|
-
// what follows the LAST double underscore.
|
|
29
|
+
// An MCP tool name is `mcp__<server>__<tool>` and the server name may itself
|
|
30
|
+
// hold underscores (`mcp__plugin_paper-desktop_paper__write_html`), so the tool
|
|
31
|
+
// is what follows the last double underscore.
|
|
83
32
|
export function workVerb(name) {
|
|
84
33
|
const n = String(name ?? '').trim();
|
|
85
34
|
if (!n) return 'did something';
|
|
86
35
|
if (VERBS[n]) return VERBS[n];
|
|
87
36
|
const mcp = n.startsWith('mcp__') ? n.slice(n.lastIndexOf('__') + 2) : n;
|
|
88
|
-
// Underscores are how a machine spells a space. She reads the result, so it
|
|
89
|
-
// gets the space: `list_work_items` is jargon and `list work items` is a
|
|
90
|
-
// name, which is all this line has ever claimed to be.
|
|
91
37
|
return mcp.replace(/_+/g, ' ').trim().toLowerCase() || 'did something';
|
|
92
38
|
}
|
|
93
39
|
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
// budget: measured over her 535 lines, three segments holds the whole of every
|
|
97
|
-
// path that sits inside a repo and the meaningful tail of every scratch path.
|
|
40
|
+
// A path is shortened from the end, where the file name is. Three segments
|
|
41
|
+
// hold the whole of most paths inside a repo.
|
|
98
42
|
const PATH_SEGMENTS = 3;
|
|
99
43
|
|
|
100
|
-
// `home`
|
|
101
|
-
//
|
|
102
|
-
// on exactly one Mac in the world. Agentbox is downloaded now: on anybody
|
|
103
|
-
// else's, `~/notes.md` came out as `/Users/leon/notes.md`, which is the
|
|
104
|
-
// prefix-only line this whole file exists to stop. Nothing is guessed when
|
|
105
|
-
// nobody says whose Mac it is; the path is shown whole, which is worse than a
|
|
106
|
-
// tilde and is at least true.
|
|
44
|
+
// `home` has no default on purpose: with none given, a path is shown whole
|
|
45
|
+
// rather than shortened against a guess at whose machine this is.
|
|
107
46
|
export function shortPath(raw, cwd = '', home = '') {
|
|
108
47
|
const s = String(raw ?? '').trim();
|
|
109
48
|
if (!s) return '';
|
|
110
49
|
if (!s.startsWith('/') && !s.startsWith('~')) return s;
|
|
111
50
|
|
|
112
51
|
let p = s;
|
|
113
|
-
//
|
|
114
|
-
// typed either. Saying `/private` first is the filesystem talking.
|
|
52
|
+
// On macOS `/tmp` is a link to `/private/tmp`; show the one people type.
|
|
115
53
|
p = p.replace(/^\/private\/tmp\//, '/tmp/');
|
|
116
54
|
|
|
117
|
-
// Inside the folder this session is running in, a path is what she would
|
|
118
|
-
// type: relative, with nothing above it.
|
|
119
55
|
const base = String(cwd ?? '').replace(/^\/private\/tmp\//, '/tmp/').replace(/\/+$/, '');
|
|
120
56
|
if (base && (p === base || p.startsWith(`${base}/`))) {
|
|
121
57
|
const rel = p === base ? '' : p.slice(base.length + 1);
|
|
@@ -128,13 +64,11 @@ export function shortPath(raw, cwd = '', home = '') {
|
|
|
128
64
|
return trimSegments(p);
|
|
129
65
|
}
|
|
130
66
|
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
// Tweaks.tsx` would read as a path from the root of something.
|
|
67
|
+
// The leading `…/` says segments were dropped. Without it `components/Tweaks.tsx`
|
|
68
|
+
// would read as a path from the root of something.
|
|
134
69
|
function trimSegments(p) {
|
|
135
|
-
// The lead is kept
|
|
136
|
-
// `x/store.mjs` are
|
|
137
|
-
// function quietly saying the wrong one.
|
|
70
|
+
// The lead is kept when nothing is trimmed, because `/x/store.mjs` and
|
|
71
|
+
// `x/store.mjs` are different files.
|
|
138
72
|
const lead = p.startsWith('~/') ? '~/' : p.startsWith('/') ? '/' : '';
|
|
139
73
|
const body = p.slice(lead.length);
|
|
140
74
|
const parts = body.split('/').filter(Boolean);
|
|
@@ -142,22 +76,9 @@ function trimSegments(p) {
|
|
|
142
76
|
return `…/${parts.slice(-PATH_SEGMENTS).join('/')}`;
|
|
143
77
|
}
|
|
144
78
|
|
|
145
|
-
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// `cd /Users/you/Desktop/dev/zero && …` the commonest by far
|
|
150
|
-
// `export PATH="$HOME/.nvm/versions/node/…" ; …` 29 times in one thread
|
|
151
|
-
// `SP=/private/tmp/claude-501/-Users-…/… ; …` a bare assignment
|
|
152
|
-
// `source ~/.zshrc && …`
|
|
153
|
-
//
|
|
154
|
-
// Each is scaffolding that says nothing about what the agent was doing, and
|
|
155
|
-
// each is long enough on its own to fill the line. They come off one at a time
|
|
156
|
-
// until what is left is a command.
|
|
157
|
-
//
|
|
158
|
-
// WHAT IT NEVER DOES IS INVENT. If stripping leaves nothing, the original
|
|
159
|
-
// stands: an agent that genuinely only ran `cd somewhere` did that, and a line
|
|
160
|
-
// reading empty would be this function lying about it.
|
|
79
|
+
// The interesting part of a command is never at the front, and an ellipsis cuts
|
|
80
|
+
// from the right. So these preludes come off the front, one at a time, until
|
|
81
|
+
// what is left is the command that ran. If nothing is left, the original stands.
|
|
161
82
|
const PRELUDE = [
|
|
162
83
|
// cd <path> && |; | \n
|
|
163
84
|
/^cd\s+(?:"[^"]*"|'[^']*'|[^\s;&|]+)\s*(?:&&|;|\n)\s*/,
|
|
@@ -168,33 +89,25 @@ const PRELUDE = [
|
|
|
168
89
|
// source|. <file> && |; | \n
|
|
169
90
|
/^(?:source|\.)\s+(?:"[^"]*"|'[^']*'|[^\s;&|]+)\s*(?:&&|;|\n)\s*/,
|
|
170
91
|
// echo "=== a banner ===" && |; | \n
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
// labelling its own terminal output is talking to itself, and four of the 31
|
|
174
|
-
// worst lines left on her screen after the preludes came off were exactly
|
|
175
|
-
// this. A plain `echo something` is a real command and is left alone, because
|
|
176
|
-
// it may be the whole of what ran.
|
|
92
|
+
// Only a banner, marked by `===` or `---`. A plain `echo` may be the whole of
|
|
93
|
+
// what ran, so it stays.
|
|
177
94
|
/^echo\s+(?:"[^"]*(?:===|---)[^"]*"|'[^']*(?:===|---)[^']*')\s*(?:&&|;|\n)\s*/,
|
|
178
95
|
];
|
|
179
96
|
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
// onward becomes an ellipsis, so the line says which program was fed a script
|
|
183
|
-
// without printing the script.
|
|
97
|
+
// Everything from a heredoc marker onward becomes an ellipsis, so the line
|
|
98
|
+
// names the program that was fed a script without printing the script.
|
|
184
99
|
const HEREDOC = /<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1([\s\S]*)$/;
|
|
185
100
|
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
// being worked on was named on the script's FIRST line, so when the command in
|
|
190
|
-
// front of the marker is this short the first line comes with it.
|
|
101
|
+
// When the command before the marker is this short (`python3 -`), the script's
|
|
102
|
+
// first line comes with it. That line usually names the file, and without it a
|
|
103
|
+
// row of `python3 - …` lines cannot be told apart.
|
|
191
104
|
const HEREDOC_HEAD = 12;
|
|
192
105
|
|
|
193
106
|
function firstScriptLine(body, marker) {
|
|
194
107
|
for (const line of String(body ?? '').split('\n')) {
|
|
195
108
|
const t = line.trim();
|
|
196
|
-
// The marker
|
|
197
|
-
//
|
|
109
|
+
// The closing marker is not a line of the script. It is matched exactly,
|
|
110
|
+
// because a one-word line of script looks just like one.
|
|
198
111
|
if (!t || t === marker) continue;
|
|
199
112
|
if (t.startsWith('#') || t.startsWith('//')) continue;
|
|
200
113
|
return t;
|
|
@@ -207,8 +120,7 @@ export function plainCommand(raw) {
|
|
|
207
120
|
if (!s) return '';
|
|
208
121
|
const whole = s;
|
|
209
122
|
|
|
210
|
-
//
|
|
211
|
-
// three of these in a row.
|
|
123
|
+
// Repeated, because `cd x && export Y=z && npm test` has two preludes.
|
|
212
124
|
for (let i = 0; i < 6; i += 1) {
|
|
213
125
|
const before = s;
|
|
214
126
|
for (const re of PRELUDE) s = s.replace(re, '');
|
|
@@ -224,35 +136,18 @@ export function plainCommand(raw) {
|
|
|
224
136
|
return s || whole.replace(/\s+/g, ' ').trim();
|
|
225
137
|
}
|
|
226
138
|
|
|
227
|
-
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
// Testers who are not programmers read the top of every thread as noise, and
|
|
231
|
-
// the top of every thread is this: a tool lookup, a claim, and later an update
|
|
232
|
-
// to the item. MEASURED over 3,344 real session traces from 2026-09-20 to
|
|
233
|
-
// 2026-10-01: 55,500 tool lines, of which 2,462 are the store's own records
|
|
234
|
-
// (claim 1,212, update 999, release 138, the lists 113) and 498 are a tool
|
|
235
|
-
// lookup. Three per cent of the lines and the first two lines of nearly every
|
|
236
|
-
// thread.
|
|
237
|
-
//
|
|
238
|
-
// NOTHING OF THE WORK IS HIDDEN BY THIS. Reading a document, writing one,
|
|
239
|
-
// filing a question and looking at a page are what the session was asked to do;
|
|
240
|
-
// taking the item and writing its status back are how it tells the app it is
|
|
241
|
-
// running. The second kind is what comes off the screen.
|
|
242
|
-
//
|
|
243
|
-
// The server is named after the product (`mcp__agentbox__`, `mcp__daydream__`),
|
|
244
|
-
// so the prefix is never one fixed string and the tool is what follows the last
|
|
245
|
-
// double underscore, the same reading `workVerb` makes.
|
|
139
|
+
// The store's own bookkeeping is not shown. Claiming an item, writing its
|
|
140
|
+
// status back and listing what is there are how a session tells the app it is
|
|
141
|
+
// running. Reading or writing a document is the work, and stays on screen.
|
|
246
142
|
const BOOKKEEPING = new Set([
|
|
247
143
|
'claim_work_item', 'release_work_item', 'update_work_item',
|
|
248
144
|
'list_work_items', 'list_documents', 'list_products', 'list_skills',
|
|
249
145
|
'list_chats', 'get_product',
|
|
250
146
|
]);
|
|
251
147
|
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
// schema and is nobody's work.
|
|
148
|
+
// A Claude transcript writes `mcp__<server>__<tool>`, with the server named
|
|
149
|
+
// after the product, and Codex reports the bare tool. `ToolSearch` is Claude
|
|
150
|
+
// Code loading a tool's schema.
|
|
256
151
|
export function isBookkeeping(name) {
|
|
257
152
|
const n = String(name ?? '').trim();
|
|
258
153
|
if (!n) return false;
|
|
@@ -260,34 +155,17 @@ export function isBookkeeping(name) {
|
|
|
260
155
|
return BOOKKEEPING.has(n.startsWith('mcp__') ? n.slice(n.lastIndexOf('__') + 2) : n);
|
|
261
156
|
}
|
|
262
157
|
|
|
263
|
-
|
|
264
|
-
// "
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
// programs, and the eleven commonest cover 80% of them (grep 5,325, git 3,998,
|
|
269
|
-
// sed 3,894, npx 2,471, python3 2,435, cat 2,074, ls 1,814, a shell -c 1,464,
|
|
270
|
-
// arch 947, a for loop 894, node 689). 55% of the lines are compound (`a && b;
|
|
271
|
-
// c`) and 48% hold a pipe, so the thing on the screen is a shell one-liner and
|
|
272
|
-
// not a sentence.
|
|
273
|
-
//
|
|
274
|
-
// THE RULE IS THAT A GUESS IS WORSE THAN A COMMAND. Every segment has to be
|
|
275
|
-
// recognised and they all have to agree on one verb; one unknown part, or two
|
|
276
|
-
// different verbs, and this returns null and the line stays the command it
|
|
277
|
-
// always was. A wrong verb on her screen cannot be checked against anything,
|
|
278
|
-
// and the raw command at least is true. The exact string is one press away
|
|
279
|
-
// either way (`full` on the work line).
|
|
280
|
-
//
|
|
281
|
-
// `doing` IS THE SAME EVENT WHILE IT IS STILL HAPPENING, because the live line
|
|
282
|
-
// under a running row reads "Reading README.md" and the thread above it reads
|
|
283
|
-
// "Read README.md". One table, so the two can never drift.
|
|
158
|
+
// A shell command in words: `cat README.md; echo ---; cat package.json` reads
|
|
159
|
+
// "read README.md and package.json". A wrong verb is worse than the raw
|
|
160
|
+
// command, so every segment must be recognised and all must agree on one verb,
|
|
161
|
+
// or the line stays the command. `doing` is the same verb for the live line
|
|
162
|
+
// under a running row, in the same table so the two cannot drift.
|
|
284
163
|
|
|
285
164
|
// Scaffolding between the real steps of a line. A banner, a `cd`, a wait.
|
|
286
165
|
const NOISE = new Set(['echo', 'cd', 'pwd', 'true', 'false', ':', 'sleep', 'clear', 'date', 'printf', 'set']);
|
|
287
166
|
|
|
288
|
-
// `2>&1` and `2>/dev/null` say nothing about what ran
|
|
289
|
-
//
|
|
290
|
-
// for, so it gives up rather than guess.
|
|
167
|
+
// `2>&1` and `2>/dev/null` say nothing about what ran. Any other redirect reads
|
|
168
|
+
// or writes a file this does not account for, so it gives up rather than guess.
|
|
291
169
|
const QUIET_REDIRECT = /^[0-9]*>>?&?(?:[0-9]+|\/dev\/null)$/;
|
|
292
170
|
|
|
293
171
|
const FAMILIES = [
|
|
@@ -306,8 +184,8 @@ const FAMILIES = [
|
|
|
306
184
|
const BY_HEAD = new Map();
|
|
307
185
|
for (const family of FAMILIES) for (const head of family.heads) BY_HEAD.set(head, family);
|
|
308
186
|
|
|
309
|
-
//
|
|
310
|
-
//
|
|
187
|
+
// Subcommands not listed keep their command, because a vague verb over
|
|
188
|
+
// `git merge-base` says less than the command does.
|
|
311
189
|
const GIT = {
|
|
312
190
|
status: { verb: 'checked what changed', doing: 'Checking what changed' },
|
|
313
191
|
diff: { verb: 'checked what changed', doing: 'Checking what changed' },
|
|
@@ -341,9 +219,8 @@ function unquote(token) {
|
|
|
341
219
|
return t;
|
|
342
220
|
}
|
|
343
221
|
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
// shell lines invents steps that never ran.
|
|
222
|
+
// Splits only outside quotes: a `;` inside a sed script or a grep pattern is
|
|
223
|
+
// not a new command.
|
|
347
224
|
function splitOutside(text, separators) {
|
|
348
225
|
const out = [];
|
|
349
226
|
let buffer = '';
|
|
@@ -379,8 +256,7 @@ function argumentsOf(words, skip = []) {
|
|
|
379
256
|
const token = unquote(raw);
|
|
380
257
|
if (!quoted) {
|
|
381
258
|
if (QUIET_REDIRECT.test(token)) continue;
|
|
382
|
-
//
|
|
383
|
-
// commands is a shape nobody taught this.
|
|
259
|
+
// Any other redirect, a subshell or backticks is a shape this cannot read.
|
|
384
260
|
if (/[<>`]/.test(token) || token.includes('$(')) return null;
|
|
385
261
|
}
|
|
386
262
|
if (isFlag(token)) {
|
|
@@ -400,7 +276,7 @@ function stageWords(stage, cwd, home) {
|
|
|
400
276
|
const head = unquote(words[0]);
|
|
401
277
|
const rest = words.slice(1);
|
|
402
278
|
if (NOISE.has(head)) {
|
|
403
|
-
// `echo something > a file` writes a file,
|
|
279
|
+
// `echo something > a file` writes a file, so it is not noise.
|
|
404
280
|
const args = argumentsOf(rest);
|
|
405
281
|
return args === null ? null : 'noise';
|
|
406
282
|
}
|
|
@@ -423,9 +299,8 @@ function stageWords(stage, cwd, home) {
|
|
|
423
299
|
if (SCRIPTS.has(head) || /\.(?:sh|mjs|js|py)$/.test(head)) {
|
|
424
300
|
const args = argumentsOf(rest);
|
|
425
301
|
if (args === null) return null;
|
|
426
|
-
// `python3 - <<'PY'`
|
|
427
|
-
//
|
|
428
|
-
// reads to tell one of 2,793 of these from another, so it is kept.
|
|
302
|
+
// `python3 - <<'PY'` has no file name. `plainCommand` put the script's
|
|
303
|
+
// first line after the ellipsis, and that line tells these apart.
|
|
429
304
|
if (stage.includes('…')) {
|
|
430
305
|
const first = stage.slice(stage.lastIndexOf('…') + 1).trim();
|
|
431
306
|
return { ...SCRIPT, names: first ? [first] : [] };
|
|
@@ -447,8 +322,8 @@ const URL_LIKE = /^(?:https?:\/\/|www\.|localhost|127\.0\.0\.1)/;
|
|
|
447
322
|
|
|
448
323
|
function namesFor(family, args, cwd, home) {
|
|
449
324
|
if (family.take === 'first') return args.slice(0, 1);
|
|
450
|
-
//
|
|
451
|
-
// /dev/null -w '%{http_code}' <url
|
|
325
|
+
// The URL is not always the first argument, as in
|
|
326
|
+
// `curl -s -o /dev/null -w '%{http_code}' <url>`.
|
|
452
327
|
if (family.take === 'url') {
|
|
453
328
|
const url = args.find((a) => URL_LIKE.test(a));
|
|
454
329
|
return url ? [url] : args.slice(0, 1);
|
|
@@ -462,10 +337,8 @@ function namesFor(family, args, cwd, home) {
|
|
|
462
337
|
return files;
|
|
463
338
|
}
|
|
464
339
|
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
// the command did. Measured over the same traces: `git add`, `git commit`,
|
|
468
|
-
// `git push` and `git merge` are 563 lines that said nothing but their verb.
|
|
340
|
+
// Keeps what the subcommand named (the file, the branch or the message),
|
|
341
|
+
// because a bare verb tells a developer less than the command did.
|
|
469
342
|
function gitWords(rest, cwd, home) {
|
|
470
343
|
const words = [...rest];
|
|
471
344
|
// `git -C <path> <subcommand>`: the folder is not the verb.
|
|
@@ -484,8 +357,7 @@ const NOT_A_REF = new Set(['origin', 'upstream', '.', '--', 'HEAD']);
|
|
|
484
357
|
|
|
485
358
|
function gitNames(sub, rest, cwd, home) {
|
|
486
359
|
if (sub === 'commit') {
|
|
487
|
-
// The message
|
|
488
|
-
// absent when the message came in on stdin (`git commit -F -`).
|
|
360
|
+
// The message is absent when it came in on stdin (`git commit -F -`).
|
|
489
361
|
const at = rest.findIndex((w) => unquote(w) === '-m' || unquote(w) === '--message');
|
|
490
362
|
const message = at >= 0 ? unquote(rest[at + 1] ?? '') : '';
|
|
491
363
|
return message ? [message] : [];
|
|
@@ -497,9 +369,8 @@ function gitNames(sub, rest, cwd, home) {
|
|
|
497
369
|
return refs.length ? [refs[refs.length - 1]] : [];
|
|
498
370
|
}
|
|
499
371
|
const files = args.filter(looksLikeAFile).map((a) => shortPath(a, cwd, home));
|
|
500
|
-
// A
|
|
501
|
-
//
|
|
502
|
-
// thread. The value a flag carried survives when no file does.
|
|
372
|
+
// A history command often names something that is not a path, like
|
|
373
|
+
// `git log --grep <word>`, so the first argument is kept when no file is.
|
|
503
374
|
if (files.length) return files;
|
|
504
375
|
return args.length ? [args[0]] : [];
|
|
505
376
|
}
|
|
@@ -512,15 +383,14 @@ function sedWords(rest, cwd, home) {
|
|
|
512
383
|
if (args === null) return null;
|
|
513
384
|
const flags = rest.map(unquote).filter(isFlag);
|
|
514
385
|
const inPlace = flags.some((f) => f.startsWith('-i'));
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
//
|
|
386
|
+
// The file is the last argument that is not a sed script. A trace line is cut
|
|
387
|
+
// at 200 characters, so the file may be missing and the script (`s#a#b#`)
|
|
388
|
+
// is then the last thing that looks like a path.
|
|
518
389
|
const file = [...args].reverse().find((a) => looksLikeAFile(a) && !SED_SCRIPT.test(a));
|
|
519
390
|
if (inPlace) {
|
|
520
391
|
return file ? { verb: 'changed', doing: 'Changing', names: [shortPath(file, cwd, home)] } : null;
|
|
521
392
|
}
|
|
522
|
-
// `sed -n 1,200p <file>` is how an agent reads part of a file
|
|
523
|
-
// 3,894 sed lines measured were exactly that, against 325 that edited.
|
|
393
|
+
// `sed -n 1,200p <file>` is how an agent reads part of a file.
|
|
524
394
|
if (!flags.some((f) => f === '-n' || f === '-e' || f === '-E')) return null;
|
|
525
395
|
return file ? { verb: 'read', doing: 'Reading', names: [shortPath(file, cwd, home)] } : null;
|
|
526
396
|
}
|
|
@@ -546,15 +416,15 @@ function runnerWords(rest, cwd, home) {
|
|
|
546
416
|
|
|
547
417
|
// A trace line is cut at 200 characters, so the last test file on a long
|
|
548
418
|
// `vitest run a b c` often arrives without its extension. Anything that looks
|
|
549
|
-
// like a path counts
|
|
419
|
+
// like a path counts.
|
|
550
420
|
function testWords(rest) {
|
|
551
421
|
const args = rest.map(unquote).filter((w) => !isFlag(w) && w !== 'run');
|
|
552
422
|
const files = args.filter((w) => TEST_FILE.test(w) || w.includes('/'));
|
|
553
423
|
return { ...TESTS, names: files.map(lastSegment) };
|
|
554
424
|
}
|
|
555
425
|
|
|
556
|
-
// Up to three names, then
|
|
557
|
-
//
|
|
426
|
+
// Up to three names, then a count that says what it counted, so the cap is
|
|
427
|
+
// never silent.
|
|
558
428
|
function joinNames(names, many = 'files') {
|
|
559
429
|
const list = [...new Set(names.filter(Boolean))];
|
|
560
430
|
if (!list.length) return '';
|
|
@@ -566,10 +436,8 @@ function joinNames(names, many = 'files') {
|
|
|
566
436
|
/**
|
|
567
437
|
* What a shell command did, in words, or null to leave it as the command.
|
|
568
438
|
*
|
|
569
|
-
* `cwd` and `home` are the
|
|
570
|
-
*
|
|
571
|
-
* shortenable against the folder it sits under, and nothing is guessed when
|
|
572
|
-
* nobody says.
|
|
439
|
+
* `cwd` and `home` are the session's folder and the machine's home, passed on
|
|
440
|
+
* to `shortPath`.
|
|
573
441
|
*/
|
|
574
442
|
export function commandWork(raw, cwd = '', home = '') {
|
|
575
443
|
const command = plainCommand(raw);
|
|
@@ -594,10 +462,8 @@ export function commandWork(raw, cwd = '', home = '') {
|
|
|
594
462
|
return { verb: said.verb, doing: said.doing, subject: joinNames(names, first.many) };
|
|
595
463
|
}
|
|
596
464
|
|
|
597
|
-
|
|
598
|
-
//
|
|
599
|
-
// folder the session is running in, which is what makes a path relative; it is
|
|
600
|
-
// optional so that nothing that calls this without one gets worse than before.
|
|
465
|
+
// What the call acted on, as short as it can be without being wrong. With a
|
|
466
|
+
// `cwd`, paths under it are shown relative to it.
|
|
601
467
|
export function workSubject(input, cwd = '', home = '') {
|
|
602
468
|
const i = input ?? {};
|
|
603
469
|
if (i.command != null && String(i.command).trim()) {
|
|
@@ -609,9 +475,7 @@ export function workSubject(input, cwd = '', home = '') {
|
|
|
609
475
|
return String(raw).replace(/\s+/g, ' ').trim();
|
|
610
476
|
}
|
|
611
477
|
|
|
612
|
-
//
|
|
613
|
-
// lost: the full string goes back on the screen above the output, and this is
|
|
614
|
-
// the function that says whether there is anything to put there.
|
|
478
|
+
// The whole string, shown above the output when a line is opened.
|
|
615
479
|
export function fullSubject(input) {
|
|
616
480
|
const i = input ?? {};
|
|
617
481
|
const raw = i.command ?? i.file_path ?? i.path ?? i.notebook_path
|
|
@@ -619,10 +483,8 @@ export function fullSubject(input) {
|
|
|
619
483
|
return String(raw).replace(/\s+$/, '');
|
|
620
484
|
}
|
|
621
485
|
|
|
622
|
-
|
|
623
|
-
//
|
|
624
|
-
// Two do not, because two clean lines say more than a fold does and 42 of her
|
|
625
|
-
// 162 runs are exactly two.
|
|
486
|
+
// Three or more consecutive tool calls fold into one line. Two stay as two
|
|
487
|
+
// lines, which say more than a fold does.
|
|
626
488
|
export const RUN_MIN = 3;
|
|
627
489
|
|
|
628
490
|
export function groupWork(events, { min = RUN_MIN } = {}) {
|
|
@@ -635,8 +497,8 @@ export function groupWork(events, { min = RUN_MIN } = {}) {
|
|
|
635
497
|
run = [];
|
|
636
498
|
};
|
|
637
499
|
for (const e of events ?? []) {
|
|
638
|
-
//
|
|
639
|
-
// snoozes are three lines
|
|
500
|
+
// What the person did is not the agent's work and never folds into it:
|
|
501
|
+
// three snoozes are three lines.
|
|
640
502
|
if (e && e.kind === 'work' && !e.yours && !e.standalone) { run.push(e); continue; }
|
|
641
503
|
flush();
|
|
642
504
|
out.push(e);
|
|
@@ -645,22 +507,15 @@ export function groupWork(events, { min = RUN_MIN } = {}) {
|
|
|
645
507
|
return out;
|
|
646
508
|
}
|
|
647
509
|
|
|
648
|
-
//
|
|
649
|
-
//
|
|
650
|
-
// codebase is a total that was rounded off without saying so.
|
|
510
|
+
// The folded line says the total first, then up to three verbs with their
|
|
511
|
+
// counts and one count for the rest:
|
|
651
512
|
//
|
|
652
|
-
// one verb
|
|
653
|
-
// one verb
|
|
654
|
-
// several
|
|
513
|
+
// one verb with a plain word `20 things it ran`
|
|
514
|
+
// one verb without `31 things it did · write html 31`
|
|
515
|
+
// several `24 things it did · ran 18 · read 4 · changed 2`
|
|
655
516
|
//
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
//
|
|
659
|
-
// A COUNT OF ONE IS NOT PRINTED. Drawn over her own threads on 2026-08-20 the
|
|
660
|
-
// first cut read `8 things it did · ran 5 · read 2 · asked a question 1`, and
|
|
661
|
-
// that trailing 1 reads as part of the name rather than as a number. A bare
|
|
662
|
-
// verb already says once.
|
|
663
|
-
const PLAIN = new Set(['ran', 'read', 'wrote', 'edited', 'looked for', 'searched for', 'fetched', 'messaged']);
|
|
517
|
+
// A count of one is not printed, because a trailing 1 reads as part of the name.
|
|
518
|
+
const PLAIN = new Set(['ran', 'read', 'wrote', 'changed', 'looked for', 'searched for', 'fetched', 'messaged']);
|
|
664
519
|
const TALLY_VERBS = 3;
|
|
665
520
|
|
|
666
521
|
export function runSummary(items) {
|
|
@@ -679,22 +534,17 @@ export function runSummary(items) {
|
|
|
679
534
|
return `${list.length} things it did · ${shown.join(' · ')}`;
|
|
680
535
|
}
|
|
681
536
|
|
|
682
|
-
//
|
|
683
|
-
//
|
|
537
|
+
// A failure inside a fold is said on the closed line, so nobody has to open it
|
|
538
|
+
// to find bad news.
|
|
684
539
|
export function runFailures(items) {
|
|
685
540
|
const bad = (items ?? []).filter((e) => e && e.failed).length;
|
|
686
541
|
if (!bad) return '';
|
|
687
542
|
return `${bad} failed`;
|
|
688
543
|
}
|
|
689
544
|
|
|
690
|
-
|
|
691
|
-
//
|
|
692
|
-
//
|
|
693
|
-
//
|
|
694
|
-
// The same four tools main/code-change.mjs reads, and they have to stay the
|
|
695
|
-
// same four: one list saying which calls become a diff and another saying which
|
|
696
|
-
// lines become a chip is two lists that drift, and the symptom of the drift is
|
|
697
|
-
// a chip that opens onto a file the change does not hold.
|
|
545
|
+
// The tools whose file the thread shows as a chip into the change. These must
|
|
546
|
+
// be the same tools main/code-change.mjs reads, or a chip opens onto a file the
|
|
547
|
+
// change does not hold.
|
|
698
548
|
export const CHANGES_A_FILE = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
|
|
699
549
|
|
|
700
550
|
/**
|
|
@@ -711,15 +561,13 @@ export function changedFile(name, input) {
|
|
|
711
561
|
/**
|
|
712
562
|
* Which entry of a change a path on a work line is, or null.
|
|
713
563
|
*
|
|
714
|
-
* A transcript records the
|
|
715
|
-
* relative to one of the run's roots when it sits under one, and
|
|
716
|
-
*
|
|
717
|
-
*
|
|
718
|
-
* whole string or a suffix of it at a segment boundary.
|
|
564
|
+
* A transcript records the absolute path it wrote. A change records the path
|
|
565
|
+
* relative to one of the run's roots when it sits under one, and absolute when
|
|
566
|
+
* not (main/code-change.mjs). So a key matches when it is the whole path or a
|
|
567
|
+
* suffix of it at a segment boundary.
|
|
719
568
|
*
|
|
720
|
-
* The
|
|
721
|
-
*
|
|
722
|
-
* first match would open whichever the change happened to list first.
|
|
569
|
+
* The longest match wins: `src/a.ts` and `renderer/src/a.ts` are both suffixes
|
|
570
|
+
* of the second one's absolute path.
|
|
723
571
|
*/
|
|
724
572
|
export function fileInChange(paths, raw) {
|
|
725
573
|
const p = String(raw ?? '').trim().replace(/^\/private\/tmp\//, '/tmp/');
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ao as r,ap as n}from"./mermaid.core-CnYzvFkz.js";const t=(a,o)=>r.lang.round(n.parse(a)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--ct-ink: #1d1d1f;--ct-dim: rgba(29, 29, 31, .68);--ct-faint: rgba(29, 29, 31, .48);--ct-mark: rgba(29, 29, 31, .42);--ct-tint: rgba(243, 244, 247, .95);--ct-tint-hover: rgba(249, 249, 251, .97);--ct-edge: inset 0 0 0 .5px rgba(0, 0, 0, .15);--ct-rule: rgba(29, 29, 31, .1);--ct-press: rgba(29, 29, 31, .06)}@media (prefers-color-scheme: dark){:root:not([data-theme=light]){--ct-ink: rgba(255, 255, 255, .86);--ct-dim: rgba(255, 255, 255, .62);--ct-faint: rgba(255, 255, 255, .44);--ct-mark: rgba(255, 255, 255, .42);--ct-tint: rgba(39, 39, 43, .95);--ct-tint-hover: rgba(50, 50, 55, .96);--ct-edge: inset 0 0 0 .5px rgba(0, 0, 0, .6), inset 0 0 0 1.5px rgba(255, 255, 255, .13);--ct-rule: rgba(255, 255, 255, .1);--ct-press: rgba(255, 255, 255, .07)}}:root[data-theme=dark]{--ct-ink: rgba(255, 255, 255, .86);--ct-dim: rgba(255, 255, 255, .62);--ct-faint: rgba(255, 255, 255, .44);--ct-mark: rgba(255, 255, 255, .42);--ct-tint: rgba(39, 39, 43, .95);--ct-tint-hover: rgba(50, 50, 55, .96);--ct-edge: inset 0 0 0 .5px rgba(0, 0, 0, .6), inset 0 0 0 1.5px rgba(255, 255, 255, .13);--ct-rule: rgba(255, 255, 255, .1);--ct-press: rgba(255, 255, 255, .07)}html,body,#root{margin:0;padding:0;background:transparent;overflow:hidden;height:100%}body{font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,sans-serif;-webkit-font-smoothing:antialiased;-webkit-user-select:none;user-select:none;cursor:default;color:var(--ct-ink)}.ct-glass{position:absolute;left:0;top:0;color:var(--ct-ink);background:var(--ct-tint);-webkit-backdrop-filter:blur(28px) saturate(180%);backdrop-filter:blur(28px) saturate(180%);box-shadow:var(--ct-edge)}.ct-tag{height:28px;padding:0 12px 0 10px;border-radius:6px;box-sizing:border-box;display:flex;align-items:center;gap:7px;font-size:12.5px;white-space:nowrap;transition:background-color .12s ease}.ct-tag .ct-words{font-weight:600}.ct-tag.working .ct-words{font-weight:500;color:var(--ct-dim)}.ct-tag:hover,.ct-tag.is-open{background:var(--ct-tint-hover)}.ct-tag.is-dragging{cursor:grabbing}.ct-go{display:flex;align-items:center;gap:8px;margin-left:1px;color:var(--ct-dim)}.ct-go:before{content:"";width:1px;height:14px;background:var(--ct-rule)}.ct-tag{padding-right:9px}.ct-go svg{display:block}.ct-dot{width:8px;height:8px;border-radius:50%;flex:none;background:radial-gradient(circle at 32% 28%,#fff 0,#fff,#f8eef6 48%,#e7ecf6,#d3d9e7);box-shadow:0 0 0 .5px #3c465a14,0 0 8px 2px #e8d0f0d9}@media (prefers-color-scheme: dark){:root:not([data-theme=light]) .ct-dot{width:7px;height:7px;background:#fff;box-shadow:0 0 6px #ffffff8c}}:root[data-theme=dark] .ct-dot{width:7px;height:7px;background:#fff;box-shadow:0 0 6px #ffffff8c}.ct-half{flex:none;display:block;color:var(--ct-mark)}.ct-card{border-radius:8px;padding:9px 8px 8px;box-sizing:border-box;width:max-content;max-width:440px;min-width:240px}.ct-line{all:unset;box-sizing:border-box;display:flex;align-items:center;gap:9px;width:100%;font-size:13px;white-space:nowrap;padding:5px 6px;border-radius:4px;cursor:pointer;color:var(--ct-ink)}.ct-line:hover{background:var(--ct-press)}.ct-line .ct-name{font-weight:600;overflow:hidden;text-overflow:ellipsis;max-width:220px}.ct-line .ct-says{color:var(--ct-dim)}.ct-line .ct-when{margin-left:auto;padding-left:14px;font-size:12px;color:var(--ct-faint)}.ct-quiet{font-size:12.5px;color:var(--ct-faint)}.ct-line.ct-quiet{color:var(--ct-faint)}.ct-static{padding:5px 6px}.ct-mark-sp{width:8px;flex:none}.ct-foot{display:flex;align-items:center;gap:2px;margin:6px 0 0;padding:7px 6px 0;border-top:1px solid var(--ct-rule);font-size:11.5px;color:var(--ct-faint);white-space:nowrap}.ct-foot>span:first-child{margin-right:2px}.ct-foot button{all:unset;cursor:pointer;padding:2px 6px;border-radius:5px;color:var(--ct-dim)}.ct-foot button:hover{background:var(--ct-press);color:var(--ct-ink)}.ct-foot .ct-sep{flex:1;min-width:16px}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as C,j as n,r as c,N as L}from"./product-name-CTKLYNWY.js";const P=[{key:"5m",label:"5 min"},{key:"30m",label:"30 min"},{key:"today",label:"Today"}];function S({ready:t=0,working:r=0}={}){return t>0?{kind:"ready",text:`${t} waiting`}:r>0?{kind:"working",text:`${r} working`}:null}function $(t,r){return[...t??[]].sort((o,s)=>(Number.isFinite(s.since)?s.since:r)-(Number.isFinite(o.since)?o.since:r))}function R(t,r){if(!Number.isFinite(t))return"";const o=Math.floor((r-t)/6e4);return o<1?"now":o<60?`${o} min`:`${Math.floor(o/60)} h`}function D(t,r,o=3){const s=$(t,r),d=s.slice(0,o).map(l=>({...l,waited:R(l.since,r)}));return{lines:d,more:Math.max(0,s.length-d.length)}}const e=window.cornerTag??null,m=new URLSearchParams(location.search),w=m.get("part")==="card"?"card":"tag";function T(){const t=m.get("demo");if(!t)return null;const r=Date.now(),o=Number(m.get("n")??2),s=["Pricing page","Login redirect","Onboarding emails","A much longer thread title that will not fit on one line","Settings page","Competitor research","Release notes"];return{state:{ready:t.startsWith("ready")?Array.from({length:o},(l,i)=>({id:`demo-${i}`,title:s[i%s.length],says:i===1?"needs a yes":"is ready for you",since:r-[2,9,25,40,70,95,130][i%7]*6e4})):[],working:t.startsWith("ready")?20-o:20,now:r},open:t.endsWith("-open")}}const u=T(),g=m.get("theme");document.documentElement.classList.toggle("ct-demo",!!u);(g==="dark"||g==="light")&&(document.documentElement.dataset.theme=g);function I(){const[t,r]=c.useState((u==null?void 0:u.state)??{ready:[],working:0,now:Date.now()});return c.useEffect(()=>e==null?void 0:e.onState(o=>r(o)),[]),t}function j({size:t=11}){return n.jsxs("svg",{className:"ct-half",width:t,height:t,viewBox:"0 0 12 12","aria-hidden":!0,children:[n.jsx("circle",{cx:"6",cy:"6",r:"5.2",fill:"none",stroke:"currentColor",strokeWidth:"1.3"}),n.jsx("path",{d:"M6 2.4a3.6 3.6 0 0 1 0 7.2z",fill:"currentColor"})]})}function M(){return n.jsx("span",{className:"ct-go","aria-hidden":!0,children:n.jsx("svg",{width:"7",height:"10",viewBox:"0 0 7 10",children:n.jsx("path",{d:"M1.5 1.5 5 5 1.5 8.5",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})})}function v(t,r,o){c.useLayoutEffect(()=>{const s=t.current;if(!s||!e)return;const d=()=>{const i=s.getBoundingClientRect();e.size(r,i.width,i.height)};d();const l=new ResizeObserver(d);return l.observe(s),()=>l.disconnect()},[o])}function y({state:t,forceOpen:r=!1}){const[o,s]=c.useState(r),[d,l]=c.useState(!1),i=c.useRef(null),h=c.useRef(!1),N={ready:t.ready.length,working:t.working},f=S(N),p=f==null?void 0:f.text;if(c.useEffect(()=>e==null?void 0:e.onOpen(a=>s(!!a)),[]),c.useEffect(()=>e==null?void 0:e.onDragging(a=>l(!!a)),[]),c.useEffect(()=>e==null?void 0:e.onReset(()=>{h.current=!1,l(!1),s(!1)}),[]),c.useEffect(()=>{const a=E=>{E.key==="Escape"&&o&&(e==null||e.toggle())};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[o]),v(i,"tag",p),!f)return null;const x=a=>{h.current&&(h.current=!1,e==null||e.release(a))};return n.jsxs("div",{ref:i,role:"button","aria-label":`${p}. Click to open ${L}, drag to move.`,"aria-expanded":o,className:`ct-glass ct-tag ${f.kind}${o?" is-open":""}${d?" is-dragging":""}`,style:u?{position:"relative"}:void 0,onPointerEnter:()=>e==null?void 0:e.hover("tag",!0),onPointerLeave:()=>{h.current||e==null||e.hover("tag",!1)},onPointerDown:a=>{a.button===0&&(a.currentTarget.setPointerCapture(a.pointerId),h.current=!0,e==null||e.press(a.screenX,a.screenY))},onPointerUp:a=>{x(!1),a.currentTarget.hasPointerCapture(a.pointerId)&&a.currentTarget.releasePointerCapture(a.pointerId)},onPointerCancel:()=>x(!0),onLostPointerCapture:()=>x(!0),onContextMenu:a=>{a.preventDefault(),e==null||e.menu()},children:[f.kind==="ready"?n.jsx("span",{className:"ct-dot"}):n.jsx(j,{}),n.jsx("span",{className:"ct-words",children:p}),n.jsx(M,{})]})}function O({state:t}){const{lines:r,more:o}=D(t.ready,t.now);return n.jsxs(n.Fragment,{children:[r.map(s=>n.jsxs("button",{className:"ct-line",onClick:()=>e==null?void 0:e.go(s.open===void 0?s.id:s.open),children:[n.jsx("span",{className:"ct-dot"}),n.jsx("span",{className:"ct-name",children:s.title}),n.jsx("span",{className:"ct-says",children:s.says}),n.jsx("span",{className:"ct-when",children:s.waited})]},s.id)),o>0&&n.jsxs("button",{className:"ct-line ct-quiet",onClick:()=>e==null?void 0:e.go(null),children:[n.jsx("span",{className:"ct-mark-sp"}),"and ",o," more"]}),!r.length&&n.jsx("div",{className:"ct-quiet ct-static",children:"Nothing is ready for you yet"}),t.working>0&&n.jsxs("button",{className:"ct-line ct-quiet",onClick:()=>e==null?void 0:e.go(null),children:[n.jsx(j,{size:10}),r.length?`${t.working} others working`:`${t.working} working`]}),n.jsxs("div",{className:"ct-foot",children:[n.jsx("span",{children:"Hide for"}),P.map(s=>n.jsx("button",{onClick:()=>e==null?void 0:e.hide(s.key),children:s.label},s.key)),n.jsx("span",{className:"ct-sep"}),n.jsx("button",{className:"ct-off",onClick:()=>e==null?void 0:e.settings(),children:"Turn off…"})]})]})}function k({state:t}){const r=c.useRef(null);return v(r,"card",null),c.useEffect(()=>{const o=s=>{s.key==="Escape"&&(e==null||e.toggle())};return window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[]),n.jsx("div",{ref:r,className:"ct-glass ct-card",style:u?{position:"relative"}:void 0,onPointerEnter:()=>e==null?void 0:e.hover("card",!0),onPointerLeave:()=>e==null?void 0:e.hover("card",!1),children:n.jsx(O,{state:t})})}function B(){const t=I();return c.useEffect(()=>{e==null||e.ready(w)},[]),u?n.jsxs("div",{style:{position:"absolute",right:16,bottom:16,display:"flex",flexDirection:"column",alignItems:"flex-end",gap:8},children:[u.open&&n.jsx(k,{state:t}),n.jsx(y,{state:t,forceOpen:u.open})]}):w==="card"?n.jsx(k,{state:t}):n.jsx(y,{state:t})}C(document.getElementById("root")).render(n.jsx(B,{}));
|