@huseynsnmz/pi-suite 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -2
  4. package/SECURITY.md +5 -0
  5. package/package.json +113 -4
  6. package/packages/board/CHANGELOG.md +340 -0
  7. package/packages/board/CONTRIBUTING.md +7 -0
  8. package/packages/board/LICENSE +21 -0
  9. package/packages/board/README.md +88 -0
  10. package/packages/board/SECURITY.md +19 -0
  11. package/packages/board/docs/how-it-works.md +31 -0
  12. package/packages/board/docs/reference.md +278 -0
  13. package/packages/board/package.json +83 -0
  14. package/packages/board/runner/job-runner-legacy.mjs +68 -0
  15. package/packages/board/runner/job-runner.mjs +834 -0
  16. package/packages/board/runner/pty-runner-legacy.mjs +50 -0
  17. package/packages/board/runner/pty-runner.mjs +2175 -0
  18. package/packages/board/runner/state-coordinator.mjs +673 -0
  19. package/packages/board/runner/state-runner.mjs +203 -0
  20. package/packages/board/runner/title-runner.mjs +80 -0
  21. package/packages/board/src/commands/agent-board.ts +394 -0
  22. package/packages/board/src/commands/attach-decision.mjs +66 -0
  23. package/packages/board/src/commands/attach-flow.ts +251 -0
  24. package/packages/board/src/commands/bg.ts +103 -0
  25. package/packages/board/src/commands/board-model.ts +21 -0
  26. package/packages/board/src/core/atomic.mjs +184 -0
  27. package/packages/board/src/core/attach-delete.mjs +24 -0
  28. package/packages/board/src/core/attach-frame-proof.mjs +870 -0
  29. package/packages/board/src/core/attach-frame-reporter.mjs +648 -0
  30. package/packages/board/src/core/attach-frame-stream.mjs +266 -0
  31. package/packages/board/src/core/attach-view-proof.mjs +709 -0
  32. package/packages/board/src/core/auto-state.mjs +377 -0
  33. package/packages/board/src/core/background-runs.mjs +273 -0
  34. package/packages/board/src/core/code-refs-store.mjs +315 -0
  35. package/packages/board/src/core/code-refs.mjs +913 -0
  36. package/packages/board/src/core/config.mjs +77 -0
  37. package/packages/board/src/core/control-protocol.mjs +482 -0
  38. package/packages/board/src/core/coordinator-client.mjs +633 -0
  39. package/packages/board/src/core/coordinator-journal.mjs +282 -0
  40. package/packages/board/src/core/coordinator-protocol.mjs +14 -0
  41. package/packages/board/src/core/cwd-stats.mjs +114 -0
  42. package/packages/board/src/core/dashboard-render.mjs +10 -0
  43. package/packages/board/src/core/derive.mjs +117 -0
  44. package/packages/board/src/core/diagnostics.mjs +133 -0
  45. package/packages/board/src/core/double-press.mjs +26 -0
  46. package/packages/board/src/core/editor-state-reporter.mjs +112 -0
  47. package/packages/board/src/core/events.mjs +286 -0
  48. package/packages/board/src/core/evidence.mjs +245 -0
  49. package/packages/board/src/core/follow-up-queue.mjs +211 -0
  50. package/packages/board/src/core/foreground-preview-cache.mjs +117 -0
  51. package/packages/board/src/core/foreground-projection-cache.mjs +99 -0
  52. package/packages/board/src/core/frame-witness-update.mjs +35 -0
  53. package/packages/board/src/core/heuristics.mjs +313 -0
  54. package/packages/board/src/core/hold-input.mjs +27 -0
  55. package/packages/board/src/core/host-coordination.mjs +300 -0
  56. package/packages/board/src/core/host-crash.mjs +79 -0
  57. package/packages/board/src/core/host-probe.mjs +207 -0
  58. package/packages/board/src/core/host-protocol.mjs +73 -0
  59. package/packages/board/src/core/ids.mjs +35 -0
  60. package/packages/board/src/core/ime-cursor-coalesce.mjs +193 -0
  61. package/packages/board/src/core/invocation.mjs +43 -0
  62. package/packages/board/src/core/launch-options.mjs +418 -0
  63. package/packages/board/src/core/launch.mjs +157 -0
  64. package/packages/board/src/core/load-config.mjs +4 -0
  65. package/packages/board/src/core/locks.mjs +530 -0
  66. package/packages/board/src/core/paths.mjs +183 -0
  67. package/packages/board/src/core/pid.mjs +75 -0
  68. package/packages/board/src/core/prewarm-schedule.mjs +41 -0
  69. package/packages/board/src/core/process-identity-probe.mjs +228 -0
  70. package/packages/board/src/core/prompt-transport.mjs +13 -0
  71. package/packages/board/src/core/pty-attach-jiggle-controller.mjs +356 -0
  72. package/packages/board/src/core/pty-attach-jiggle-retry.mjs +109 -0
  73. package/packages/board/src/core/pty-attach-reconnect.mjs +61 -0
  74. package/packages/board/src/core/pty-attach-render.mjs +229 -0
  75. package/packages/board/src/core/pty-links.mjs +81 -0
  76. package/packages/board/src/core/pty-scroll.mjs +522 -0
  77. package/packages/board/src/core/pty-support.mjs +327 -0
  78. package/packages/board/src/core/repo.mjs +103 -0
  79. package/packages/board/src/core/rows.mjs +398 -0
  80. package/packages/board/src/core/screen-log-gc.mjs +198 -0
  81. package/packages/board/src/core/screen-log.mjs +160 -0
  82. package/packages/board/src/core/session-view.mjs +174 -0
  83. package/packages/board/src/core/slash-commands.mjs +52 -0
  84. package/packages/board/src/core/state-commands.mjs +757 -0
  85. package/packages/board/src/core/status-consistency.mjs +98 -0
  86. package/packages/board/src/core/steering-prompts.mjs +34 -0
  87. package/packages/board/src/core/steering.mjs +133 -0
  88. package/packages/board/src/core/stop-latch.mjs +41 -0
  89. package/packages/board/src/core/store.mjs +523 -0
  90. package/packages/board/src/core/suspend.mjs +36 -0
  91. package/packages/board/src/core/terminal-attach-client.mjs +981 -0
  92. package/packages/board/src/core/terminal-attach-protocol.mjs +460 -0
  93. package/packages/board/src/core/terminal-model.mjs +222 -0
  94. package/packages/board/src/core/terminal-query-sequences.mjs +239 -0
  95. package/packages/board/src/core/terminal-reply-guard.mjs +222 -0
  96. package/packages/board/src/core/terminal-snapshot.mjs +440 -0
  97. package/packages/board/src/core/test-knobs.mjs +28 -0
  98. package/packages/board/src/core/title.mjs +43 -0
  99. package/packages/board/src/core/types.mjs +414 -0
  100. package/packages/board/src/core/warm-host-sweeper.mjs +168 -0
  101. package/packages/board/src/core/worktree.mjs +65 -0
  102. package/packages/board/src/index.ts +303 -0
  103. package/packages/board/src/runtime/service.mjs +3222 -0
  104. package/packages/board/src/ui/chrome.ts +20 -0
  105. package/packages/board/src/ui/dashboard-decisions.mjs +55 -0
  106. package/packages/board/src/ui/dashboard-evidence.mjs +85 -0
  107. package/packages/board/src/ui/dashboard.ts +2221 -0
  108. package/packages/board/src/ui/double-left.ts +30 -0
  109. package/packages/board/src/ui/external-editor.ts +25 -0
  110. package/packages/board/src/ui/focus.ts +32 -0
  111. package/packages/board/src/ui/pty-attach.ts +3184 -0
  112. package/packages/board/src/ui/waiting.ts +30 -0
  113. package/packages/frame/CHANGELOG.md +113 -0
  114. package/packages/frame/CONTRIBUTING.md +7 -0
  115. package/packages/frame/LICENSE +22 -0
  116. package/packages/frame/README.md +105 -0
  117. package/packages/frame/SECURITY.md +16 -0
  118. package/packages/frame/docs/how-it-works.md +53 -0
  119. package/packages/frame/docs/reference.md +78 -0
  120. package/packages/frame/package.json +75 -0
  121. package/packages/frame/src/data/git.ts +90 -0
  122. package/packages/frame/src/data/github.ts +140 -0
  123. package/packages/frame/src/data/gitlab.ts +63 -0
  124. package/packages/frame/src/data/mode.ts +23 -0
  125. package/packages/frame/src/data/past-prompts.ts +57 -0
  126. package/packages/frame/src/data/runner.ts +32 -0
  127. package/packages/frame/src/data/snapshot.ts +223 -0
  128. package/packages/frame/src/data/start-notes.ts +104 -0
  129. package/packages/frame/src/data/telemetry.ts +337 -0
  130. package/packages/frame/src/data/todos.ts +162 -0
  131. package/packages/frame/src/data/totals.ts +94 -0
  132. package/packages/frame/src/data/visits.ts +45 -0
  133. package/packages/frame/src/index.ts +676 -0
  134. package/packages/frame/src/render/diff.ts +104 -0
  135. package/packages/frame/src/render/fit.ts +86 -0
  136. package/packages/frame/src/render/format.ts +108 -0
  137. package/packages/frame/src/render/frame.ts +106 -0
  138. package/packages/frame/src/render/glyphs.ts +189 -0
  139. package/packages/frame/src/render/keys.ts +43 -0
  140. package/packages/frame/src/render/lead.ts +37 -0
  141. package/packages/frame/src/render/paint.ts +135 -0
  142. package/packages/frame/src/render/safe.ts +19 -0
  143. package/packages/frame/src/render/segments.ts +735 -0
  144. package/packages/frame/src/render/streaming.ts +223 -0
  145. package/packages/frame/src/render/timing.ts +106 -0
  146. package/packages/frame/src/render/tools.ts +167 -0
  147. package/packages/frame/src/settings.ts +105 -0
  148. package/packages/frame/src/ui/editor.ts +272 -0
  149. package/packages/frame/src/ui/effort.ts +36 -0
  150. package/packages/frame/src/ui/footer.ts +165 -0
  151. package/packages/frame/src/ui/header.ts +362 -0
  152. package/packages/frame/src/ui/history-search.ts +23 -0
  153. package/packages/frame/src/ui/interrupt.ts +21 -0
  154. package/packages/frame/src/ui/outputs.ts +170 -0
  155. package/packages/frame/src/ui/pi-entries.ts +276 -0
  156. package/packages/frame/src/ui/previews.ts +72 -0
  157. package/packages/frame/src/ui/queue.ts +75 -0
  158. package/packages/frame/src/ui/screen.ts +59 -0
  159. package/packages/frame/src/ui/timings.ts +48 -0
  160. package/packages/frame/src/ui/todos.ts +205 -0
  161. package/packages/frame/src/ui/tool-frame.ts +595 -0
  162. package/packages/frame/src/ui/working.ts +25 -0
  163. package/packages/frame/src/view/state.ts +97 -0
  164. package/packages/frame/src/view/transcript.ts +175 -0
  165. package/packages/frame/src/view/view.ts +162 -0
  166. package/packages/memory/CONTRIBUTING.md +7 -0
  167. package/packages/memory/LICENSE +21 -0
  168. package/packages/memory/README.md +44 -0
  169. package/packages/memory/SECURITY.md +20 -0
  170. package/packages/memory/package.json +55 -0
  171. package/packages/memory/skills/review-memory/SKILL.md +48 -0
  172. package/packages/memory/src/index.ts +163 -0
  173. package/packages/memory/src/prompt.ts +25 -0
  174. package/packages/memory/src/review.ts +150 -0
  175. package/packages/memory/src/secrets.ts +22 -0
  176. package/packages/memory/src/store.ts +173 -0
  177. package/packages/modes/CHANGELOG.md +104 -0
  178. package/packages/modes/CONTRIBUTING.md +7 -0
  179. package/packages/modes/LICENSE +21 -0
  180. package/packages/modes/README.md +174 -0
  181. package/packages/modes/SECURITY.md +17 -0
  182. package/packages/modes/package.json +55 -0
  183. package/packages/modes/skills/review-permissions/SKILL.md +54 -0
  184. package/packages/modes/src/bash.ts +218 -0
  185. package/packages/modes/src/deny-off.ts +62 -0
  186. package/packages/modes/src/git-safety.ts +72 -0
  187. package/packages/modes/src/index.ts +338 -0
  188. package/packages/modes/src/interview.ts +206 -0
  189. package/packages/modes/src/judge-gate.ts +113 -0
  190. package/packages/modes/src/modes.ts +121 -0
  191. package/packages/modes/src/review/apply.mjs +156 -0
  192. package/packages/modes/src/review/collect.mjs +208 -0
  193. package/packages/modes/src/review/tools.ts +71 -0
  194. package/packages/modes/src/sandbox.ts +37 -0
  195. package/packages/router/CHANGELOG.md +8 -0
  196. package/packages/router/LICENSE +21 -0
  197. package/packages/router/README.md +90 -0
  198. package/packages/router/package.json +51 -0
  199. package/packages/router/src/config.ts +65 -0
  200. package/packages/router/src/index.ts +172 -0
  201. package/packages/router/src/judge.ts +51 -0
  202. package/packages/router/src/route.ts +67 -0
  203. package/packages/web/CHANGELOG.md +36 -0
  204. package/packages/web/LICENSE +21 -0
  205. package/packages/web/README.md +87 -0
  206. package/packages/web/SECURITY.md +13 -0
  207. package/packages/web/package.json +61 -0
  208. package/packages/web/src/config.ts +109 -0
  209. package/packages/web/src/dnsfilter.ts +97 -0
  210. package/packages/web/src/extract.ts +65 -0
  211. package/packages/web/src/index.ts +217 -0
  212. package/packages/web/src/net.ts +109 -0
  213. package/packages/web/src/reader.ts +73 -0
  214. package/packages/web/src/sanitize.ts +50 -0
  215. package/packages/web/src/search.ts +76 -0
  216. package/packages/web/src/secrets.ts +22 -0
  217. package/packages/web/src/validate.ts +186 -0
  218. package/src/index.ts +156 -0
  219. package/src/settings.ts +78 -0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Tells the board when a session is waiting on you. A permission prompt or any other dialog takes
