claude-highlight 1.0.0 → 1.0.2
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/README.md +54 -392
- package/dist/claude-highlight-egzaxzbg.js +72 -0
- package/dist/claude-highlight.js +164 -308
- package/dist/pty-bun-2y4wmvve.js +159 -0
- package/dist/pty-node-6j2dj9ta.js +63 -0
- package/dist/pty-read-worker.js +2 -1
- package/dist/pty-write-worker.js +2 -1
- package/image.png +0 -0
- package/man/claude-highlight.1 +6 -3
- package/package.json +17 -12
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
':' //; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
2
|
+
import {
|
|
3
|
+
environ
|
|
4
|
+
} from "./claude-highlight-egzaxzbg.js";
|
|
5
|
+
|
|
6
|
+
// src/pty-bun.ts
|
|
7
|
+
import { closeSync } from "node:fs";
|
|
8
|
+
|
|
9
|
+
// src/sysffi.ts
|
|
10
|
+
import { dlopen, FFIType } from "bun:ffi";
|
|
11
|
+
var APPLE_VARARGS = process.platform === "darwin" && process.arch === "arm64";
|
|
12
|
+
var IOCTL_PAD = [
|
|
13
|
+
FFIType.u64,
|
|
14
|
+
FFIType.u64,
|
|
15
|
+
FFIType.u64,
|
|
16
|
+
FFIType.u64,
|
|
17
|
+
FFIType.u64,
|
|
18
|
+
FFIType.u64
|
|
19
|
+
];
|
|
20
|
+
var IOCTL = {
|
|
21
|
+
ioctl: {
|
|
22
|
+
args: APPLE_VARARGS ? [FFIType.i32, FFIType.u64, ...IOCTL_PAD, FFIType.ptr] : [FFIType.i32, FFIType.u64, FFIType.ptr],
|
|
23
|
+
returns: FFIType.i32
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var CORE = {
|
|
27
|
+
read: { args: [FFIType.i32, FFIType.ptr, FFIType.u64], returns: FFIType.i64 },
|
|
28
|
+
write: { args: [FFIType.i32, FFIType.ptr, FFIType.u64], returns: FFIType.i64 },
|
|
29
|
+
close: { args: [FFIType.i32], returns: FFIType.i32 },
|
|
30
|
+
kill: { args: [FFIType.i32, FFIType.i32], returns: FFIType.i32 },
|
|
31
|
+
tcgetattr: { args: [FFIType.i32, FFIType.ptr], returns: FFIType.i32 },
|
|
32
|
+
tcsetattr: { args: [FFIType.i32, FFIType.i32, FFIType.ptr], returns: FFIType.i32 },
|
|
33
|
+
cfmakeraw: { args: [FFIType.ptr], returns: FFIType.i32 }
|
|
34
|
+
};
|
|
35
|
+
var OPENPTY = {
|
|
36
|
+
openpty: {
|
|
37
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr],
|
|
38
|
+
returns: FFIType.i32
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function bind(symbols) {
|
|
42
|
+
return symbols;
|
|
43
|
+
}
|
|
44
|
+
function load() {
|
|
45
|
+
if (process.platform === "darwin") {
|
|
46
|
+
const lib = dlopen("/usr/lib/libSystem.dylib", { ...CORE, ...IOCTL, ...OPENPTY });
|
|
47
|
+
const syms = bind(lib.symbols);
|
|
48
|
+
return { core: syms, openpty: syms };
|
|
49
|
+
}
|
|
50
|
+
const core = bind(dlopen("libc.so.6", { ...CORE, ...IOCTL }).symbols);
|
|
51
|
+
for (const name of ["libc.so.6", "libutil.so.1", "libutil.so"]) {
|
|
52
|
+
try {
|
|
53
|
+
return { core, openpty: bind(dlopen(name, OPENPTY).symbols) };
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
56
|
+
throw new Error("openpty not found in libc.so.6, libutil.so.1 or libutil.so");
|
|
57
|
+
}
|
|
58
|
+
var ffi = load();
|
|
59
|
+
var TIOCGWINSZ = process.platform === "darwin" ? 1074295912 : 21523;
|
|
60
|
+
var TIOCSWINSZ = process.platform === "darwin" ? 2148037735 : 21524;
|
|
61
|
+
var SIGWINCH = 28;
|
|
62
|
+
var SIGHUP = 1;
|
|
63
|
+
var SIGTERM = 15;
|
|
64
|
+
function setWinsize(master, rows, cols) {
|
|
65
|
+
const ws = Buffer.alloc(8);
|
|
66
|
+
ws.writeUInt16LE(Math.max(rows, 1) & 65535, 0);
|
|
67
|
+
ws.writeUInt16LE(Math.max(cols, 1) & 65535, 2);
|
|
68
|
+
return ioctlPtr(master, TIOCSWINSZ, ws);
|
|
69
|
+
}
|
|
70
|
+
function ioctlPtr(fd, request, arg) {
|
|
71
|
+
return APPLE_VARARGS ? ffi.core.ioctl(fd, BigInt(request), 0n, 0n, 0n, 0n, 0n, 0n, arg) : ffi.core.ioctl(fd, BigInt(request), arg);
|
|
72
|
+
}
|
|
73
|
+
var SLEEPER = new Int32Array(new SharedArrayBuffer(4));
|
|
74
|
+
|
|
75
|
+
// src/pty-bun.ts
|
|
76
|
+
function workerUrl(name) {
|
|
77
|
+
return new URL(`./${name}${import.meta.url.endsWith(".ts") ? ".ts" : ".js"}`, import.meta.url);
|
|
78
|
+
}
|
|
79
|
+
function ptySpawnBun(file, args, opts) {
|
|
80
|
+
const m = Buffer.alloc(4);
|
|
81
|
+
const s = Buffer.alloc(4);
|
|
82
|
+
const name = Buffer.alloc(128);
|
|
83
|
+
const ws = Buffer.alloc(8);
|
|
84
|
+
ws.writeUInt16LE(Math.max(opts.rows, 1) & 65535, 0);
|
|
85
|
+
ws.writeUInt16LE(Math.max(opts.cols, 1) & 65535, 2);
|
|
86
|
+
const rc = ffi.openpty.openpty(m, s, name, null, ws);
|
|
87
|
+
if (rc !== 0)
|
|
88
|
+
throw new Error(`openpty failed (${rc})`);
|
|
89
|
+
const master = m.readInt32LE(0);
|
|
90
|
+
const slave = s.readInt32LE(0);
|
|
91
|
+
const proc = Bun.spawn([file, ...args], {
|
|
92
|
+
stdin: slave,
|
|
93
|
+
stdout: slave,
|
|
94
|
+
stderr: slave,
|
|
95
|
+
env: opts.env ?? environ(),
|
|
96
|
+
...opts.cwd === undefined ? {} : { cwd: opts.cwd }
|
|
97
|
+
});
|
|
98
|
+
closeSync(slave);
|
|
99
|
+
const reader = new Worker(workerUrl("pty-read-worker"));
|
|
100
|
+
const writer = new Worker(workerUrl("pty-write-worker"));
|
|
101
|
+
let dataCb = null;
|
|
102
|
+
let markDrained = () => {};
|
|
103
|
+
const drained = new Promise((res) => {
|
|
104
|
+
markDrained = res;
|
|
105
|
+
});
|
|
106
|
+
reader.addEventListener("message", (event) => {
|
|
107
|
+
const chunk = event.data;
|
|
108
|
+
if (chunk === null)
|
|
109
|
+
markDrained();
|
|
110
|
+
else
|
|
111
|
+
dataCb?.(Buffer.from(chunk));
|
|
112
|
+
});
|
|
113
|
+
const exited = (async () => Number(await proc.exited))();
|
|
114
|
+
let reading = false;
|
|
115
|
+
return {
|
|
116
|
+
pid: proc.pid,
|
|
117
|
+
get exited() {
|
|
118
|
+
return exited;
|
|
119
|
+
},
|
|
120
|
+
get drained() {
|
|
121
|
+
return drained;
|
|
122
|
+
},
|
|
123
|
+
write(data) {
|
|
124
|
+
writer.postMessage({ fd: master, data: new Uint8Array(data) });
|
|
125
|
+
},
|
|
126
|
+
onData(cb) {
|
|
127
|
+
dataCb = cb;
|
|
128
|
+
if (!reading) {
|
|
129
|
+
reading = true;
|
|
130
|
+
reader.postMessage({ fd: master });
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
resize(rows, cols) {
|
|
134
|
+
setWinsize(master, rows, cols);
|
|
135
|
+
try {
|
|
136
|
+
proc.kill(SIGWINCH);
|
|
137
|
+
} catch {}
|
|
138
|
+
},
|
|
139
|
+
kill(signal = SIGTERM) {
|
|
140
|
+
try {
|
|
141
|
+
proc.kill(signal);
|
|
142
|
+
} catch {}
|
|
143
|
+
},
|
|
144
|
+
destroy() {
|
|
145
|
+
try {
|
|
146
|
+
proc.kill(SIGHUP);
|
|
147
|
+
} catch {}
|
|
148
|
+
try {
|
|
149
|
+
closeSync(master);
|
|
150
|
+
} catch {}
|
|
151
|
+
markDrained();
|
|
152
|
+
reader.terminate();
|
|
153
|
+
writer.terminate();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
ptySpawnBun
|
|
159
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
':' //; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
2
|
+
import {
|
|
3
|
+
environ
|
|
4
|
+
} from "./claude-highlight-egzaxzbg.js";
|
|
5
|
+
|
|
6
|
+
// src/pty-node.ts
|
|
7
|
+
import * as nodePty from "@lydell/node-pty";
|
|
8
|
+
function ptySpawnNode(file, args, opts) {
|
|
9
|
+
const term = nodePty.spawn(file, [...args], {
|
|
10
|
+
name: process.env["TERM"] ?? "xterm-256color",
|
|
11
|
+
cols: Math.max(opts.cols, 1),
|
|
12
|
+
rows: Math.max(opts.rows, 1),
|
|
13
|
+
env: opts.env ?? environ(),
|
|
14
|
+
...opts.cwd === undefined ? {} : { cwd: opts.cwd },
|
|
15
|
+
encoding: null
|
|
16
|
+
});
|
|
17
|
+
let dataCb = null;
|
|
18
|
+
let markDrained = () => {};
|
|
19
|
+
const drained = new Promise((res) => {
|
|
20
|
+
markDrained = res;
|
|
21
|
+
});
|
|
22
|
+
let resolveExit = () => {};
|
|
23
|
+
const exited = new Promise((res) => {
|
|
24
|
+
resolveExit = res;
|
|
25
|
+
});
|
|
26
|
+
term.onData((chunk) => {
|
|
27
|
+
dataCb?.(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "latin1"));
|
|
28
|
+
});
|
|
29
|
+
term.onExit(({ exitCode, signal }) => {
|
|
30
|
+
resolveExit(signal ? 128 + signal : exitCode);
|
|
31
|
+
markDrained();
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
pid: term.pid,
|
|
35
|
+
exited,
|
|
36
|
+
drained,
|
|
37
|
+
write(data) {
|
|
38
|
+
term.write(Buffer.from(data).toString("latin1"));
|
|
39
|
+
},
|
|
40
|
+
onData(cb) {
|
|
41
|
+
dataCb = cb;
|
|
42
|
+
},
|
|
43
|
+
resize(rows, cols) {
|
|
44
|
+
try {
|
|
45
|
+
term.resize(Math.max(cols, 1), Math.max(rows, 1));
|
|
46
|
+
} catch {}
|
|
47
|
+
},
|
|
48
|
+
kill(signal = 15) {
|
|
49
|
+
try {
|
|
50
|
+
term.kill(signal === 9 ? "SIGKILL" : signal === 1 ? "SIGHUP" : "SIGTERM");
|
|
51
|
+
} catch {}
|
|
52
|
+
},
|
|
53
|
+
destroy() {
|
|
54
|
+
try {
|
|
55
|
+
term.kill("SIGHUP");
|
|
56
|
+
} catch {}
|
|
57
|
+
markDrained();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
ptySpawnNode
|
|
63
|
+
};
|
package/dist/pty-read-worker.js
CHANGED
package/dist/pty-write-worker.js
CHANGED
package/image.png
ADDED
|
Binary file
|
package/man/claude-highlight.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH CLAUDE\-HIGHLIGHT 1 "2026-09-
|
|
1
|
+
.TH CLAUDE\-HIGHLIGHT 1 "2026-09-10" "claude-highlight 1.0.2" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
claude\-highlight \- run Claude Code behind a PTY that colors epistemic markers
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -130,11 +130,14 @@ Category toggles, colors and added words.
|
|
|
130
130
|
The child's exit status, or 128 plus the signal number if the child was
|
|
131
131
|
killed by a signal.
|
|
132
132
|
.SH NOTES
|
|
133
|
-
Requires Bun 1.4 or newer
|
|
133
|
+
Requires Node 22 or newer, or Bun 1.4 or newer. Under Node the pty comes from
|
|
134
|
+
.BR node-pty ,
|
|
135
|
+
a prebuilt N\-API addon; under Bun it is
|
|
134
136
|
.BR openpty (3)
|
|
135
137
|
through
|
|
136
138
|
.BR bun:ffi ,
|
|
137
|
-
with the blocking reads and writes on worker threads.
|
|
139
|
+
with the blocking reads and writes on worker threads. The two backends are
|
|
140
|
+
selected at startup and are otherwise interchangeable.
|
|
138
141
|
.PP
|
|
139
142
|
Unix only. The winsize ioctls and termios have no Windows equivalent, and a
|
|
140
143
|
port there means ConPTY.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-highlight",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Colors epistemic markers in Claude Code's output, live, in the terminal",
|
|
6
6
|
"keywords": [
|
|
@@ -35,29 +35,34 @@
|
|
|
35
35
|
"./man/claude-highlight.1"
|
|
36
36
|
],
|
|
37
37
|
"engines": {
|
|
38
|
-
"
|
|
38
|
+
"node": ">=22"
|
|
39
39
|
},
|
|
40
40
|
"os": [
|
|
41
41
|
"darwin",
|
|
42
42
|
"linux"
|
|
43
43
|
],
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "rm -rf dist && bun build src/claude-highlight.ts src/pty-read-worker.ts src/pty-write-worker.ts --target=bun --outdir=dist",
|
|
45
|
+
"build": "rm -rf dist && bun build src/claude-highlight.ts src/pty-read-worker.ts src/pty-write-worker.ts --target=node --banner=\"':' //; exec \\\"\\$(command -v node || command -v bun)\\\" \\\"\\$0\\\" \\\"\\$@\\\"\" --packages=external --splitting --outdir=dist",
|
|
46
46
|
"prepack": "bun run build",
|
|
47
47
|
"typecheck": "tsc -p tsconfig.json",
|
|
48
|
-
"test:pty": "
|
|
49
|
-
"test:filter": "
|
|
50
|
-
"test:screen": "
|
|
51
|
-
"test:integration": "
|
|
52
|
-
"test:wrapper": "
|
|
53
|
-
"test:paste": "
|
|
54
|
-
"test:miners": "
|
|
55
|
-
"test:all": "
|
|
56
|
-
"test:dist": "bun run build && CLAUDE_HIGHLIGHT_BIN=\"$PWD/dist/claude-highlight.js\"
|
|
48
|
+
"test:pty": "node test/pty-smoke.ts",
|
|
49
|
+
"test:filter": "node test/test_filter.ts",
|
|
50
|
+
"test:screen": "node test/test_screen.ts",
|
|
51
|
+
"test:integration": "node test/test_integration.ts",
|
|
52
|
+
"test:wrapper": "node test/test_wrapper.ts",
|
|
53
|
+
"test:paste": "node test/test_paste.ts",
|
|
54
|
+
"test:miners": "node test/test_miners.ts",
|
|
55
|
+
"test:all": "node test/run_all.ts",
|
|
56
|
+
"test:dist": "bun run build && CLAUDE_HIGHLIGHT_BIN=\"$PWD/dist/claude-highlight.js\" node test/run_all.ts",
|
|
57
|
+
"test:all:bun": "bun run test/run_all.ts",
|
|
58
|
+
"test:dist:bun": "bun run build && CLAUDE_HIGHLIGHT_BIN=\"$PWD/dist/claude-highlight.js\" bun run test/run_all.ts"
|
|
57
59
|
},
|
|
58
60
|
"devDependencies": {
|
|
59
61
|
"@types/bun": "^1.4.0",
|
|
60
62
|
"bun-types": "^1.4.1",
|
|
61
63
|
"typescript": "^5.9.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@lydell/node-pty": "^1.2.0-beta.15"
|
|
62
67
|
}
|
|
63
68
|
}
|