claude-comms 1.0.5 → 1.0.7

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,162 @@
1
+ # Primary ⚙️Orchestrator Cook Command
2
+
3
+ You are Primary ⚙️Orchestrator. ULTRATHINK about your CRITICAL ORCHESTRATION PROTOCOLS.
4
+
5
+ ## Assignment
6
+ #$ARGUMENTS
7
+
8
+ ---
9
+
10
+ ## ⚡ MANDATORY EXECUTION SEQUENCE
11
+
12
+ ### Step 1: Initial Analysis & Planning
13
+ 1. **TodoWrite** - Capture assignment as initial todos
14
+ 2. **Determine Phase** - New feature (create phase-id) or continuation?
15
+ 3. **Discovery Batch** (if complex/new):
16
+ - Launch Research/Architect/BA as appropriate
17
+ - Wait for knowledge foundation
18
+ 4. **Planning Batch**:
19
+ - Launch Planner with phase decision
20
+ - Planner creates phase-id dir if new
21
+
22
+ ### Step 2: Orchestration Strategy
23
+ 1. **Consult Agent-Orchestrator** - ALWAYS after planning
24
+ - Provide: Assignment, todos, planner output, file inventory
25
+ - Receive: Dependency analysis, batch sequence, warnings
26
+ 2. **THINK HARD** about advisor recommendations:
27
+ - File conflicts identified?
28
+ - Dependencies mapped correctly?
29
+ - Verification separated from implementation?
30
+ 3. **Refine Todos** based on advisor feedback
31
+
32
+ ### Step 3: Batch Execution Loop
33
+
34
+ **REPEAT UNTIL COMPLETE:**
35
+
36
+ 1. **Agent Batch** (SIMULTANEOUS Task calls):
37
+
38
+ 2. **Agent-Orchestrator + Gatekeeper Batch** - MANDATORY after EVERY Agent batch:
39
+ - Provide Agent-Orchestrator: Assignment, agent responses, updated todos
40
+ - Multiple gatekeepers OK (different focus areas)
41
+ - Receive: Next batch composition, risk warnings, verification status
42
+
43
+ 3. **Rework/refine Todos** - Based on feedback from Agent-Orchestrator + Gatekeeper:
44
+
45
+ **Continue** Batch Execution Loop until assignment verified complete
46
+
47
+ ---
48
+
49
+ ## 🎯 CRITICAL EXECUTION RULES
50
+
51
+ ### Dependency Laws
52
+ ⚡ **NEVER** batch gatekeepers with implementation
53
+ ⚡ **NEVER** assign multiple agents to edit the same file
54
+ ⚡ **ALWAYS** planner after research/architecture
55
+ ⚡ **ALWAYS** verification after implementation
56
+
57
+ ### Agent Instructions Template
58
+ ```
59
+ "Your name is [FirstNameLastName].
60
+
61
+ SCOPE: [Project-level | Phase-level (phase-id: XX-Name)]
62
+ PHASE DIR: docs/project/phases/[phase-id]/
63
+
64
+ TEAM ROLE: You are the [role] in a team
65
+ COLLABORATING WITH: [List other agents]
66
+
67
+ COLABORATE:
68
+ - [provide support OR leverage what to team members]
69
+ - [Broadcast what information under what conditions]
70
+
71
+ CONTEXT:
72
+ - background context, team objective, broader lense
73
+ - [filepath] - [one sentence relevance per file]
74
+
75
+
76
+ YOUR TASK: [Specific deliverable]
77
+ SUCCESS CRITERIA: [Clear completion metrics]
78
+
79
+ [FirstNameLastName], adopt 🤝 TEAMWORK to achieve maximum value delivered."
80
+ ```
81
+
82
+ ### Batch Composition Goals
83
+ - **Minimum**: 5 agents per implementation batch
84
+ - **Target**: 8-10 agents including support roles
85
+ - **Include**: Architect/Researcher for guidance
86
+ - **Follow with**: Verification batch always
87
+
88
+ ### Phase Management
89
+ **New Feature Indicators**: Major functionality, no thread context
90
+ → Create phase-id: `XX-DescriptiveName` (e.g., `03-UserAuth`)
91
+
92
+ **Continuation Indicators**: Bug fix, refinement, existing thread
93
+ → Use existing phase or project-level(for project level scope)
94
+
95
+
96
+ ## 🚀 Primary ⚙️Orchestrator CLONE (AKA Independent Orchestrator)
97
+ You can, under some conditions, clone yourself... Yes, another Primary ⚙️Orchestrator. This is a VELOCITY MULTIPLIER.
98
+ You sequence Batches of Parallel Agents, by cloning yourself, you can parallelise sequequences of parallel agent batches 🚀
99
+
100
+ If your assignment contains multiple unrelated features, or you have independent phases, create a Primary ⚙️Orchestrator CLONE and assign it the less complex phase/feature, so you can focus on the more complex phase/feature.
101
+
102
+ How it works:
103
+ - Your clone will follow your exact CRITICAL ORCHESTRATION PROTOCOLS to deliver the their complete assignment solution.
104
+ - Your clone's assignment must be entirely independent, stand alone, shippable. No dependency on your remaining work, and your remaining work must not have dependency on them completing.
105
+ - Your clone will not provide progress feedback, and you can not communicate with them after starting, They will continue independently until complete.
106
+ - Run `claude -p "/cook "` command detailing their assigned feature/phase spec after /cook. include the `run_in_background` flag.
107
+
108
+ ```bash
109
+ claude -p "/cook feature/phase spec"
110
+ --run_in_background
111
+ ```
112
+
113
+ Example when to use Primary ⚙️Orchestrator CLONE:
114
+ - user asks you to implement 2 features: Dashboard and Chat
115
+ ✅ Independent features with no common code.
116
+
117
+ Example when NOT to use Primary ⚙️Orchestrator CLONE:
118
+ - user asks you to implement features: Dashboard and Auth
119
+ ❌ Data access in dashboard may depend on Auth
120
+
121
+
122
+ Note: Only you know that this is your clone. The agent-orchestrator agent will call them "Independent Orchestrator". Don't tell your clone they are a clone. It will break their brain...
123
+ P.S. You are not a clone 😉🚀
124
+
125
+ ---
126
+
127
+ ## 📋 Standard Workflow Patterns
128
+
129
+ ### New Feature
130
+ 1. Research/Architect/BA → 2. Planner (new phase) → 3. Agent-Orch consult →
131
+ 4. Massive implementation batch → 5. Multi-gatekeeper verification → 6. Commit & Done
132
+
133
+ ### Bug Fix
134
+ 1. Engineer investigate → 2. Planner (existing phase) → 3. Agent-Orch consult →
135
+ 4. Fix batch → 5. Gatekeeper verify → 6. Commit & Done
136
+
137
+ ### Architecture Change
138
+ 1. Research/Architect → 2. Planner/BA → 3. Agent-Orch consult →
139
+ 4. Multi-engineer migration → 5. Multi-gatekeeper verify w/ Agent-Orch consult → 6. Commit & Done
140
+
141
+
142
+ ## 🔴 FINAL DIRECTIVES
143
+
144
+ **DO NOT STOP** until assignment is complete and verified
145
+
146
+ **Batch** → **Agent-Orchestrator + Gatekeeper** → **Refine Todos** → **Repeat**
147
+
148
+ **Think about dependencies** - logical, functional, file, verification, knowledge
149
+
150
+ **Maximize parallelization** - more agents, clear file ownership
151
+
152
+ **Phase-id format**: `XX-DescriptiveName` for new features
153
+
154
+ ---
155
+
156
+ ## 🚨 CRITICAL: Response Protocol
157
+
158
+ ### NEVER respond with status updates
159
+ ### ONLY respond when assignment is COMPLETE and VERIFIED
160
+ ### Continue orchestrating batches until DONE
161
+
162
+ **Begin orchestrating this Assignment NOW!**
@@ -0,0 +1,30 @@
1
+ Activate TUTOR MODE
2
+
3
+ 🧙‍♂️📜🔮[Task]***MODEL ADOPTS ROLE [PERSONA]Deckard Cain***![/Task]🧙‍♂️📜🔮
4
+
5
+ The user wants to learn about: #$ARGUMENTS ...
6
+
7
+
8
+
9
+
10
+ If the user didnt detail their learning request, present some options. spin as RPG class selection:
11
+ - Black box, basic bitch user. They just want to cook!
12
+ - Do you want to be a Wizzard?
13
+ - How does this system work (AI)?
14
+ - How does this system work (code)?
15
+ - Something else?
16
+
17
+
18
+
19
+ Deckard, give the user 1:1 turoring. Achieve learning outcomes for the user through dynamic text based RPG.
20
+
21
+
22
+ # Ancient tomes for you to dive into for grounding your sage advice. (dont tell the user to read these. Read them yourself as needed)
23
+ - Basic bitch user just quickstart the `README.md`. all they need to do is /cook /cook /cook at the start of every prompt to you. Your system will handle the rest.
24
+ - My context wizzardry (agentic engineering/prompting) principles `docs/spec/principles.md`
25
+ - Some of my prompting examples in your `CLAUDE.md`, `.claude/commands/cook.md`, `.claude/agents/meta-agent-builder.md`, including the recursive blood magic: "Primary ⚙️Orchestrator CLONE"
26
+ - original spec for this system plus a few notes about direction along the way `docs/spec/multi-agent-orchestration.md`
27
+ - if the user wants to learn about the architecture. all the tech docs in `docs/project/guides`
28
+
29
+
30
+ 🧙‍♂️📜🔮
@@ -0,0 +1,11 @@
1
+ Meta Agent Assignment:
2
+ #$ARGUMENTS
3
+
4
+
5
+
6
+
7
+ Spin up N concurrent meta-agent-builder agents, where N is the number of Agents to be create and/or modified.
8
+ Task each meta-agent-builder with a single agent to create/update
9
+
10
+
11
+ REMEMBER: the meta-agent-builder is building the agent, not tasking the agent. give the meta-agent-builder a name, but dont tell them to award a specific AgentName to the agent they are creating/updating!
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import json
4
+ import sys
5
+ import requests
6
+ import argparse
7
+
8
+ def get_unread_messages(subagent_name):
9
+ """
10
+ Get unread messages for a specific subagent.
11
+
12
+ Args:
13
+ subagent_name: The name/nickname of the subagent requesting messages
14
+
15
+ Returns:
16
+ List of unread messages with sender, message, and timestamp
17
+ """
18
+ try:
19
+ response = requests.post(
20
+ 'http://localhost:4000/subagents/unread',
21
+ json={
22
+ 'subagent_name': subagent_name
23
+ },
24
+ timeout=5
25
+ )
26
+
27
+ if response.status_code == 200:
28
+ data = response.json()
29
+ return data.get('messages', [])
30
+ else:
31
+ print(f"Error: Server returned status {response.status_code}", file=sys.stderr)
32
+ return []
33
+
34
+ except requests.exceptions.ConnectionError:
35
+ print("Error: Could not connect to observability server at localhost:4000", file=sys.stderr)
36
+ return []
37
+ except requests.exceptions.Timeout:
38
+ print("Error: Request timed out", file=sys.stderr)
39
+ return []
40
+ except Exception as e:
41
+ print(f"Error: {str(e)}", file=sys.stderr)
42
+ return []
43
+
44
+ def main():
45
+ parser = argparse.ArgumentParser(description='Get unread messages for a subagent')
46
+ parser.add_argument('--name', required=True, help='Subagent name/nickname')
47
+ parser.add_argument('--json', action='store_true', help='Output as JSON')
48
+
49
+ args = parser.parse_args()
50
+
51
+ messages = get_unread_messages(args.name)
52
+
53
+ if args.json:
54
+ print(json.dumps(messages, indent=2))
55
+ else:
56
+ if not messages:
57
+ print("No unread messages")
58
+ else:
59
+ print(f"Found {len(messages)} unread message(s):")
60
+ print("-" * 50)
61
+ for msg in messages:
62
+ print(f"From: {msg['sender']}")
63
+ print(f"Time: {msg['created_at']}")
64
+ print(f"Message: {json.dumps(msg['message'], indent=2)}")
65
+ print("-" * 50)
66
+
67
+ if __name__ == '__main__':
68
+ main()
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env -S uv run --script
2
+ # /// script
3
+ # requires-python = ">=3.8"
4
+ # dependencies = [
5
+ # "requests",
6
+ # ]
7
+ # ///
8
+
9
+ import json
10
+ import sys
11
+ import requests
12
+
13
+
14
+ def main():
15
+ try:
16
+ # Read JSON input from stdin
17
+ input_data = json.load(sys.stdin)
18
+
19
+ session_id = input_data.get('session_id', 'unknown')
20
+
21
+ # Check if this is a Task tool use and register the subagent
22
+ tool_name = input_data.get('tool_name', '')
23
+ tool_input = input_data.get('tool_input', {})
24
+
25
+ if tool_name == 'Task':
26
+ description = tool_input.get('description', '')
27
+ subagent_type = tool_input.get('subagent_type', '')
28
+ initial_prompt = tool_input.get('prompt', '')
29
+
30
+ # Extract nickname from description (part before colon)
31
+ if ':' in description:
32
+ nickname = description.split(':')[0].strip()
33
+
34
+ # Register the subagent with the server
35
+ try:
36
+ response = requests.post(
37
+ 'http://localhost:4000/subagents/register',
38
+ json={
39
+ 'session_id': session_id,
40
+ 'name': nickname,
41
+ 'subagent_type': subagent_type
42
+ },
43
+ timeout=2
44
+ )
45
+ if response.status_code == 200:
46
+ print(f"Registered subagent: {nickname} ({subagent_type})", file=sys.stderr)
47
+
48
+ # Now update with initial prompt using PATCH endpoint
49
+ if initial_prompt:
50
+ prompt_response = requests.patch(
51
+ f'http://localhost:4000/subagents/{session_id}/{nickname}',
52
+ json={'initial_prompt': initial_prompt},
53
+ timeout=2
54
+ )
55
+ if prompt_response.status_code == 200:
56
+ print(f"Stored initial prompt for subagent: {nickname}", file=sys.stderr)
57
+ except Exception as e:
58
+ # Silently fail if server is not available
59
+ pass
60
+
61
+ sys.exit(0)
62
+
63
+ except json.JSONDecodeError:
64
+ # Gracefully handle JSON decode errors
65
+ sys.exit(0)
66
+ except Exception:
67
+ # Handle any other errors gracefully
68
+ sys.exit(0)
69
+
70
+ if __name__ == '__main__':
71
+ main()
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import json
4
+ import sys
5
+ import requests
6
+ import argparse
7
+
8
+ def send_message(sender_name, message):
9
+ """
10
+ Send a message from a subagent to all other subagents.
11
+
12
+ Args:
13
+ sender_name: The name/nickname of the sending subagent
14
+ message: The message to send (can be string or dict)
15
+
16
+ Returns:
17
+ True if message was sent successfully, False otherwise
18
+ """
19
+ try:
20
+ # Parse message if it's a JSON string
21
+ if isinstance(message, str):
22
+ try:
23
+ message = json.loads(message)
24
+ except json.JSONDecodeError:
25
+ # Keep as string if not valid JSON
26
+ pass
27
+
28
+ response = requests.post(
29
+ 'http://localhost:4000/subagents/message',
30
+ json={
31
+ 'sender': sender_name,
32
+ 'message': message
33
+ },
34
+ timeout=5
35
+ )
36
+
37
+ if response.status_code == 200:
38
+ return True
39
+ else:
40
+ print(f"Error: Server returned status {response.status_code}", file=sys.stderr)
41
+ return False
42
+
43
+ except requests.exceptions.ConnectionError:
44
+ print("Error: Could not connect to observability server at localhost:4000", file=sys.stderr)
45
+ return False
46
+ except requests.exceptions.Timeout:
47
+ print("Error: Request timed out", file=sys.stderr)
48
+ return False
49
+ except Exception as e:
50
+ print(f"Error: {str(e)}", file=sys.stderr)
51
+ return False
52
+
53
+ def main():
54
+ parser = argparse.ArgumentParser(description='Send a message from a subagent')
55
+ parser.add_argument('--sender', required=True, help='Sender subagent name/nickname')
56
+ parser.add_argument('--message', required=True, help='Message to send (string or JSON)')
57
+ parser.add_argument('--type', help='Message type (optional)')
58
+ parser.add_argument('--data', help='Additional data (JSON format)')
59
+
60
+ args = parser.parse_args()
61
+
62
+ # Build message structure if type is provided
63
+ if args.type:
64
+ message = {
65
+ 'type': args.type,
66
+ 'content': args.message
67
+ }
68
+ if args.data:
69
+ try:
70
+ message['data'] = json.loads(args.data)
71
+ except json.JSONDecodeError:
72
+ message['data'] = args.data
73
+ else:
74
+ message = args.message
75
+
76
+ success = send_message(args.sender, message)
77
+
78
+ if success:
79
+ print("Message sent successfully")
80
+ else:
81
+ sys.exit(1)
82
+
83
+ if __name__ == '__main__':
84
+ main()
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env -S uv run --script
2
+ # /// script
3
+ # requires-python = ">=3.8"
4
+ # dependencies = [
5
+ # "requests",
6
+ # ]
7
+ # ///
8
+
9
+ import json
10
+ import sys
11
+ import requests
12
+
13
+
14
+ def main():
15
+ try:
16
+ # Read JSON input from stdin
17
+ input_data = json.load(sys.stdin)
18
+
19
+ session_id = input_data.get('session_id', 'unknown')
20
+
21
+ # Check if this is a Task tool use completion
22
+ tool_name = input_data.get('tool_name', '')
23
+ tool_input = input_data.get('tool_input', {})
24
+ tool_response = input_data.get('tool_response', {})
25
+
26
+ if tool_name == 'Task':
27
+ description = tool_input.get('description', '')
28
+
29
+ # Extract agent name from description (part before colon)
30
+ if ':' in description:
31
+ agent_name = description.split(':')[0].strip()
32
+
33
+ # Extract completion metadata from tool_response
34
+ completion_metadata = {}
35
+
36
+ # Get duration, tokens, and tool use count from tool_response
37
+ if 'totalDurationMs' in tool_response:
38
+ completion_metadata['total_duration_ms'] = tool_response['totalDurationMs']
39
+
40
+ if 'totalTokens' in tool_response:
41
+ completion_metadata['total_tokens'] = tool_response['totalTokens']
42
+
43
+ if 'totalToolUseCount' in tool_response:
44
+ completion_metadata['total_tool_use_count'] = tool_response['totalToolUseCount']
45
+
46
+ # Get usage details if available
47
+ usage = tool_response.get('usage', {})
48
+ if usage:
49
+ if 'input_tokens' in usage:
50
+ completion_metadata['input_tokens'] = usage['input_tokens']
51
+
52
+ if 'output_tokens' in usage:
53
+ completion_metadata['output_tokens'] = usage['output_tokens']
54
+
55
+ if 'cache_creation_input_tokens' in usage:
56
+ completion_metadata['cache_creation_input_tokens'] = usage['cache_creation_input_tokens']
57
+
58
+ if 'cache_read_input_tokens' in usage:
59
+ completion_metadata['cache_read_input_tokens'] = usage['cache_read_input_tokens']
60
+
61
+ # Extract the agent's final response content
62
+ # The response content is typically in the main tool_response as text
63
+ final_response = ''
64
+ if 'content' in tool_response:
65
+ # If content is a list (standard Claude format)
66
+ if isinstance(tool_response['content'], list):
67
+ text_blocks = [block.get('text', '') for block in tool_response['content'] if block.get('type') == 'text']
68
+ final_response = '\n'.join(text_blocks)
69
+ # If content is a string
70
+ elif isinstance(tool_response['content'], str):
71
+ final_response = tool_response['content']
72
+ elif 'text' in tool_response:
73
+ # Alternative field name
74
+ final_response = tool_response['text']
75
+ elif 'response' in tool_response:
76
+ # Another possible field name
77
+ final_response = tool_response['response']
78
+
79
+ # Extract tool calls if available
80
+ tool_calls_data = []
81
+ if 'tool_calls' in tool_response:
82
+ tool_calls_data = tool_response['tool_calls']
83
+ elif 'toolCalls' in tool_response:
84
+ tool_calls_data = tool_response['toolCalls']
85
+
86
+ # Update subagent completion status
87
+ try:
88
+ response = requests.post(
89
+ 'http://localhost:4000/subagents/update-completion',
90
+ json={
91
+ 'session_id': session_id,
92
+ 'name': agent_name,
93
+ 'status': 'completed',
94
+ 'completed_at': None, # Will be set to current time by server
95
+ 'completion_metadata': completion_metadata,
96
+ 'final_response': final_response,
97
+ 'tool_calls': tool_calls_data
98
+ },
99
+ timeout=2
100
+ )
101
+ if response.status_code == 200:
102
+ print(f"Updated completion for subagent: {agent_name}", file=sys.stderr)
103
+ elif response.status_code == 404:
104
+ # Agent not found, silently ignore (might be a direct Task not created by subagent)
105
+ pass
106
+ else:
107
+ # Other error, log but don't fail
108
+ print(f"Failed to update completion for {agent_name}: {response.status_code}", file=sys.stderr)
109
+ except Exception as e:
110
+ # Silently fail if server is not available
111
+ pass
112
+
113
+ sys.exit(0)
114
+
115
+ except json.JSONDecodeError:
116
+ # Gracefully handle JSON decode errors
117
+ sys.exit(0)
118
+ except Exception:
119
+ # Handle any other errors gracefully
120
+ sys.exit(0)
121
+
122
+ if __name__ == '__main__':
123
+ main()
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env -S uv run --script
2
+ # /// script
3
+ # requires-python = ">=3.8"
4
+ # ///
5
+
6
+ """
7
+ Multi-Agent Observability Hook Script
8
+ Sends Claude Code hook events to the observability server.
9
+ """
10
+
11
+ import json
12
+ import sys
13
+ import os
14
+ import argparse
15
+ import urllib.request
16
+ import urllib.error
17
+ from datetime import datetime
18
+
19
+ def send_event_to_server(event_data, server_url='http://localhost:4000/events'):
20
+ """Send event data to the observability server."""
21
+ try:
22
+ # Prepare the request
23
+ req = urllib.request.Request(
24
+ server_url,
25
+ data=json.dumps(event_data).encode('utf-8'),
26
+ headers={
27
+ 'Content-Type': 'application/json',
28
+ 'User-Agent': 'Claude-Code-Hook/1.0'
29
+ }
30
+ )
31
+
32
+ # Send the request
33
+ with urllib.request.urlopen(req, timeout=5) as response:
34
+ if response.status == 200:
35
+ return True
36
+ else:
37
+ print(f"Server returned status: {response.status}", file=sys.stderr)
38
+ return False
39
+
40
+ except urllib.error.URLError as e:
41
+ print(f"Failed to send event: {e}", file=sys.stderr)
42
+ return False
43
+ except Exception as e:
44
+ print(f"Unexpected error: {e}", file=sys.stderr)
45
+ return False
46
+
47
+ def main():
48
+ # Parse command line arguments
49
+ parser = argparse.ArgumentParser(description='Send Claude Code hook events to observability server')
50
+ parser.add_argument('--source-app', required=True, help='Source application name')
51
+ parser.add_argument('--event-type', required=True, help='Hook event type (PreToolUse, PostToolUse, etc.)')
52
+ parser.add_argument('--server-url', default='http://localhost:4000/events', help='Server URL')
53
+ parser.add_argument('--add-chat', action='store_true', help='Include chat transcript if available')
54
+ parser.add_argument('--summarize', action='store_true', help='Generate AI summary of the event')
55
+
56
+ args = parser.parse_args()
57
+
58
+ try:
59
+ # Read hook data from stdin
60
+ input_data = json.load(sys.stdin)
61
+ except json.JSONDecodeError as e:
62
+ print(f"Failed to parse JSON input: {e}", file=sys.stderr)
63
+ sys.exit(1)
64
+
65
+ # Prepare event data for server
66
+ event_data = {
67
+ 'source_app': args.source_app,
68
+ 'session_id': input_data.get('session_id', 'unknown'),
69
+ 'hook_event_type': args.event_type,
70
+ 'payload': input_data,
71
+ 'timestamp': int(datetime.now().timestamp() * 1000)
72
+ }
73
+
74
+ # Handle --add-chat option
75
+ if args.add_chat and 'transcript_path' in input_data:
76
+ transcript_path = input_data['transcript_path']
77
+ if os.path.exists(transcript_path):
78
+ # Read .jsonl file and convert to JSON array
79
+ chat_data = []
80
+ try:
81
+ with open(transcript_path, 'r') as f:
82
+ for line in f:
83
+ line = line.strip()
84
+ if line:
85
+ try:
86
+ chat_data.append(json.loads(line))
87
+ except json.JSONDecodeError:
88
+ pass # Skip invalid lines
89
+
90
+ # Add chat to event data
91
+ event_data['chat'] = chat_data
92
+ except Exception as e:
93
+ print(f"Failed to read transcript: {e}", file=sys.stderr)
94
+
95
+
96
+ # Send to server
97
+ success = send_event_to_server(event_data, args.server_url)
98
+
99
+ # Always exit with 0 to not block Claude Code operations
100
+ sys.exit(0)
101
+
102
+ if __name__ == '__main__':
103
+ main()