claude_stream_viewer 1.0.9

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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=claude-stream-viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-stream-viewer.d.ts","sourceRoot":"","sources":["../src/claude-stream-viewer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ // Reads Claude API stream-json events line-by-line from stdin and pretty-prints
3
+ // them with colorized output. Designed to wrap an upstream `claude --stream-json`
4
+ // pipe, e.g. `claude … | claude_stream_viewer`.
5
+ import readline from 'node:readline';
6
+ import chalk from 'chalk';
7
+ const colors = {
8
+ text: chalk.white,
9
+ tool: chalk.cyan,
10
+ system: chalk.gray,
11
+ error: chalk.red,
12
+ json: chalk.dim,
13
+ header: chalk.yellow.bold,
14
+ };
15
+ function printText(text) {
16
+ process.stdout.write(colors.text(text));
17
+ }
18
+ function printNewline() {
19
+ process.stdout.write('\n');
20
+ }
21
+ function printHeader(label) {
22
+ printNewline();
23
+ console.log(colors.header(`\n=== ${label} ===`));
24
+ }
25
+ function printJSON(obj) {
26
+ console.log(colors.json(JSON.stringify(obj, null, 2)));
27
+ }
28
+ function handleEvent(data) {
29
+ try {
30
+ // Newer SDKs wrap each event in a `stream_event` envelope.
31
+ if (data.type === 'stream_event' && data.event) {
32
+ const evt = data.event;
33
+ if (evt.delta?.type === 'text_delta' && evt.delta.text) {
34
+ printText(evt.delta.text);
35
+ return;
36
+ }
37
+ if (evt.type === 'tool_use') {
38
+ printHeader('TOOL USE');
39
+ printJSON(evt);
40
+ return;
41
+ }
42
+ // `content_block_delta` is the parent envelope of `text_delta` —
43
+ // already rendered above, so skip it here to avoid duplicating
44
+ // the text stream as a JSON dump.
45
+ if (evt.type && evt.type !== 'content_block_delta') {
46
+ printHeader(`EVENT: ${evt.type}`);
47
+ printJSON(evt);
48
+ return;
49
+ }
50
+ }
51
+ // Legacy format: pre-`stream_event` SDKs emit raw `message_delta`
52
+ // events with the text delta hanging off the root.
53
+ if (data.type === 'message_delta') {
54
+ const text = data.delta?.text;
55
+ if (text) {
56
+ printText(text);
57
+ return;
58
+ }
59
+ }
60
+ // Final assistant message bundling all content blocks for the turn.
61
+ if (data.message?.content) {
62
+ printHeader('FINAL MESSAGE');
63
+ for (const block of data.message.content) {
64
+ if (block.text) {
65
+ console.log(colors.text(block.text));
66
+ }
67
+ }
68
+ return;
69
+ }
70
+ // Unrecognized shape — dump it raw so the user can extend ClaudeEvent.
71
+ printHeader('UNKNOWN EVENT');
72
+ printJSON(data);
73
+ }
74
+ catch (err) {
75
+ // Never let a single malformed event take down the stream — log and move on.
76
+ console.error(colors.error('Error processing event:'), err);
77
+ printJSON(data);
78
+ }
79
+ }
80
+ const rl = readline.createInterface({
81
+ input: process.stdin,
82
+ crlfDelay: Infinity,
83
+ });
84
+ rl.on('line', (line) => {
85
+ // stream-json pipes occasionally pad with blank lines between events.
86
+ if (line.trim() === '')
87
+ return;
88
+ try {
89
+ const parsed = JSON.parse(line);
90
+ handleEvent(parsed);
91
+ }
92
+ catch (err) {
93
+ console.error(colors.error('Invalid JSON:'), line);
94
+ }
95
+ });
96
+ rl.on('close', () => {
97
+ printNewline();
98
+ console.log(colors.system('\n[stream ended]'));
99
+ });
100
+ //# sourceMappingURL=claude-stream-viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-stream-viewer.js","sourceRoot":"","sources":["../src/claude-stream-viewer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=claude_stream_viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude_stream_viewer.d.ts","sourceRoot":"","sources":["../src/claude_stream_viewer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ // Reads Claude API stream-json events line-by-line from stdin and pretty-prints
3
+ // them with colorized output. Designed to wrap an upstream `claude --stream-json`
4
+ // pipe, e.g. `claude … | claude_stream_viewer`.
5
+ import readline from 'node:readline';
6
+ import chalk from 'chalk';
7
+ const colors = {
8
+ text: chalk.white,
9
+ tool: chalk.cyan,
10
+ system: chalk.gray,
11
+ error: chalk.red,
12
+ json: chalk.dim,
13
+ header: chalk.yellow.bold,
14
+ };
15
+ function printText(text) {
16
+ process.stdout.write(colors.text(text));
17
+ }
18
+ function printNewline() {
19
+ process.stdout.write('\n');
20
+ }
21
+ function printHeader(label) {
22
+ printNewline();
23
+ console.log(colors.header(`\n=== ${label} ===`));
24
+ }
25
+ function printJSON(obj) {
26
+ console.log(colors.json(JSON.stringify(obj, null, 2)));
27
+ }
28
+ function handleEvent(data) {
29
+ try {
30
+ // Newer SDKs wrap each event in a `stream_event` envelope.
31
+ if (data.type === 'stream_event' && data.event) {
32
+ const evt = data.event;
33
+ if (evt.delta?.type === 'text_delta' && evt.delta.text) {
34
+ printText(evt.delta.text);
35
+ return;
36
+ }
37
+ if (evt.type === 'tool_use') {
38
+ printHeader('TOOL USE');
39
+ printJSON(evt);
40
+ return;
41
+ }
42
+ // `content_block_delta` is the parent envelope of `text_delta` —
43
+ // already rendered above, so skip it here to avoid duplicating
44
+ // the text stream as a JSON dump.
45
+ if (evt.type && evt.type !== 'content_block_delta') {
46
+ printHeader(`EVENT: ${evt.type}`);
47
+ printJSON(evt);
48
+ return;
49
+ }
50
+ }
51
+ // Legacy format: pre-`stream_event` SDKs emit raw `message_delta`
52
+ // events with the text delta hanging off the root.
53
+ if (data.type === 'message_delta') {
54
+ const text = data.delta?.text;
55
+ if (text) {
56
+ printText(text);
57
+ return;
58
+ }
59
+ }
60
+ // Final assistant message bundling all content blocks for the turn.
61
+ if (data.message?.content) {
62
+ printHeader('FINAL MESSAGE');
63
+ for (const block of data.message.content) {
64
+ if (block.text) {
65
+ console.log(colors.text(block.text));
66
+ }
67
+ }
68
+ return;
69
+ }
70
+ // Unrecognized shape — dump it raw so the user can extend ClaudeEvent.
71
+ printHeader('UNKNOWN EVENT');
72
+ printJSON(data);
73
+ }
74
+ catch (err) {
75
+ // Never let a single malformed event take down the stream — log and move on.
76
+ console.error(colors.error('Error processing event:'), err);
77
+ printJSON(data);
78
+ }
79
+ }
80
+ const rl = readline.createInterface({
81
+ input: process.stdin,
82
+ crlfDelay: Infinity,
83
+ });
84
+ rl.on('line', (line) => {
85
+ // stream-json pipes occasionally pad with blank lines between events.
86
+ if (line.trim() === '')
87
+ return;
88
+ try {
89
+ const parsed = JSON.parse(line);
90
+ handleEvent(parsed);
91
+ }
92
+ catch (err) {
93
+ console.error(colors.error('Invalid JSON:'), line);
94
+ }
95
+ });
96
+ rl.on('close', () => {
97
+ printNewline();
98
+ console.log(colors.system('\n[stream ended]'));
99
+ });
100
+ //# sourceMappingURL=claude_stream_viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude_stream_viewer.js","sourceRoot":"","sources":["../src/claude_stream_viewer.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "claude_stream_viewer",
3
+ "version": "1.0.9",
4
+ "description": "Claude Stream Viewer - Pretty-prints Claude API stream-json events from stdin with colorized output.",
5
+ "main": "dist/claude_stream_viewer.js",
6
+ "bin": {
7
+ "claude_stream_viewer": "dist/claude_stream_viewer.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsx ./src/claude_stream_viewer.ts",
11
+ "build": "tsc -p tsconfig.build.json",
12
+ "postbuild": "chmod +x ./dist/claude_stream_viewer.js",
13
+ "version:bump": "npm version patch --no-git-tag-version && git commit -a -m \"feat: bump version in claude_stream_viewer package.json\"",
14
+ "publish:all": "npm run version:bump && npm run build && npm publish --access public",
15
+ "typecheck": "tsc --noEmit",
16
+ "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "keywords": [],
19
+ "author": "",
20
+ "license": "ISC",
21
+ "type": "module",
22
+ "dependencies": {
23
+ "chalk": "^5.6.2"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.0.0",
27
+ "tsx": "^4.21.0",
28
+ "typescript": "^5.3.3"
29
+ }
30
+ }
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Reads Claude API stream-json events line-by-line from stdin and pretty-prints
4
+ // them with colorized output. Designed to wrap an upstream `claude --stream-json`
5
+ // pipe, e.g. `claude … | claude_stream_viewer`.
6
+
7
+ import readline from 'node:readline';
8
+ import chalk from 'chalk';
9
+
10
+ // Partial typing on purpose: stream-json's shape varies across SDK versions and
11
+ // event subtypes, and we only consume the few fields we render. Anything we
12
+ // don't recognize falls through to the UNKNOWN EVENT branch so the user can
13
+ // extend this type incrementally as new shapes show up.
14
+ type ClaudeEvent = {
15
+ type?: string;
16
+ event?: {
17
+ type?: string;
18
+ delta?: {
19
+ type?: string;
20
+ text?: string;
21
+ };
22
+ };
23
+ // Legacy `message_delta` format places the text delta one level higher,
24
+ // directly on the root event rather than inside `event.delta`.
25
+ delta?: {
26
+ text?: string;
27
+ };
28
+ message?: {
29
+ content?: Array<{ type?: string; text?: string }>;
30
+ };
31
+ };
32
+
33
+ const colors = {
34
+ text: chalk.white,
35
+ tool: chalk.cyan,
36
+ system: chalk.gray,
37
+ error: chalk.red,
38
+ json: chalk.dim,
39
+ header: chalk.yellow.bold,
40
+ };
41
+
42
+ function printText(text: string) {
43
+ process.stdout.write(colors.text(text));
44
+ }
45
+
46
+ function printNewline() {
47
+ process.stdout.write('\n');
48
+ }
49
+
50
+ function printHeader(label: string) {
51
+ printNewline();
52
+ console.log(colors.header(`\n=== ${label} ===`));
53
+ }
54
+
55
+ function printJSON(obj: unknown) {
56
+ console.log(colors.json(JSON.stringify(obj, null, 2)));
57
+ }
58
+
59
+ function handleEvent(data: ClaudeEvent) {
60
+ try {
61
+ // Newer SDKs wrap each event in a `stream_event` envelope.
62
+ if (data.type === 'stream_event' && data.event) {
63
+ const evt = data.event;
64
+
65
+ if (evt.delta?.type === 'text_delta' && evt.delta.text) {
66
+ printText(evt.delta.text);
67
+ return;
68
+ }
69
+
70
+ if (evt.type === 'tool_use') {
71
+ printHeader('TOOL USE');
72
+ printJSON(evt);
73
+ return;
74
+ }
75
+
76
+ // `content_block_delta` is the parent envelope of `text_delta` —
77
+ // already rendered above, so skip it here to avoid duplicating
78
+ // the text stream as a JSON dump.
79
+ if (evt.type && evt.type !== 'content_block_delta') {
80
+ printHeader(`EVENT: ${evt.type}`);
81
+ printJSON(evt);
82
+ return;
83
+ }
84
+ }
85
+
86
+ // Legacy format: pre-`stream_event` SDKs emit raw `message_delta`
87
+ // events with the text delta hanging off the root.
88
+ if (data.type === 'message_delta') {
89
+ const text = data.delta?.text;
90
+ if (text) {
91
+ printText(text);
92
+ return;
93
+ }
94
+ }
95
+
96
+ // Final assistant message bundling all content blocks for the turn.
97
+ if (data.message?.content) {
98
+ printHeader('FINAL MESSAGE');
99
+ for (const block of data.message.content) {
100
+ if (block.text) {
101
+ console.log(colors.text(block.text));
102
+ }
103
+ }
104
+ return;
105
+ }
106
+
107
+ // Unrecognized shape — dump it raw so the user can extend ClaudeEvent.
108
+ printHeader('UNKNOWN EVENT');
109
+ printJSON(data);
110
+ } catch (err) {
111
+ // Never let a single malformed event take down the stream — log and move on.
112
+ console.error(colors.error('Error processing event:'), err);
113
+ printJSON(data);
114
+ }
115
+ }
116
+
117
+ const rl = readline.createInterface({
118
+ input: process.stdin,
119
+ crlfDelay: Infinity,
120
+ });
121
+
122
+ rl.on('line', (line) => {
123
+ // stream-json pipes occasionally pad with blank lines between events.
124
+ if (line.trim() === '') return;
125
+
126
+ try {
127
+ const parsed = JSON.parse(line);
128
+ handleEvent(parsed);
129
+ } catch (err) {
130
+ console.error(colors.error('Invalid JSON:'), line);
131
+ }
132
+ });
133
+
134
+ rl.on('close', () => {
135
+ printNewline();
136
+ console.log(colors.system('\n[stream ended]'));
137
+ });
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src/",
5
+ "outDir": "dist",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true
9
+ },
10
+ "include": [
11
+ "src/**/*"
12
+ ]
13
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "node16",
5
+ "lib": [
6
+ "ES2020"
7
+ ],
8
+ "moduleResolution": "node16",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "types": [
15
+ "node"
16
+ ],
17
+ "rootDir": ".",
18
+ },
19
+ "include": [
20
+ "src/**/*",
21
+ "tests/**/*",
22
+ "examples/**/*",
23
+ ],
24
+ "exclude": [
25
+ "node_modules",
26
+ "dist"
27
+ ]
28
+ }