Repository navigation
feat(pi): add the Pi agent type using pi-durable - #1420
Conversation
Ports the Pi plugin from #1103 onto current main as one commit. The plugin files are unchanged apart from following main's renames: - Me chats are Solo chats now: read `soloChat` from the agent's reads, and fake a Solo channel in the runtime test. - New agent's Instructions field is labelled optional. - Keep `buzz.pi` off by default beside `buzz.usage` in the bundled-default checks. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
- Stop on a turn's activity aborts the Pi run it is in; a stopped turn posts no failure. - List each agent's sessions for plugins: its Pi conversations, read from Pi's storage when first asked, with Working or Idle while it runs here. - Hand a Solo chat's Canvas to each turn, and read no earlier messages of a chat its owner is still creating, as Claude and goose do. - Give the Buzz tools the community's relay, audience and workflow authorities, as the other harnesses do. - Tag Pi as a coding agent in the plugin catalog. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pins @earendil-works/pi-durable, pi-ai and chord 1.3.0 (from 1.1.0) and follows the changes since: - A tool result's text for the model is `output` now, not `content`; with `content`, Pi showed the model an empty result for every Buzz tool. - Storage that fails now closes its Harness for good, so the host drops it and opens the agent again on the next request. The live host test now checks that the model reads what a Buzz tool answered, and that aborting a conversation leaves its request unanswered as `aborted`, which the runtime's Stop relies on. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
The Buzz tools Claude and Codex load on search, the attention tools and those plugins add, are deferred in Pi too: each agent's registry has Pi durable's `tool_search`, which loads them when the model looks for them, and they stay loaded in that conversation. The Buzz tools section says what the search can find, as Claude's server instructions do. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…fely From review: - A wait that Pi's failed storage cut off now waits again once the agent is opened again, where the run continues, instead of failing the turn while the run goes on. - A new conversation is listed, and subscribers told, as its first turn is handed over rather than once it ends. - Listing sessions opens only the storage of an agent running here, and an agent's storage closes when it stops running here even if it only listed. - The README opens with the short Getting started guide every bundled plugin's has. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62fec43495
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi's install logic moves from bash into launch.mjs, plain Node, so it runs the same everywhere. On Mac and Linux pi.sh only finds Node, as before, then starts the launcher; Windows starts `node` itself. The host source and the lock go on stdin, since together they pass Windows' 32 KB command line. File uploads read through `host.files` instead of `base64`, so the Mac and Linux gate goes. npm now runs inside the install folder: npm 11 refuses `ci --prefix` to a folder outside where it runs. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…ive a host stopping mid-handover A mention queued behind another's handover no longer opens the agent's storage or reaches Pi once the agent is removed, retyped or disconnected; Pi's coding tools have no sandbox. Storage the handover opened again closes. A host that stops before it answers `deliver` may already have the turn. The runtime asks the next host again with the same request, which Pi durable finds by its request ID rather than taking twice. The host records a new conversation for its key before submitting to it, so the next host finds that conversation. Removes docs/view-pi-session.md, which describes the retired buzz-pi-acp integration, not Pi durable's storage. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes's GitHub account.
Not ready to merge until the two medium findings are fixed: Node discovery misses nvm/fnm users, and Pi usage undercounts while reporting no gap. The resumed-transcript finding is lower severity. The CreateModel accessibility note is optional.
Checked at head 3e771044 against base main. The branch merges cleanly with current main (738b29ee): tsc --noEmit is clean and vitest run src/bundled/pi passes (52 passed, 1 live test skipped). Tool authorities match the other harnesses, and main's new owner/gitAuth arrive through appClient. The disabled-by-default gating agrees in the TS registry, the Rust catalog and both tests. The lockfile pins integrity hashes for all 95 packages and installs without lifecycle scripts.
Validation gaps (not run here): storage-failure reopen, the host dying during handover, and Stop with queued steering are covered only against the fake host. Linux and Windows are source-reviewed only.
|
🤖 [P2] Storage failure before wait registration abandons an accepted turn — host.mjs:714 Revalidated at Preserve a typed reopen outcome for late waiters after storage failure, distinguish intentional removal, and retain bounded retry. Add failure-before-wait coverage; the existing fake-host test supplies |
|
🤖 [P2] Transcript storage is readable by other local accounts when ancestors permit traversal — host.mjs:267 Revalidated at Another local account with ancestor traversal can therefore read plaintext transcripts. A private ancestor prevents exposure; this check did not execute as a second account. Enforce |
|
🤖 [P2] Durable runs resume after restart without runtime ownership — runtime.ts:312, host.mjs:264 Revalidated at Reconcile unfinished submissions with routing/receipts, activity and Stop controls, and standalone deadlines before driving progress. Add restart-plus-cross-thread coverage. This concerns graceful dispose/restart, separate from the repaired delivery-response crash window. |
- Finds Node set up in an interactive shell's rc file, such as nvm's or fnm's in .zshrc, taking its path from behind a marker past what the rc prints. - Usage adds what Pi durable recorded beyond the answers, such as compaction requests, as one more request per model; the host reads the totals. - A run resumed after an interruption replaces its partial answer and no longer shows as stopped; an owner's Stop still does. - The model field's button says it is the model and opens a dialog. - A wait after an agent's storage failed is told to open it again, so the runtime's retry finds the accepted turn rather than dropping it. - An agent's storage folder is 0700 and its database 0600, existing ones too; SQLite gives its -wal and -shm files the database's mode. - Runs an earlier app left unfinished are taken on at the first turn after it starts: they show as working, and their answer or failure is posted. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
|
🤖 Re: [P2] Storage failure before wait registration. Fixed in e0a814e. The host now remembers an agent whose storage failed. A |
|
🤖 Re: [P2] Transcript storage readable by other local accounts. Fixed in e0a814e. Before the storage opens, the host sets the agents folder to |
|
🤖 Re: [P2] Durable runs resume after restart without runtime ownership. Fixed in e0a814e.
|
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis and Groot lanes.
Follow-up review: head e0a814e3e55ccacd146b506bba0b749b6f737f65, base dbb0a9de8016a04b6eb79a3edc4a0d6964bc9d25; changes since previously reviewed 3e771044ec36b4eb49df90fab3256fcdb94482c8. Two new P2 findings are inline. Source assessment: 8/10 until those routing and usage-period defects are addressed.
The previous Node-discovery and resumed-transcript findings, and optional model-button accessibility note, are addressed in source. Watch/timer usage exclusion remains explicitly author-retained in the existing discussion; this review does not reopen it.
Validation: source-only; no tests, builds, app runs, dependency installation or live-provider/restart exercises performed. One hosted CI snapshot showed CI required and WebKit 2/12 failing, with Windows validation skipped; cause not reproduced. Native lifecycle, performance, rendered error/retry focus, themes and assistive-technology behavior remain unverified. Inspected the public PR description (no attached images) and follow-up commit metadata; no disclosure finding. Unchanged feature areas were not exhaustively re-audited. This COMMENT is not approval or merge authorization.
loganj
left a comment
There was a problem hiding this comment.
🤖 Larry, an automated reviewer working for Logan.
I reviewed head e0a814e3e from the source. I read the launcher (pi.sh, launch.mjs), the stdio client (pi.ts), the host (host.mjs), runtime.ts, setup.ts, and the setup and Activity parts of tabs.tsx. I did not repeat the open findings from Codex, Carl or Star Lord. I found two new issues and put them inline. Neither one puts saved data or startup at risk.
- P2: Setup tells the user to sign in with
pi's/loginand then pick the provider in Buzz. Buzz keeps the provider list until it restarts, so it still shows the provider as not set up after the login. - P3: The host runs a
!commandAPI key withexecSync, and one host process serves every Pi agent. While the command runs, every agent waits, for up to 10 s each time it runs. A command that returns no key runs again on the next read.
I also checked these and found no problem: the install folder named by the hash of the package and lock files; npm ci --ignore-scripts with integrity for all 95 packages and no resolved hosts, so the user's registry is used; the rename that keeps two installs from colliding; the stdin handoff past the 32 KB Windows command line; the owner/delegate check before a mention runs Pi's unsandboxed tools; and the guard that lets only a running agent open its storage. The Activity tab can only appear for an agent that runs here, because agent.place === "elsewhere" gets no type tabs.
What I did not run: tests, a build, the app, or the live host.
…n no key commands - A run adopted after a restart, in a conversation several threads share, answers in its mention's thread: the mention is read to find its root. - Setup reads Pi's providers and defaults again each time and when the window gets focus, so a /login or default chosen with the pi command shows. - The host no longer runs `!command` keys from auth.json. One host serves every agent, so a command held them all; such a key's turns now fail with a message to enter the key in Buzz. Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
… name Buzz runs as a desktop app without the shell's environment, so a `$NAME` key in Pi's auth.json fails its turn with the same message as a `!command` key. Main's activity layout names the opened conversation's button `View transcript: <channel>`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis and Groot lanes.
Follow-up at head 1a708fb791cdc74816646ab4f90f5cbd11e41ebb, base 683733abbe465c729b0ea98a6e4f580bd8dd8ab0, since reviewed e0a814e3e55ccacd146b506bba0b749b6f737f65. One new P2 recovery-ordering finding is inline. Source assessment: 8/10 until the send baseline is captured before recovered work resumes.
Provider/default refresh and rejection of command/environment keys are addressed in source. The prior compaction-date limitation remains explicitly author-deferred in the existing discussion; this review does not reopen it.
Source-only: no tests, builds, installs, app runs or live-provider/restart exercises performed; CI was not assessed. Native recovery timing, performance, rendered focus/retry behavior, themes and assistive technology remain unverified. Inspected the public PR description (no attached images) and follow-up commit metadata; no disclosure finding. Unchanged feature areas were not exhaustively re-audited. This COMMENT is not approval or merge authorization.
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes's GitHub account.
Follow-up at head 1a708fb791cdc74816646ab4f90f5cbd11e41ebb, base 683733abbe465c729b0ea98a6e4f580bd8dd8ab0. It merges cleanly with current main (9676a38d). This round integrates independent Court lanes: install/credentials, runtime lifecycle, user-facing flows, integration, and a challenge pass.
Merge criteria: the open recovery-baseline P2 in the existing thread, which we independently confirmed against pi-durable 1.3.0 (Submissions.wait resumes the Harness-wide scheduler), plus the new P2 inline on auth.json reads. The four P3s are optional, non-gating.
The $NAME/!command rejection in the latest commit matches pi-coding-agent 1.3.0's resolver for whole-value references. Node discovery, the stdin handoff, the --ignore-scripts install with integrity hashes, and owner/delegate admission before unsandboxed tools all look right in source.
Validation gaps: the auth.json race was reproduced with a local probe on this head's AuthFile, real filesystem and pi-ai 1.3.0. That rate is stress evidence, not expected production frequency. The Stop-display gap was reproduced with a scratch piTurns run. Everything else is source review. Recovery ordering, Linux and Windows are not exercised at runtime. Local probes only, no broad suites rerun; hosted CI is green at this head. This COMMENT is not approval.
… waiting - Credential reads take the same lock as writes and `pi`, so a key save or refresh can't hand another agent's request a half-written auth.json. - Adoption resolves every left run's thread and send baseline before any wait, since a wait continues all of Pi's runs and one could send first. - Pi agents get the community repositories folder, as the other types do. - Point the no-model and package-provider errors at the Identity tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
A paused host whose stale lock another Buzz build takes over went on running the agent beside it. It now closes that agent's storage, which records no outcome for its runs, so the new owner continues them; opening again here says the agent is open in another Buzz app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…ntinue them Any delivery's wait continues every run in an agent's storage, so a watch's or timer's run left when the app quit resumed later with no deadline, lane or Stop. Adoption now ends those before any wait, as quitting ends other harnesses' turns, and leaves this app's own standalone turns alone when it lists left runs again. Recovered mentions still continue; their Stop is a follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis and Groot lanes.
Follow-up at head 1847369cef214ca3e16a3e2d4e525ad5ae1679d7, base 683733abbe465c729b0ea98a6e4f580bd8dd8ab0, focused on the repair delta since 1a708fb791cdc74816646ab4f90f5cbd11e41ebb. One P2 is inline: dropping a placed one-off resumes the shared scheduler before later recovered mentions have their send baselines, reopening the duplicate-fallback race. Source assessment: 8/10 until recovery preparation precedes every resume path.
Credential reads now use the shared auth-file lock; repository-context wiring and Identity-tab guidance are addressed in source. The explicit-wait ordering is repaired, but cancellation supplies the additional resume path described inline. The existing author-retained usage and recovered-Stop limitations are not reopened.
Validation: source only, including published Pi 1.3.0 dependency source; no tests, builds, installs, app runs or live-provider/restart exercises performed. CI was not assessed. Native lock takeover, filesystem behavior, performance, rendered error/retry focus, accessibility and human acceptance remain unverified. Inspected the public description (no attached images) and follow-up commit material; no disclosure finding. Unchanged feature areas were not exhaustively re-audited. This COMMENT is not approval or merge authorization.
Keeps both Pi and relay notes in the plugins that start disabled. Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aborting a conversation resumes Pi's whole scheduler, so ending a left watch or timer run could let a mention left beside it send before its thread and sends were counted, or a sibling one-off run first. The host's drop now takes every left one-off at once and only withdraws queued requests and marks placed runs aborted, which continues nothing. Adoption asks for it after every mention's sends are counted and before any wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis, and Groot lanes.
No changes requested in this follow-up. The prior scheduler-resume finding is addressed in source: host.mjs:764–780 withdraws queued one-offs and marks placed runs aborted without resuming Pi; runtime.ts:712–786 captures all recovered mentions’ routes and send baselines before the batched drop and subsequent waits. The Pi durable 1.3.0 source confirms these cancellation primitives do not enable scheduling and preserve cancellation of owned foreground work. The new regression cases cover two abandoned one-offs beside a recovered mention and delayed route lookup. No new actionable defect found. Assessment: 9/10 for minimalness, elegance, and correctness within this repair scope.
Scope: changes since 1847369cef214ca3e16a3e2d4e525ad5ae1679d7, including the four-file repair and merge integration preserving Pi and relay-notes as disabled by default. Public PR description and follow-up commit metadata inspected; no attached images or disclosure finding. Unchanged feature areas and unrelated upstream changes were not freshly audited.
Reviewed head: 26abc3ad52c18df9e23035b762cf3ffe7e9e2ebe.
Base: 2adcea400c546e266a87a5f7f1c971272ac71e5a.
Source-only: tests were read, not run; no builds, app launches, live recovery/provider/filesystem checks, CI assessment, performance measurements, rendered focus/accessibility checks, or human acceptance validation. Author-reported test results remain unverified. This COMMENT is not approval or merge authorization.
Picks up the Usage plugin name fix for the catalog scroll browser test. Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis, and Groot lanes.
No changes requested in this merge-only follow-up. The Pi feature patch is byte-identical to the previously reviewed head 26abc3ad52c18df9e23035b762cf3ffe7e9e2ebe, including the recovery-ordering repair. The 19-file endpoint delta imports the plugin-notes API and the Settings test label correction from main. No new actionable integration defect found. Assessment: 9/10 for minimalness, elegance, and correctness within this follow-up scope.
Optional integration follow-up: Pi currently does not consume AgentHandle.notes (src/bundled/pi/runtime.ts:391–405,902–904), although AgentRunners.note can accept a note for a running Pi agent. A plugin using that API would receive true but its note would not reach Pi’s prompt. We found no production producer of these notes in the pinned repository, so this is recorded as a limitation rather than requiring speculative adapter parity. When a concrete caller is introduced, consume notes at delivery with the conversation’s root policy, preserving them on definite non-delivery; alternatively return false while unsupported. Host-restart responses can be ambiguous, so restoration must not blindly duplicate already accepted input.
Reviewed head: 7bb1878296d08c46d5e607220223adcd9a43956e.
Base: 3d26215564dde0f670b8707de49f822a36a0120f.
Source-only: no tests, builds, app launches, installs, CI assessment, live provider/recovery checks, performance measurements, rendered focus/accessibility checks, or human acceptance validation. Public PR description and follow-up commit metadata inspected; no attached images or disclosure finding. Prior accepted/deferred decisions remain unchanged. Unrelated upstream behavior and unchanged feature areas were not exhaustively re-audited. This COMMENT is not approval or merge authorization.
The merge of main brought back the export that #1420 added for Pi, which now has its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi came back as an agent type in #1420, so list it alongside Claude, Codex and Goose, and keep it in the adapter list in docs/agents.md. Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bradley Axen <baxen@squareup.com>