3
+ * the keyboard from the prompt while a run is under way, whichever extension opened it.
4
+ */
5
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
6
+ import { watchFocus } from "./focus.ts";
7
+
8
+ const CHECK_MS = 500;
9
+
10
+ export function watchWaiting(ctx: ExtensionContext, changed: (waiting: boolean) => void): () => void {
11
+ const focus = watchFocus(ctx, "agent-board-waiting");
12
+ let waiting = false;
13
+ const timer = setInterval(() => {
14
+ try {
15
+ const now = focus.known() && !ctx.isIdle() && focus.promptText() === undefined;
16
+ if (now === waiting) return;
17
+ waiting = now;
18
+ // If the run has ended too, its own end has already said so, and this would undo that.
19
+ if (now || !ctx.isIdle()) changed(now);
20
+ } catch {
21
+ // The session was replaced before this could be stopped. An error here would end Pi.
22
+ clearInterval(timer);
23
+ }
24
+ }, CHECK_MS);
25
+ timer.unref?.();
26
+ return () => {
27
+ clearInterval(timer);
28
+ focus.stop();
29
+ };
30
+ }
@@ -0,0 +1,113 @@
1
+ # Changelog
2
+
3
+ ## 0.6.0
4
+
5
+ ### Added
6
+
7
+ - With a virtual model such as `router/auto`, the frame names the model that answered, such as `Auto → Haiku`.
8
+
9
+ ## 0.5.0
10
+
11
+ ### Added
12
+
13
+ - A checklist the agent keeps while it works, in a frame above the editor, using a new `todo` tool. One call finishes a step and starts the next, and it survives `/reload`, resuming and moving around the session tree.
14
+ - After compaction, the agent gets its unfinished list back.
15
+ - `/todos` shows the list, and `/todos clear` empties it.
16
+
17
+ ## 0.4.1
18
+
19
+ ### Changed
20
+
21
+ - Long sessions draw faster. Each response's timing line was built again on every redraw, which took over half the drawing time when typing or scrolling.
22
+
23
+ ## 0.4.0
24
+
25
+ ### Added
26
+
27
+ - `Ctrl+R` searches what you've typed, here and in this folder's other recent sessions. It takes this key from Pi's Rename; `/name` still renames.
28
+ - A `/command` or `!command` at the start of the prompt is colored.
29
+
30
+ ## 0.3.7
31
+
32
+ ### Added
33
+
34
+ - A line under each response with when the call started, to the millisecond, the wait for the first token, server latency, thinking time, total time and speed. It updates while the response is written, names any stall of 2 seconds or more, and leaves stalls out of the speed.
35
+ - Finished tool boxes show when they started and how long they took, to the millisecond.
36
+ - A **Timings** setting in the Frame tab turns both off.
37
+
38
+ ## 0.3.6
39
+
40
+ ### Changed
41
+
42
+ - The start card shows `/sandbox-disable` or `/sandbox-enable` instead of a key, and Option+S no longer turns `ß` into the sandbox's key.
43
+
44
+ ## 0.3.5
45
+
46
+ ### Changed
47
+
48
+ - Option+S turns the sandbox on and off on a Mac even when the terminal types `ß`.
49
+ - The extensions on the start card show their versions, like `pi-subagents@0.84.0`.
50
+ - Updated `diff` to 9.0.0.
51
+
52
+ ## 0.3.4
53
+
54
+ ### Changed
55
+
56
+ - Settings open with `Ctrl+S`, because `pi-sandbox` needs `Alt+S`. `Cmd+S` still works where the terminal can send it.
57
+ - The start card no longer lists effort. `/effort` is unchanged.
58
+
59
+ ### Added
60
+
61
+ - With `pi-sandbox` installed, the start card shows its on/off key, and the bottom line shows `sandbox: on` or `sandbox: off` after the mode.
62
+
63
+ ## 0.3.3
64
+
65
+ ### Changed
66
+
67
+ - The bottom line starts with the mode, then a divider and the other statuses, then the session's numbers.
68
+
69
+ ## 0.3.2
70
+
71
+ ### Changed
72
+
73
+ - The context, skills, prompts and extensions Pi loaded are rows in the start card, with `+N` for what doesn't fit. `Ctrl+O` still shows them in full, and problems still get their own frame.
74
+
75
+ ## 0.3.1
76
+
77
+ ### Added
78
+
79
+ - `Cmd+S` and `Cmd+O` on a Mac, in a terminal with the kitty keyboard protocol, and the start card says so.
80
+
81
+ ## 0.3.0
82
+
83
+ ### Added
84
+
85
+ - With a GitLab remote, the start card shows a failed pipeline and merge requests waiting on you, through `glab`.
86
+ - `Ctrl+C` while something runs stops it and says `Aborted`. With nothing running, it clears the editor and says to press it again within 2 seconds to exit.
87
+ - The red `yolo` frame for the new pi-modes mode.
88
+
89
+ ### Changed
90
+
91
+ - What Pi loaded at startup (context, skills, prompts, extensions) is drawn in frames.
92
+ - On a Mac, key hints say `option+` instead of `alt+`, or `cmd+` where it works: `Cmd+S` and `Cmd+O` in a terminal with the kitty keyboard protocol.
93
+ - A status with no color of its own, like `perm-judge:`, is drawn dim like the rest of the footer.
94
+ - When the start card's bottom edge is short of room, the key hints go before the count of what waits on you.
95
+
96
+ ## 0.2.0
97
+
98
+ ### Added
99
+
100
+ - `/effort` picks the effort, and `/effort high` sets it directly.
101
+ - With pi-modes, the editor frame's color follows the mode.
102
+
103
+ ### Changed
104
+
105
+ - The effort shows as a divider and its level, without the meter.
106
+
107
+ ### Fixed
108
+
109
+ - With pi-modes, the start card no longer says `shift+tab` changes the effort. It says `shift+tab mode`, and names `/effort` or your other effort key.
110
+
111
+ ## 0.1.0
112
+
113
+ First release, forked from `@adeildo/pi-look` 6.0.0 and renamed to `pi-frame`: the package, settings keys, session entries and start card.
@@ -0,0 +1,7 @@
1
+ # Contributing
2
+
3
+ Issues are welcome. Pull requests are turned off, because this fork follows how its maintainer works and every line needs to be written and reviewed by them.
4
+
5
+ If you have a fix, open an issue that describes the problem and how to reproduce it. For a problem that also affects `pi-look`, consider [reporting it there](https://github.com/felipeadeildo/pi-harness/issues) too.
6
+
7
+ For security problems, follow [SECURITY.md](SECURITY.md) instead.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 adeildo
4
+ Copyright (c) 2026 Hüseyin Sönmez
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,105 @@
1
+ # pi-frame
2
+
3
+ Part of [pi-suite](../../README.md). Turn it off with `/suite off frame`.
4
+
5
+ A redrawn screen for [Pi](https://pi.dev): a start card, an editor framed with the branch and the model, a box around every tool call, and a footer with what the session costs.
6
+
7
+ It started as a fork of [`@adeildo/pi-look`](https://github.com/felipeadeildo/pi-harness/tree/main/packages/look) 6.0.0 by [adeildo](https://adeildo.dev), so the design and most of the code are theirs, under the MIT license. I keep this fork so I can shape it around how I work. If you want the original with the rest of pi-harness, use that instead.
8
+
9
+ ## Quick start
10
+
11
+ Requires Pi 1.5 or newer.
12
+
13
+ 1. Remove `pi-look` if you have it, because the two draw the same screen:
14
+
15
+ ```bash
16
+ pi remove npm:@adeildo/pi-look
17
+ ```
18
+
19
+ 2. Install it:
20
+
21
+ ```bash
22
+ pi install npm:@huseynsnmz/pi-suite@0.1.0
23
+ ```
24
+
25
+ 3. Restart Pi. The start card says `π-frame`.
26
+
27
+ Your `pi-look` settings don't carry over. Press `Ctrl+S` and open the **Frame** tab to set them again.
28
+
29
+ On a Mac, `Alt` means the Option key, which needs Left Option set to `Esc+` in iTerm2, `"terminal": { "option_as_meta": true }` in Zed, or "Use Option as Meta" in Terminal.app. Otherwise Option types a character such as `ß`. In a terminal that uses the kitty keyboard protocol, such as Ghostty or kitty, `Cmd+S` and `Cmd+O` work too, and the start card says so.
30
+
31
+ ## What you see
32
+
33
+ - **Start card:** the folder, branch and model, then only what changed or waits on you since your last session in that folder: conflicts, a branch behind its remote, a failed CI run, a pull or merge request waiting on you. On a quiet day it says nothing is new. Below that, a row each for the context, skills, prompts and extensions Pi loaded, with `+N` for what doesn't fit. `Ctrl+O` shows them in full. CI and request lines use `gh` for a GitHub remote and `glab` for a GitLab one, and stay out without the tool or a network.
34
+ - **Tool boxes:** each call shows the command, who allowed it and why, the output, when it started and how long it took.
35
+ - **Timings:** a line under each response shows when the call started, the wait for the first token, server latency, thinking time, total time, any stall and the speed. It updates as the response is written. Turn it off under **Timings** in the Frame tab. An edit shows the diff, with only the changed words highlighted.
36
+ - **Todo list:** for work with several steps, the agent keeps a checklist in a frame above the editor, with what it's doing now highlighted. Finished items make way first when the list is long, and `Ctrl+O` shows all of it. A finished list leaves at your next prompt. The list stays with its branch of the session, and the agent gets it back after compaction.
37
+ - **Above the editor:** a stopwatch, the number of model calls, and how the last call went. Messages you send while the agent works wait here in a dashed frame.
38
+ - **Editor frame:** the branch, folder, model, effort and how full the context is. With [pi-modes](https://github.com/huseynsnmz/pi-suite/tree/main/packages/modes), its color follows the mode: yellow for `auto`, the accent color for `plan`, green for `read-only`, red for `yolo`, using your theme's versions of those colors. In `normal` it stays Pi's own color.
39
+ - **Footer:** first the `mode:` from [pi-modes](https://github.com/huseynsnmz/pi-suite/tree/main/packages/modes), then a divider and what other extensions report, then cost, tokens, cache and speeds.
40
+
41
+ Some pieces, such as rooms, boards, peers and the remote page, appear only with other pi-harness packages installed.
42
+
43
+ | Key | What it does |
44
+ |---|---|
45
+ | `Ctrl+C` | Stops a run and says `Aborted`. With nothing running, it clears the editor and says to press it again within 2 seconds to exit |
46
+ | `Ctrl+R` | Search what you've typed, here and in this folder's other sessions. `Ctrl+R` again goes further back, `Enter` sends, `Esc` cancels, any other key keeps the match to edit. It replaces Pi's Rename on this key; `/name` still renames |
47
+ | `Alt+O` | Scroll through the session's tool output, one call at a time |
48
+ | `Ctrl+O` | Expand every call in the transcript |
49
+ | `/todos` | Show the agent's todo list. `/todos clear` empties it and tells the agent |
50
+ | `Ctrl+S` | Settings: icons, editor frame style, framed messages. With the editor frame off, use `/harness` |
51
+ | `/effort` | Pick how hard the model thinks |
52
+ | `/effort high` | Set it directly: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` or `max`. If the model can't use that level, Pi picks the closest one and you're told |
53
+
54
+ More detail is in [How it works](docs/how-it-works.md) and the [reference](docs/reference.md).
55
+
56
+ ## Using it with other extensions
57
+
58
+ **`rpiv-todo`:** remove it if you have it, because both add a tool named `todo`.
59
+
60
+ **`pi-modes`:** version 0.6.0 or newer lets the agent use its todo list in `plan` and `read-only`. It takes over `Shift+Tab`, so the start card says `shift+tab mode`. To keep a key for effort, set `app.thinking.cycle` in `~/.pi/agent/keybindings.json`, or use `/effort`.
61
+
62
+ **`pi-sandbox`:** the bottom line shows `sandbox: on` or `sandbox: off` after the mode. Its longer status moves to `/sandbox`.
63
+
64
+ **`pi-router`:** the frame shows which model answered, such as `Auto → Haiku`.
65
+
66
+ **`pi-board`:** use `@huseynsnmz/pi-board`. The frame comes back when its board closes.
67
+
68
+ **`pi-cloak`:** no changes are needed. Tool boxes, `Alt+O` and the session file all show the masked text, and the automated run covers it.
69
+
70
+ **`pi-permission-system`, `pi-permission-classifier`:** no conflicts were found, and the automated run covers them together with `pi-modes`.
71
+
72
+ **`pi-claude-code-ui`:** it also draws tool calls, so list it above pi-suite in `packages`. `pi install` adds packages last, so this is the order you get if you install pi-frame second. Even then, it draws a call to a tool that doesn't exist outside pi-frame's box, so escape sequences in that call's arguments still reach the terminal. In the other order, or with `pi-claude-code-ui` alone, arguments of real tools leak too. To close this, remove `pi-claude-code-ui`.
73
+
74
+ ## Security limits
75
+
76
+ - **This isn't a sandbox.** Use a container for repositories you don't trust.
77
+ - **It only cleans what it draws.** Pi draws assistant messages and some entries itself.
78
+ - **`gh` and `glab` still run in their repositories,** and they call `git` for reads such as the remote list.
79
+ - **`glab` needs a host with `gitlab` in its name.** It starts for that remote, and if a token is set in the environment (`GITLAB_TOKEN`), only for `gitlab.com` or `GITLAB_HOST`, because a repository picks its own remote.
80
+ - **The `git status` protections cover the ways to run code that I know of.** New Git options could add others.
81
+ - **`@adeildo/pi-kit` is still an upstream dependency,** pinned to exact version 6.0.0. Its settings stay in `~/.pi/agent/extensions/pi-harness/settings.json`.
82
+ - **Another session's view isn't fully covered.** Opening one needs other pi-harness packages, and that view hasn't been tested.
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ npm install --ignore-scripts
88
+ npm run check
89
+ npm test # needs bun
90
+ npm run e2e # real Pi in a real terminal with a fake model, in a throwaway home directory
91
+ ```
92
+
93
+ To test with other packages, list them in `PI_FRAME_E2E_WITH`, or in `PI_FRAME_E2E_AFTER` to load them later.
94
+
95
+ ## Contributing
96
+
97
+ Issues are welcome, and pull requests are turned off. See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
98
+
99
+ ## Credits and license
100
+
101
+ MIT. See [LICENSE](LICENSE), which keeps the original copyright notice.
102
+
103
+ - [pi-look and pi-harness](https://github.com/felipeadeildo/pi-harness) by [adeildo](https://adeildo.dev)
104
+ - The framed editor comes from [pi-open-tui](https://github.com/OldSuns/pi-open-tui), by way of pi-look.
105
+ - The segments with priorities come from [oh-my-pi](https://github.com/can1357/oh-my-pi), by way of pi-look.
@@ -0,0 +1,16 @@
1
+ # Security
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please don't open a public issue for a security problem. Use **Report a vulnerability** on the repository's Security tab, or email huseyin@snmz.org.
6
+
7
+ Examples that count as security problems:
8
+
9
+ - A repository that gets code to run through what pi-frame starts, such as `git status`.
10
+ - Text from a tool, a model or a repository that reaches the terminal as a command through something pi-frame draws.
11
+
12
+ Fixes go into the latest release only.
13
+
14
+ ## What this extension does not protect against
15
+
16
+ pi-frame draws the screen and is not a sandbox. The limits are listed in the README under "Security limits". A problem that falls under one of those limits is a normal issue.
@@ -0,0 +1,53 @@
1
+ # How pi-frame works
2
+
3
+ The [README](../README.md) covers what you see. This page covers the details.
4
+
5
+ ## The boxes
6
+
7
+ Every command gets its own box, so you see it before it runs:
8
+
9
+ - The top line has the tool's name.
10
+ - Under it come the command, and who let it run and why.
11
+ - A line separates the command from what it printed.
12
+ - The bottom line says how it went and how long it took. A command that still runs says `running`.
13
+
14
+ For an `edit` or a `write`, the box shows the diff. The line numbers sit on the left, and only the words that changed get a light background. While the model is still writing the call, the box shows what has arrived so far, with a cursor at the end.
15
+
16
+ `Alt+O` opens the tool output of the session in a frame that you can scroll, one call at a time, newest first. `Ctrl+O` opens every call in the transcript at once.
17
+
18
+ ## The editor and the footer
19
+
20
+ The editor's border starts with this session's name. It also shows the branch, the folder, the permission mode, the model with its effort after a divider, and how full the context is. The border warms up as the model thinks harder, and it takes the shell's color in bash mode.
21
+
22
+ Instead of "Working", the spinner says what the model is doing: `waiting`, `thinking`, `writing`, `drafting bash` or `running bash`.
23
+
24
+ Your messages sit in a frame named `you`, like the tool calls. `frame.userFrame` turns this off.
25
+
26
+ The footer is about the session: the cost, the tokens sent and received, how much came from the cache, the average speeds, and how much of your plan is left, with when it resets.
27
+
28
+ ## Another session
29
+
30
+ With the other pi-harness packages installed, you can use the `Alt+G` panel or the remote to open another session over the whole screen. You see its transcript with the same boxes as yours, and an editor box with its folder, machine and model. The top edge says `live` while the session runs and `ended` once it closes. `Alt+G` or `Esc` takes you back.
31
+
32
+ ## Nothing jumps around
33
+
34
+ The numbers change on every frame, and the screen must not move with them.
35
+
36
+ - Every number sits in a cell of fixed width, so ` 17` and `2.8k` take the same room.
37
+ - A value that nobody knows yet shows as `–`, instead of appearing later in the middle of the line.
38
+ - The working time counts like a stopwatch: `0:59`, then `1:00`.
39
+ - An estimate is drawn in a quieter color, so you know it is one.
40
+ - The strip and the footer keep their line from the start, so the editor does not jump when the first answer arrives.
41
+
42
+ ## Details
43
+
44
+ - A tool from an MCP server shows its server and tool, instead of `mcp__server__tool`.
45
+ - A tool that brings no drawing of its own gets the same box, with its name and arguments on top.
46
+ - Pi's own entries use the same frames: compactions and branch summaries under `π`, skills, and `!` commands.
47
+ - The mark of a long phase moves: a clock while a call runs, an hourglass while it waits on you, and a spinner while the model writes it. The mark stops when the call ends.
48
+ - The π on the start card rises from its feet once per launch. pi-frame sets pi's `quietStartup` to `header`, so pi's list of what it loaded stays out of the way.
49
+ - Pi records usage without a duration, so each answer leaves a small entry with its time to the first token and its writing time. That keeps the averages right after you resume a session.
50
+
51
+ ## Your theme
52
+
53
+ pi-frame never names a color. Every piece uses a theme token, so it follows whatever theme pi runs. The [reference](reference.md#colours) lists the token for each piece.
@@ -0,0 +1,78 @@
1
+ # Reference
2
+
3
+ Everything the [README](../README.md) leaves out: the colours, every setting and segment, and how a line fits.
4
+
5
+ ## Colours
6
+
7
+ pi-frame never names a colour. Every piece names a **theme token**, so it follows whatever theme pi runs: `system`, `dark`, `light`, or a JSON of yours. Change the theme and every piece moves with it.
8
+
9
+ | What | Token |
10
+ | --------------------------------------------- | ----------------------------------------------------------- |
11
+ | Folder | `accent` |
12
+ | Branch | `syntaxFunction` |
13
+ | Ahead, staged / behind, modified / conflicted | `success` / `warning` / `error` |
14
+ | Machine, cache | `syntaxType` |
15
+ | Session name | `mdHeading` |
16
+ | Provider, model icon | `syntaxKeyword` |
17
+ | Model | `text`, bold |
18
+ | Sent `↑` | `mdLink` |
19
+ | Received `↓`, speeds, done | `success` |
20
+ | Time to first token, money | `syntaxNumber`, then `warning` past $1 and `error` past $10 |
21
+ | Context | `success`, `warning` past 70%, `error` past 90% |
22
+ | Effort level, the π | `thinkingMinimal` … `thinkingMax`, one per level |
23
+ | Labels, units, separators | `dim` |
24
+ | Editor frame | pi's effort border colour; with pi-modes, `warning` in auto, `accent` in plan, `success` in read-only |
25
+
26
+ Syntax tokens are borrowed on purpose: a theme spends its hues there, so they give the line variety that stays in the theme's family. The table lives in `src/render/paint.ts` as `ROLE_TOKENS`, and a test fails if any entry is ever a colour instead of a token.
27
+
28
+ ## Settings
29
+
30
+ They live in the file `@adeildo/pi-kit` keeps, `~/.pi/agent/extensions/pi-harness/settings.json`, under `frame.`.
31
+
32
+ | Key | Default | Does |
33
+ | ------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | `icons` | `"auto"` | `nerd`, `unicode` or `ascii`. Auto uses plain Unicode, or ASCII without UTF-8 |
35
+ | `style` | `"rounded"` | `rounded`, `square`, `heavy` or `line` (pi's two rules, written into) |
36
+ | `userFrame` | `true` | Your messages in a frame named `you`. Off, pi draws them its own way |
37
+
38
+ The rest is fixed. The line above the editor shows `elapsed` and `last`, with `board` on its right. The frame shows `branch` and `session` at the top left, `path` and `host` at the top right, the permission mode, `model` and `effort` at the bottom left, and `context` at the bottom right. The line below the editor shows `cost`, `tokens`, `cache`, `average` and `statuses`, with `quota` on its right. Every number has its word.
39
+
40
+ ### Segments
41
+
42
+ What each piece of the screen shows.
43
+
44
+ | Id | Shows |
45
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | `path` | The working folder, with home as `~` |
47
+ | `branch` | The branch, ahead `↑` and behind `↓`, staged `+`, modified `~`, untracked `?`, conflicts `!`, stashes |
48
+ | `host` | The machine, so a pasted line says where it came from |
49
+ | `session` | The session name, once it has one |
50
+ | `board` | The card being done (`+1` when there are more), then how many open cards are blocked (`⚑ 1`, in yellow), are to do (`⧗ 3`) and are done (`✓ 2`, in green), from the session's board |
51
+ | `version` | The pi version |
52
+ | `clock` | The time |
53
+ | `model` | `provider/model`, with the account in parentheses when one is in use |
54
+ | `provider` | The provider on its own |
55
+ | `quota` | Every window of the plan, with its percent and its reset |
56
+ | `effort` | A divider and the effort level. Hidden for models that do not reason |
57
+ | `context` | Context used, the gauge, tokens over the window |
58
+ | `speed` | This answer's speeds, received `↓` and sent `↑` |
59
+ | `wait` | Time to the first token |
60
+ | `server` | The last call's time from the request leaving to the response headers |
61
+ | `elapsed` | The agent's working time, as a stopwatch, and its calls |
62
+ | `last` | The last finished call: its wait, its thought time and its writing speed |
63
+ | `request` | This answer's tokens, with the cache |
64
+ | `costRate` | This answer's cost per million tokens |
65
+ | `cost` | The session's cost |
66
+ | `tokens` | The session's tokens, with the cache |
67
+ | `cache` | The cache hit of the last prompt |
68
+ | `average` | The session's average speeds, both ways |
69
+ | `statuses` | The mode and sandbox state first, then what other packages report through `setStatus`, minus the ones placed on their own |
70
+ | `status:<key>` | One package's status, by the key it passes to `setStatus` |
71
+
72
+ A `status:<key>` segment is how a package gets a place of its own. [`@adeildo/pi-ask-permission`](../ask-permission) reports its mode as `pi-ask-permission:mode`, and the mode says what happens to what you type, so by default it sits in the frame next to the model instead of in the footer with everything else. `@adeildo/pi-providers` still reports its account as `pi-providers:account`, but the `model` segment draws it in parentheses, so the statuses segment skips it.
73
+
74
+ ## How it fits
75
+
76
+ When a line runs out of room, the least important pieces take their short form first (the folder becomes its last part, the model loses its provider, the effort meter loses its label), and then leave, one at a time. The two sides of a border compete for the same width, so an unimportant piece on the right leaves before an important one on the left. A line never goes empty: the last piece stays and is cut.
77
+
78
+ The frame is the editor pi already has. Typing, history, autocomplete and every app key stay pi's; the class only changes what the lines look like and shifts mouse clicks by the width of the frame. The spinner is pi's, embedded through pi's own hook, and the working line only speaks through `setWorkingMessage`.
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@huseynsnmz/pi-frame",
3
+ "version": "0.5.0",
4
+ "description": "Pi's start screen, a framed editor, a box around every tool call and a session footer. A fork of @adeildo/pi-look.",
5
+ "keywords": [
6
+ "pi",
7
+ "pi-coding-agent",
8
+ "pi-extension",
9
+ "pi-package",
10
+ "statusline",
11
+ "theme",
12
+ "tui"
13
+ ],
14
+ "license": "MIT",
15
+ "author": {
16
+ "name": "Hüseyin Sönmez",
17
+ "email": "huseyin@snmz.org",
18
+ "url": "https://github.com/huseynsnmz"
19
+ },
20
+ "contributors": [
21
+ {
22
+ "name": "adeildo",
23
+ "url": "https://github.com/felipeadeildo"
24
+ }
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/huseynsnmz/pi-suite.git",
29
+ "directory": "packages/frame"
30
+ },
31
+ "files": [
32
+ "src",
33
+ "docs",
34
+ "README.md",
35
+ "CHANGELOG.md",
36
+ "LICENSE"
37
+ ],
38
+ "type": "module",
39
+ "exports": {
40
+ ".": "./src/index.ts"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "check": "tsc --noEmit",
47
+ "test": "bun test",
48
+ "e2e": "python3 test/e2e/run.py"
49
+ },
50
+ "dependencies": {
51
+ "@adeildo/pi-kit": "6.0.0",
52
+ "diff": "9.0.0"
53
+ },
54
+ "peerDependencies": {
55
+ "@earendil-works/pi-ai": "*",
56
+ "@earendil-works/pi-coding-agent": "*",
57
+ "@earendil-works/pi-tui": "*",
58
+ "typebox": "*"
59
+ },
60
+ "devDependencies": {
61
+ "@earendil-works/pi-ai": "1.5.0",
62
+ "@earendil-works/pi-coding-agent": "1.5.0",
63
+ "@earendil-works/pi-tui": "1.5.0",
64
+ "@types/bun": "1.4.4",
65
+ "@types/node": "26.6.6",
66
+ "typebox": "1.4.3",
67
+ "typescript": "7.0.2"
68
+ },
69
+ "pi": {
70
+ "extensions": [
71
+ "./src/index.ts"
72
+ ]
73
+ },
74
+ "private": true
75
+ }
@@ -0,0 +1,90 @@
1
+ import { processRunner, type Runner } from "./runner.ts";
2
+
3
+ export interface GitState {
4
+ ahead: number;
5
+ behind: number;
6
+ staged: number;
7
+ modified: number;
8
+ untracked: number;
9
+ conflicted: number;
10
+ stashed: number;
11
+ }
12
+
13
+ export function emptyGit(): GitState {
14
+ return { ahead: 0, behind: 0, staged: 0, modified: 0, untracked: 0, conflicted: 0, stashed: 0 };
15
+ }
16
+
17
+ export function parseStatus(output: string): GitState {
18
+ const state = emptyGit();
19
+
20
+ for (const line of output.split("\n")) {
21
+ if (line.startsWith("# branch.ab ")) {
22
+ const [, , ahead, behind] = line.split(" ");
23
+ state.ahead = Math.abs(Number(ahead ?? "0")) || 0;
24
+ state.behind = Math.abs(Number(behind ?? "0")) || 0;
25
+ } else if (line.startsWith("# stash ")) {
26
+ state.stashed = Number(line.slice("# stash ".length)) || 0;
27
+ } else if (line.startsWith("1 ") || line.startsWith("2 ")) {
28
+ // `XY` is the index column, then the tree column, with `.` for unchanged.
29
+ const index = line[2];
30
+ const tree = line[3];
31
+ if (index !== undefined && index !== ".") state.staged++;
32
+ if (tree !== undefined && tree !== ".") state.modified++;
33
+ } else if (line.startsWith("u ")) {
34
+ state.conflicted++;
35
+ } else if (line.startsWith("? ")) {
36
+ state.untracked++;
37
+ }
38
+ }
39
+
40
+ return state;
41
+ }
42
+
43
+ /**
44
+ * `git status` runs after every tool call, outside any permission check, so nothing the repository
45
+ * configures may run through it: an agent can write `.git/config`, and a downloaded folder can bring one.
46
+ */
47
+ const SAFE_STATUS = [
48
+ "-c",
49
+ "core.fsmonitor=false",
50
+ "-c",
51
+ "core.hooksPath=/dev/null",
52
+ "--no-optional-locks",
53
+ ];
54
+
55
+ /** Keys and scopes separated by NUL, as `git config --show-scope --name-only --list -z` prints them. */
56
+ export function hasRepoFilter(config: string): boolean {
57
+ const fields = config.split("\0");
58
+ for (let at = 0; at + 1 < fields.length; at += 2) {
59
+ const scope = fields[at];
60
+ if (scope !== "local" && scope !== "worktree") continue;
61
+ if (/^filter\..+\.(?:clean|smudge|process)$/is.test(fields[at + 1] ?? "")) return true;
62
+ }
63
+ return false;
64
+ }
65
+
66
+ export async function readGit(
67
+ cwd: string,
68
+ run: Runner = processRunner(cwd, 2000),
69
+ ): Promise<GitState | undefined> {
70
+ try {
71
+ // A filter's name is free, so no option can turn them all off.
72
+ const config = await run("git", ["-C", cwd, "config", "--show-scope", "--name-only", "--list", "-z"]);
73
+ if (hasRepoFilter(config)) return undefined;
74
+ return parseStatus(
75
+ await run("git", [
76
+ ...SAFE_STATUS,
77
+ "-C",
78
+ cwd,
79
+ "status",
80
+ "--porcelain=v2",
81
+ "--branch",
82
+ "--show-stash",
83
+ "--ignore-submodules=all",
84
+ ]),
85
+ );
86
+ } catch {
87
+ // Not a repository, or git did not answer: the card says nothing.
88
+ return undefined;
89
+ }
90
+ }