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 ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Standardized repository structure with `src/` layout and root shim entrypoint.
6
+ - Kept terminal clear-sequence patch behavior unchanged.
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
+ ![alt text](asset/pi-startup-redraw-fix.png)
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
@@ -0,0 +1,3 @@
1
+ {
2
+ "enabled": true
3
+ }
package/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import startupRedrawFixExtension from "./src/index.js";
2
+
3
+ export default startupRedrawFixExtension;
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
+ }
@@ -0,0 +1,2 @@
1
+ export const BROKEN_FULL_CLEAR_SEQUENCE = "\x1b[3J\x1b[2J\x1b[H";
2
+ export const FIXED_FULL_CLEAR_SEQUENCE = "\x1b[H\x1b[2J\x1b[3J";
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
+ }