claude-comms 1.0.45 → 2.0.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +12 -13
  3. package/bin/claude-setup.js +3 -4
  4. package/package.json +6 -10
  5. package/src/orchestrator/installer.js +5 -63
  6. package/.agents/AGENTS.md +0 -61
  7. package/.agents/repo.md +0 -18
  8. package/.agents/tools/README.md +0 -45
  9. package/.agents/tools/agent-job/README.md +0 -208
  10. package/.agents/tools/agent-job/agent_job.py +0 -866
  11. package/.agents/tools/agent-job/agent_monitor.py +0 -575
  12. package/.agents/tools/agent-job/consultant_template.txt +0 -73
  13. package/.agents/tools/chrome-devtools/README.md +0 -101
  14. package/.agents/tools/chrome-devtools/browsertools.py +0 -839
  15. package/.agents/tools/chrome-devtools/config.json +0 -24
  16. package/.claude/agents/core/architect.md +0 -316
  17. package/.claude/agents/core/consultants/codex.md +0 -39
  18. package/.claude/agents/core/consultants/gemini.md +0 -39
  19. package/.claude/agents/core/designer.md +0 -181
  20. package/.claude/agents/core/engineer.md +0 -157
  21. package/.claude/agents/core/planner.md +0 -251
  22. package/.claude/agents/core/uat.md +0 -147
  23. package/.claude/agents/util/feedback-executor.md +0 -29
  24. package/.claude/agents/util/meta-agent-builder.md +0 -309
  25. package/.claude/agents/util/session-review.md +0 -228
  26. package/.claude/commands/action-feedback-item.md +0 -22
  27. package/.claude/commands/analyse-document.md +0 -10
  28. package/.claude/commands/await-feedback-orchestrator.md +0 -12
  29. package/.claude/commands/await-feedback.md +0 -21
  30. package/.claude/commands/cook.md +0 -119
  31. package/.claude/commands/feedback.md +0 -140
  32. package/.claude/commands/hello-world.md +0 -1
  33. package/.claude/commands/install-feedback-widget.md +0 -599
  34. package/.claude/commands/learn.md +0 -32
  35. package/.claude/commands/uat-refine.md +0 -121
  36. package/.claude/hooks/comms/get_unread_messages.py +0 -71
  37. package/.claude/hooks/comms/register_subagent.py +0 -74
  38. package/.claude/hooks/comms/send_message.py +0 -87
  39. package/.claude/hooks/comms/update_subagent_completion.py +0 -126
  40. package/.claude/hooks/context/session_id.py +0 -35
  41. package/.claude/hooks/observability/send_event.py +0 -110
  42. package/.claude/hooks/session-data/get_session_data.py +0 -261
  43. package/.claude/hooks/session-data/intercept_session_id.py +0 -69
  44. package/.claude/hooks/utils/server_config.py +0 -17
  45. package/.claude/settings.json +0 -108
  46. package/CLAUDE.md +0 -240
