@bachi/pi-coder 1.0.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 +24 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/config/AGENTS.md +100 -0
- package/config/pi-statusline.json +140 -0
- package/config/settings.json +38 -0
- package/config/web-search.json +5 -0
- package/docs/README.md +14 -0
- package/docs/configuration.md +123 -0
- package/docs/development.md +177 -0
- package/docs/extensions.md +292 -0
- package/docs/handbook.zh.md +432 -0
- package/docs/installation.md +124 -0
- package/docs/themes.md +107 -0
- package/extensions/ask-user-question/answers.test.ts +104 -0
- package/extensions/ask-user-question/answers.ts +72 -0
- package/extensions/ask-user-question/dialog.test.ts +180 -0
- package/extensions/ask-user-question/dialog.ts +102 -0
- package/extensions/ask-user-question/index.ts +253 -0
- package/extensions/ask-user-question/model.test.ts +275 -0
- package/extensions/ask-user-question/model.ts +259 -0
- package/extensions/ask-user-question/schema.ts +49 -0
- package/extensions/ask-user-question/types.ts +86 -0
- package/extensions/ask-user-question/validate.test.ts +183 -0
- package/extensions/ask-user-question/validate.ts +110 -0
- package/extensions/ask-user-question/view.ts +262 -0
- package/extensions/auto-default-model/default-model.test.ts +268 -0
- package/extensions/auto-default-model/index.ts +87 -0
- package/extensions/bash-command-collapse.ts +1476 -0
- package/extensions/below-editor-after-statusline.ts +118 -0
- package/extensions/clear-command.ts +29 -0
- package/extensions/cwd-statusline.ts +39 -0
- package/extensions/exit-command.ts +59 -0
- package/extensions/fenceless-code-block/index.test.ts +208 -0
- package/extensions/fenceless-code-block/index.ts +28 -0
- package/extensions/fenceless-code-block/render.test.ts +177 -0
- package/extensions/fenceless-code-block/render.ts +142 -0
- package/extensions/folder-history.ts +197 -0
- package/extensions/init-command.ts +163 -0
- package/extensions/prompt-editor/bash-prompt.test.ts +94 -0
- package/extensions/prompt-editor/bash-prompt.ts +59 -0
- package/extensions/prompt-editor/render.test.ts +283 -0
- package/extensions/prompt-editor.ts +212 -0
- package/extensions/read-path-collapse.ts +474 -0
- package/extensions/recap/index.test.ts +348 -0
- package/extensions/recap/index.ts +462 -0
- package/extensions/recap/subagents.test.ts +144 -0
- package/extensions/recap/subagents.ts +128 -0
- package/extensions/rewind/README.md +229 -0
- package/extensions/rewind/checkpoints.test.ts +560 -0
- package/extensions/rewind/checkpoints.ts +820 -0
- package/extensions/rewind/flow.test.ts +756 -0
- package/extensions/rewind/flow.ts +362 -0
- package/extensions/rewind/index.ts +400 -0
- package/extensions/rewind/picker.ts +135 -0
- package/extensions/rewind/viewport.test.ts +76 -0
- package/extensions/rewind/viewport.ts +48 -0
- package/extensions/simple-task/gap.test.ts +147 -0
- package/extensions/simple-task/gap.ts +122 -0
- package/extensions/simple-task/index.ts +439 -0
- package/extensions/simple-task/types.ts +53 -0
- package/extensions/simple-task/widget.ts +86 -0
- package/extensions/startup-logo/header-guard.test.ts +274 -0
- package/extensions/startup-logo/header-guard.ts +166 -0
- package/extensions/startup-logo/index.test.ts +305 -0
- package/extensions/startup-logo/index.ts +194 -0
- package/extensions/startup-logo/loaded-sections.test.ts +257 -0
- package/extensions/startup-logo/loaded-sections.ts +267 -0
- package/extensions/startup-logo/logo.test.ts +124 -0
- package/extensions/startup-logo/logo.ts +124 -0
- package/extensions/statusline/footer-guard.test.ts +273 -0
- package/extensions/statusline/footer-guard.ts +171 -0
- package/extensions/statusline/git.test.ts +174 -0
- package/extensions/statusline/git.ts +142 -0
- package/extensions/statusline/index.ts +294 -0
- package/extensions/statusline/line.test.ts +316 -0
- package/extensions/statusline/line.ts +201 -0
- package/extensions/subagent-log-guard/filter.test.ts +85 -0
- package/extensions/subagent-log-guard/filter.ts +32 -0
- package/extensions/subagent-log-guard/index.ts +112 -0
- package/extensions/theme-command.ts +263 -0
- package/extensions/thinking-collapse/window.test.ts +321 -0
- package/extensions/thinking-collapse/window.ts +354 -0
- package/extensions/thinking-collapse.ts +60 -0
- package/extensions/tool-diff/title-row.test.ts +254 -0
- package/extensions/tool-diff/title-row.ts +191 -0
- package/extensions/tool-diff.ts +1276 -0
- package/extensions/working-indicator/bash-spinner.test.ts +135 -0
- package/extensions/working-indicator/bash-spinner.ts +114 -0
- package/extensions/working-indicator/index.test.ts +579 -0
- package/extensions/working-indicator/index.ts +940 -0
- package/extensions/working-indicator/spinner-frames.test.ts +219 -0
- package/extensions/working-indicator/spinner-frames.ts +156 -0
- package/extensions/working-indicator/summary-request.test.ts +195 -0
- package/extensions/working-indicator/summary-request.ts +207 -0
- package/extensions/working-indicator/working-summary.test.ts +499 -0
- package/extensions/working-indicator/working-summary.ts +375 -0
- package/package.json +71 -0
- package/themes/ayu.json +97 -0
- package/themes/catppuccin.json +103 -0
- package/themes/summer-night.json +87 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
## Layout rules pi enforces
|
|
4
|
+
|
|
5
|
+
These come from pi's extension discovery and they decide where a file may live:
|
|
6
|
+
|
|
7
|
+
| Path | Loaded as an extension? |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `extensions/*.ts`, `extensions/*.js` | **Yes** — top-level files only. |
|
|
10
|
+
| `extensions/<dir>/index.ts` or `index.js` | **Yes**. |
|
|
11
|
+
| `extensions/<dir>/*.ts` without an `index` | No. Helper modules, imported by other extensions. |
|
|
12
|
+
| `extensions/<dir>/*.test.ts` | No — only the directory's `index.ts` is loaded. |
|
|
13
|
+
| `extensions/*.test.ts` (top level) | **Yes** — pi would try to load it. Never put tests at the top level. |
|
|
14
|
+
|
|
15
|
+
`thinking-collapse/`, `tool-diff/` and `prompt-editor/` are the three helper-only directories here: `thinking-collapse.ts`, `tool-diff.ts` and `prompt-editor.ts` import them, and pi never loads them directly.
|
|
16
|
+
|
|
17
|
+
Two consequences worth remembering:
|
|
18
|
+
|
|
19
|
+
- `recap/index.ts` imports `../simple-task/gap.ts` across directories. Both must ship together.
|
|
20
|
+
- `package.json`'s `pi.extensions: ["./extensions"]` resolves a directory with exactly these rules, so the manifest and the convention directory behave identically.
|
|
21
|
+
|
|
22
|
+
## Tests
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm test # node --test — 454 tests, ~72 s
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The pure-logic modules are written so this works: they do not import `@earendil-works/pi-*` at all, take injected dependencies instead (a `widthOf` function, an `exec` function, a minimal theme interface), and are duck-typed against structural interfaces. That is why `thinking-collapse/window.ts`, `statusline/line.ts`, `tool-diff/title-row.ts`, `rewind/checkpoints.ts`, `prompt-editor/bash-prompt.ts` and the rest can run under plain `node --test`.
|
|
29
|
+
|
|
30
|
+
One test file goes the other way: [`prompt-editor/render.test.ts`](../extensions/prompt-editor/render.test.ts) loads the **real** extension through pi's own loader and asserts the `!` bash-mode render contract line by line and column by column, with only the surroundings faked (a `tui` that has just `terminal.rows` and `requestRender()`, an identity `borderColor`, keybindings that never match). It locates pi's library entry by reading the `# cmd-shim-target=` line out of the `pi` shim, and it **skips** — rather than failing or faking a pass — when pi cannot be resolved, because the copy under `~/.pi/agent/npm` is often an empty shell after `pi update --extensions`. Point it at a real entry with `PI_TEST_PI_ENTRY=/path/to/index.js`.
|
|
31
|
+
|
|
32
|
+
**Tests passing is not enough.** pi loads `.ts` with its own loader, and a construct node accepts can still fail there:
|
|
33
|
+
|
|
34
|
+
> A single invalid annotation (`readonly (readonly 0 | 1)[][]`) left 22 unit tests green while pi raised `ParseError` and refused to load the whole extension.
|
|
35
|
+
|
|
36
|
+
So every change ends with a real pi start. The cheapest reliable procedure is below.
|
|
37
|
+
|
|
38
|
+
## Running your checkout against a real pi
|
|
39
|
+
|
|
40
|
+
The extensions are already in this package, so a plain `pi -e ./path` **collides with any copy in `~/.pi/agent/extensions/`** and aborts:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Error: Failed to load extension ".../extensions/bash-command-collapse.ts":
|
|
44
|
+
Tool "bash" conflicts with .../pi-coder/extensions/bash-command-collapse.ts
|
|
45
|
+
Hint: Start without extensions using "pi -ne".
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Isolate the run instead — a scratch agent directory has no global extensions, so only the checkout loads:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
PI_CODING_AGENT_DIR=$(mktemp -d) pi -e /absolute/path/to/pi-coder
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then check that all 22 loaded by reading the startup list:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
[Extensions]
|
|
58
|
+
ask-user-question, auto-default-model, bash-command-collapse.ts, ... working-indicator
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`/reload` re-reads the checkout, so the loop is: edit → `/reload` → look. That works for `pi -e` runs as well as for an installed package; you do not need to restart pi for extension edits. `settings.json` and `AGENTS.md` are read once at startup, so those do need a restart.
|
|
62
|
+
|
|
63
|
+
For scripted checks, run pi inside tmux and grep the pane rather than trusting a single screenshot:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
tmux new-session -d -s pi-check -x 200 -y 50 \
|
|
67
|
+
"PI_CODING_AGENT_DIR=$(mktemp -d) pi -e $PWD; sleep 60"
|
|
68
|
+
sleep 15
|
|
69
|
+
tmux capture-pane -p -t pi-check | grep -i "failed to load\|parseerror"
|
|
70
|
+
tmux kill-session -t pi-check
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Traps this codebase already paid for
|
|
74
|
+
|
|
75
|
+
Everything below is documented because it cost real debugging time. The full reasoning is in the file headers named next to each item.
|
|
76
|
+
|
|
77
|
+
- **A hidden column still accepts the cursor.** `prompt-editor` hides the `!` of bash mode, but `Editor` keeps the cursor column in private state with no public setter, so the extension calls `setCursorCol(1)` directly and degrades to "the cursor stays at column 0" if pi ever renames it — a cosmetic regression only. Letting the cursor sit on the hidden column writes `x!ls` into the text, at which point pi decides it is no longer bash mode.
|
|
78
|
+
- **A `ctx` captured before a session replacement goes stale**, and reading `ctx.ui` throws `This extension ctx is stale after session replacement or reload`. The throw happens when you read the property, before any widget `render()` runs, so a `try/catch` inside `render()` cannot catch it. A timer that outlives the session takes the host process down with it (`exit=1`). `simple-task/` and `working-indicator/` therefore all three: catch inside the callback and stop the timer, wrap every `ctx.ui` access, and stop timers in `session_shutdown`.
|
|
79
|
+
- **A throwing `renderCall` is silently swallowed** and replaced by `createCallFallback()`: something disappears from the UI and nothing is logged.
|
|
80
|
+
- **Tool registration is first-registration-wins per name.** A second extension registering `bash` is ignored without a warning — which is why everything that shapes `bash` rendering lives in one file.
|
|
81
|
+
- **`keyHint` and `keyText` must not be imported from the package root.** In the bundled CLI, `@earendil-works/pi-coding-agent` is aliased to a different module instance, so the extension gets another copy of the stateful APIs (`Theme not initialized`, or an empty string). Read key names from `~/.pi/agent/keybindings.json` instead. `startup-logo` is the one file that imports from the package root, wrapped in a `try/catch`.
|
|
82
|
+
- **Patching a pi-tui prototype works; patching the copy in `node_modules` does nothing** — silently. pi's bundled loader points extensions at its own inlined namespace, which is why `fenceless-code-block/` can patch `Markdown.prototype` and `index.test.ts` can assert it with pi's own renderer.
|
|
83
|
+
- **`usage.output` is always `0` while streaming**, so token counts must be estimated from streamed characters.
|
|
84
|
+
- **`renderResult` receives no `isError`**; read it from `context`. Reading `result.isError` silently paints failures as successes.
|
|
85
|
+
|
|
86
|
+
## Adding an extension
|
|
87
|
+
|
|
88
|
+
1. Decide the shape: a single `extensions/<name>.ts`, or `extensions/<name>/index.ts` plus helper modules.
|
|
89
|
+
2. Export `default function (pi: ExtensionAPI)`.
|
|
90
|
+
3. Put logic that deserves tests in a module that imports nothing from pi, and inject what it needs.
|
|
91
|
+
4. Add a header comment: what problem it solves, which pi internals it depends on, what you tried that did not work. These headers are the reason this package is maintainable.
|
|
92
|
+
5. `npm test`, then the tmux check above.
|
|
93
|
+
6. If the extension has a switch, follow the existing convention: `PI_<NAME>=off` disables it, read on each use rather than cached at load, and document it in the README table and [extensions.md](extensions.md).
|
|
94
|
+
|
|
95
|
+
A new tool name and a new command name must not collide with any other extension here; the list is in [extensions.md](extensions.md#commands).
|
|
96
|
+
|
|
97
|
+
## Keeping this package in sync
|
|
98
|
+
|
|
99
|
+
This package is a distribution copy, not the master copy. The author's live environment is `~/.pi/agent/`, snapshotted into a separate repository under `clients/pi/`; this package was produced by copying that snapshot verbatim (extensions, themes, and the config files) with two deliberate deltas:
|
|
100
|
+
|
|
101
|
+
1. `config/models.json` is not shipped, and the three model-selection keys were removed from `config/settings.json` (`defaultProvider`, `defaultModel`, `modelThinkingLevels`). See [configuration.md](configuration.md#what-is-not-shipped).
|
|
102
|
+
2. `docs/handbook.zh.md` is the snapshot's README, kept verbatim as the Chinese handbook.
|
|
103
|
+
|
|
104
|
+
So when the extensions change upstream:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
SRC=/Users/bachi/jaylli/litellm-any/clients/pi # the snapshot the extension lives in
|
|
108
|
+
DST=/Users/bachi/jaylli/pi-coder # this package
|
|
109
|
+
cp -R "$SRC/extensions/." "$DST/extensions/"
|
|
110
|
+
cp "$SRC/themes/"*.json "$DST/themes/"
|
|
111
|
+
diff -r "$SRC/extensions" "$DST/extensions" # expect: no output
|
|
112
|
+
npm test
|
|
113
|
+
# bump "version" in package.json, add a CHANGELOG entry
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Keep the copies byte-identical. The only files that should ever differ from the snapshot are `config/settings.json` (the removed model keys) and anything under `docs/`.
|
|
117
|
+
|
|
118
|
+
## Publishing
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm login # first time only
|
|
122
|
+
npm pack --dry-run # inspect the tarball before publishing
|
|
123
|
+
npm publish
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- The name is scoped, so `publishConfig.access: "public"` is already set — without it npm refuses to publish a scoped package publicly on a free account.
|
|
127
|
+
- `keywords` includes `pi-package`, which is what makes the package appear in the [pi.dev gallery](https://pi.dev/packages). Keep it.
|
|
128
|
+
- `files` decides the tarball contents; nothing else is uploaded. `.git` and `node_modules` are never included.
|
|
129
|
+
- Bump `version` before each publish (npm rejects re-publishing an existing version) and add the corresponding `CHANGELOG.md` entry.
|
|
130
|
+
- To test the exact artifact that would be published, pack it and install the **extracted directory** — pi treats a file path as a single extension, so a `.tgz` path fails with `Unknown file extension ".tgz"`:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm pack
|
|
134
|
+
mkdir -p /tmp/pi-pkg && tar -xzf bachi-pi-coder-1.0.0.tgz -C /tmp/pi-pkg
|
|
135
|
+
PI_CODING_AGENT_DIR=$(mktemp -d) pi install /tmp/pi-pkg/package
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## How the package appears on pi.dev
|
|
139
|
+
|
|
140
|
+
The [package catalog](https://pi.dev/packages) is indexed from npm — there is no submission form or upload endpoint (`/api/*` answers `501 API routes are reserved for future features`). Publishing to npm with `pi-package` in `keywords` is the whole mechanism; the crawl picks the package up within minutes and it appears in the *Recently published* feed and in the full list.
|
|
141
|
+
|
|
142
|
+
The **detail page renders this README as its body**, so `README.md` is the gallery landing page, not just npm metadata:
|
|
143
|
+
|
|
144
|
+
- Relative links (`docs/extensions.md`, `extensions/tool-diff.ts`) are rewritten against the `repository` field, so they resolve in the gallery — both `https://github.com/jayli/pi-coder/blob/main/docs/...` and a jsDelivr CDN form are used.
|
|
145
|
+
- The page leads with the description, badges, and any `pi.image` / `pi.video` preview, then the README.
|
|
146
|
+
- Resource chips (`extension`, `theme`, …) come from the `pi` manifest, so an accurate manifest is also accurate marketing.
|
|
147
|
+
- The catalog adds a `report` link to `earendil-works/pi` issues automatically.
|
|
148
|
+
|
|
149
|
+
### Post-publish checklist
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm view @bachi/pi-coder version --registry=https://registry.npmjs.org # the version you just pushed
|
|
153
|
+
curl -s -o /dev/null -w '%{http_code}\n' https://pi.dev/packages/@bachi/pi-coder # 404 before indexing, 200 after
|
|
154
|
+
pi install npm:@bachi/pi-coder
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Once the package exists on npm, two optional additions become safe (they render as broken until then):
|
|
158
|
+
|
|
159
|
+
1. **Badges** at the top of the README, as the reference packages do:
|
|
160
|
+
|
|
161
|
+
```markdown
|
|
162
|
+

|
|
163
|
+

|
|
164
|
+

|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
2. **A preview asset**, which is what makes a TUI package legible in the gallery. Upload a screenshot (PNG/JPEG/GIF/WebP) or a screencast (MP4 only) — a `github.com/user-attachments/...` URL from a README upload works — then declare it:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
"pi": {
|
|
171
|
+
"extensions": ["./extensions"],
|
|
172
|
+
"themes": ["./themes"],
|
|
173
|
+
"video": "https://github.com/user-attachments/assets/..."
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`video` takes precedence over `image` when both are set; on desktop the video autoplays on hover and opens fullscreen on click. Re-publish after changing the manifest.
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Extensions reference
|
|
2
|
+
|
|
3
|
+
22 extensions load from this package. Twelve are single files in `extensions/`, ten are directories whose entry point is `index.ts`. Three more directories (`thinking-collapse/`, `tool-diff/`, `prompt-editor/`) contain pure-logic modules only — they have no `index.ts`, so pi never loads them as extensions, but the top-level files import them.
|
|
4
|
+
|
|
5
|
+
Every extension is also documented in its own header comment (Chinese, except `rewind/`): the pi internals it relies on, the failure that motivated it and the trade-offs that are not visible in the code. This page is the map.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
| Command | Extension | Arguments |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `/ask` | `ask-user-question` | — Previews the questionnaire with a demo question. |
|
|
12
|
+
| `/bash-collapse` | `bash-command-collapse` | `off` \| `on` \| `<1-50>` — Collapsed visual lines for the command. |
|
|
13
|
+
| `/bash-preview` | `bash-command-collapse` | `off` \| `<1-50>` — Output preview lines; `off` restores pi's built-in preview. |
|
|
14
|
+
| `/bash-stream` | `bash-command-collapse` | `off` \| `on` — `on` uses pi's native streaming instead of the collapse path. |
|
|
15
|
+
| `/bash-timeout` | `bash-command-collapse` | — Prints the default, maximum and env-overridden bash timeout. |
|
|
16
|
+
| `/bash-tree` | `bash-command-collapse` | `off` \| `on` — Tree indentation (`│`/`└`) for bash output. |
|
|
17
|
+
| `/clear` | `clear-command` | — Alias of `/new`. |
|
|
18
|
+
| `/exit` | `exit-command` | — Alias of `/quit` (the argument-free form of the quit words). |
|
|
19
|
+
| `/init` | `init-command` | `[file.md] [extra instructions]` |
|
|
20
|
+
| `/read-collapse` | `read-path-collapse` | `off` \| `on` |
|
|
21
|
+
| `/recap` | `recap` | — Summarizes the conversation now. |
|
|
22
|
+
| `/rewind` | `rewind` | — Checkpoint menu; also Esc Esc at an empty prompt. |
|
|
23
|
+
| `/tasks` | `simple-task` | `status` (default) \| `clear` \| `on` \| `off` |
|
|
24
|
+
| `/theme` | `theme-command` | `[name]` — Without arguments: picker with live preview. |
|
|
25
|
+
|
|
26
|
+
## Tool overrides
|
|
27
|
+
|
|
28
|
+
pi registers one handler per tool name (first registration wins), so each of these owns a builtin tool outright.
|
|
29
|
+
|
|
30
|
+
### `bash-command-collapse.ts` — the `bash` tool
|
|
31
|
+
|
|
32
|
+
Collapses long commands to N visual lines (default 3) followed by `… (123 tokens hidden)`, hard-wrapping at the column budget the way CSS `word-break: break-all` does rather than pre-wrapping whole words: a 78-column path fills the line completely and breaks at the edge. `ctrl+o` expansion shows the command in full. The extension also draws its own background box, tree-indents output, syntax-highlights the command line, and can give bash output its own color through the `bashOutput` theme token ([themes.md](themes.md#bashoutput-in-detail)).
|
|
33
|
+
|
|
34
|
+
- `PI_BASH_MIN_TIME_MS` (default `2000`) — only show the elapsed-time footer above this duration.
|
|
35
|
+
- `PI_BASH_HIGHLIGHT=off` — disable shell syntax highlighting.
|
|
36
|
+
- `PI_BASH_TREE=off` — disable tree indentation.
|
|
37
|
+
- `PI_BASH_SPINNER=off` — disable the `●` on running rows (implemented in `working-indicator`).
|
|
38
|
+
|
|
39
|
+
Two details that look simplified but cannot be: it decides "arguments are still streaming" from `!streaming && !argsComplete && isPartial === true` (both thresholds are required, or `/resume` replays lose the command line entirely), and `isError` must be read from `context`, not `result`, because pi's result renderer is called without that field.
|
|
40
|
+
|
|
41
|
+
### `read-path-collapse.ts` — the `read` tool
|
|
42
|
+
|
|
43
|
+
Keeps the `read` title row on exactly one line. Long paths lose their front and keep the informative tail — the file name and last directories — as `Read …@earendil-works/pi-coding-agent/dist/core/extensions/loader.js:62-116`. No folding, no second row.
|
|
44
|
+
|
|
45
|
+
- `/read-collapse off | on`, `PI_READ_COLLAPSE=off` — restore pi's builtin title row.
|
|
46
|
+
|
|
47
|
+
### `tool-diff.ts` — the `edit` and `write` tools
|
|
48
|
+
|
|
49
|
+
Claude Code style diffs: full-line background for added and removed lines (including the line-number gutter), inline highlight of the changed span, and syntax highlighting. Both tools are re-registered with `renderShell: "self"`, which is what makes per-line backgrounds possible — the default shell paints whole blocks by status and would cover them.
|
|
50
|
+
|
|
51
|
+
The two line backgrounds come from theme tokens that pi's official schema does not define: `toolDiffAddedBg` and `toolDiffRemovedBg`. When a theme omits them, the extension falls back to the much flatter `toolSuccessBg` / `toolErrorBg`. See [themes.md](themes.md#custom-tokens).
|
|
52
|
+
|
|
53
|
+
### `thinking-collapse.ts` — thinking blocks
|
|
54
|
+
|
|
55
|
+
Registered as a markdown transformer for `assistant-thinking`. Every thinking block renders as **one continuous line**, no matter how many paragraphs, list items or fenced blocks the model wrote:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Think: …latest token keeps appending at the end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Nothing is line-wrapped. When the line exceeds the terminal width, characters are dropped **from the front** and a leading `…` is added, so the end of the line is always the newest token.
|
|
62
|
+
- At paragraph seams (blank lines in the original) a Chinese comma is inserted between two Chinese paragraphs; a paragraph that already ends in punctuation is left alone, and Latin text keeps the space rule.
|
|
63
|
+
- There is deliberately no "backfill to fill the row" logic, and no `… (N tokens hidden)` notice — the spinner below already counts tokens.
|
|
64
|
+
- No command, no environment switch. The only form is this one.
|
|
65
|
+
|
|
66
|
+
### `fenceless-code-block/` — markdown code blocks
|
|
67
|
+
|
|
68
|
+
Removes code fences, including the language label, and lays the code out with pi's own indentation and syntax colors — no background is added. It works by patching `Markdown.prototype.renderToken` at module evaluation time, which is effective because pi's bundled loader points extensions at its own inlined `@earendil-works/pi-tui` namespace. Installing twice (after `/reload`) wraps only once.
|
|
69
|
+
|
|
70
|
+
- `PI_FENCELESS_CODE=off` — keep the fences.
|
|
71
|
+
|
|
72
|
+
## TUI chrome
|
|
73
|
+
|
|
74
|
+
### `statusline/` — the footer
|
|
75
|
+
|
|
76
|
+
Replaces pi's footer with one status line and one status row:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
⚡️ qwen3.8-flash/xhigh | Ctx 0.0% | main | (+0,-0)
|
|
80
|
+
📁 /Users/you/project
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The main row shows model/thinking level, context usage, git branch and diff stat; when the working directory is not a git repository it says `no git`. The second row renders whatever other extensions pass to `ctx.ui.setStatus()` (this is where `cwd-statusline`, `simple-task` and `rewind` write). Lines are truncated, never wrapped. Git reads happen on a debounced background path (400 ms after `turn_end`/`agent_end`/`tool_execution_end`, immediately on branch change, with a 30 s fallback poll) so the render path is a map lookup.
|
|
84
|
+
|
|
85
|
+
- `PI_STATUSLINE_FREEZE=off` — disable the footer freeze. On every session switch pi unconditionally restores its builtin footer and clears all `setStatus` values, and no extension hook runs before that frame. The guard replays the previous frame's lines instead, which removes a visible flash. Turning it off restores the flash.
|
|
86
|
+
- No config file. Colors come from `theme.fg(...)`, so `/theme` repaints on the next frame.
|
|
87
|
+
|
|
88
|
+
### `cwd-statusline.ts` — full working directory
|
|
89
|
+
|
|
90
|
+
Prints the complete `ctx.cwd` as an extension status line, deliberately uncompressed: no `~` shortening, no truncation of middle segments. Only terminal width truncates it, with an ellipsis, never a wrap.
|
|
91
|
+
|
|
92
|
+
- `PI_CWD_STATUSLINE=off`, `PI_CWD_ICON` (default ` 📁`).
|
|
93
|
+
|
|
94
|
+
### `startup-logo/` — the header
|
|
95
|
+
|
|
96
|
+
Replaces pi's header with a static pi logo, the version and the working directory (shortened to `~/...` inside the home directory), keeping the compact key hints below so no information is lost. It also prunes the `[Context]`, `[Prompts]` and `[Themes]` sections from the startup resource list — those three carry no information — while keeping `[Skills]`, `[Extensions]` and every diagnostic section. Pruning works by locating the mounted header component, so it only happens when the logo is installed.
|
|
97
|
+
|
|
98
|
+
The logo is static by design: no frame table, no timers, no `requestRender`. Narrow terminals degrade to a single-line wordmark.
|
|
99
|
+
|
|
100
|
+
- `PI_LOGO=off` — do not install the header.
|
|
101
|
+
- `header-guard.ts` freezes the header across session switches for the same reason the statusline freeze exists: pi restores its builtin header first, and that frame is visible.
|
|
102
|
+
|
|
103
|
+
### `below-editor-after-statusline.ts` — widget placement
|
|
104
|
+
|
|
105
|
+
pi mounts `belowEditor` widgets between the editor and the footer, which pushes the statusline to the very bottom of the screen. `pi-subagents`' fleet status line is registered that way. This extension finds the container holding the probe widget by object identity (never by index) and moves it to the end, so the fleet line sinks below the statusline and the editor keeps the statusline next to it.
|
|
106
|
+
|
|
107
|
+
The probe must be registered with `placement: "belowEditor"`. Omitting it silently moves the *upper* container instead, with no runtime error. Nothing happens if the container cannot be found.
|
|
108
|
+
|
|
109
|
+
- `PI_BELOW_EDITOR_AFTER_STATUSLINE=off`.
|
|
110
|
+
|
|
111
|
+
### `prompt-editor.ts` — the input box
|
|
112
|
+
|
|
113
|
+
Three changes to the editor.
|
|
114
|
+
|
|
115
|
+
**A `❯ ` gutter.** The real editing area is narrowed and the gutter is re-added per line, so cursor placement, IME positioning and mouse clicks all stay correct.
|
|
116
|
+
|
|
117
|
+
**A blank line** between a visible autocomplete list and the statusline, added only when the list is actually rendered (judged by the public `isShowingAutocomplete()`), so the static layout is unchanged.
|
|
118
|
+
|
|
119
|
+
**`!` bash mode**, matching Claude Code: when the prompt starts with `!` the gutter shows `!` instead of `❯` and the `!` you typed is hidden, so the body reads as the command itself. The mode is render-only — not a single character of the text changes. Detection copies pi's own (`text.trimStart().startsWith("!")`, the same flag that colors the editor border), and Enter submission, ↑ history and Esc clearing keep going through pi's own paths, so there is nothing to keep in sync. Hiding a column has two consequences: the body shifts one column left, so mouse clicks count one extra column, and the cursor has to be pushed off column 0 — otherwise the reverse-video cursor lands on the blank column, and typing there would inject `x!ls` into the text and drop pi out of bash mode. Leaving the mode needs no code: backspacing over the `!`, submitting, or Esc all make pi's own `isBashMode` false again and the next frame draws `❯`. `PI_EDITOR_PROMPT` changes the `❯` but not the bash `!`.
|
|
120
|
+
|
|
121
|
+
- `PI_EDITOR_PROMPT` (default `❯`), `PI_EDITOR_AUTOCOMPLETE_GAP=off`, `PI_EDITOR_AUTOCOMPLETE_SHIFT` (default 1 column).
|
|
122
|
+
- Pure logic lives in [`prompt-editor/bash-prompt.ts`](../extensions/prompt-editor/bash-prompt.ts); the render contract is covered by [`prompt-editor/render.test.ts`](../extensions/prompt-editor/render.test.ts), which loads the real extension through pi's own loader.
|
|
123
|
+
|
|
124
|
+
### `working-indicator/` — the working message
|
|
125
|
+
|
|
126
|
+
Replaces the fixed `Working` loader with a semantic label, a token count for the current segment and an elapsed time:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
Tools Calling (↓ 70 tokens · 10s) Editing (↓ 40 tokens · 3s)
|
|
130
|
+
Writing (↓ 120 tokens · 8s) Reading (↓ 12 tokens · 1s)
|
|
131
|
+
Thinking (↓ 900 tokens · 22s) Working (5s)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The token count is **per segment**, not per turn: each reasoning segment and each tool-argument segment starts from zero, so a `bash` command's count reflects that command. Body text is deliberately not counted. `usage.output` is always `0` while streaming, so counts are estimated from streamed characters. Elapsed time is `42s`, `1m 23s` or `1h 23m 32s`. The label uses the normal foreground color while the statistics stay muted, which requires composing everything into the single string pi receives.
|
|
135
|
+
|
|
136
|
+
The same extension draws the `●` on a running bash row.
|
|
137
|
+
|
|
138
|
+
- `PI_BASH_SPINNER=off`, `PI_SPINNER_RAINBOW=off`, `PI_SPINNER_COLOR_HOLD` (default `19` frames per color).
|
|
139
|
+
- `PI_WORKING_SUMMARY=off` — disable the prompt summary line entirely.
|
|
140
|
+
- `PI_WORKING_SUMMARY_LLM=off` — truncate long prompts instead of asking a model to compress them.
|
|
141
|
+
- `PI_WORKING_SUMMARY_TRIGGER` (default `1.2`) — ask for a summary when the prompt exceeds the available width by this factor.
|
|
142
|
+
- `PI_WORKING_SUMMARY_MODEL` — `provider/modelId` for that request; defaults to the session model so a typo can only cost the summary, never the request.
|
|
143
|
+
- `PI_WORKING_SUMMARY_GAP` (default `1`).
|
|
144
|
+
|
|
145
|
+
## Workflow
|
|
146
|
+
|
|
147
|
+
### `simple-task/` — task list
|
|
148
|
+
|
|
149
|
+
A lightweight task list: `task_set`, `task_update`, `task_get` and `/tasks`. Three states (`pending`, `in_progress`, `done`), no blocks, no dependency graph, no notes, no title-length validation.
|
|
150
|
+
|
|
151
|
+
State is written with `pi.appendEntry()`, so it rides the session log and **nothing is written into your repository** — no `.pi/tasks/*.json` to gitignore. Rebuilding reads `ctx.sessionManager.getBranch()`, not `getEntries()`, so branch navigation cannot resurrect a discarded branch's tasks.
|
|
152
|
+
|
|
153
|
+
`/tasks` with no argument or `status` prints the list, `clear` empties it, `on` / `off` toggle the widget.
|
|
154
|
+
|
|
155
|
+
### `recap/` — conversation summary
|
|
156
|
+
|
|
157
|
+
`/recap` summarizes the conversation on demand; the same summary appears automatically above the editor after **30 seconds of idling** with no new input, and disappears as soon as you type.
|
|
158
|
+
|
|
159
|
+
The delay is the point: the recap exists to tell you what a session was doing when you come back to the window, so it is idle-based rather than turn-based. The timer first asks whether any subagent is still running (an in-process RPC to `pi-subagents`, no file import — a missing package is treated as "no subagents") so a background delegation is never summarized as finished.
|
|
160
|
+
|
|
161
|
+
Deliberately not implemented: no local storage, no session entry, no configuration. The summary lives in memory only, so `/new` or `/resume` does not restore it and it is never sent to the model as context. The summary text itself is **generated in Chinese** (the prompt is hardcoded), which is worth knowing if you do not read Chinese.
|
|
162
|
+
|
|
163
|
+
### `rewind/` — checkpoints and `/rewind`
|
|
164
|
+
|
|
165
|
+
Claude Code style checkpointing. Before every prompt that starts a turn, the working tree is snapshotted into a **shadow git repository** under `~/.pi/agent/rewind/<project-hash>/git` with `GIT_DIR` pointed at it and `GIT_WORK_TREE` at your project. Your repository's HEAD, index, refs and status are never touched, and this works in directories that are not git repositories at all.
|
|
166
|
+
|
|
167
|
+
Files the snapshot cannot see — anything `edit`/`write` touches outside the project root, inside it but `.gitignore`d, or inside a nested repository — are covered by lazy pre-image mirroring driven by tool-call events, with blobs addressed by content.
|
|
168
|
+
|
|
169
|
+
`/rewind`, or Esc Esc at an empty prompt, opens a menu: restore code and conversation, conversation only, code only, summarize from here, or never mind. Conversation restore uses pi's native session-tree navigation, which drops the selected user message and puts its text back into the editor.
|
|
170
|
+
|
|
171
|
+
- **Requires `doubleEscapeAction: "none"`** in `settings.json`. The extension warns once at session start if the built-in tree navigator would fire instead. It consumes the second Esc (the selector takes focus synchronously, so letting it through would cancel the menu it just opened) while leaving the first Esc alone, so Esc still aborts streaming.
|
|
172
|
+
- Known limits: only the `edit` and `write` tools are tracked (`bash` writes outside the root cannot be parsed), and files larger than 8 MB are not copied.
|
|
173
|
+
|
|
174
|
+
### `init-command.ts` — `/init`
|
|
175
|
+
|
|
176
|
+
Claude Code style repository memory file generation. Target selection looks only at `ctx.cwd`:
|
|
177
|
+
|
|
178
|
+
1. `CLAUDE.md` exists → update `CLAUDE.md`
|
|
179
|
+
2. else `AGENTS.md` exists → update `AGENTS.md`
|
|
180
|
+
3. else create `AGENTS.md`
|
|
181
|
+
|
|
182
|
+
`/init <file.md> [extra instructions]` overrides the target and appends your requirements. The extension writes nothing itself: it resolves the target and sends a prompt as a user message, so the model's own `read`/`write`/`edit` calls do the work and you can watch and correct them. It waits for the current turn to finish before sending.
|
|
183
|
+
|
|
184
|
+
### `theme-command.ts` — `/theme`
|
|
185
|
+
|
|
186
|
+
A one-step theme picker with live preview. Arrow keys preview, Enter persists, Esc cancels. `/theme <name>` switches and persists directly.
|
|
187
|
+
|
|
188
|
+
The preview works because `ctx.ui.setTheme()` has two distinct paths: passing a **Theme object** only recolors the running UI (`setThemeInstance()`), while passing a **name** applies it and immediately writes `settings.json` (`setThemeName()`). So browsing never touches your settings, and only Enter does. In non-TUI modes the command notifies instead of silently failing.
|
|
189
|
+
|
|
190
|
+
### `folder-history.ts` — cross-session command history
|
|
191
|
+
|
|
192
|
+
Persists command history per working directory in `~/.pi/folder-history/<path-with-dashes>.jsonl` and injects previous sessions' entries into the editor's own history array, which makes the **native ↑/↓** walk across sessions.
|
|
193
|
+
|
|
194
|
+
The mechanism matters: previous sessions' entries are appended to the tail of `Editor.history` (tail = older), so ↑ goes further back in time. No shortcut is registered — a registered `up` key would swallow cursor movement in multi-line prompts and arrow navigation in every selector. `PI_FOLDER_HISTORY_INJECT` (default `100`) caps how many entries come from earlier sessions.
|
|
195
|
+
|
|
196
|
+
### `clear-command.ts` — `/clear`
|
|
197
|
+
|
|
198
|
+
Alias of `/new` implemented through `ctx.newSession()` — the same replacement flow the builtin uses, so behavior and on-disk format match. It calls `ctx.waitForIdle()` first so an in-flight turn (including retries and auto-compaction) is never racing the session swap.
|
|
199
|
+
|
|
200
|
+
### `exit-command.ts` — quit words
|
|
201
|
+
|
|
202
|
+
Typing `exit`, `quit` or `bye` as the entire prompt quits pi cleanly (sessions are saved; `session_shutdown` still runs). Matching is exact and case-insensitive, so "exit the loop and print a summary" is untouched, and messages with attachments pass through. It only applies in TUI mode: in `--print`, `--mode json` and RPC mode these remain ordinary prompts. Also registers `/exit` as an alias of `/quit`.
|
|
203
|
+
|
|
204
|
+
- `PI_EXIT_WORDS="exit,quit"` — replace the words; `off` disables the interception.
|
|
205
|
+
|
|
206
|
+
## Model and tooling
|
|
207
|
+
|
|
208
|
+
### `auto-default-model/` — persistent model switches
|
|
209
|
+
|
|
210
|
+
pi's `/model` picker only changes the current session; persisting it takes a separate Ctrl+S (`setModel(model, { persist: true })`). This extension performs that step automatically on every model switch — the picker, Ctrl+P cycling, a subagent profile switch, anything that calls `pi.setModel()`.
|
|
211
|
+
|
|
212
|
+
It writes through pi's own `SettingsManager`, so it uses the same file lock as pi (`proper-lockfile`), merges only the changed fields into the newest on-disk content, and therefore cannot clobber concurrent `/theme` or `/settings` writes. It skips session restore (`source === "restore"`) and skips no-op writes. Failures are notified; successes are silent, because the model name is already visible in the statusline.
|
|
213
|
+
|
|
214
|
+
- `PI_AUTO_DEFAULT_MODEL=off`.
|
|
215
|
+
|
|
216
|
+
### `ask-user-question/` — the `ask_user_question` tool
|
|
217
|
+
|
|
218
|
+
A Claude Code style structured question tool. The model asks instead of guessing; a questionnaire appears in the terminal with up to **4 questions**, each with **2–4** described options, a free-text row appended automatically, Space to multi-select, ↑/↓ to navigate and Esc to abandon the whole questionnaire. Answers return to the model as structured text.
|
|
219
|
+
|
|
220
|
+
The labels `Other` and `Type something.` are reserved — validation rejects them — and the number of questions and options is enforced by the tool's TypeBox schema, while string length limits are enforced by runtime truncation. Non-TUI hosts (RPC, print) fall back to sequential `select`/`input` dialogs. The tool removes itself in child sessions where `ctx.hasUI` is false.
|
|
221
|
+
|
|
222
|
+
- `PI_ASK_USER_QUESTION=off` — do not register the tool.
|
|
223
|
+
- `/ask` previews the dialog with a demo questionnaire.
|
|
224
|
+
|
|
225
|
+
### `subagent-log-guard/` — stderr guard
|
|
226
|
+
|
|
227
|
+
`pi-subagents` prints launch diagnostics such as `[pi-subagents] Agent 'researcher': host runtime tool availability omitted [...]` with `console.warn`. In interactive mode pi does not take over stdout/stderr, so that text is written straight into the alternate screen at the hardware cursor — right on top of the editor row — and the differential renderer will not repaint it. The result is permanent garbage across the input box.
|
|
228
|
+
|
|
229
|
+
This extension wraps `process.stderr.write` in processes that have a UI and stops lines beginning with `[pi-subagents]` from reaching the terminal. Only that prefix is filtered; everything else writes through untouched. Processes without a UI (RPC, print, subagent runners) are not patched at all, which is why background subagent diagnostics still land in `runner.stderr.log`.
|
|
230
|
+
|
|
231
|
+
- `PI_SUBAGENT_LOG_GUARD=notify` — route the messages through `ctx.ui.notify(..., "warning")` instead of dropping them.
|
|
232
|
+
- `PI_SUBAGENT_LOG_GUARD=off` — remove the guard (useful when tracing who printed a line).
|
|
233
|
+
|
|
234
|
+
## Environment switches
|
|
235
|
+
|
|
236
|
+
Every switch is an environment variable read at use time, not cached at load, so it can be scoped per project or set in a shell alias. An unset variable means "on"; `off` always disables.
|
|
237
|
+
|
|
238
|
+
| Variable | Default | Owning extension | Effect |
|
|
239
|
+
| --- | --- | --- | --- |
|
|
240
|
+
| `PI_ASK_USER_QUESTION=off` | on | `ask-user-question` | Do not register the `ask_user_question` tool. |
|
|
241
|
+
| `PI_AUTO_DEFAULT_MODEL=off` | on | `auto-default-model` | Do not persist model switches to `settings.json`. |
|
|
242
|
+
| `PI_BASH_HIGHLIGHT=off` | on | `bash-command-collapse` | Disable shell syntax highlighting in bash title rows. |
|
|
243
|
+
| `PI_BASH_MIN_TIME_MS` | `2000` | `bash-command-collapse` | Only show the elapsed-time footer above this duration. |
|
|
244
|
+
| `PI_BASH_PREVIEW` | `3` | `bash-command-collapse` | bash output preview lines (1–50); `off` restores pi's built-in preview. |
|
|
245
|
+
| `PI_BASH_SPINNER=off` | on | `working-indicator` | Disable the `●` spinner on running bash rows. |
|
|
246
|
+
| `PI_BASH_STREAM=on` | off | `bash-command-collapse` | Use pi's native streaming for bash instead of the collapse path. |
|
|
247
|
+
| `PI_BASH_TREE=off` | on | `bash-command-collapse` | Disable tree indentation (`│`/`└`) for bash output. |
|
|
248
|
+
| `PI_BELOW_EDITOR_AFTER_STATUSLINE=off` | on | `below-editor-after-statusline` | Leave `belowEditor` widgets where pi puts them. |
|
|
249
|
+
| `PI_CWD_ICON` | ` 📁` | `cwd-statusline` | Icon used by the cwd status line. |
|
|
250
|
+
| `PI_CWD_STATUSLINE=off` | on | `cwd-statusline` | Do not print the cwd status line. |
|
|
251
|
+
| `PI_EDITOR_AUTOCOMPLETE_GAP=off` | on | `prompt-editor` | Do not add the blank line under the autocomplete list. |
|
|
252
|
+
| `PI_EDITOR_AUTOCOMPLETE_SHIFT` | `1` | `prompt-editor` | Columns to shift the autocomplete list left. |
|
|
253
|
+
| `PI_EDITOR_PROMPT` | `❯` | `prompt-editor` | Editor prompt character. The bash-mode `!` is not affected. |
|
|
254
|
+
| `PI_EXIT_WORDS` | `exit,quit,bye` | `exit-command` | Comma-separated quit words; `off` disables the input interception. |
|
|
255
|
+
| `PI_FENCELESS_CODE=off` | on | `fenceless-code-block` | Keep Markdown code fences. |
|
|
256
|
+
| `PI_FOLDER_HISTORY_INJECT` | `100` | `folder-history` | History entries injected from previous sessions. |
|
|
257
|
+
| `PI_LOGO=off` | on | `startup-logo` | Do not install the startup header. |
|
|
258
|
+
| `PI_READ_COLLAPSE=off` | on | `read-path-collapse` | Keep pi's built-in `read` title row. |
|
|
259
|
+
| `PI_SPINNER_COLOR_HOLD` | `19` | `working-indicator` | Frames per color in the spinner cycle. |
|
|
260
|
+
| `PI_SPINNER_RAINBOW=off` | on | `working-indicator` | Disable the rainbow spinner. |
|
|
261
|
+
| `PI_STATUSLINE_FREEZE=off` | on | `statusline` | Disable the footer freeze that hides the one-frame flash on session switch. |
|
|
262
|
+
| `PI_SUBAGENT_LOG_GUARD` | `drop` | `subagent-log-guard` | `notify` shows the diagnostics through `ctx.ui.notify`; `off` disables the guard. |
|
|
263
|
+
| `PI_WORKING_SUMMARY=off` | on | `working-indicator` | Disable the prompt summary line. |
|
|
264
|
+
| `PI_WORKING_SUMMARY_GAP` | `1` | `working-indicator` | Minimum blank columns between the working label and the summary. |
|
|
265
|
+
| `PI_WORKING_SUMMARY_LLM=off` | on | `working-indicator` | Truncate the summary instead of asking a model to compress it. |
|
|
266
|
+
| `PI_WORKING_SUMMARY_MODEL` | session model | `working-indicator` | `provider/modelId` used for the summary request. |
|
|
267
|
+
| `PI_WORKING_SUMMARY_TRIGGER` | `1.2` | `working-indicator` | Ask for a summary when the prompt exceeds this multiple of the available width. |
|
|
268
|
+
|
|
269
|
+
## Extension interactions
|
|
270
|
+
|
|
271
|
+
- **Esc Esc is shared.** `rewind` replaces pi's built-in double-Escape action and needs `doubleEscapeAction: "none"`; see above.
|
|
272
|
+
- **The `bash` tool can only be registered once.** Everything that shapes its rendering lives in `bash-command-collapse.ts` for that reason — a second file registering `bash` would be ignored silently.
|
|
273
|
+
- **`recap` imports `simple-task/gap.ts`.** The neighbour-gap heuristic is shared rather than duplicated, so `recap` and `simple-task` must be installed together. In this package they always are; if you copy extensions individually, copy both.
|
|
274
|
+
- **The theme preview and the theme files are coupled.** `/theme` persists the name it previewed, and the name must match the `theme` field's expectations in [themes.md](themes.md).
|
|
275
|
+
- **Three extensions read theme tokens that pi's schema does not define** (`toolDiffAddedBg`, `toolDiffRemovedBg`, `bashOutput`) and degrade quietly when a theme omits them.
|
|
276
|
+
|
|
277
|
+
## State on disk
|
|
278
|
+
|
|
279
|
+
| Location | Written by | Contents |
|
|
280
|
+
| --- | --- | --- |
|
|
281
|
+
| `~/.pi/agent/settings.json` | `auto-default-model` | `defaultProvider` / `defaultModel` on every model switch. |
|
|
282
|
+
| `~/.pi/agent/rewind/<project-hash>/git` | `rewind` | Shadow git repository with pre-turn snapshots. Never touched by your repository. |
|
|
283
|
+
| `~/.pi/folder-history/<path-with-dashes>.jsonl` | `folder-history` | Command history per working directory. |
|
|
284
|
+
| Session log (via `appendEntry`) | `simple-task` | Task list state; discarded with the session, never written to the repo. |
|
|
285
|
+
| In memory only | `recap` | The current summary; lost on `/new` or `/resume` by design. |
|
|
286
|
+
| Nothing | everything else | The remaining extensions are pure display or event wiring. |
|
|
287
|
+
|
|
288
|
+
## Adding, disabling and removing extensions
|
|
289
|
+
|
|
290
|
+
- **Disable one** — `pi config` lists every resource from packages and local directories with an on/off toggle, in global or project scope. Or set the switch listed above when the extension has one.
|
|
291
|
+
- **Remove one** — delete its file (or its directory) from the package, or copy the ones you want into `~/.pi/agent/extensions/` and stop installing the package. Deleting subdirectories is safe except for the directories other files import: the helper-only `thinking-collapse/`, `tool-diff/` and `prompt-editor/`, and `simple-task/`, whose `gap.ts` is imported by `recap`.
|
|
292
|
+
- **Edit one** — work in a checkout and run pi against it; see [development.md](development.md).
|