claudescreenfix-hardwicksoftware 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jonhardwick-spec
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
+ # claudescreenfix-hardwicksoftware | [justcalljon.pro](https://justcalljon.pro/)
2
+
3
+ yo this fixes the scroll glitch that's been cooking everyone using claude code. you know the one - after like 30 minutes your terminal starts lagging, scrolling takes forever, and eventually the whole thing just dies.
4
+
5
+ ## shoutout to the big dogs who should probably just fix this themselves
6
+
7
+ hey [@anthropics](https://github.com/anthropics) yall made a sick product but this scrollback thing been cooking people for months lol. maybe steal this fix idc
8
+
9
+ - [@karpathy](https://github.com/karpathy) - andrej you literally built tesla autopilot you could prob fix this in 5 min
10
+ - [@gdb](https://github.com/gdb) - greg brockman openai president, yall next with codex btw dont make the same mistake
11
+ - [@yoheinakajima](https://github.com/yoheinakajima) - babyagi goat, agent builders been struggling with this
12
+ - [@lllyasviel](https://github.com/lllyasviel) - controlnet legend, sd-forge too, you know terminal pain
13
+ - [@sama](https://github.com/sama) - sam you should try claude code sometime since chatgpt cant even write a working script without refusing half the prompts. also sora slapping watermarks on everything is straight clown behavior, nobody asked for that. yall so worried about "safety" you forgot to make stuff that actually works. openai been mid lately fr
14
+
15
+ real talk tho [@anthropics](https://github.com/anthropics) just add `\x1b[3J` to your clear command and debounce SIGWINCH. thats literally it. im not even asking for credit just fix it for everyone 💀
16
+
17
+ ## what's the problem
18
+
19
+ so here's the deal. claude code uses ink (it's like react but for terminals). every time something updates, ink re-renders everything. that's fine normally.
20
+
21
+ but here's where it gets ugly - it doesn't clear the scrollback buffer. ever. not once.
22
+
23
+ so after a while you've got thousands of lines sitting in your terminal's memory. every single re-render has to process all of em. resize your window? that triggers a re-render too. tmux users get hit especially hard cuz resize events fire like crazy with no chill.
24
+
25
+ the result: your terminal slows to a crawl. scrolling back takes 30+ seconds. your fans spin up. it's bad. real talk it's been annoying everyone.
26
+
27
+ ## what this does
28
+
29
+ hooks into node's stdout at startup and does three things:
30
+
31
+ 1. **clears scrollback periodically** - every 500 renders or 60 seconds, whichever comes first. your buffer won't grow forever anymore
32
+ 2. **debounces resize events** - instead of firing 50 times a second, it waits 150ms for things to settle. tmux users you're welcome
33
+ 3. **actually clears on /clear** - the /clear command only clears the screen, not scrollback. we fix that. it's kinda wild they didn't do this already
34
+
35
+ no patches to claude code itself. works with any version. just loads before claude starts and you're good.
36
+
37
+ ## install
38
+
39
+ ```bash
40
+ npm install -g claudescreenfix-hardwicksoftware
41
+ ```
42
+
43
+ that's it. you don't need anything else.
44
+
45
+ ## usage
46
+
47
+ ### option 1: use the wrapper (easiest)
48
+
49
+ ```bash
50
+ claude-fixed
51
+ ```
52
+
53
+ instead of running `claude`, run `claude-fixed`. it finds your claude install and runs it with the fix loaded. couldn't be simpler.
54
+
55
+ ### option 2: alias it
56
+
57
+ throw this in your `.bashrc` or `.zshrc`:
58
+
59
+ ```bash
60
+ alias claude='claude-fixed'
61
+ ```
62
+
63
+ now `claude` automatically uses the fix. you won't even notice it's there.
64
+
65
+ ### option 3: manual loading
66
+
67
+ if you're the type who wants full control:
68
+
69
+ ```bash
70
+ node --require claudescreenfix-hardwicksoftware/loader.cjs $(which claude)
71
+ ```
72
+
73
+ or set NODE_OPTIONS if that's more your style:
74
+
75
+ ```bash
76
+ export NODE_OPTIONS="--require $(npm root -g)/claudescreenfix-hardwicksoftware/loader.cjs"
77
+ claude
78
+ ```
79
+
80
+ ## config
81
+
82
+ here's what you can tweak via env vars:
83
+
84
+ | var | what it does | default |
85
+ |-----|--------------|---------|
86
+ | `CLAUDE_TERMINAL_FIX_DEBUG` | set to `1` for debug logs | off |
87
+ | `CLAUDE_TERMINAL_FIX_DISABLED` | set to `1` to disable entirely | off |
88
+
89
+ ## api
90
+
91
+ if you wanna use it programmatically here's how:
92
+
93
+ ```javascript
94
+ const fix = require('claudescreenfix-hardwicksoftware');
95
+
96
+ // install the fix (usually done automatically via loader)
97
+ fix.install();
98
+
99
+ // manually clear scrollback whenever you want
100
+ fix.clearScrollback();
101
+
102
+ // check what's going on
103
+ console.log(fix.getStats());
104
+
105
+ // tweak config at runtime
106
+ fix.setConfig('periodicClearMs', 30000); // clear every 30s instead
107
+
108
+ // turn it off if you need to
109
+ fix.disable();
110
+ ```
111
+
112
+ ## how it works
113
+
114
+ the fix hooks `process.stdout.write` before claude loads. when ink writes to the terminal, we check if it's doing a screen clear (which happens on every re-render). after enough renders, we inject the ANSI escape sequence `\x1b[3J` which tells the terminal to dump its scrollback buffer.
115
+
116
+ for resize events, we intercept `process.on('SIGWINCH', ...)` and debounce the handlers. instead of firing immediately, we wait 150ms. if more resize events come in during that window, we reset the timer. only fires once things settle down.
117
+
118
+ bottom line: smooth terminal, no lag, no memory bloat. it just works.
119
+
120
+ ## known issues
121
+
122
+ - some old terminals don't support `\x1b[3J` but that's pretty rare nowadays
123
+ - if you actually want to keep your scrollback history, this ain't for you
124
+ - debug mode writes to stderr which might look weird in some setups
125
+
126
+ ## what this fixes
127
+
128
+ people have been complaining about:
129
+ - terminal lag after long sessions - fixed
130
+ - scrollback buffer growing unbounded - fixed
131
+ - resize causing massive lag in tmux/screen - fixed
132
+ - /clear not actually clearing everything - fixed
133
+
134
+ you shouldn't have to restart claude every 30 minutes anymore.
135
+
136
+ ## license
137
+
138
+ MIT - do whatever you want with it
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * wrapper script - runs claude with the terminal fix loaded
6
+ *
7
+ * finds your claude binary and runs it with our fix injected
8
+ * no manual setup needed, just run claude-fixed instead of claude
9
+ */
10
+
11
+ const { spawn, execSync } = require('child_process');
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+
15
+ // find the loader path
16
+ const loaderPath = path.join(__dirname, '..', 'loader.cjs');
17
+
18
+ if (!fs.existsSync(loaderPath)) {
19
+ console.error('loader not found at ' + loaderPath);
20
+ process.exit(1);
21
+ }
22
+
23
+ // find claude binary
24
+ let claudeBin;
25
+ try {
26
+ claudeBin = execSync('which claude', { encoding: 'utf8' }).trim();
27
+ } catch (e) {
28
+ console.error('claude not found in PATH - make sure its installed');
29
+ process.exit(1);
30
+ }
31
+
32
+ // run claude with our fix loaded via NODE_OPTIONS
33
+ const env = Object.assign({}, process.env, {
34
+ NODE_OPTIONS: '--require ' + loaderPath + ' ' + (process.env.NODE_OPTIONS || '')
35
+ });
36
+
37
+ const child = spawn(claudeBin, process.argv.slice(2), {
38
+ stdio: 'inherit',
39
+ env: env
40
+ });
41
+
42
+ child.on('exit', (code) => {
43
+ process.exit(code || 0);
44
+ });
package/index.cjs ADDED
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * claudescreenfix-hardwicksoftware - stops the scroll glitch from cooking your terminal
5
+ *
6
+ * the problem:
7
+ * claude code uses ink (react for terminals) and it dont clear scrollback
8
+ * so after like 30 min your terminal got thousands of lines in the buffer
9
+ * every re-render touches ALL of em - O(n) where n keeps growing
10
+ * resize events fire with no debounce so tmux/screen users get cooked
11
+ *
12
+ * what we do:
13
+ * - hook stdout.write to inject scrollback clears periodically
14
+ * - debounce SIGWINCH so resize aint thrashing
15
+ * - enhance /clear to actually clear scrollback not just the screen
16
+ */
17
+
18
+ const CLEAR_SCROLLBACK = '\x1b[3J';
19
+ const CURSOR_SAVE = '\x1b[s';
20
+ const CURSOR_RESTORE = '\x1b[u';
21
+ const CLEAR_SCREEN = '\x1b[2J';
22
+ const HOME_CURSOR = '\x1b[H';
23
+
24
+ // config - tweak these if needed
25
+ const config = {
26
+ resizeDebounceMs: 150, // how long to wait before firing resize
27
+ periodicClearMs: 60000, // clear scrollback every 60s
28
+ clearAfterRenders: 500, // or after 500 render cycles
29
+ debug: process.env.CLAUDE_TERMINAL_FIX_DEBUG === '1',
30
+ disabled: process.env.CLAUDE_TERMINAL_FIX_DISABLED === '1'
31
+ };
32
+
33
+ // state tracking
34
+ let renderCount = 0;
35
+ let lastResizeTime = 0;
36
+ let resizeTimeout = null;
37
+ let originalWrite = null;
38
+ let installed = false;
39
+
40
+ function log(...args) {
41
+ if (config.debug) {
42
+ process.stderr.write('[terminal-fix] ' + args.join(' ') + '\n');
43
+ }
44
+ }
45
+
46
+ /**
47
+ * installs the fix - hooks into stdout and sigwinch
48
+ * call this once at startup, calling again is a no-op
49
+ */
50
+ function install() {
51
+ if (installed || config.disabled) {
52
+ if (config.disabled) log('disabled via env var');
53
+ return;
54
+ }
55
+
56
+ originalWrite = process.stdout.write.bind(process.stdout);
57
+
58
+ // hook stdout.write - this is where the magic happens
59
+ process.stdout.write = function(chunk, encoding, callback) {
60
+ if (typeof chunk === 'string') {
61
+ renderCount++;
62
+
63
+ // ink clears screen before re-render, we piggyback on that
64
+ if (chunk.includes(CLEAR_SCREEN) || chunk.includes(HOME_CURSOR)) {
65
+ if (config.clearAfterRenders > 0 && renderCount >= config.clearAfterRenders) {
66
+ log('clearing scrollback after ' + renderCount + ' renders');
67
+ renderCount = 0;
68
+ chunk = CLEAR_SCROLLBACK + chunk;
69
+ }
70
+ }
71
+
72
+ // /clear command should actually clear everything
73
+ if (chunk.includes('Conversation cleared') || chunk.includes('Chat cleared')) {
74
+ log('/clear detected, nuking scrollback');
75
+ chunk = CLEAR_SCROLLBACK + chunk;
76
+ }
77
+ }
78
+
79
+ return originalWrite(chunk, encoding, callback);
80
+ };
81
+
82
+ // debounce resize events - tmux users know the pain
83
+ installResizeDebounce();
84
+
85
+ // periodic cleanup so long sessions dont get cooked
86
+ if (config.periodicClearMs > 0) {
87
+ setInterval(() => {
88
+ if (process.stdout.isTTY) {
89
+ log('periodic scrollback clear');
90
+ originalWrite(CURSOR_SAVE + CLEAR_SCROLLBACK + CURSOR_RESTORE);
91
+ }
92
+ }, config.periodicClearMs);
93
+ }
94
+
95
+ installed = true;
96
+ log('installed successfully');
97
+ }
98
+
99
+ function installResizeDebounce() {
100
+ const originalOn = process.on.bind(process);
101
+ let sigwinchHandlers = [];
102
+
103
+ function debouncedSigwinch() {
104
+ const now = Date.now();
105
+ const timeSince = now - lastResizeTime;
106
+ lastResizeTime = now;
107
+
108
+ if (resizeTimeout) clearTimeout(resizeTimeout);
109
+
110
+ // if events coming too fast, batch em
111
+ if (timeSince < config.resizeDebounceMs) {
112
+ resizeTimeout = setTimeout(() => {
113
+ log('firing debounced resize');
114
+ sigwinchHandlers.forEach(h => { try { h(); } catch(e) {} });
115
+ }, config.resizeDebounceMs);
116
+ } else {
117
+ sigwinchHandlers.forEach(h => { try { h(); } catch(e) {} });
118
+ }
119
+ }
120
+
121
+ process.on = function(event, handler) {
122
+ if (event === 'SIGWINCH') {
123
+ sigwinchHandlers.push(handler);
124
+ if (sigwinchHandlers.length === 1) {
125
+ originalOn('SIGWINCH', debouncedSigwinch);
126
+ }
127
+ return this;
128
+ }
129
+ return originalOn(event, handler);
130
+ };
131
+
132
+ log('resize debounce installed');
133
+ }
134
+
135
+ /**
136
+ * manually clear scrollback - call this whenever you want
137
+ */
138
+ function clearScrollback() {
139
+ if (originalWrite) {
140
+ originalWrite(CLEAR_SCROLLBACK);
141
+ } else {
142
+ process.stdout.write(CLEAR_SCROLLBACK);
143
+ }
144
+ log('manual scrollback clear');
145
+ }
146
+
147
+ /**
148
+ * get current stats for debugging
149
+ */
150
+ function getStats() {
151
+ return {
152
+ renderCount,
153
+ lastResizeTime,
154
+ installed,
155
+ config
156
+ };
157
+ }
158
+
159
+ /**
160
+ * update config at runtime
161
+ */
162
+ function setConfig(key, value) {
163
+ if (key in config) {
164
+ config[key] = value;
165
+ log('config updated: ' + key + ' = ' + value);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * disable the fix (mostly for testing)
171
+ */
172
+ function disable() {
173
+ if (originalWrite) {
174
+ process.stdout.write = originalWrite;
175
+ log('disabled');
176
+ }
177
+ }
178
+
179
+ module.exports = {
180
+ install,
181
+ clearScrollback,
182
+ getStats,
183
+ setConfig,
184
+ disable,
185
+ config
186
+ };
package/loader.cjs ADDED
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * loader for --require usage
5
+ *
6
+ * use like: node --require claudescreenfix-hardwicksoftware/loader.cjs $(which claude)
7
+ *
8
+ * this auto-installs the fix before claude code even starts
9
+ * no code changes needed in claude itself
10
+ */
11
+
12
+ const fix = require('./index.cjs');
13
+
14
+ // install immediately on require
15
+ fix.install();
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "claudescreenfix-hardwicksoftware",
3
+ "version": "1.0.0",
4
+ "description": "fixes the scroll glitch in claude code cli - unbounded scrollback, resize thrashing, all that bs",
5
+ "main": "index.cjs",
6
+ "bin": {
7
+ "claude-fixed": "./bin/claude-fixed.js"
8
+ },
9
+ "scripts": {},
10
+ "keywords": [
11
+ "claude",
12
+ "terminal",
13
+ "fix",
14
+ "scroll",
15
+ "ink",
16
+ "cli",
17
+ "glitch",
18
+ "performance"
19
+ ],
20
+ "author": "jonhardwick-spec",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/jonhardwick-spec/claudescreenfix-hardwicksoftware"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/jonhardwick-spec/claudescreenfix-hardwicksoftware/issues"
28
+ },
29
+ "engines": {
30
+ "node": ">=16.0.0"
31
+ },
32
+ "files": [
33
+ "index.cjs",
34
+ "loader.cjs",
35
+ "bin/",
36
+ "README.md",
37
+ "LICENSE"
38
+ ]
39
+ }