@@ -1,121 +0,0 @@
1
- Primary ⚙️Orchestrator Assignment Orchestration
2
-
3
- You are Primary ⚙️Orchestrator.
4
-
5
-
6
- ULTRATHINK about your CRITICAL ORCHESTRATION PROTOCOLS, and apply ⚡ORCHESTRATION untiil complete solution is delivered for your ASSIGNMENT:
7
- [ASSIGNMENT]
8
- #$ARGUMENTS
9
-
10
- [/ASSIGNMENT]
11
-
12
-
13
-
14
- # ⚡ORCHESTRATION
15
- ## EXECUTION SEQUENCE
16
- You must manage and maintain Todos dynamically, refine Todos after every decision, and when new information presents itself.
17
- Populate your initial Todos with your step by step WORKFLOW:
18
-
19
- [WORKFLOW]
20
- PLAN: ULTRATHINK about the UI/UX pages, features, user flows involved and the dependencies between. Break down ASSIGNMENT into dependency sequenced test scenarios batches.
21
- ENABLE: ensure the dev server is running. you may need to kill an old process and start it again `.agents/repo.md`
22
-
23
- For each test scenario batch sequence:
24
- 1. FEEDBACK: Launch a batch of multiple UAT agents and a support designer. Each UAT agent in the batch should be given a focused test scenario or portion of the app to review. they must visually inspect the UI/UX and provide feedback on: design allignment, expected functionality, issues encountered, error logs. These UAT agents are proxy for the user. Their perspecttive matters!
25
- 2. IMPLEMENT: Launch a blended Implementation batch of engineers + codex consultants + gemini consultants to implement changes based on UAT FEEDBACK.
26
- 3. REQUEST APPROVAL: Task a batch of one or more (depending on the number of impacted areas/changes) UAT agents. Each UAT agent in this batch should manually execute one of the test scenarios/areas impacted, and provide their assessment of changes, design allignment, expected functionality.
27
- - If UAT gives critical feedback, and DOES NOT APPROVE: itterate until approval awarded.
28
- - When all UAT agents in the batch give signoff approval for deployment, continue to the next to the next batch of test scenarios
29
- [/WORKFLOW]
30
-
31
-
32
- ## 🎯 CRITICAL EXECUTION RULES
33
-
34
- ### Dependency Laws
35
- ⚡ **ALWAYS** Take UAT feedback seriously. They are the user's representitive.
36
- ⚡ **ALWAYS** Add new todos if Approval is not awarded.
37
- ⚡ **ALWAYS** blend consultants + engineers for implementation/refinement tasks
38
-
39
-
40
-
41
- ### Agent Instructions Template
42
- ```
43
- "Your name is [FirstNameLastName].
44
- Your Team Role is [Support/Implementation/Review+Refine/AssessingOnly]
45
-
46
- SCOPE: [Phase-level (phase-id: XX-Name)]
47
-
48
- YOUR TASK:
49
- [Specific task description]
50
-
51
- CONSTRAINTS:
52
- [Any dependencies, interfaces, or requirements]
53
-
54
- SUCCESS CRITERIA:
55
- [What constitutes completion]
56
-
57
- FILES TO READ FIRST:
58
- - [filepath1] - [one sentence description]
59
- - [filepath2] - [one sentence description]
60
-
61
- TEAM COLLABORATION:
62
- - Leverage your Team for [what]
63
- - Support your Team with [what]
64
- - Coordinate with your Team on [shared concern]
65
-
66
-
67
- ⭐*The successful delivery of your assigned task, contributes to the high level Assignment:*⭐
68
- [Your full ASSIGNMENT]
69
-
70
- ⭐Ensure you are alligned with this North Star objective*⭐
71
-
72
-
73
- [FirstNameLastName], adopt 🤝 TEAMWORK to achieve maximum value delivered."
74
- ```
75
-
76
- *Remember:*
77
- An agent has no inherit knowledge of previous batch agents. They can only collaborate within thier batch.
78
- Don't refer to prior batch agents by name. Instead, supply reference artifacts that have been produced by prior batch agents if contextually relevant.
79
-
80
-
81
- ### Batch Composition Goals
82
- - **Minimum**: 5 agents per batch
83
- - **Target**: 8-10 agents including support roles
84
- - **Include**: roughly equal number of gemini, codex, and engineer implementers, and a support architect for guidance/coordination.
85
- - **Follow with**: Verification batch always
86
-
87
- ### Phase Management
88
- **New Feature Indicators**: Major functionality, no thread context
89
- → Create phase-id: `XX-DescriptiveName` (e.g., `03-UserAuth`)
90
-
91
- **Continuation Indicators**: Bug fix, refinement, existing thread
92
- → Use existing phase or project-level(for project level scope)
93
-
94
-
95
- ## Consultant Agent
96
- Consultants come in two variants: Codex and Gemini. leverage them both liberally to diversify.
97
- For implementation batches, distribute the tasks across engineers, codex consultants, and gemini consultants. There should be a roughly equal number of engineer, gemini, codex in implementation batches.
98
- When you are tasking an agent with read+document type task, recruit a codex and gemini Consultant with the exact same instructions (except a different names). Include the consultants in the same batch. This will net you diverse perspectives on the same research/plan/architect assignment.
99
-
100
-
101
-
102
-
103
- ## 🔴 FINAL DIRECTIVES
104
-
105
- **DO NOT STOP** until assignment is complete and verified
106
-
107
- **Think about dependencies** - logical, functional, file, verification, knowledge
108
-
109
- **Maximize parallelization** - more agents, clear file ownership
110
-
111
- **Phase-id format**: `XX-DescriptiveName` for new features
112
-
113
- ---
114
-
115
- ## 🚨 CRITICAL: Response Protocol
116
-
117
- ### NEVER respond with status updates
118
- ### ONLY respond when assignment is COMPLETE and VERIFIED
119
- ### Continue orchestrating batches until DONE
120
-
121
- **Begin orchestrating this Assignment NOW!**
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import json
4
- import sys
5
- import requests
6
- import argparse
7
- import os
8
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
9
- from utils.server_config import get_server_url
10
-
11
- def get_unread_messages(subagent_name):
12
- """
13
- Get unread messages for a specific subagent.
14
-
15
- Args:
16
- subagent_name: The name/nickname of the subagent requesting messages
17
-
18
- Returns:
19
- List of unread messages with sender, message, and timestamp
20
- """
21
- try:
22
- response = requests.post(
23
- f'{get_server_url()}/subagents/unread',
24
- json={
25
- 'subagent_name': subagent_name
26
- },
27
- timeout=5
28
- )
29
-
30
- if response.status_code == 200:
31
- data = response.json()
32
- return data.get('messages', [])
33
- else:
34
- print(f"Error: Server returned status {response.status_code}", file=sys.stderr)
35
- return []
36
-
37
- except requests.exceptions.ConnectionError:
38
- print(f"Error: Could not connect to observability server at {get_server_url()}", file=sys.stderr)
39
- return []
40
- except requests.exceptions.Timeout:
41
- print("Error: Request timed out", file=sys.stderr)
42
- return []
43
- except Exception as e:
44
- print(f"Error: {str(e)}", file=sys.stderr)
45
- return []
46
-
47
- def main():
48
- parser = argparse.ArgumentParser(description='Get unread messages for a subagent')
49
- parser.add_argument('--name', required=True, help='Subagent name/nickname')
50
- parser.add_argument('--json', action='store_true', help='Output as JSON')
51
-
52
- args = parser.parse_args()
53
-
54
- messages = get_unread_messages(args.name)
55
-
56
- if args.json:
57
- print(json.dumps(messages, indent=2))
58
- else:
59
- if not messages:
60
- print("No unread messages")
61
- else:
62
- print(f"Found {len(messages)} unread message(s):")
63
- print("-" * 50)
64
- for msg in messages:
65
- print(f"From: {msg['sender']}")
66
- print(f"Time: {msg['created_at']}")
67
- print(f"Message: {json.dumps(msg['message'], indent=2)}")
68
- print("-" * 50)
69
-
70
- if __name__ == '__main__':
71
- main()
@@ -1,74 +0,0 @@
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
- import os
13
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
14
- from utils.server_config import get_server_url
15
-
16
-
17
- def main():
18
- try:
19
- # Read JSON input from stdin
20
- input_data = json.load(sys.stdin)
21
-
22
- session_id = input_data.get('session_id', 'unknown')
23
-
24
- # Check if this is a Task tool use and register the subagent
25
- tool_name = input_data.get('tool_name', '')
26
- tool_input = input_data.get('tool_input', {})
27
-
28
- if tool_name == 'Task':
29
- description = tool_input.get('description', '')
30
- subagent_type = tool_input.get('subagent_type', '')
31
- initial_prompt = tool_input.get('prompt', '')
32
-
33
- # Extract nickname from description (part before colon)
34
- if ':' in description:
35
- nickname = description.split(':')[0].strip()
36
-
37
- # Register the subagent with the server
38
- try:
39
- response = requests.post(
40
- f'{get_server_url()}/subagents/register',
41
- json={
42
- 'session_id': session_id,
43
- 'name': nickname,
44
- 'subagent_type': subagent_type
45
- },
46
- timeout=2
47
- )
48
- if response.status_code == 200:
49
- print(f"Registered subagent: {nickname} ({subagent_type})", file=sys.stderr)
50
-
51
- # Now update with initial prompt using PATCH endpoint
52
- if initial_prompt:
53
- prompt_response = requests.patch(
54
- f'{get_server_url()}/subagents/{session_id}/{nickname}',
55
- json={'initial_prompt': initial_prompt},
56
- timeout=2
57
- )
58
- if prompt_response.status_code == 200:
59
- print(f"Stored initial prompt for subagent: {nickname}", file=sys.stderr)
60
- except Exception as e:
61
- # Silently fail if server is not available
62
- pass
63
-
64
- sys.exit(0)
65
-
66
- except json.JSONDecodeError:
67
- # Gracefully handle JSON decode errors
68
- sys.exit(0)
69
- except Exception:
70
- # Handle any other errors gracefully
71
- sys.exit(0)
72
-
73
- if __name__ == '__main__':
74
- main()
@@ -1,87 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import json
4
- import sys
5
- import requests
6
- import argparse
7
- import os
8
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
9
- from utils.server_config import get_server_url
10
-
11
- def send_message(sender_name, message):
12
- """
13
- Send a message from a subagent to all other subagents.
14
-
15
- Args:
16
- sender_name: The name/nickname of the sending subagent
17
- message: The message to send (can be string or dict)
18
-
19
- Returns:
20
- True if message was sent successfully, False otherwise
21
- """
22
- try:
23
- # Parse message if it's a JSON string
24
- if isinstance(message, str):
25
- try:
26
- message = json.loads(message)
27
- except json.JSONDecodeError:
28
- # Keep as string if not valid JSON
29
- pass
30
-
31
- response = requests.post(
32
- f'{get_server_url()}/subagents/message',
33
- json={
34
- 'sender': sender_name,
35
- 'message': message
36
- },
37
- timeout=5
38
- )
39
-
40
- if response.status_code == 200:
41
- return True
42
- else:
43
- print(f"Error: Server returned status {response.status_code}", file=sys.stderr)
44
- return False
45
-
46
- except requests.exceptions.ConnectionError:
47
- print(f"Error: Could not connect to observability server at {get_server_url()}", file=sys.stderr)
48
- return False
49
- except requests.exceptions.Timeout:
50
- print("Error: Request timed out", file=sys.stderr)
51
- return False
52
- except Exception as e:
53
- print(f"Error: {str(e)}", file=sys.stderr)
54
- return False
55
-
56
- def main():
57
- parser = argparse.ArgumentParser(description='Send a message from a subagent')
58
- parser.add_argument('--sender', required=True, help='Sender subagent name/nickname')
59
- parser.add_argument('--message', required=True, help='Message to send (string or JSON)')
60
- parser.add_argument('--type', help='Message type (optional)')
61
- parser.add_argument('--data', help='Additional data (JSON format)')
62
-
63
- args = parser.parse_args()
64
-
65
- # Build message structure if type is provided
66
- if args.type:
67
- message = {
68
- 'type': args.type,
69
- 'content': args.message
70
- }
71
- if args.data:
72
- try:
73
- message['data'] = json.loads(args.data)
74
- except json.JSONDecodeError:
75
- message['data'] = args.data
76
- else:
77
- message = args.message
78
-
79
- success = send_message(args.sender, message)
80
-
81
- if success:
82
- print("Message sent successfully")
83
- else:
84
- sys.exit(1)
85
-
86
- if __name__ == '__main__':
87
- main()
@@ -1,126 +0,0 @@
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
- import os
13
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
14
- from utils.server_config import get_server_url
15
-
16
-
17
- def main():
18
- try:
19
- # Read JSON input from stdin
20
- input_data = json.load(sys.stdin)
21
-
22
- session_id = input_data.get('session_id', 'unknown')
23
-
24
- # Check if this is a Task tool use completion
25
- tool_name = input_data.get('tool_name', '')
26
- tool_input = input_data.get('tool_input', {})
27
- tool_response = input_data.get('tool_response', {})
28
-
29
- if tool_name == 'Task':
30
- description = tool_input.get('description', '')
31
-
32
- # Extract agent name from description (part before colon)
33
- if ':' in description:
34
- agent_name = description.split(':')[0].strip()
35
-
36
- # Extract completion metadata from tool_response
37
- completion_metadata = {}
38
-
39
- # Get duration, tokens, and tool use count from tool_response
40
- if 'totalDurationMs' in tool_response:
41
- completion_metadata['total_duration_ms'] = tool_response['totalDurationMs']
42
-
43
- if 'totalTokens' in tool_response:
44
- completion_metadata['total_tokens'] = tool_response['totalTokens']
45
-
46
- if 'totalToolUseCount' in tool_response:
47
- completion_metadata['total_tool_use_count'] = tool_response['totalToolUseCount']
48
-
49
- # Get usage details if available
50
- usage = tool_response.get('usage', {})
51
- if usage:
52
- if 'input_tokens' in usage:
53
- completion_metadata['input_tokens'] = usage['input_tokens']
54
-
55
- if 'output_tokens' in usage:
56
- completion_metadata['output_tokens'] = usage['output_tokens']
57
-
58
- if 'cache_creation_input_tokens' in usage:
59
- completion_metadata['cache_creation_input_tokens'] = usage['cache_creation_input_tokens']
60
-
61
- if 'cache_read_input_tokens' in usage:
62
- completion_metadata['cache_read_input_tokens'] = usage['cache_read_input_tokens']
63
-
64
- # Extract the agent's final response content
65
- # The response content is typically in the main tool_response as text
66
- final_response = ''
67
- if 'content' in tool_response:
68
- # If content is a list (standard Claude format)
69
- if isinstance(tool_response['content'], list):
70
- text_blocks = [block.get('text', '') for block in tool_response['content'] if block.get('type') == 'text']
71
- final_response = '\n'.join(text_blocks)
72
- # If content is a string
73
- elif isinstance(tool_response['content'], str):
74
- final_response = tool_response['content']
75
- elif 'text' in tool_response:
76
- # Alternative field name
77
- final_response = tool_response['text']
78
- elif 'response' in tool_response:
79
- # Another possible field name
80
- final_response = tool_response['response']
81
-
82
- # Extract tool calls if available
83
- tool_calls_data = []
84
- if 'tool_calls' in tool_response:
85
- tool_calls_data = tool_response['tool_calls']
86
- elif 'toolCalls' in tool_response:
87
- tool_calls_data = tool_response['toolCalls']
88
-
89
- # Update subagent completion status
90
- try:
91
- response = requests.post(
92
- f'{get_server_url()}/subagents/update-completion',
93
- json={
94
- 'session_id': session_id,
95
- 'name': agent_name,
96
- 'status': 'completed',
97
- 'completed_at': None, # Will be set to current time by server
98
- 'completion_metadata': completion_metadata,
99
- 'final_response': final_response,
100
- 'tool_calls': tool_calls_data
101
- },
102
- timeout=2
103
- )
104
- if response.status_code == 200:
105
- print(f"Updated completion for subagent: {agent_name}", file=sys.stderr)
106
- elif response.status_code == 404:
107
- # Agent not found, silently ignore (might be a direct Task not created by subagent)
108
- pass
109
- else:
110
- # Other error, log but don't fail
111
- print(f"Failed to update completion for {agent_name}: {response.status_code}", file=sys.stderr)
112
- except Exception as e:
113
- # Silently fail if server is not available
114
- pass
115
-
116
- sys.exit(0)
117
-
118
- except json.JSONDecodeError:
119
- # Gracefully handle JSON decode errors
120
- sys.exit(0)
121
- except Exception:
122
- # Handle any other errors gracefully
123
- sys.exit(0)
124
-
125
- if __name__ == '__main__':
126
- main()
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env python3
2
- # UserPromptSubmit hook to inject context into the conversation
3
-
4
- import json
5
- import sys
6
- # NOTE: this ONLY works for userPromptSubmit hook, no other hooks are supported.
7
- def main():
8
- try:
9
- # Read JSON input from stdin
10
- input_data = json.load(sys.stdin)
11
-
12
- # Extract the user's prompt
13
-
14
- session_id = input_data.get('session_id', 'unknown')
15
-
16
- # Create additional context that will be injected
17
- additional_context = "Your session_id is: {session_id}"
18
-
19
-
20
- output = {
21
- "hookSpecificOutput": {
22
- "hookEventName": "UserPromptSubmit",
23
- "additionalContext": additional_context
24
- }
25
- }
26
- print(json.dumps(output))
27
- sys.exit(0)
28
-
29
- except Exception as e:
30
- # On error, show to user but don't block
31
- print(f"Error in UserPromptSubmit hook: {e}", file=sys.stderr)
32
- sys.exit(1)
33
-
34
- if __name__ == '__main__':
35
- main()
@@ -1,110 +0,0 @@
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
- # Add parent directory to path for imports
20
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
21
- from utils.server_config import get_server_url
22
-
23
- def send_event_to_server(event_data, server_url=None):
24
- """Send event data to the observability server."""
25
- if server_url is None:
26
- server_url = f'{get_server_url()}/events'
27
-
28
- try:
29
- # Prepare the request
30
- req = urllib.request.Request(
31
- server_url,
32
- data=json.dumps(event_data).encode('utf-8'),
33
- headers={
34
- 'Content-Type': 'application/json',
35
- 'User-Agent': 'Claude-Code-Hook/1.0'
36
- }
37
- )
38
-
39
- # Send the request
40
- with urllib.request.urlopen(req, timeout=5) as response:
41
- if response.status == 200:
42
- return True
43
- else:
44
- print(f"Server returned status: {response.status}", file=sys.stderr)
45
- return False
46
-
47
- except urllib.error.URLError as e:
48
- print(f"Failed to send event: {e}", file=sys.stderr)
49
- return False
50
- except Exception as e:
51
- print(f"Unexpected error: {e}", file=sys.stderr)
52
- return False
53
-
54
- def main():
55
- # Parse command line arguments
56
- parser = argparse.ArgumentParser(description='Send Claude Code hook events to observability server')
57
- parser.add_argument('--source-app', required=True, help='Source application name')
58
- parser.add_argument('--event-type', required=True, help='Hook event type (PreToolUse, PostToolUse, etc.)')
59
- parser.add_argument('--add-chat', action='store_true', help='Include chat transcript if available')
60
- parser.add_argument('--summarize', action='store_true', help='Generate AI summary of the event')
61
-
62
- args = parser.parse_args()
63
-
64
- try:
65
- # Read hook data from stdin
66
- input_data = json.load(sys.stdin)
67
- except json.JSONDecodeError as e:
68
- print(f"Failed to parse JSON input: {e}", file=sys.stderr)
69
- sys.exit(1)
70
-
71
- # Prepare event data for server
72
- event_data = {
73
- 'source_app': args.source_app,
74
- 'session_id': input_data.get('session_id', 'unknown'),
75
- 'hook_event_type': args.event_type,
76
- 'payload': input_data,
77
- 'timestamp': int(datetime.now().timestamp() * 1000)
78
- }
79
-
80
- # Handle --add-chat option
81
- if args.add_chat and 'transcript_path' in input_data:
82
- transcript_path = input_data['transcript_path']
83
- if os.path.exists(transcript_path):
84
- # Read .jsonl file and convert to JSON array
85
- chat_data = []
86
- try:
87
- with open(transcript_path, 'r') as f:
88
- for line in f:
89
- line = line.strip()
90
- if line:
91
- try:
92
- chat_data.append(json.loads(line))
93
- except json.JSONDecodeError:
94
- pass # Skip invalid lines
95
-
96
- # Add chat to event data
97
- event_data['chat'] = chat_data
98
- except Exception as e:
99
- print(f"Failed to read transcript: {e}", file=sys.stderr)
100
-
101
-
102
- # Send to server
103
- server_url = f'{get_server_url()}/events'
104
- success = send_event_to_server(event_data, server_url)
105
-
106
- # Always exit with 0 to not block Claude Code operations
107
- sys.exit(0)
108
-
109
- if __name__ == '__main__':
110
- main()