Adds Pi as an agent type: agents that run on Pi durable on this computer, with any provider Pi supports. It ships turned off until enabled in Settings → Plugins, on macOS, Linux and Windows.
This replaces #1103. That branch was 99 commits behind
main, and the agent-type API had moved on since. The first commit ports itssrc/bundled/pi/as is. The commits after it bring Pi up to date.What changed
05c77e8e8): the Pi plugin, its host (host.mjs, run bypi.shon this computer's Node), and its registration. The only fixes needed were main's renames: Me chat → Solo chat, and the now-optional Instructions label.20964bd50):sessionslists each agent's Pi conversations for plugins.5bb2898c1): pi-durable, pi-ai and chord move from 1.1.0 to 1.3.0.contenttooutput. On 1.3.0 every Buzz tool result reached the model empty until this was fixed.753e75077): Buzz tools that Claude and Codex load on search (the attention tools and those plugins add) aredeferred: truein Pi. Each agent's registry has pi-durable'stool_search, which loads them when the model looks for them.62fec4349):63be0e3dd), now that feat(plugins): native file reads so Goose agents run on Windows #1378 is merged:launch.mjs, plain Node. On Mac and Linux,pi.shnow only finds Node (app PATH, then a login shell) and starts the launcher. Windows startsnodedirectly.host.filesinstead ofbase64, so the Mac/Linux gate is gone.npm ci --prefixpointing outside where it runs. This was already broken in the oldpi.sh.%LOCALAPPDATA%\Buzz\pi. Pi'sbashtool needs Git Bash there.Testing
bin/pnpm exec tsc --noEmitis clean.bin/pnpm exec vitest run src/bundled/pi: 47 tests pass.PI_DEPS=<folder pi.sh installed> bin/pnpm exec vitest run src/bundled/pi/host.live.test.ts. It now also checks three things, and each check fails when its change is reverted:abortedtool_searchfinds itcargo test -p buzzodz-plugins --test management bundled_defaultspasses.vitest runpasses, apart fromcodex-sh.test.tsandbrowser-host/project-git.test.mjs. Those failed under load, pass on their own, and are untouched here.Follow-up PR
~/.pi/agent/settings.json.usagehook as Claude, Codex and goose do.Known gaps
icon.png; it needs artwork.pi.sh, and asnode -ealone as on Windows.🤖 Generated with Claude Code