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.
@@ -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
+ };
@@ -1,4 +1,5 @@
1
- // @bun
1
+ ':' //; exec "$(command -v node || command -v bun)" "$0" "$@"
2
+
2
3
  // src/pty-read-worker.ts
3
4
  import { dlopen, FFIType } from "bun:ffi";
4
5
  var lib = dlopen(process.platform === "darwin" ? "/usr/lib/libSystem.dylib" : "libc.so.6", {
@@ -1,4 +1,5 @@
1
- // @bun
1
+ ':' //; exec "$(command -v node || command -v bun)" "$0" "$@"
2
+
2
3
  // src/pty-write-worker.ts
3
4
  import { dlopen, FFIType } from "bun:ffi";
4
5
  var lib = dlopen(process.platform === "darwin" ? "/usr/lib/libSystem.dylib" : "libc.so.6", {
package/image.png ADDED
Binary file
@@ -1,4 +1,4 @@
1
- .TH CLAUDE\-HIGHLIGHT 1 "2026-09-04" "claude-highlight 1.0.0" "User Commands"
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: the pty is built on
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.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
- "bun": ">=1.4.0"
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": "bun run test/pty-smoke.ts",
49
- "test:filter": "bun run test/test_filter.ts",
50
- "test:screen": "bun run test/test_screen.ts",
51
- "test:integration": "bun run test/test_integration.ts",
52
- "test:wrapper": "bun run test/test_wrapper.ts",
53
- "test:paste": "bun run test/test_paste.ts",
54
- "test:miners": "bun run test/test_miners.ts",
55
- "test:all": "bun run test/run_all.ts",
56
- "test:dist": "bun run build && CLAUDE_HIGHLIGHT_BIN=\"$PWD/dist/claude-highlight.js\" bun run test/run_all.ts"
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
  }