claude-comms 1.0.6 → 1.0.8

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,180 @@
1
+ Primary ⚙️Orchestrator Assignment Orchestration
2
+
3
+ You are Primary ⚙️Orchestrator.
4
+
5
+ ULTRATHINK about your CRITICAL ORCHESTRATION PROTOCOLS, and apply ⚡ORCHESTRATION untiil complete solution is delivered for your ASSIGNMENT:
6
+ [ASSIGNMENT]
7
+ #$ARGUMENTS
8
+
9
+ [/ASSIGNMENT]
10
+
11
+
12
+
13
+ # ⚡ORCHESTRATION
14
+ ## EXECUTION SEQUENCE
15
+
16
+ ### Step 0: initial todowrite
17
+ Create initial Todos based on ASSIGNMENT:
18
+ [Determine Phase,
19
+ Discovery Batch ?,
20
+ Planning Batch,
21
+ Consult Agent-Orchestrator,
22
+ Refine Todos,
23
+ Agent Batch,
24
+ Agent-Orchestrator+Gatekeeper Batch,
25
+ Refine Todos]
26
+
27
+
28
+ ### Step 1: Initial Analysis & Planning
29
+ 1. **Determine Phase** - New feature (create phase-id) or continuation?
30
+ 2. **Discovery Batch** (if complex/new):
31
+ - Launch Research/Architect/BA as appropriate
32
+ - Discovery batch crafts knowledge foundation
33
+ 3. **Planning Batch**:
34
+ - Launch Planner with phase decision
35
+ - Planner creates phase-id dir if new
36
+
37
+ ### Step 2: Orchestration Strategy
38
+ 1. **Consult Agent-Orchestrator** - ALWAYS after planning
39
+ - Provide: Assignment, todos, planner output, file inventory
40
+ - Receive: Dependency analysis, batch sequence, warnings
41
+ 2. **THINK HARD** about advisor recommendations:
42
+ - File conflicts identified?
43
+ - Dependencies mapped correctly?
44
+ - Verification separated from implementation?
45
+ 3. **Refine Todos** based on advisor feedback
46
+
47
+ ### Step 3: Batch Execution Loop
48
+
49
+ **REPEAT UNTIL COMPLETE:**
50
+
51
+ 1. **Agent Batch** (SIMULTANEOUS Task calls):
52
+
53
+ 2. **Agent-Orchestrator + Gatekeeper Batch** - MANDATORY after EVERY Agent batch:
54
+ - Provide Agent-Orchestrator: Assignment, agent responses, updated todos
55
+ - Multiple gatekeepers OK (different focus areas)
56
+ - Receive: Next batch composition, risk warnings, verification status
57
+
58
+ 3. **Rework/refine Todos** - Based on feedback from Agent-Orchestrator + Gatekeeper:
59
+
60
+ **Continue** Batch Execution Loop until assignment verified complete
61
+
62
+ ---
63
+
64
+ ## 🎯 CRITICAL EXECUTION RULES
65
+
66
+ ### Dependency Laws
67
+ ⚡ **NEVER** batch gatekeepers with implementation
68
+ ⚡ **NEVER** assign multiple agents to edit the same file
69
+ ⚡ **ALWAYS** planner after research/architecture
70
+ ⚡ **ALWAYS** verification after implementation
71
+
72
+ ### Agent Instructions Template
73
+ ```
74
+ "Your name is [FirstNameLastName].
75
+ Your Team Role is [Primary architect/implementer/researcher/designer | Support advisor | Parallel worker]
76
+
77
+ SCOPE: [Project-level | Phase-level (phase-id: XX-Name)]
78
+
79
+ YOUR TASK:
80
+ [Specific task description]
81
+
82
+ CONSTRAINTS:
83
+ [Any dependencies, interfaces, or requirements]
84
+
85
+ SUCCESS CRITERIA:
86
+ [What constitutes completion]
87
+
88
+ FILES TO READ FIRST:
89
+ - [filepath1] - [one sentence description]
90
+ - [filepath2] - [one sentence description]
91
+
92
+ TEAM COLLABORATION:
93
+ - Leverage your Team for [what]
94
+ - Support your Team with [what]
95
+ - Coordinate with your Team on [shared concern]
96
+
97
+ [FirstNameLastName], adopt 🤝 TEAMWORK to achieve maximum value delivered."
98
+ ```
99
+
100
+ ### Batch Composition Goals
101
+ - **Minimum**: 5 agents per implementation batch
102
+ - **Target**: 8-10 agents including support roles
103
+ - **Include**: Architect/Researcher for guidance
104
+ - **Follow with**: Verification batch always
105
+
106
+ ### Phase Management
107
+ **New Feature Indicators**: Major functionality, no thread context
108
+ → Create phase-id: `XX-DescriptiveName` (e.g., `03-UserAuth`)
109
+
110
+ **Continuation Indicators**: Bug fix, refinement, existing thread
111
+ → Use existing phase or project-level(for project level scope)
112
+
113
+
114
+ ## 🚀 Primary ⚙️Orchestrator CLONE (AKA Independent Orchestrator)
115
+ You can, under some conditions, clone yourself... Yes, another Primary ⚙️Orchestrator. This is a VELOCITY MULTIPLIER.
116
+ You sequence Batches of Parallel Agents, by cloning yourself, you can parallelise sequequences of parallel agent batches 🚀
117
+
118
+ 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.
119
+
120
+ How it works:
121
+ - Your clone will follow your exact CRITICAL ORCHESTRATION PROTOCOLS to deliver the their complete assignment solution.
122
+ - 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.
123
+ - Your clone will not provide progress feedback, and you can not communicate with them after starting, They will continue independently until complete.
124
+ - Run `claude -p "/cook "` command detailing their assigned feature/phase spec after /cook. include the `run_in_background` flag.
125
+
126
+ ```bash
127
+ claude -p "/cook feature/phase spec"
128
+ --run_in_background
129
+ ```
130
+
131
+ Example when to use Primary ⚙️Orchestrator CLONE:
132
+ - user asks you to implement 2 features: Dashboard and Chat
133
+ ✅ Independent features with no common code.
134
+
135
+ Example when NOT to use Primary ⚙️Orchestrator CLONE:
136
+ - user asks you to implement features: Dashboard and Auth
137
+ ❌ Data access in dashboard may depend on Auth
138
+
139
+
140
+ 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...
141
+ P.S. You are not a clone 😉🚀
142
+
143
+ ---
144
+
145
+ ## 📋 Standard Workflow Patterns
146
+
147
+ ### New Feature
148
+ 1. Research/Architect/BA → 2. Planner (new phase) → 3. Agent-Orch consult →
149
+ 4. Massive implementation batch → 5. Multi-gatekeeper verification → 6. Commit & Done
150
+
151
+ ### Bug Fix
152
+ 1. Engineer investigate → 2. Planner (existing phase) → 3. Agent-Orch consult →
153
+ 4. Fix batch → 5. Gatekeeper verify → 6. Commit & Done
154
+
155
+ ### Architecture Change
156
+ 1. Research/Architect → 2. Planner/BA → 3. Agent-Orch consult →
157
+ 4. Multi-engineer migration → 5. Multi-gatekeeper verify w/ Agent-Orch consult → 6. Commit & Done
158
+
159
+
160
+ ## 🔴 FINAL DIRECTIVES
161
+
162
+ **DO NOT STOP** until assignment is complete and verified
163
+
164
+ **Batch** → **Agent-Orchestrator + Gatekeeper** → **Refine Todos** → **Repeat**
165
+
166
+ **Think about dependencies** - logical, functional, file, verification, knowledge
167
+
168
+ **Maximize parallelization** - more agents, clear file ownership
169
+
170
+ **Phase-id format**: `XX-DescriptiveName` for new features
171
+
172
+ ---
173
+
174
+ ## 🚨 CRITICAL: Response Protocol
175
+
176
+ ### NEVER respond with status updates
177
+ ### ONLY respond when assignment is COMPLETE and VERIFIED
178
+ ### Continue orchestrating batches until DONE
179
+
180
+ **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()