pi-startup-redraw-fix 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.
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/config/config.example.json +3 -0
- package/index.ts +3 -0
- package/package.json +57 -0
- package/src/constants.ts +2 -0
- package/src/index.ts +18 -0
- package/src/normalize-clear-sequence.ts +9 -0
- package/src/terminal-clear-patch.ts +44 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MasuRii
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# pi-startup-redraw-fix
|
|
2
|
+
|
|
3
|
+
Startup redraw / full-clear ordering fix for the Pi coding agent.
|
|
4
|
+
|
|
5
|
+
This extension patches the terminal “full clear” escape sequence emitted during startup so the screen/scrollback clear happens in a stable order, avoiding redraw glitches in some terminal setups.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Normalizes the startup full-clear escape sequence order:
|
|
12
|
+
- **From:** `\x1b[3J\x1b[2J\x1b[H`
|
|
13
|
+
- **To:** `\x1b[H\x1b[2J\x1b[3J`
|
|
14
|
+
- Applies the patch **once per Node process** by monkey-patching `ProcessTerminal.prototype.write`.
|
|
15
|
+
- On `session_start`, shows a **UI warning** (when UI is available) if patching failed.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
### Local extension folder
|
|
20
|
+
|
|
21
|
+
Place this folder in one of Pi’s auto-discovered extension locations:
|
|
22
|
+
|
|
23
|
+
- Global: `~/.pi/agent/extensions/pi-startup-redraw-fix`
|
|
24
|
+
- Project: `.pi/extensions/pi-startup-redraw-fix`
|
|
25
|
+
|
|
26
|
+
If you keep it elsewhere, add the path to your Pi settings `extensions` array.
|
|
27
|
+
|
|
28
|
+
### As an npm package
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pi install npm:pi-startup-redraw-fix
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or from git:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pi install git:github.com/MasuRii/pi-startup-redraw-fix
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
(For development or non-Pi environments, `npm i pi-startup-redraw-fix` also works.)
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
This extension has **no commands**.
|
|
45
|
+
|
|
46
|
+
When the extension is loaded, it immediately attempts to patch `ProcessTerminal.prototype.write`. On each new session (`session_start`), if Pi has a UI and the patch did not apply, it emits a warning notification.
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
### Enable / disable
|
|
51
|
+
|
|
52
|
+
Runtime config is stored alongside the extension at:
|
|
53
|
+
|
|
54
|
+
- `~/.pi/agent/extensions/pi-startup-redraw-fix/config.json`
|
|
55
|
+
|
|
56
|
+
A starter template is included at:
|
|
57
|
+
|
|
58
|
+
- `config/config.example.json`
|
|
59
|
+
|
|
60
|
+
Minimal config:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"enabled": true
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Notes:
|
|
69
|
+
|
|
70
|
+
- In environments where Pi honors an extension-local `enabled` flag, set `"enabled": false` to disable the extension.
|
|
71
|
+
- If your Pi build does not use `enabled` for extension loading, disable by removing the extension from your Pi settings `extensions` list (or by uninstalling it).
|
|
72
|
+
|
|
73
|
+
## How it works (high level)
|
|
74
|
+
|
|
75
|
+
Pi’s TUI uses `@mariozechner/pi-tui`’s `ProcessTerminal` to write escape sequences to the terminal.
|
|
76
|
+
|
|
77
|
+
This extension:
|
|
78
|
+
|
|
79
|
+
1. Wraps `ProcessTerminal.prototype.write`.
|
|
80
|
+
2. For each write, replaces any occurrence of the exact “broken” full-clear sequence:
|
|
81
|
+
- `ESC [ 3 J` (clear scrollback)
|
|
82
|
+
- `ESC [ 2 J` (clear screen)
|
|
83
|
+
- `ESC [ H` (cursor home)
|
|
84
|
+
3. With the “fixed” order:
|
|
85
|
+
- `ESC [ H` (cursor home)
|
|
86
|
+
- `ESC [ 2 J` (clear screen)
|
|
87
|
+
- `ESC [ 3 J` (clear scrollback)
|
|
88
|
+
|
|
89
|
+
The patch is guarded by an internal flag so it is **idempotent** (loading the extension multiple times will not stack-wrap `write`).
|
|
90
|
+
|
|
91
|
+
## Limitations / compatibility
|
|
92
|
+
|
|
93
|
+
- Only affects data written via `ProcessTerminal.prototype.write`.
|
|
94
|
+
- Only rewrites the **exact** byte sequence `\x1b[3J\x1b[2J\x1b[H`.
|
|
95
|
+
- If your terminal redraw issue is caused by different escape sequences, this extension will not help.
|
|
96
|
+
- If `ProcessTerminal.write` is not available (or the underlying API changes), the patch will fail and you’ll see a warning on `session_start` (when UI is available).
|
|
97
|
+
|
|
98
|
+
## Troubleshooting
|
|
99
|
+
|
|
100
|
+
### I see a warning: “failed to patch terminal clear sequence …”
|
|
101
|
+
|
|
102
|
+
This warning is shown on `session_start` when:
|
|
103
|
+
|
|
104
|
+
- Pi has a UI (`ctx.hasUI`), and
|
|
105
|
+
- the extension could not patch `ProcessTerminal.prototype.write`.
|
|
106
|
+
|
|
107
|
+
Common causes:
|
|
108
|
+
|
|
109
|
+
- Incompatible Pi / `@mariozechner/pi-tui` version where `ProcessTerminal.write` is missing or changed.
|
|
110
|
+
- Another extension or runtime modified the terminal layer in an unexpected way.
|
|
111
|
+
|
|
112
|
+
What to try:
|
|
113
|
+
|
|
114
|
+
1. Update Pi and your extensions.
|
|
115
|
+
2. Temporarily disable other terminal/TUI-related extensions to identify conflicts.
|
|
116
|
+
3. Verify the extension is actually being loaded (installed to an auto-discovery folder or referenced in your Pi settings).
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm run build
|
|
122
|
+
npm run lint
|
|
123
|
+
npm run test
|
|
124
|
+
npm run check
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Project layout
|
|
128
|
+
|
|
129
|
+
- `index.ts` — root entrypoint (kept for Pi auto-discovery / package export)
|
|
130
|
+
- `src/index.ts` — extension bootstrap + `session_start` warning notification
|
|
131
|
+
- `src/terminal-clear-patch.ts` — `ProcessTerminal.prototype.write` monkey-patch + idempotency guard
|
|
132
|
+
- `src/normalize-clear-sequence.ts` — string rewrite helper
|
|
133
|
+
- `src/constants.ts` — escape sequence constants
|
|
134
|
+
- `config/config.example.json` — starter config template
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-startup-redraw-fix",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pi extension that patches terminal full-clear ordering to avoid startup redraw glitches.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.ts",
|
|
12
|
+
"src",
|
|
13
|
+
"config/config.example.json",
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noCheck",
|
|
20
|
+
"lint": "npm run build",
|
|
21
|
+
"test": "node --test",
|
|
22
|
+
"check": "npm run lint && npm run test"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"pi-package",
|
|
26
|
+
"pi",
|
|
27
|
+
"pi-extension",
|
|
28
|
+
"terminal",
|
|
29
|
+
"startup",
|
|
30
|
+
"redraw"
|
|
31
|
+
],
|
|
32
|
+
"author": "MasuRii",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/MasuRii/pi-startup-redraw-fix.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/MasuRii/pi-startup-redraw-fix/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/MasuRii/pi-startup-redraw-fix#readme",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"pi": {
|
|
49
|
+
"extensions": [
|
|
50
|
+
"./index.ts"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
55
|
+
"@mariozechner/pi-tui": "*"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/constants.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
import { applyTerminalClearSequencePatch } from "./terminal-clear-patch.js";
|
|
4
|
+
|
|
5
|
+
export default function startupRedrawFixExtension(pi: ExtensionAPI): void {
|
|
6
|
+
const patchResult = applyTerminalClearSequencePatch();
|
|
7
|
+
|
|
8
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
9
|
+
if (!ctx.hasUI) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!patchResult.patched && !patchResult.alreadyPatched) {
|
|
14
|
+
const reason = patchResult.error ?? "unknown error";
|
|
15
|
+
ctx.ui.notify(`startup-redraw-fix: failed to patch terminal clear sequence (${reason})`, "warning");
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BROKEN_FULL_CLEAR_SEQUENCE, FIXED_FULL_CLEAR_SEQUENCE } from "./constants.js";
|
|
2
|
+
|
|
3
|
+
export function normalizeTerminalClearSequence(data: string): string {
|
|
4
|
+
if (!data.includes(BROKEN_FULL_CLEAR_SEQUENCE)) {
|
|
5
|
+
return data;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return data.split(BROKEN_FULL_CLEAR_SEQUENCE).join(FIXED_FULL_CLEAR_SEQUENCE);
|
|
9
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ProcessTerminal } from "@mariozechner/pi-tui";
|
|
2
|
+
|
|
3
|
+
import { normalizeTerminalClearSequence } from "./normalize-clear-sequence.js";
|
|
4
|
+
|
|
5
|
+
const PATCH_FLAG_KEY = "__piStartupRedrawFixPatched__" as const;
|
|
6
|
+
|
|
7
|
+
type ProcessTerminalPrototype = typeof ProcessTerminal.prototype & {
|
|
8
|
+
[PATCH_FLAG_KEY]?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface PatchResult {
|
|
12
|
+
patched: boolean;
|
|
13
|
+
alreadyPatched: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function applyTerminalClearSequencePatch(): PatchResult {
|
|
18
|
+
const prototype = ProcessTerminal.prototype as ProcessTerminalPrototype;
|
|
19
|
+
|
|
20
|
+
if (prototype[PATCH_FLAG_KEY]) {
|
|
21
|
+
return { patched: false, alreadyPatched: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const originalWrite = prototype.write;
|
|
25
|
+
if (typeof originalWrite !== "function") {
|
|
26
|
+
return {
|
|
27
|
+
patched: false,
|
|
28
|
+
alreadyPatched: false,
|
|
29
|
+
error: "ProcessTerminal.write is unavailable",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
prototype.write = function patchedProcessTerminalWrite(data: string): void {
|
|
34
|
+
const normalized = typeof data === "string"
|
|
35
|
+
? normalizeTerminalClearSequence(data)
|
|
36
|
+
: data;
|
|
37
|
+
|
|
38
|
+
originalWrite.call(this, normalized);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
prototype[PATCH_FLAG_KEY] = true;
|
|
42
|
+
|
|
43
|
+
return { patched: true, alreadyPatched: false };
|
|
44
|
+
}
|