@haemmid/pi-processes 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ This project follows semantic versioning for public releases.
6
+
7
+ ## [0.9.0] - 2026-07-01
8
+
9
+ Initial public release of `@haemmid/pi-processes`.
10
+
11
+ ### Added
12
+
13
+ - Agent-facing `process` tool for Pi and pi-web automation workflows.
14
+ - Managed process actions:
15
+ - `start`
16
+ - `restart`
17
+ - `list`
18
+ - `output`
19
+ - `logs`
20
+ - `kill`
21
+ - `clear`
22
+ - File-backed stdout/stderr/combined logs.
23
+ - Stable process names for dev-server workflows.
24
+ - Duplicate running-name protection before spawning a process.
25
+ - Dedicated safe `restart` action: await kill, then start.
26
+ - `cwd` support for `start` and `restart`.
27
+ - Background-command interception for fragile shell patterns:
28
+ - `&`
29
+ - `nohup`
30
+ - `disown`
31
+ - `setsid`
32
+ - `npm run dev`
33
+ - `pnpm dev`
34
+ - `yarn dev`
35
+ - `bun run dev`
36
+ - `npx vite`
37
+ - `npx astro dev`
38
+ - `docker compose up`
39
+ - `tail -f`
40
+ - `kubectl port-forward`
41
+ - Session cleanup for managed processes.
42
+
43
+ ### Changed
44
+
45
+ - Repositioned the package as a plain-text process manager for Pi/pi-web automation.
46
+ - Simplified the original process-management workflow for agent use.
47
+ - README now focuses on dev-server automation, especially Astro/Vite workflows.
48
+ - Process name resolution now prioritizes live processes over old finished duplicates.
49
+
50
+ ### Removed
51
+
52
+ - TUI widgets and overlays.
53
+ - `/ps` overlay workflow.
54
+ - Status widgets.
55
+ - Rich TUI rendering in tool output.
56
+ - Auto-notification claims after process exit.
57
+ - Unneeded TUI dependencies.
58
+
59
+ ### Notes
60
+
61
+ This package started as a fork of `mjakl/pi-processes`, which was based on `aliou/pi-processes`.
62
+
63
+ This release focuses on a narrower use case: reliable, plain-text, agent-facing process management for automated Pi and pi-web coding workflows.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 haemmid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ # ⚙️ pi-processes
2
+
3
+ > Agent-facing process manager for Pi and pi-web automation workflows.
4
+
5
+ `pi-processes` lets the agent start, inspect, restart, and stop long-running commands through a managed `process` tool instead of fragile shell backgrounding.
6
+
7
+ Designed for use with [jmfederico/pi-web](https://github.com/jmfederico/pi-web).
8
+
9
+ [![npm](https://img.shields.io/npm/v/@haemmid/pi-processes)](https://www.npmjs.com/package/@haemmid/pi-processes)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)](https://www.typescriptlang.org/)
12
+
13
+ <p align="center">
14
+ <img src="https://raw.githubusercontent.com/haemmid/pi-processes/v0.9.0/assets/hero.svg" alt="pi-processes managed process workflow" width="760">
15
+ </p>
16
+
17
+ ## Why
18
+
19
+ Agents often waste time or break sessions by running:
20
+
21
+ - `npm run dev &`
22
+ - `nohup pnpm dev ...`
23
+ - `pkill -f vite`
24
+ - repeated `timeout 10 npm run dev`
25
+
26
+ This package gives the agent a stable process lifecycle instead:
27
+
28
+ - `process start`
29
+ - `process list`
30
+ - `process output`
31
+ - `process restart`
32
+ - `process kill`
33
+
34
+ ## Table of Contents
35
+
36
+ - [Features](#features)
37
+ - [Install](#install)
38
+ - [Usage](#usage)
39
+ - [Configuration](#configuration)
40
+ - [Tool API](#tool-api)
41
+ - [Killing processes](#killing-processes)
42
+ - [Limitations](#limitations)
43
+ - [Development](#development)
44
+ - [Acknowledgements](#acknowledgements)
45
+ - [Changelog](#changelog)
46
+ - [License](#license)
47
+
48
+ ## Features
49
+
50
+ - **Agent-facing `process` tool** — start, list, kill, get output, get log paths, and clear managed processes.
51
+ - **File-backed logs** — process output is preserved in temp files outside the agent context window.
52
+ - **Background-command interception** — optional guardrails steer the agent away from shell backgrounding, `nohup`, and obvious long-running foreground commands, and toward the `process` tool.
53
+ - **Session cleanup** — managed processes are terminated when the session shuts down.
54
+ - **Duplicate name protection** — refuses to spawn if a live process with the same name already exists.
55
+ - **Dedicated `restart` action** — safely awaits kill before starting a new process.
56
+
57
+ ## Install
58
+
59
+ Install from npm:
60
+
61
+ ```bash
62
+ pi install npm:@haemmid/pi-processes
63
+ ```
64
+
65
+ Install from git:
66
+
67
+ ```bash
68
+ pi install git:github.com/haemmid/pi-processes
69
+ ```
70
+
71
+ Or install from a local checkout:
72
+
73
+ ```bash
74
+ pi install /path/to/pi-processes
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ The `process` tool is for the agent, not for direct user input. Ask the agent to start or inspect long-running work.
80
+
81
+ Example user prompts:
82
+
83
+ ```text
84
+ Start the dev server with pnpm dev and call it backend-dev.
85
+ Show me the latest output from backend-dev.
86
+ Stop the backend-dev process.
87
+ ```
88
+
89
+ The agent should start managed processes through the `process` tool instead of running shell backgrounding such as `command &`, `nohup`, `disown`, or `setsid`.
90
+
91
+ ## Astro / Vite workflow
92
+
93
+ For Astro/Vite dev servers, ask the agent:
94
+
95
+ ```text
96
+ Use the process tool for the Astro dev server.
97
+ Start `npm run dev -- --host 0.0.0.0` as `my-site:astro`.
98
+ Use `process output` when you need logs.
99
+ Do not restart after ordinary .astro, .ts, or .css edits.
100
+ Restart only after package/config/env changes or if the server exits.
101
+ ```
102
+
103
+ Typical tool flow:
104
+
105
+ ```text
106
+ process list
107
+ process start "npm run dev -- --host 0.0.0.0" name="my-site:astro"
108
+ process output id="my-site:astro"
109
+ process restart "npm run dev -- --host 0.0.0.0" name="my-site:astro"
110
+ process kill id="my-site:astro"
111
+ ```
112
+
113
+ ## Demo
114
+
115
+ <p align="center">
116
+ <img src="https://raw.githubusercontent.com/haemmid/pi-processes/v0.9.0/assets/demo-pi-web.gif" alt="pi-processes demo in pi-web" width="760">
117
+ </p>
118
+
119
+ ## Configuration
120
+
121
+ Global config lives in:
122
+
123
+ ```text
124
+ ~/.pi/agent/extensions/process.json
125
+ ```
126
+
127
+ Example:
128
+
129
+ ```json
130
+ {
131
+ "output": {
132
+ "defaultTailLines": 100,
133
+ "maxOutputLines": 200
134
+ },
135
+ "execution": {
136
+ "shellPath": "/absolute/path/to/bash"
137
+ },
138
+ "interception": {
139
+ "blockBackgroundCommands": true
140
+ }
141
+ }
142
+ ```
143
+
144
+ Options:
145
+
146
+ - `output.defaultTailLines` — default number of lines returned by `process output`.
147
+ - `output.maxOutputLines` — hard cap for `process output`.
148
+ - `execution.shellPath` — absolute shell path override used for process startup.
149
+ - `interception.blockBackgroundCommands` — block shell backgrounding and obvious long-running foreground commands such as `pnpm dev`, `docker compose up`, `tail -f`, or `kubectl port-forward`, and guide the agent to use the `process` tool instead.
150
+
151
+ ## Tool API
152
+
153
+ The tool is named `process`.
154
+
155
+ ### Actions
156
+
157
+ | Action | Description |
158
+ |--------|-------------|
159
+ | `start` | Start a managed process. |
160
+ | `restart` | Kill existing process and start a new one (safe: await kill → start). |
161
+ | `list` | List managed processes. |
162
+ | `output` | Return a one-off tailed stdout/stderr snapshot. |
163
+ | `logs` | Return file paths for stdout, stderr, and combined logs. |
164
+ | `kill` | Terminate or force-kill a process. |
165
+ | `clear` | Remove finished processes from the manager. |
166
+
167
+ ### Tool-call examples
168
+
169
+ ```text
170
+ process start "pnpm dev" name="backend-dev"
171
+ process start "pnpm test --watch" name="tests"
172
+ process start "pnpm dev" name="backend-dev" cwd="/path/to/project"
173
+ process restart "pnpm dev" name="backend-dev"
174
+ process restart "pnpm dev" name="backend-dev" force=true
175
+ process list
176
+ process output id="backend-dev"
177
+ process logs id="proc_1"
178
+ process kill id="backend-dev"
179
+ process kill id="proc_1" force=true
180
+ process clear
181
+ ```
182
+
183
+ ### Field rules
184
+
185
+ - `start`/`restart` require `command` and `name`.
186
+ - `output`, `logs`, and `kill` require `id`.
187
+ - `kill` accepts `force=true` to send `SIGKILL` instead of `SIGTERM`.
188
+ - `start` refuses if a process with the same name is already running.
189
+ - `restart` safely kills the existing process (awaited) before starting a new one.
190
+ - `restart` accepts `force=true` to send `SIGKILL` instead of `SIGTERM`.
191
+ - `start`/`restart` accept `cwd` to override the working directory (defaults to session cwd).
192
+
193
+ ## Killing processes
194
+
195
+ - `process kill id="..."` sends `SIGTERM`.
196
+ - `process kill id="..." force=true` sends `SIGKILL`.
197
+ - Tool-triggered kills never notify the agent.
198
+
199
+ ## Limitations
200
+
201
+ - **Linux/macOS only.** The extension disables itself on Windows.
202
+ - **Session-scoped.** Processes are cleaned up on session shutdown.
203
+ - **Not a system service manager.** Does not persist process registry across sessions.
204
+ - **Manages only tool-started processes.** Does not track externally started processes.
205
+ - **No TUI widgets.** Does not provide `/ps` overlays or status widgets.
206
+
207
+ ## Development
208
+
209
+ There are no Git hooks installed by this repository. Before committing or opening a PR, consider running:
210
+
211
+ ```bash
212
+ pnpm typecheck
213
+ pnpm lint
214
+ pnpm test
215
+ ```
216
+
217
+ After dependency changes, also verify the lockfile with:
218
+
219
+ ```bash
220
+ pnpm install --frozen-lockfile --ignore-scripts
221
+ ```
222
+
223
+ ## Acknowledgements
224
+
225
+ This package started as a fork of [`mjakl/pi-processes`](https://github.com/mjakl/pi-processes), which was based on [`aliou/pi-processes`](https://github.com/aliou/pi-processes). This fork focuses specifically on plain-text, pi-web-friendly, agent-facing process management without TUI widgets or overlays.
226
+
227
+ ## Changelog
228
+
229
+ See [CHANGELOG.md](CHANGELOG.md).
230
+
231
+ ## License
232
+
233
+ MIT
Binary file
@@ -0,0 +1,141 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">
2
+ <title id="title">@haemmid/pi-processes workflow</title>
3
+ <desc id="desc">Minimal technical diagram: Pi Agent starts a process tool, which manages an Astro or Vite dev server, captures logs and output, and speeds up pi-web automation.</desc>
4
+
5
+ <defs>
6
+ <style>
7
+ .bg { fill: #f5f5f2; }
8
+ .halo { fill: #f0f1ee; opacity: .58; }
9
+ .grid { fill: none; stroke: #c9ccc5; stroke-width: 1; opacity: .45; }
10
+ .shell, .card { fill: #ffffff; stroke: #c9ccc5; stroke-width: 1.2; }
11
+ .soft, .chip { fill: #f0f1ee; stroke: #c9ccc5; stroke-width: 1.1; }
12
+ .arrow { fill: none; stroke: #586f61; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow); }
13
+ .arrow2 { fill: none; stroke: #8f9891; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow2); }
14
+ .stroke { fill: none; stroke: #8f9891; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
15
+ .stroke-soft { fill: none; stroke: #c9ccc5; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
16
+ .dot-muted { fill: #8f9891; }
17
+ .dot-ok { fill: #62806c; }
18
+ .bolt { fill: #918156; }
19
+ .title-text { font: 650 22px/1.1 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; letter-spacing: -0.03em; fill: #202427; }
20
+ .label { font: 520 14px/1.2 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #6f7772; }
21
+ .tiny { font: 650 11px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: .055em; fill: #6f7772; text-transform: uppercase; }
22
+ .mono { font: 520 14px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; fill: #202427; }
23
+ .mono2 { font: 500 13px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; fill: #6f7772; }
24
+ .arrow-head { fill: #586f61; }
25
+ .arrow-head2 { fill: #8f9891; }
26
+ .accent-stroke { stroke: #586f61; }
27
+ .accent-dot { fill: #586f61; }
28
+ .accent-dot2 { fill: #7f9488; }
29
+
30
+ @media (prefers-color-scheme: dark) {
31
+ .bg { fill: #121416; }
32
+ .halo { fill: #202529; opacity: .58; }
33
+ .grid { stroke: #353b3b; opacity: .55; }
34
+ .shell, .card { fill: #191d20; stroke: #353b3b; }
35
+ .soft, .chip { fill: #202529; stroke: #353b3b; }
36
+ .arrow { stroke: #a9c8b5; }
37
+ .arrow2, .stroke { stroke: #6e7872; }
38
+ .stroke-soft { stroke: #353b3b; }
39
+ .dot-muted { fill: #6e7872; }
40
+ .dot-ok { fill: #9bc5ad; }
41
+ .bolt { fill: #d2c088; }
42
+ .title-text, .mono { fill: #edf0ed; }
43
+ .label, .tiny, .mono2 { fill: #a5ada8; }
44
+ .arrow-head { fill: #a9c8b5; }
45
+ .arrow-head2 { fill: #6e7872; }
46
+ .accent-stroke { stroke: #a9c8b5; }
47
+ .accent-dot { fill: #a9c8b5; }
48
+ .accent-dot2 { fill: #86a090; }
49
+ }
50
+ </style>
51
+
52
+ <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
53
+ <path d="M40 0H0V40" class="grid"/>
54
+ </pattern>
55
+
56
+ <marker id="arrow" viewBox="0 0 10 10" refX="8.8" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
57
+ <path d="M0 0 10 5 0 10z" class="arrow-head"/>
58
+ </marker>
59
+ <marker id="arrow2" viewBox="0 0 10 10" refX="8.6" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
60
+ <path d="M0 0 10 5 0 10z" class="arrow-head2"/>
61
+ </marker>
62
+ </defs>
63
+
64
+ <rect width="1200" height="630" class="bg"/>
65
+ <rect width="1200" height="630" fill="url(#grid)"/>
66
+ <circle cx="600" cy="315" r="260" class="halo"/>
67
+ <path d="M140 488C280 416 372 505 516 447C676 383 719 214 858 216C982 218 1038 325 1091 278" class="stroke-soft" opacity=".65"/>
68
+
69
+ <rect x="70" y="64" width="1060" height="502" rx="36" class="shell"/>
70
+ <rect x="96" y="92" width="1008" height="48" rx="18" class="soft"/>
71
+ <circle cx="124" cy="116" r="6" class="dot-muted" opacity=".6"/>
72
+ <circle cx="146" cy="116" r="6" class="dot-muted" opacity=".38"/>
73
+ <circle cx="168" cy="116" r="6" class="dot-muted" opacity=".25"/>
74
+ <text x="198" y="121" class="tiny">@haemmid/pi-processes</text>
75
+ <rect x="930" y="105" width="140" height="22" rx="11" class="chip"/>
76
+ <text x="953" y="120" class="tiny">managed runs</text>
77
+
78
+ <g transform="translate(96 176)">
79
+ <rect width="260" height="190" rx="24" class="card"/>
80
+ <rect x="22" y="22" width="216" height="48" rx="14" class="soft"/>
81
+ <path d="M45 47l13 12-13 12M72 69h43" class="stroke"/>
82
+ <text x="22" y="106" class="title-text">Pi Agent</text>
83
+ <text x="22" y="132" class="label">asks for a dev process</text>
84
+ <rect x="22" y="150" width="92" height="20" rx="10" class="chip"/>
85
+ <text x="39" y="164" class="tiny">start</text>
86
+ <rect x="124" y="150" width="92" height="20" rx="10" class="chip"/>
87
+ <text x="145" y="164" class="tiny">status</text>
88
+ </g>
89
+
90
+ <g transform="translate(468 166)">
91
+ <rect width="264" height="210" rx="28" class="card"/>
92
+ <circle cx="132" cy="73" r="40" class="soft"/>
93
+ <path d="M132 43v12M132 91v12M102 73h12M150 73h12M111 52l9 9M144 85l9 9M153 52l-9 9M120 85l-9 9" class="stroke"/>
94
+ <circle cx="132" cy="73" r="14" fill="none" class="accent-stroke" stroke-width="3"/>
95
+ <text x="36" y="136" class="title-text">process tool</text>
96
+ <text x="36" y="162" class="label">dedupe • resolve • stop</text>
97
+ <rect x="36" y="178" width="82" height="20" rx="10" class="chip"/>
98
+ <text x="55" y="192" class="tiny">pid</text>
99
+ <rect x="128" y="178" width="100" height="20" rx="10" class="chip"/>
100
+ <text x="148" y="192" class="tiny">logs</text>
101
+ </g>
102
+
103
+ <g transform="translate(844 176)">
104
+ <rect width="260" height="190" rx="24" class="card"/>
105
+ <text x="28" y="-14" class="title-text">managed dev server</text>
106
+ <rect x="28" y="28" width="204" height="36" rx="12" class="soft"/>
107
+ <rect x="28" y="76" width="204" height="36" rx="12" class="soft"/>
108
+ <rect x="28" y="124" width="204" height="36" rx="12" class="soft"/>
109
+ <circle cx="50" cy="46" r="5" class="dot-ok"/>
110
+ <circle cx="50" cy="94" r="5" class="dot-ok"/>
111
+ <circle cx="50" cy="142" r="5" class="dot-ok"/>
112
+ <path d="M72 46h104M72 94h126M72 142h78" class="stroke-soft"/>
113
+ <text x="28" y="186" class="label">Astro / Vite, kept under control</text>
114
+ </g>
115
+
116
+ <path d="M368 271C404 271 421 271 456 271" class="arrow"/>
117
+ <path d="M746 271C785 271 800 271 832 271" class="arrow"/>
118
+
119
+ <g transform="translate(390 420)">
120
+ <rect width="420" height="94" rx="22" class="card"/>
121
+ <text x="26" y="34" class="mono">stdout.log</text>
122
+ <text x="26" y="62" class="mono2">ready in 312ms • localhost:5173</text>
123
+ <text x="26" y="82" class="mono2">[managed] reused existing process</text>
124
+ <path d="M324 31h58M324 53h42M324 75h70" class="stroke-soft"/>
125
+ </g>
126
+ <path d="M600 388C600 404 600 410 600 418" class="arrow2"/>
127
+
128
+ <g transform="translate(842 423)">
129
+ <rect width="241" height="91" rx="22" class="card"/>
130
+ <circle cx="48" cy="46" r="25" class="soft"/>
131
+ <path d="M51 23 35 50h18l-5 20 22-33H52z" class="bolt"/>
132
+ <text x="88" y="40" class="title-text">faster pi-web</text>
133
+ <text x="88" y="66" class="label">automation loop</text>
134
+ </g>
135
+ <path d="M812 467C822 467 829 467 840 467" class="arrow2"/>
136
+
137
+ <circle cx="748" cy="232" r="5" class="accent-dot" opacity=".55"/>
138
+ <circle cx="457" cy="304" r="4" class="accent-dot2" opacity=".55"/>
139
+ <circle cx="600" cy="395" r="4" class="dot-muted" opacity=".45"/>
140
+
141
+ </svg>
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@haemmid/pi-processes",
3
+ "version": "0.9.0",
4
+ "description": "Agent-facing background process manager for Pi and pi-web automation workflows.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "private": false,
8
+ "homepage": "https://github.com/haemmid/pi-processes#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/haemmid/pi-processes/issues"
11
+ },
12
+ "keywords": [
13
+ "pi-package",
14
+ "pi-extension",
15
+ "pi",
16
+ "pi-web",
17
+ "processes",
18
+ "background-processes",
19
+ "dev-server",
20
+ "astro",
21
+ "vite"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/haemmid/pi-processes.git"
26
+ },
27
+ "pi": {
28
+ "extensions": [
29
+ "./src/index.ts"
30
+ ]
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "files": [
36
+ "src",
37
+ "!src/**/*.test.ts",
38
+ "README.md",
39
+ "CHANGELOG.md",
40
+ "LICENSE",
41
+ "assets"
42
+ ],
43
+ "dependencies": {
44
+ "@aliou/sh": "^0.1.0"
45
+ },
46
+ "peerDependencies": {
47
+ "@earendil-works/pi-coding-agent": "*",
48
+ "typebox": "*"
49
+ },
50
+ "devDependencies": {
51
+ "@biomejs/biome": "^2.3.13",
52
+ "@earendil-works/pi-coding-agent": "0.75.1",
53
+ "@types/node": "^25.0.10",
54
+ "typebox": "^1.1.24",
55
+ "typescript": "^5.9.3",
56
+ "vitest": "^4.0.18"
57
+ },
58
+ "scripts": {
59
+ "typecheck": "tsc --noEmit",
60
+ "lint": "biome check",
61
+ "format": "biome check --write",
62
+ "test": "vitest run"
63
+ },
64
+ "packageManager": "pnpm@10.26.1",
65
+ "peerDependenciesMeta": {
66
+ "@earendil-works/pi-coding-agent": {
67
+ "optional": true
68
+ },
69
+ "typebox": {
70
+ "optional": true
71
+ }
72
+ }
73
+ }
package/src/config.ts ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Runtime configuration for the processes extension.
3
+ *
4
+ * Global: ~/.pi/agent/extensions/process.json
5
+ */
6
+
7
+ import { readFile } from "node:fs/promises";
8
+ import { resolve } from "node:path";
9
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
10
+
11
+ interface ProcessesConfig {
12
+ output?: {
13
+ /** Default number of tail lines returned to the agent. */
14
+ defaultTailLines?: number;
15
+ /** Hard cap on output lines returned to the agent. */
16
+ maxOutputLines?: number;
17
+ };
18
+ execution?: {
19
+ /** Absolute shell path override. Leave unset to auto-resolve. */
20
+ shellPath?: string;
21
+ };
22
+ interception?: {
23
+ /** Block shell backgrounding and obvious long-running bash commands, and guide the model to use the process tool. */
24
+ blockBackgroundCommands?: boolean;
25
+ };
26
+ }
27
+
28
+ export interface ResolvedProcessesConfig {
29
+ output: {
30
+ defaultTailLines: number;
31
+ maxOutputLines: number;
32
+ };
33
+ execution: {
34
+ shellPath?: string;
35
+ };
36
+ interception: {
37
+ blockBackgroundCommands: boolean;
38
+ };
39
+ }
40
+
41
+ const DEFAULT_CONFIG: ResolvedProcessesConfig = {
42
+ output: {
43
+ defaultTailLines: 100,
44
+ maxOutputLines: 200,
45
+ },
46
+ execution: {},
47
+ interception: {
48
+ blockBackgroundCommands: true,
49
+ },
50
+ };
51
+
52
+ class ProcessesConfigLoader {
53
+ private resolved: ResolvedProcessesConfig | null = null;
54
+
55
+ async load(): Promise<void> {
56
+ const rawConfig = await readGlobalConfig();
57
+ this.resolved = resolveConfig(rawConfig);
58
+ }
59
+
60
+ getConfig(): ResolvedProcessesConfig {
61
+ if (!this.resolved) {
62
+ throw new Error("Config not loaded. Call load() first.");
63
+ }
64
+ return this.resolved;
65
+ }
66
+ }
67
+
68
+ async function readGlobalConfig(): Promise<ProcessesConfig | null> {
69
+ const path = resolve(getAgentDir(), "extensions/process.json");
70
+
71
+ try {
72
+ const content = await readFile(path, "utf-8");
73
+ const parsed: unknown = JSON.parse(content);
74
+ if (!isRecord(parsed)) return null;
75
+
76
+ const { $schema: _schema, ...config } = parsed;
77
+ return config as ProcessesConfig;
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+
83
+ function resolveConfig(
84
+ config: ProcessesConfig | null,
85
+ ): ResolvedProcessesConfig {
86
+ return {
87
+ output: {
88
+ defaultTailLines: numberOrDefault(
89
+ config?.output?.defaultTailLines,
90
+ DEFAULT_CONFIG.output.defaultTailLines,
91
+ ),
92
+ maxOutputLines: numberOrDefault(
93
+ config?.output?.maxOutputLines,
94
+ DEFAULT_CONFIG.output.maxOutputLines,
95
+ ),
96
+ },
97
+ execution: {
98
+ shellPath: stringOrUndefined(config?.execution?.shellPath),
99
+ },
100
+ interception: {
101
+ blockBackgroundCommands: booleanOrDefault(
102
+ config?.interception?.blockBackgroundCommands,
103
+ DEFAULT_CONFIG.interception.blockBackgroundCommands,
104
+ ),
105
+ },
106
+ };
107
+ }
108
+
109
+ function numberOrDefault(value: unknown, fallback: number): number {
110
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
111
+ }
112
+
113
+ function stringOrUndefined(value: unknown): string | undefined {
114
+ return typeof value === "string" && value.length > 0 ? value : undefined;
115
+ }
116
+
117
+ function booleanOrDefault(value: unknown, fallback: boolean): boolean {
118
+ return typeof value === "boolean" ? value : fallback;
119
+ }
120
+
121
+ function isRecord(value: unknown): value is Record<string, unknown> {
122
+ return typeof value === "object" && value !== null && !Array.isArray(value);
123
+ }
124
+
125
+ export const configLoader = new ProcessesConfigLoader();
@@ -0,0 +1,11 @@
1
+ export type {
2
+ ExecuteResult,
3
+ KillResult,
4
+ ManagerEvent,
5
+ ProcessesDetails,
6
+ ProcessInfo,
7
+ ProcessStatus,
8
+ ResolveProcessResult,
9
+ } from "./types";
10
+
11
+ export { LIVE_STATUSES } from "./types";