Create, run, rate, and iterate on your Claude Skills
claude-skills
1export interface StreamEvent {
2 type: string;
3 subtype?: string;
4 message?: {
5 content?: Array<{
6 type: string;
7 name?: string;
8 input?: Record<string, unknown>;
9 text?: string;
10 }>;
11 };
12 tool_use_result?: {
13 success?: boolean;
14 commandName?: string;
15 content?: string;
16 };
17 result?: string;
18 is_error?: boolean;
19 duration_ms?: number;
20 total_cost_usd?: number;
21}
22
23export function formatEventForLog(event: StreamEvent): string | null {
24 if (event.type === "system" && event.subtype === "init") {
25 return `=== Session started ===\n`;
26 }
27
28 if (event.type === "assistant" && event.message?.content) {
29 const parts: string[] = [];
30 for (const block of event.message.content) {
31 if (block.type === "text" && block.text) {
32 parts.push(`[assistant]\n${block.text}\n`);
33 } else if (block.type === "tool_use" && block.name) {
34 const inputStr = block.input
35 ? JSON.stringify(block.input, null, 2)
36 : "{}";
37 parts.push(`[tool_use: ${block.name}]\n${inputStr}\n`);
38 }
39 }
40 return parts.length > 0 ? parts.join("\n") : null;
41 }
42
43 if (event.type === "user" && event.message?.content) {
44 const parts: string[] = [];
45 for (const block of event.message.content as Array<{
46 type: string;
47 content?: string;
48 }>) {
49 if (block.type === "tool_result" && block.content) {
50 parts.push(`[tool_result]\n${block.content}\n`);
51 }
52 }
53 return parts.length > 0 ? parts.join("\n") : null;
54 }
55
56 if (event.type === "result") {
57 const lines = [`=== Result ===`];
58 if (event.is_error) lines.push(`ERROR`);
59 if (event.result) lines.push(event.result);
60 if (event.duration_ms)
61 lines.push(`Duration: ${(event.duration_ms / 1000).toFixed(1)}s`);
62 if (event.total_cost_usd)
63 lines.push(`Cost: $${event.total_cost_usd.toFixed(4)}`);
64 return lines.join("\n") + "\n";
65 }
66
67 return null;
68}
69
70export function parseStreamEvents(output: string): StreamEvent[] {
71 return output
72 .split("\n")
73 .filter((line) => line.trim())
74 .map((line) => {
75 try {
76 return JSON.parse(line) as StreamEvent;
77 } catch {
78 return null;
79 }
80 })
81 .filter((e): e is StreamEvent => e !== null);
82}