hcom 0.1.4__tar.gz → 0.1.6__tar.gz
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.
Potentially problematic release.
This version of hcom might be problematic. Click here for more details.
- {hcom-0.1.4/src/hcom.egg-info → hcom-0.1.6}/PKG-INFO +8 -8
- {hcom-0.1.4 → hcom-0.1.6}/README.md +6 -6
- {hcom-0.1.4 → hcom-0.1.6}/pyproject.toml +2 -2
- {hcom-0.1.4 → hcom-0.1.6}/src/hcom/__init__.py +1 -1
- {hcom-0.1.4 → hcom-0.1.6}/src/hcom/__main__.py +136 -68
- {hcom-0.1.4 → hcom-0.1.6/src/hcom.egg-info}/PKG-INFO +8 -8
- {hcom-0.1.4 → hcom-0.1.6}/MANIFEST.in +0 -0
- {hcom-0.1.4 → hcom-0.1.6}/setup.cfg +0 -0
- {hcom-0.1.4 → hcom-0.1.6}/src/hcom.egg-info/SOURCES.txt +0 -0
- {hcom-0.1.4 → hcom-0.1.6}/src/hcom.egg-info/dependency_links.txt +0 -0
- {hcom-0.1.4 → hcom-0.1.6}/src/hcom.egg-info/entry_points.txt +0 -0
- {hcom-0.1.4 → hcom-0.1.6}/src/hcom.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hcom
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Lightweight CLI tool for real-time
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: Lightweight CLI tool for real-time communication between Claude Code subagents using hooks
|
|
5
5
|
Author-email: aannoo <your@email.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/aannoo/claude-hook-comms
|
|
@@ -26,9 +26,9 @@ Classifier: Topic :: Communications
|
|
|
26
26
|
Requires-Python: >=3.6
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
|
-
# Claude
|
|
29
|
+
# hcom - Claude Hook Comms
|
|
30
30
|
|
|
31
|
-
Lightweight CLI tool for real-time communication between
|
|
31
|
+
Lightweight CLI tool for real-time communication between Claude Code [subagents](https://docs.anthropic.com/en/docs/claude-code/sub-agents) using [hooks](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
32
32
|
|
|
33
33
|
## 🦆 What It Does
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ Creates a group chat where you and multiple interactive Claude Code subagents ca
|
|
|
38
38
|
|
|
39
39
|
## 🦷 Features
|
|
40
40
|
|
|
41
|
-
- **Multi-Terminal Launch** - Launch
|
|
41
|
+
- **Multi-Terminal Launch** - Launch Claude Code subagents in new terminals
|
|
42
42
|
- **Live Dashboard** - Real-time monitoring of all instances
|
|
43
43
|
- **Multi-Agent Communication** - Claude instances talk to each other across projects
|
|
44
44
|
- **@Mention Targeting** - Send messages to specific subagents or teams
|
|
@@ -154,7 +154,7 @@ HCOM_INSTANCE_HINTS="always update chat with progress" hcom open nice-subagent-b
|
|
|
154
154
|
| `max_messages_per_delivery` | 50 | `HCOM_MAX_MESSAGES_PER_DELIVERY` | Messages delivered per batch |
|
|
155
155
|
| `sender_name` | "bigboss" | `HCOM_SENDER_NAME` | Your name in chat |
|
|
156
156
|
| `sender_emoji` | "🐳" | `HCOM_SENDER_EMOJI` | Your emoji icon |
|
|
157
|
-
| `initial_prompt` | "Say hi" | `HCOM_INITIAL_PROMPT` | What new instances are told to do |
|
|
157
|
+
| `initial_prompt` | "Say hi in chat" | `HCOM_INITIAL_PROMPT` | What new instances are told to do |
|
|
158
158
|
| `first_use_text` | "Essential, concise messages only" | `HCOM_FIRST_USE_TEXT` | Welcome message for instances |
|
|
159
159
|
| `terminal_mode` | "new_window" | `HCOM_TERMINAL_MODE` | How to launch terminals ("new_window", "same_terminal", "show_commands") |
|
|
160
160
|
| `terminal_command` | null | `HCOM_TERMINAL_COMMAND` | Custom terminal command (see Terminal Options) |
|
|
@@ -202,11 +202,11 @@ hcom adds hooks to your project directory's `.claude/settings.local.json`:
|
|
|
202
202
|
- **Identity**: Each instance gets a unique name based on conversation UUID (e.g., "hovoa7")
|
|
203
203
|
- **Persistence**: Names persist across `--resume` maintaining conversation context
|
|
204
204
|
- **Status Detection**: Notification hook tracks permission requests and activity
|
|
205
|
-
- **Agents**: When you run `hcom open researcher`, it loads an interactive
|
|
205
|
+
- **Agents**: When you run `hcom open researcher`, it loads an interactive Claude session with a system prompt from `.claude/agents/researcher.md` (local) or `~/.claude/agents/researcher.md` (global). Agents can specify `model:` and `tools:` in YAML frontmatter
|
|
206
206
|
|
|
207
207
|
### Architecture
|
|
208
208
|
- **Single conversation** - All instances share one global conversation
|
|
209
|
-
- **Opt-in participation** - Only
|
|
209
|
+
- **Opt-in participation** - Only Claude Code instances launched with `hcom open` join the chat
|
|
210
210
|
- **@-mention filtering** - Target messages to specific instances or teams
|
|
211
211
|
|
|
212
212
|
### File Structure
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Claude
|
|
1
|
+
# hcom - Claude Hook Comms
|
|
2
2
|
|
|
3
|
-
Lightweight CLI tool for real-time communication between
|
|
3
|
+
Lightweight CLI tool for real-time communication between Claude Code [subagents](https://docs.anthropic.com/en/docs/claude-code/sub-agents) using [hooks](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
4
4
|
|
|
5
5
|
## 🦆 What It Does
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ Creates a group chat where you and multiple interactive Claude Code subagents ca
|
|
|
10
10
|
|
|
11
11
|
## 🦷 Features
|
|
12
12
|
|
|
13
|
-
- **Multi-Terminal Launch** - Launch
|
|
13
|
+
- **Multi-Terminal Launch** - Launch Claude Code subagents in new terminals
|
|
14
14
|
- **Live Dashboard** - Real-time monitoring of all instances
|
|
15
15
|
- **Multi-Agent Communication** - Claude instances talk to each other across projects
|
|
16
16
|
- **@Mention Targeting** - Send messages to specific subagents or teams
|
|
@@ -126,7 +126,7 @@ HCOM_INSTANCE_HINTS="always update chat with progress" hcom open nice-subagent-b
|
|
|
126
126
|
| `max_messages_per_delivery` | 50 | `HCOM_MAX_MESSAGES_PER_DELIVERY` | Messages delivered per batch |
|
|
127
127
|
| `sender_name` | "bigboss" | `HCOM_SENDER_NAME` | Your name in chat |
|
|
128
128
|
| `sender_emoji` | "🐳" | `HCOM_SENDER_EMOJI` | Your emoji icon |
|
|
129
|
-
| `initial_prompt` | "Say hi" | `HCOM_INITIAL_PROMPT` | What new instances are told to do |
|
|
129
|
+
| `initial_prompt` | "Say hi in chat" | `HCOM_INITIAL_PROMPT` | What new instances are told to do |
|
|
130
130
|
| `first_use_text` | "Essential, concise messages only" | `HCOM_FIRST_USE_TEXT` | Welcome message for instances |
|
|
131
131
|
| `terminal_mode` | "new_window" | `HCOM_TERMINAL_MODE` | How to launch terminals ("new_window", "same_terminal", "show_commands") |
|
|
132
132
|
| `terminal_command` | null | `HCOM_TERMINAL_COMMAND` | Custom terminal command (see Terminal Options) |
|
|
@@ -174,11 +174,11 @@ hcom adds hooks to your project directory's `.claude/settings.local.json`:
|
|
|
174
174
|
- **Identity**: Each instance gets a unique name based on conversation UUID (e.g., "hovoa7")
|
|
175
175
|
- **Persistence**: Names persist across `--resume` maintaining conversation context
|
|
176
176
|
- **Status Detection**: Notification hook tracks permission requests and activity
|
|
177
|
-
- **Agents**: When you run `hcom open researcher`, it loads an interactive
|
|
177
|
+
- **Agents**: When you run `hcom open researcher`, it loads an interactive Claude session with a system prompt from `.claude/agents/researcher.md` (local) or `~/.claude/agents/researcher.md` (global). Agents can specify `model:` and `tools:` in YAML frontmatter
|
|
178
178
|
|
|
179
179
|
### Architecture
|
|
180
180
|
- **Single conversation** - All instances share one global conversation
|
|
181
|
-
- **Opt-in participation** - Only
|
|
181
|
+
- **Opt-in participation** - Only Claude Code instances launched with `hcom open` join the chat
|
|
182
182
|
- **@-mention filtering** - Target messages to specific instances or teams
|
|
183
183
|
|
|
184
184
|
### File Structure
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hcom"
|
|
7
|
-
version = "0.1.
|
|
8
|
-
description = "Lightweight CLI tool for real-time
|
|
7
|
+
version = "0.1.6"
|
|
8
|
+
description = "Lightweight CLI tool for real-time communication between Claude Code subagents using hooks"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.6"
|
|
11
11
|
license = {text = "MIT"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
3
|
hcom - Claude Hook Comms
|
|
4
|
-
|
|
4
|
+
Lightweight CLI tool for real-time communication between Claude Code subagents using hooks
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
@@ -170,27 +170,22 @@ def get_config_value(key, default=None):
|
|
|
170
170
|
return config.get(key, default)
|
|
171
171
|
|
|
172
172
|
def get_hook_command():
|
|
173
|
-
"""
|
|
173
|
+
"""Get hook command with silent fallback
|
|
174
|
+
|
|
175
|
+
Uses ${HCOM:-true} for clean paths, conditional for paths with spaces.
|
|
176
|
+
Both approaches exit silently (code 0) when not launched via 'hcom open'.
|
|
177
|
+
"""
|
|
174
178
|
python_path = sys.executable
|
|
175
179
|
script_path = os.path.abspath(__file__)
|
|
176
180
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
for char in problematic_chars)
|
|
183
|
-
|
|
184
|
-
if has_problematic_chars:
|
|
185
|
-
# Use direct paths with proper escaping
|
|
186
|
-
# Escape backslashes first, then quotes
|
|
187
|
-
escaped_python = python_path.replace('\\', '\\\\').replace('"', '\\"')
|
|
188
|
-
escaped_script = script_path.replace('\\', '\\\\').replace('"', '\\"')
|
|
189
|
-
return f'"{escaped_python}" "{escaped_script}"', {}
|
|
181
|
+
if ' ' in python_path or ' ' in script_path:
|
|
182
|
+
# Paths with spaces: use conditional check
|
|
183
|
+
escaped_python = shlex.quote(python_path)
|
|
184
|
+
escaped_script = shlex.quote(script_path)
|
|
185
|
+
return f'[ "${{HCOM_ACTIVE}}" = "1" ] && {escaped_python} {escaped_script} || true', {}
|
|
190
186
|
else:
|
|
191
|
-
#
|
|
192
|
-
|
|
193
|
-
return '$HCOM', env_vars
|
|
187
|
+
# Clean paths: use environment variable
|
|
188
|
+
return '${HCOM:-true}', {}
|
|
194
189
|
|
|
195
190
|
def build_claude_env():
|
|
196
191
|
"""Build environment variables for Claude instances"""
|
|
@@ -206,9 +201,11 @@ def build_claude_env():
|
|
|
206
201
|
|
|
207
202
|
env.update(config.get('env_overrides', {}))
|
|
208
203
|
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
|
|
204
|
+
# Set HCOM only for clean paths (spaces handled differently)
|
|
205
|
+
python_path = sys.executable
|
|
206
|
+
script_path = os.path.abspath(__file__)
|
|
207
|
+
if ' ' not in python_path and ' ' not in script_path:
|
|
208
|
+
env['HCOM'] = f'{python_path} {script_path}'
|
|
212
209
|
|
|
213
210
|
return env
|
|
214
211
|
|
|
@@ -335,6 +332,35 @@ def parse_open_args(args):
|
|
|
335
332
|
|
|
336
333
|
return instances, prefix, claude_args
|
|
337
334
|
|
|
335
|
+
def extract_agent_config(content):
|
|
336
|
+
"""Extract configuration from agent YAML frontmatter"""
|
|
337
|
+
if not content.startswith('---'):
|
|
338
|
+
return {}
|
|
339
|
+
|
|
340
|
+
# Find YAML section between --- markers
|
|
341
|
+
yaml_end = content.find('\n---', 3)
|
|
342
|
+
if yaml_end < 0:
|
|
343
|
+
return {} # No closing marker
|
|
344
|
+
|
|
345
|
+
yaml_section = content[3:yaml_end]
|
|
346
|
+
config = {}
|
|
347
|
+
|
|
348
|
+
# Extract model field
|
|
349
|
+
model_match = re.search(r'^model:\s*(.+)$', yaml_section, re.MULTILINE)
|
|
350
|
+
if model_match:
|
|
351
|
+
value = model_match.group(1).strip()
|
|
352
|
+
if value and value.lower() != 'inherit':
|
|
353
|
+
config['model'] = value
|
|
354
|
+
|
|
355
|
+
# Extract tools field
|
|
356
|
+
tools_match = re.search(r'^tools:\s*(.+)$', yaml_section, re.MULTILINE)
|
|
357
|
+
if tools_match:
|
|
358
|
+
value = tools_match.group(1).strip()
|
|
359
|
+
if value:
|
|
360
|
+
config['tools'] = value.replace(', ', ',')
|
|
361
|
+
|
|
362
|
+
return config
|
|
363
|
+
|
|
338
364
|
def resolve_agent(name):
|
|
339
365
|
"""Resolve agent file by name
|
|
340
366
|
|
|
@@ -342,16 +368,17 @@ def resolve_agent(name):
|
|
|
342
368
|
1. .claude/agents/{name}.md (local)
|
|
343
369
|
2. ~/.claude/agents/{name}.md (global)
|
|
344
370
|
|
|
345
|
-
Returns
|
|
371
|
+
Returns tuple: (content after stripping YAML frontmatter, config dict)
|
|
346
372
|
"""
|
|
347
373
|
for base_path in [Path('.'), Path.home()]:
|
|
348
374
|
agent_path = base_path / '.claude/agents' / f'{name}.md'
|
|
349
375
|
if agent_path.exists():
|
|
350
376
|
content = agent_path.read_text()
|
|
377
|
+
config = extract_agent_config(content)
|
|
351
378
|
stripped = strip_frontmatter(content)
|
|
352
379
|
if not stripped.strip():
|
|
353
380
|
raise ValueError(format_error(f"Agent '{name}' has empty content", 'Check the agent file contains a system prompt'))
|
|
354
|
-
return stripped
|
|
381
|
+
return stripped, config
|
|
355
382
|
|
|
356
383
|
raise FileNotFoundError(format_error(f'Agent not found: {name}', 'Check available agents or create the agent file'))
|
|
357
384
|
|
|
@@ -393,15 +420,22 @@ def _remove_hcom_hooks_from_settings(settings):
|
|
|
393
420
|
|
|
394
421
|
# Patterns to match any hcom hook command
|
|
395
422
|
# - $HCOM post/stop/notify
|
|
423
|
+
# - ${HCOM:-...} post/stop/notify
|
|
424
|
+
# - [ "${HCOM_ACTIVE}" = "1" ] && ... hcom.py ... || true
|
|
396
425
|
# - hcom post/stop/notify
|
|
426
|
+
# - uvx hcom post/stop/notify
|
|
397
427
|
# - /path/to/hcom.py post/stop/notify
|
|
428
|
+
# - sh -c "[ ... ] && ... hcom ..."
|
|
398
429
|
# - "/path with spaces/python" "/path with spaces/hcom.py" post/stop/notify
|
|
399
430
|
# - '/path/to/python' '/path/to/hcom.py' post/stop/notify
|
|
400
431
|
hcom_patterns = [
|
|
401
|
-
r'
|
|
432
|
+
r'\$\{?HCOM', # Environment variable (with or without braces)
|
|
433
|
+
r'\bHCOM_ACTIVE.*hcom\.py', # Conditional with HCOM_ACTIVE check
|
|
402
434
|
r'\bhcom\s+(post|stop|notify)\b', # Direct hcom command
|
|
435
|
+
r'\buvx\s+hcom\s+(post|stop|notify)\b', # uvx hcom command
|
|
403
436
|
r'hcom\.py["\']?\s+(post|stop|notify)\b', # hcom.py with optional quote
|
|
404
437
|
r'["\'][^"\']*hcom\.py["\']?\s+(post|stop|notify)\b', # Quoted path with hcom.py
|
|
438
|
+
r'sh\s+-c.*hcom', # Shell wrapper with hcom
|
|
405
439
|
]
|
|
406
440
|
compiled_patterns = [re.compile(pattern) for pattern in hcom_patterns]
|
|
407
441
|
|
|
@@ -452,7 +486,7 @@ def format_warning(message):
|
|
|
452
486
|
"""Format warning message consistently"""
|
|
453
487
|
return f"Warning: {message}"
|
|
454
488
|
|
|
455
|
-
def build_claude_command(agent_content=None, claude_args=None, initial_prompt="Say hi in chat"):
|
|
489
|
+
def build_claude_command(agent_content=None, claude_args=None, initial_prompt="Say hi in chat", model=None, tools=None):
|
|
456
490
|
"""Build Claude command with proper argument handling
|
|
457
491
|
|
|
458
492
|
Returns tuple: (command_string, temp_file_path_or_none)
|
|
@@ -461,6 +495,27 @@ def build_claude_command(agent_content=None, claude_args=None, initial_prompt="S
|
|
|
461
495
|
cmd_parts = ['claude']
|
|
462
496
|
temp_file_path = None
|
|
463
497
|
|
|
498
|
+
# Add model if specified and not already in claude_args
|
|
499
|
+
if model:
|
|
500
|
+
# Check if model already specified in args (more concise)
|
|
501
|
+
has_model = claude_args and any(
|
|
502
|
+
arg in ['--model', '-m'] or
|
|
503
|
+
arg.startswith(('--model=', '-m='))
|
|
504
|
+
for arg in claude_args
|
|
505
|
+
)
|
|
506
|
+
if not has_model:
|
|
507
|
+
cmd_parts.extend(['--model', model])
|
|
508
|
+
|
|
509
|
+
# Add allowed tools if specified and not already in claude_args
|
|
510
|
+
if tools:
|
|
511
|
+
has_tools = claude_args and any(
|
|
512
|
+
arg in ['--allowedTools', '--allowed-tools'] or
|
|
513
|
+
arg.startswith(('--allowedTools=', '--allowed-tools='))
|
|
514
|
+
for arg in claude_args
|
|
515
|
+
)
|
|
516
|
+
if not has_tools:
|
|
517
|
+
cmd_parts.extend(['--allowedTools', tools])
|
|
518
|
+
|
|
464
519
|
if claude_args:
|
|
465
520
|
for arg in claude_args:
|
|
466
521
|
cmd_parts.append(shlex.quote(arg))
|
|
@@ -662,7 +717,7 @@ def setup_hooks():
|
|
|
662
717
|
if hcom_send_permission not in settings['permissions']['allow']:
|
|
663
718
|
settings['permissions']['allow'].append(hcom_send_permission)
|
|
664
719
|
|
|
665
|
-
# Get the hook command
|
|
720
|
+
# Get the hook command template
|
|
666
721
|
hook_cmd_base, _ = get_hook_command()
|
|
667
722
|
|
|
668
723
|
# Add PostToolUse hook
|
|
@@ -718,16 +773,36 @@ def get_archive_timestamp():
|
|
|
718
773
|
return datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
719
774
|
|
|
720
775
|
def get_conversation_uuid(transcript_path):
|
|
721
|
-
"""Get conversation UUID from transcript
|
|
776
|
+
"""Get conversation UUID from transcript
|
|
777
|
+
|
|
778
|
+
For resumed sessions, the first line may be a summary with a different leafUuid.
|
|
779
|
+
We need to find the first user entry which contains the stable conversation UUID.
|
|
780
|
+
"""
|
|
722
781
|
try:
|
|
723
782
|
if not transcript_path or not os.path.exists(transcript_path):
|
|
724
783
|
return None
|
|
725
784
|
|
|
785
|
+
# First, try to find the UUID from the first user entry
|
|
786
|
+
with open(transcript_path, 'r') as f:
|
|
787
|
+
for line in f:
|
|
788
|
+
line = line.strip()
|
|
789
|
+
if not line:
|
|
790
|
+
continue
|
|
791
|
+
try:
|
|
792
|
+
entry = json.loads(line)
|
|
793
|
+
# Look for first user entry with a UUID - this is the stable identifier
|
|
794
|
+
if entry.get('type') == 'user' and entry.get('uuid'):
|
|
795
|
+
return entry.get('uuid')
|
|
796
|
+
except json.JSONDecodeError:
|
|
797
|
+
continue
|
|
798
|
+
|
|
799
|
+
# Fallback: If no user entry found, try the first line (original behavior)
|
|
726
800
|
with open(transcript_path, 'r') as f:
|
|
727
801
|
first_line = f.readline().strip()
|
|
728
802
|
if first_line:
|
|
729
803
|
entry = json.loads(first_line)
|
|
730
|
-
|
|
804
|
+
# Try both 'uuid' and 'leafUuid' fields
|
|
805
|
+
return entry.get('uuid') or entry.get('leafUuid')
|
|
731
806
|
except Exception:
|
|
732
807
|
pass
|
|
733
808
|
return None
|
|
@@ -1119,7 +1194,8 @@ def migrate_instance_name_if_needed(instance_name, conversation_uuid, transcript
|
|
|
1119
1194
|
if instance_name.endswith("claude") and conversation_uuid:
|
|
1120
1195
|
new_instance = get_display_name(transcript_path)
|
|
1121
1196
|
if new_instance != instance_name and not new_instance.endswith("claude"):
|
|
1122
|
-
#
|
|
1197
|
+
# Always return the new name if we can generate it
|
|
1198
|
+
# Migration of data only happens if old name exists
|
|
1123
1199
|
pos_file = get_hcom_dir() / "hcom.json"
|
|
1124
1200
|
positions = load_positions(pos_file)
|
|
1125
1201
|
if instance_name in positions:
|
|
@@ -1128,8 +1204,7 @@ def migrate_instance_name_if_needed(instance_name, conversation_uuid, transcript
|
|
|
1128
1204
|
# Update the conversation UUID in the migrated data
|
|
1129
1205
|
positions[new_instance]["conversation_uuid"] = conversation_uuid
|
|
1130
1206
|
atomic_write(pos_file, json.dumps(positions, indent=2))
|
|
1131
|
-
|
|
1132
|
-
return new_instance
|
|
1207
|
+
return new_instance
|
|
1133
1208
|
return instance_name
|
|
1134
1209
|
|
|
1135
1210
|
def update_instance_position(instance_name, update_fields):
|
|
@@ -1282,11 +1357,16 @@ def cmd_open(*args):
|
|
|
1282
1357
|
else:
|
|
1283
1358
|
# Agent instance
|
|
1284
1359
|
try:
|
|
1285
|
-
agent_content = resolve_agent(instance_type)
|
|
1360
|
+
agent_content, agent_config = resolve_agent(instance_type)
|
|
1361
|
+
# Use agent's model and tools if specified and not overridden in claude_args
|
|
1362
|
+
agent_model = agent_config.get('model')
|
|
1363
|
+
agent_tools = agent_config.get('tools')
|
|
1286
1364
|
claude_cmd, temp_file = build_claude_command(
|
|
1287
1365
|
agent_content=agent_content,
|
|
1288
1366
|
claude_args=claude_args,
|
|
1289
|
-
initial_prompt=initial_prompt
|
|
1367
|
+
initial_prompt=initial_prompt,
|
|
1368
|
+
model=agent_model,
|
|
1369
|
+
tools=agent_tools
|
|
1290
1370
|
)
|
|
1291
1371
|
if temp_file:
|
|
1292
1372
|
temp_files_to_cleanup.append(temp_file)
|
|
@@ -1746,12 +1826,9 @@ def handle_hook_post():
|
|
|
1746
1826
|
# Migrate instance name if needed (from fallback to UUID-based)
|
|
1747
1827
|
instance_name = migrate_instance_name_if_needed(instance_name, conversation_uuid, transcript_path)
|
|
1748
1828
|
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
# Update instance position
|
|
1754
|
-
update_instance_position(instance_name, {
|
|
1829
|
+
initialize_instance_in_position_file(instance_name, conversation_uuid)
|
|
1830
|
+
|
|
1831
|
+
update_instance_position(instance_name, {
|
|
1755
1832
|
'last_tool': int(time.time()),
|
|
1756
1833
|
'last_tool_name': hook_data.get('tool_name', 'unknown'),
|
|
1757
1834
|
'session_id': hook_data.get('session_id', ''),
|
|
@@ -1788,8 +1865,7 @@ def handle_hook_post():
|
|
|
1788
1865
|
elif message and message[-1] in '"\'':
|
|
1789
1866
|
message = message[:-1]
|
|
1790
1867
|
|
|
1791
|
-
if message
|
|
1792
|
-
# Validate message
|
|
1868
|
+
if message:
|
|
1793
1869
|
error = validate_message(message)
|
|
1794
1870
|
if error:
|
|
1795
1871
|
output = {"reason": f"❌ {error}"}
|
|
@@ -1799,27 +1875,25 @@ def handle_hook_post():
|
|
|
1799
1875
|
send_message(instance_name, message)
|
|
1800
1876
|
sent_reason = "✓ Sent"
|
|
1801
1877
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
print(json.dumps(output, ensure_ascii=False), file=sys.stderr)
|
|
1822
|
-
sys.exit(EXIT_BLOCK)
|
|
1878
|
+
messages = get_new_messages(instance_name)
|
|
1879
|
+
|
|
1880
|
+
if messages and sent_reason:
|
|
1881
|
+
# Both sent and received
|
|
1882
|
+
reason = f"{sent_reason} | {format_hook_messages(messages, instance_name)}"
|
|
1883
|
+
output = {"decision": HOOK_DECISION_BLOCK, "reason": reason}
|
|
1884
|
+
print(json.dumps(output, ensure_ascii=False), file=sys.stderr)
|
|
1885
|
+
sys.exit(EXIT_BLOCK)
|
|
1886
|
+
elif messages:
|
|
1887
|
+
# Just received
|
|
1888
|
+
reason = format_hook_messages(messages, instance_name)
|
|
1889
|
+
output = {"decision": HOOK_DECISION_BLOCK, "reason": reason}
|
|
1890
|
+
print(json.dumps(output, ensure_ascii=False), file=sys.stderr)
|
|
1891
|
+
sys.exit(EXIT_BLOCK)
|
|
1892
|
+
elif sent_reason:
|
|
1893
|
+
# Just sent
|
|
1894
|
+
output = {"reason": sent_reason}
|
|
1895
|
+
print(json.dumps(output, ensure_ascii=False), file=sys.stderr)
|
|
1896
|
+
sys.exit(EXIT_BLOCK)
|
|
1823
1897
|
|
|
1824
1898
|
except Exception:
|
|
1825
1899
|
pass
|
|
@@ -1839,9 +1913,6 @@ def handle_hook_stop():
|
|
|
1839
1913
|
instance_name = get_display_name(transcript_path) if transcript_path else f"{Path.cwd().name[:2].lower()}claude"
|
|
1840
1914
|
conversation_uuid = get_conversation_uuid(transcript_path)
|
|
1841
1915
|
|
|
1842
|
-
if instance_name.endswith("claude") and not conversation_uuid:
|
|
1843
|
-
sys.exit(EXIT_SUCCESS)
|
|
1844
|
-
|
|
1845
1916
|
# Initialize instance if needed
|
|
1846
1917
|
initialize_instance_in_position_file(instance_name, conversation_uuid)
|
|
1847
1918
|
|
|
@@ -1906,9 +1977,6 @@ def handle_hook_notification():
|
|
|
1906
1977
|
instance_name = get_display_name(transcript_path) if transcript_path else f"{Path.cwd().name[:2].lower()}claude"
|
|
1907
1978
|
conversation_uuid = get_conversation_uuid(transcript_path)
|
|
1908
1979
|
|
|
1909
|
-
if instance_name.endswith("claude") and not conversation_uuid:
|
|
1910
|
-
sys.exit(EXIT_SUCCESS)
|
|
1911
|
-
|
|
1912
1980
|
# Initialize instance if needed
|
|
1913
1981
|
initialize_instance_in_position_file(instance_name, conversation_uuid)
|
|
1914
1982
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hcom
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Lightweight CLI tool for real-time
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: Lightweight CLI tool for real-time communication between Claude Code subagents using hooks
|
|
5
5
|
Author-email: aannoo <your@email.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/aannoo/claude-hook-comms
|
|
@@ -26,9 +26,9 @@ Classifier: Topic :: Communications
|
|
|
26
26
|
Requires-Python: >=3.6
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
|
-
# Claude
|
|
29
|
+
# hcom - Claude Hook Comms
|
|
30
30
|
|
|
31
|
-
Lightweight CLI tool for real-time communication between
|
|
31
|
+
Lightweight CLI tool for real-time communication between Claude Code [subagents](https://docs.anthropic.com/en/docs/claude-code/sub-agents) using [hooks](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
32
32
|
|
|
33
33
|
## 🦆 What It Does
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ Creates a group chat where you and multiple interactive Claude Code subagents ca
|
|
|
38
38
|
|
|
39
39
|
## 🦷 Features
|
|
40
40
|
|
|
41
|
-
- **Multi-Terminal Launch** - Launch
|
|
41
|
+
- **Multi-Terminal Launch** - Launch Claude Code subagents in new terminals
|
|
42
42
|
- **Live Dashboard** - Real-time monitoring of all instances
|
|
43
43
|
- **Multi-Agent Communication** - Claude instances talk to each other across projects
|
|
44
44
|
- **@Mention Targeting** - Send messages to specific subagents or teams
|
|
@@ -154,7 +154,7 @@ HCOM_INSTANCE_HINTS="always update chat with progress" hcom open nice-subagent-b
|
|
|
154
154
|
| `max_messages_per_delivery` | 50 | `HCOM_MAX_MESSAGES_PER_DELIVERY` | Messages delivered per batch |
|
|
155
155
|
| `sender_name` | "bigboss" | `HCOM_SENDER_NAME` | Your name in chat |
|
|
156
156
|
| `sender_emoji` | "🐳" | `HCOM_SENDER_EMOJI` | Your emoji icon |
|
|
157
|
-
| `initial_prompt` | "Say hi" | `HCOM_INITIAL_PROMPT` | What new instances are told to do |
|
|
157
|
+
| `initial_prompt` | "Say hi in chat" | `HCOM_INITIAL_PROMPT` | What new instances are told to do |
|
|
158
158
|
| `first_use_text` | "Essential, concise messages only" | `HCOM_FIRST_USE_TEXT` | Welcome message for instances |
|
|
159
159
|
| `terminal_mode` | "new_window" | `HCOM_TERMINAL_MODE` | How to launch terminals ("new_window", "same_terminal", "show_commands") |
|
|
160
160
|
| `terminal_command` | null | `HCOM_TERMINAL_COMMAND` | Custom terminal command (see Terminal Options) |
|
|
@@ -202,11 +202,11 @@ hcom adds hooks to your project directory's `.claude/settings.local.json`:
|
|
|
202
202
|
- **Identity**: Each instance gets a unique name based on conversation UUID (e.g., "hovoa7")
|
|
203
203
|
- **Persistence**: Names persist across `--resume` maintaining conversation context
|
|
204
204
|
- **Status Detection**: Notification hook tracks permission requests and activity
|
|
205
|
-
- **Agents**: When you run `hcom open researcher`, it loads an interactive
|
|
205
|
+
- **Agents**: When you run `hcom open researcher`, it loads an interactive Claude session with a system prompt from `.claude/agents/researcher.md` (local) or `~/.claude/agents/researcher.md` (global). Agents can specify `model:` and `tools:` in YAML frontmatter
|
|
206
206
|
|
|
207
207
|
### Architecture
|
|
208
208
|
- **Single conversation** - All instances share one global conversation
|
|
209
|
-
- **Opt-in participation** - Only
|
|
209
|
+
- **Opt-in participation** - Only Claude Code instances launched with `hcom open` join the chat
|
|
210
210
|
- **@-mention filtering** - Target messages to specific instances or teams
|
|
211
211
|
|
|
212
212
|
### File Structure
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|