You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: add Claude-oriented process tools (Bash, Monitor) on the existing extension-protocol surface
Note
Automated check by Pi, model aperture/neuralwatt/kimi-k3-flex. Prepared against the current
codebase, aliou/pi-processes on branch main @ 0.12.0. See the local run log for the full investigation.
Re-scopes and replaces #81 against main @ 0.12.0. The original issue assumed protocol plumbing that has since
landed: COMMAND_START, COMMAND_ADOPT, typed payloads, per-line log subscriptions, and a
notification pipeline with exactly the 20-per-minute throttle + suppression summary + completion
delivery the original design asked for. What remains is one small core change (notify config
through the start/adopt channels) plus the claude-processes extension itself.
Goal
Add a claude-processes Pi extension that provides Claude-oriented process tools while using the
existing pi-processes manager, logs, lifecycle handling, and /ps UI.
Initial tools:
Bash
Monitor
Future companions:
TaskStop
TaskOutput
TaskList
Current foundation (main @ 0.12.0)
What already exists, with the paths as of today:
Start/kill/clear protocol: CHANNELS.COMMAND_START, COMMAND_KILL, COMMAND_CLEAR in extensions/shared/protocol/channels.ts, with typed payloads in extensions/shared/protocol/commands.ts. CommandStartResult carries ProcessInfo, which already includes stdoutFile / stderrFile
log paths (src/types.ts). Handlers live in extensions/processes/handlers/commands.ts.
Adopt protocol: COMMAND_ADOPT hands an externally spawned (detached, piped-stdio) ChildProcess to the manager along with pre-handover stdout/stderr. examples/bash-background.ts is a complete worked example of a bash tool override built on it
(foreground run, background handoff on shortcut/timeout/2 min).
Typed client pattern: extensions/processes/client.ts (requestStart, requestKill with an
absent-listener safety timeout, requestCombinedOutput, requestClear, requestPin). extensions/processes-logs/client.ts and extensions/processes-dock/client.ts show how a separate extension ships its own client over the same bus. claude-processes follows that
pattern; it must not import src/manager or extensions/processes/* internals.
(getManager() from the original issue no longer exists — core constructs the single ProcessManager in extensions/processes/index.ts.)
Per-line streaming: LOGS_SUBSCRIBE / LOGS_UNSUBSCRIBE / LOGS_CHUNK
(extensions/shared/protocol/logs.ts, extensions/processes/handlers/subscriptions.ts) deliver
an initial tail plus appended { type: "stdout" | "stderr", text } lines to a subscriberId,
auto-removed on process end. The dock follow-overlay consumes this today.
Lifecycle + log-match notifications: extensions/processes/notifications/service.ts
classifies ends and evaluates log matchers; extensions/processes/handlers/notifications.ts
caps log_match delivery at 20 per 60 s window (shared across processes), flushing one log_match_suppressed summary at context attention per window. Semantics (forced display for
crash/failure, intentional-stop bypass, attention defaults) are documented in docs/notifications.md.
Lifecycle broadcasts: STARTED / ENDED / OUTPUT_CHANGED on pi.events
(extensions/shared/protocol/broadcasts.ts) for external listeners.
Gap analysis
One core change unlocks the rest:
Notify config through the protocol. Both command handlers call notifications.register(info.id, {}), so every protocol-started process gets default lifecycle
notifications (success/failure → turn, killed → context). A foreground Bash tool that returns
its own result would produce a duplicate notification — the problem #81 solved with "process-owner
metadata." Today the same suppression is expressible as per-process attention ignore, but no
protocol channel accepts notify config for externally started processes; only the in-process process tool does (tools/start, tools/update).
Proposed minimal change:
Add optional notify to CommandStartPayload and CommandAdoptPayload, typed as a
protocol-safe mirror of NotifyConfig in extensions/shared/protocol/.
In handlers/commands.ts, normalize and register the payload's notify config instead of {},
reusing the validation semantics in extensions/processes/tools/notify.ts (reject empty/
whitespace-only patterns, validate regexes).
Document in docs/notifications.md that ignore is force-downgraded to context for
crash/failure, so a crashed foreground Bash still leaves a context-level trace.
Optionally add a source?: string field (e.g. "claude") to the start payload so future task
companion tools can filter their own processes out of the global list without owner semantics.
Design
claude-processes extension
→ pi.events protocol (CHANNELS from extensions/shared/protocol)
→ processes core extension (handlers in extensions/processes/handlers/*)
→ single ProcessManager (src/manager)
→ logs, /ps UI, lifecycle + log-match notifications
Bash
Start via COMMAND_START for both modes so the process is visible and controllable through /ps
from the start (the adopt-pattern keeps a foreground run invisible until handoff, which conflicts
with the /ps visibility requirement).
Foreground: subscribe to CHANNELS.ENDED filtered by id (with a reply-timeout safety net),
read the result via REQUEST_COMBINED_OUTPUT, and suppress the core lifecycle notification via notify: { onSuccess: "ignore", onFailure: "ignore", onKilled: "ignore" } (failure/crash still
lands as context — see gap analysis). Non-zero exit, timeout (own timer → COMMAND_KILL), and
abort are tool errors containing the output tail.
Background: return immediately with the opaque id and the stdoutFile/stderrFile paths;
completion arrives via the core lifecycle notification with default attention.
Accept dangerouslyDisableSandbox as a compatibility no-op.
Interplay: core's background-blocker hook (interception.blockBackgroundCommands) targets the
built-in bash tool by name; decide and document what happens when claude-processes also
registers a bash tool.
Monitor
Two viable builds; pick during implementation:
(a) Matcher-based (preferred): start with notify carrying logMatches: [{ pattern: ".", mode: "regex", repeat: true }]. Each stdout/stderr line becomes a log_match notification through the existing pipeline — inheriting the 20/minute cap, the
suppression summary, classification, and persistence — with the completion delivered by the core
lifecycle notification. Trade-off: notification wording is the generic core phrasing, not a
"this is monitor output, not user input" frame.
(b) Subscription-based: LOGS_SUBSCRIBE and turn LOGS_CHUNK lines into custom
notifications in the extension (full wording control, explicit "not user input" framing), at the
cost of implementing a per-monitor throttle locally. Final completion handled by subscribing to CHANNELS.ENDED.
Persistent monitors run until stopped or session shutdown; non-persistent ones use an extension
side timer + COMMAND_KILL, which is classified as an intentional stop (attention per notify
config, never a spurious failure).
Task companions (future)
TaskList = REQUEST_LIST (+ source filter if added).
TaskWrite (stdin) has no protocol channel today — the process tool's write action is
in-process only. Defer or add a COMMAND_WRITE channel as a separate change.
Constraints
One process manager per session; claude-processes never imports src/manager.
All Claude-oriented processes remain visible and controllable through /ps.
Session shutdown remains owned by the core extension (hooks/cleanup.ts).
Process IDs remain opaque.
No duplicate lifecycle messages for foreground Bash or Monitor (via notify config, not owner
metadata).
New core-side user-visible strings go through the i18n layer (extensions/processes/i18n).
Work breakdown
Add optional notify (+ optional source) to CommandStartPayload / CommandAdoptPayload;
normalize in handlers/commands.ts; extend extensions/shared/protocol/ types.
Create extensions/claude-processes/ with its own typed client (copy the requestStart / requestKill / timeout pattern from extensions/processes/client.ts); register it in package.jsonpi.extensions after the core extension.
Implement Bash foreground and background modes.
Implement Monitor (matcher-based first); decide (a) vs (b) with the wording trade-off
documented.
Add custom tool renderers while retaining /ps as the canonical process UI.
Follow-ups: task companion tools; Anthropic-model-aware active-tool management that does not
clobber a user's active-tool selection (nothing in the repo covers this today).
Tests
Unit (patterns in extensions/processes/handlers/commands.test.ts and extensions/processes/client.test.ts):
start/adopt handlers normalize and register payload notify config;
protocol client absent-core reply timeout (pattern already in requestKill/requestPin);
foreground Bash success, failure, abort, and timeout;
background Bash immediate response and completion delivery;
Monitor line delivery via matcher path; intentional-stop classification for timeout kills.
E2E (tests/e2e/** with fixtures.ts):
Bash- and Monitor-started processes appear in /ps;
background completion and per-line Monitor notifications arrive, throttled per docs/notifications.md;
task stop works;
session shutdown kills all Claude-oriented processes.
Verification
pnpm typecheck
pnpm lint
pnpm test
pnpm test:e2e
Documentation
On landing, update README.md, the repo AGENTS.md (structure section — its src/ ... protocol
line is stale since the protocol moved to extensions/shared/protocol/), skills/pi-processes/SKILL.md, docs/notifications.md (notify-via-protocol semantics), and add a Changeset.
Run host: solar-al-khwarizmi · Session: 01a04285-dfda-7dd4-887a-1154983d73d4 · Model: aperture/neuralwatt/kimi-k3-flex
feat: add Claude-oriented process tools (Bash, Monitor) on the existing extension-protocol surface
Note
Automated check by Pi, model
aperture/neuralwatt/kimi-k3-flex. Prepared against the currentcodebase,
aliou/pi-processeson branchmain@ 0.12.0. See the local run log for the full investigation.Supersedes #81.
Summary
Re-scopes and replaces #81 against
main@ 0.12.0. The original issue assumed protocol plumbing that has sincelanded:
COMMAND_START,COMMAND_ADOPT, typed payloads, per-line log subscriptions, and anotification pipeline with exactly the 20-per-minute throttle + suppression summary + completion
delivery the original design asked for. What remains is one small core change (notify config
through the start/adopt channels) plus the
claude-processesextension itself.Goal
Add a
claude-processesPi extension that provides Claude-oriented process tools while using theexisting
pi-processesmanager, logs, lifecycle handling, and/psUI.Initial tools:
BashMonitorFuture companions:
TaskStopTaskOutputTaskListCurrent foundation (main @ 0.12.0)
What already exists, with the paths as of today:
CHANNELS.COMMAND_START,COMMAND_KILL,COMMAND_CLEARinextensions/shared/protocol/channels.ts, with typed payloads inextensions/shared/protocol/commands.ts.CommandStartResultcarriesProcessInfo, which already includesstdoutFile/stderrFilelog paths (
src/types.ts). Handlers live inextensions/processes/handlers/commands.ts.COMMAND_ADOPThands an externally spawned (detached, piped-stdio)ChildProcessto the manager along with pre-handover stdout/stderr.examples/bash-background.tsis a complete worked example of abashtool override built on it(foreground run, background handoff on shortcut/timeout/2 min).
extensions/processes/client.ts(requestStart,requestKillwith anabsent-listener safety timeout,
requestCombinedOutput,requestClear,requestPin).extensions/processes-logs/client.tsandextensions/processes-dock/client.tsshow how aseparate extension ships its own client over the same bus.
claude-processesfollows thatpattern; it must not import
src/managerorextensions/processes/*internals.(
getManager()from the original issue no longer exists — core constructs the singleProcessManagerinextensions/processes/index.ts.)LOGS_SUBSCRIBE/LOGS_UNSUBSCRIBE/LOGS_CHUNK(
extensions/shared/protocol/logs.ts,extensions/processes/handlers/subscriptions.ts) deliveran initial tail plus appended
{ type: "stdout" | "stderr", text }lines to asubscriberId,auto-removed on process end. The dock follow-overlay consumes this today.
extensions/processes/notifications/service.tsclassifies ends and evaluates log matchers;
extensions/processes/handlers/notifications.tscaps
log_matchdelivery at 20 per 60 s window (shared across processes), flushing onelog_match_suppressedsummary atcontextattention per window. Semantics (forced display forcrash/failure, intentional-stop bypass, attention defaults) are documented in
docs/notifications.md.STARTED/ENDED/OUTPUT_CHANGEDonpi.events(
extensions/shared/protocol/broadcasts.ts) for external listeners.Gap analysis
One core change unlocks the rest:
Notify config through the protocol. Both command handlers call
notifications.register(info.id, {}), so every protocol-started process gets default lifecyclenotifications (success/failure →
turn, killed →context). A foregroundBashtool that returnsits own result would produce a duplicate notification — the problem #81 solved with "process-owner
metadata." Today the same suppression is expressible as per-process attention
ignore, but noprotocol channel accepts notify config for externally started processes; only the in-process
processtool does (tools/start,tools/update).Proposed minimal change:
notifytoCommandStartPayloadandCommandAdoptPayload, typed as aprotocol-safe mirror of
NotifyConfiginextensions/shared/protocol/.handlers/commands.ts, normalize and register the payload's notify config instead of{},reusing the validation semantics in
extensions/processes/tools/notify.ts(reject empty/whitespace-only patterns, validate regexes).
docs/notifications.mdthatignoreis force-downgraded tocontextforcrash/failure, so a crashed foreground
Bashstill leaves a context-level trace.Optionally add a
source?: stringfield (e.g."claude") to the start payload so future taskcompanion tools can filter their own processes out of the global list without owner semantics.
Design
Bash
Start via
COMMAND_STARTfor both modes so the process is visible and controllable through/psfrom the start (the adopt-pattern keeps a foreground run invisible until handoff, which conflicts
with the
/psvisibility requirement).CHANNELS.ENDEDfiltered by id (with a reply-timeout safety net),read the result via
REQUEST_COMBINED_OUTPUT, and suppress the core lifecycle notification vianotify: { onSuccess: "ignore", onFailure: "ignore", onKilled: "ignore" }(failure/crash stilllands as context — see gap analysis). Non-zero exit, timeout (own timer →
COMMAND_KILL), andabort are tool errors containing the output tail.
stdoutFile/stderrFilepaths;completion arrives via the core lifecycle notification with default attention.
dangerouslyDisableSandboxas a compatibility no-op.background-blockerhook (interception.blockBackgroundCommands) targets thebuilt-in
bashtool by name; decide and document what happens whenclaude-processesalsoregisters a
bashtool.Monitor
Two viable builds; pick during implementation:
notifycarryinglogMatches: [{ pattern: ".", mode: "regex", repeat: true }]. Each stdout/stderr line becomes alog_matchnotification through the existing pipeline — inheriting the 20/minute cap, thesuppression summary, classification, and persistence — with the completion delivered by the core
lifecycle notification. Trade-off: notification wording is the generic core phrasing, not a
"this is monitor output, not user input" frame.
LOGS_SUBSCRIBEand turnLOGS_CHUNKlines into customnotifications in the extension (full wording control, explicit "not user input" framing), at the
cost of implementing a per-monitor throttle locally. Final completion handled by subscribing to
CHANNELS.ENDED.Persistent monitors run until stopped or session shutdown; non-persistent ones use an extension
side timer +
COMMAND_KILL, which is classified as an intentional stop (attention per notifyconfig, never a spurious failure).
Task companions (future)
TaskList=REQUEST_LIST(+sourcefilter if added).TaskOutput=REQUEST_COMBINED_OUTPUT/REQUEST_LOG_FILES.TaskStop=COMMAND_KILL.TaskWrite(stdin) has no protocol channel today — theprocesstool's write action isin-process only. Defer or add a
COMMAND_WRITEchannel as a separate change.Constraints
claude-processesnever importssrc/manager./ps.hooks/cleanup.ts).BashorMonitor(via notify config, not ownermetadata).
extensions/processes/i18n).Work breakdown
notify(+ optionalsource) toCommandStartPayload/CommandAdoptPayload;normalize in
handlers/commands.ts; extendextensions/shared/protocol/types.extensions/claude-processes/with its own typed client (copy therequestStart/requestKill/ timeout pattern fromextensions/processes/client.ts); register it inpackage.jsonpi.extensionsafter the core extension.Bashforeground and background modes.Monitor(matcher-based first); decide (a) vs (b) with the wording trade-offdocumented.
/psas the canonical process UI.clobber a user's active-tool selection (nothing in the repo covers this today).
Tests
Unit (patterns in
extensions/processes/handlers/commands.test.tsandextensions/processes/client.test.ts):requestKill/requestPin);Bashsuccess, failure, abort, and timeout;Bashimmediate response and completion delivery;Monitorline delivery via matcher path; intentional-stop classification for timeout kills.E2E (
tests/e2e/**withfixtures.ts):Bash- andMonitor-started processes appear in/ps;Monitornotifications arrive, throttled perdocs/notifications.md;Verification
pnpm typecheck pnpm lint pnpm test pnpm test:e2eDocumentation
On landing, update
README.md, the repoAGENTS.md(structure section — itssrc/ ... protocolline is stale since the protocol moved to
extensions/shared/protocol/),skills/pi-processes/SKILL.md,docs/notifications.md(notify-via-protocol semantics), and add a Changeset.Run host:
solar-al-khwarizmi· Session:01a04285-dfda-7dd4-887a-1154983d73d4· Model:aperture/neuralwatt/kimi-k3-flex