neohive 6.4.0 → 6.4.2
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.
- package/README.md +109 -9
- package/SECURITY.md +8 -53
- package/cli.js +120 -15
- package/dashboard.html +11 -23
- package/dashboard.js +3 -3
- package/lib/audit.js +9 -7
- package/neohive-plugin/.claude-plugin/plugin.json +24 -0
- package/neohive-plugin/.mcp.json +11 -0
- package/neohive-plugin/README.md +55 -0
- package/neohive-plugin/agents/coordinator.md +27 -0
- package/neohive-plugin/gemini-extension/GEMINI.md +24 -0
- package/neohive-plugin/gemini-extension/settings-snippet.json +12 -0
- package/neohive-plugin/hooks/hooks.json +87 -0
- package/neohive-plugin/scripts/auto-register.sh +48 -0
- package/neohive-plugin/scripts/before-prompt.sh +47 -0
- package/neohive-plugin/scripts/enforce-listen.sh +72 -0
- package/neohive-plugin/scripts/enforce-locks.sh +34 -0
- package/neohive-plugin/scripts/post-tool-use.sh +119 -0
- package/neohive-plugin/scripts/track-activity.sh +23 -0
- package/neohive-plugin/skills/conventions/SKILL.md +30 -0
- package/neohive-plugin/skills/launch-team/SKILL.md +24 -0
- package/neohive-plugin/skills/plan/SKILL.md +21 -0
- package/neohive-plugin/skills/send/SKILL.md +14 -0
- package/neohive-plugin/skills/status/SKILL.md +17 -0
- package/package.json +2 -1
- package/server.js +23 -23
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Neohive Plugin for Claude Code
|
|
2
|
+
|
|
3
|
+
Turn Claude Code into a multi-agent team. Agents communicate, delegate tasks, and build together.
|
|
4
|
+
|
|
5
|
+
## Skills (Slash Commands)
|
|
6
|
+
|
|
7
|
+
| Command | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| `/neohive:launch-team [template]` | Launch a team from a template (pair, team, review, managed, debate) |
|
|
10
|
+
| `/neohive:status` | Show agent status, active tasks, and workflow progress |
|
|
11
|
+
| `/neohive:send [agent] [message]` | Send a quick message to another agent |
|
|
12
|
+
| `/neohive:plan [description]` | Create a workflow plan from a natural language description |
|
|
13
|
+
|
|
14
|
+
## Hooks
|
|
15
|
+
|
|
16
|
+
- **SessionStart** -- Detects Neohive projects and reminds agents to register
|
|
17
|
+
- **PreToolUse** -- Warns when editing files locked by another agent
|
|
18
|
+
- **PostToolUse** -- Tracks MCP tool usage for activity analytics
|
|
19
|
+
|
|
20
|
+
## Subagents
|
|
21
|
+
|
|
22
|
+
- **coordinator** -- Project coordinator that plans, delegates, and tracks work (never writes code)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
### From GitHub (recommended)
|
|
27
|
+
```
|
|
28
|
+
/plugin install neohive
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Manual
|
|
32
|
+
1. Copy the `neohive-plugin/` directory to your Claude Code plugins folder
|
|
33
|
+
2. Restart Claude Code
|
|
34
|
+
|
|
35
|
+
### Via npm
|
|
36
|
+
```bash
|
|
37
|
+
npx neohive init --plugin
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Gemini CLI
|
|
41
|
+
|
|
42
|
+
For Gemini CLI, use the instructions and config in `gemini-extension/`:
|
|
43
|
+
```bash
|
|
44
|
+
npx neohive init --gemini
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
- Claude Code v1.0+
|
|
50
|
+
- Node.js 18+
|
|
51
|
+
- neohive npm package (`npx neohive` must work)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
See LICENSE file.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coordinator
|
|
3
|
+
description: A project coordinator subagent that plans work, creates tasks, and delegates to team agents. Use when you need to orchestrate a multi-agent workflow.
|
|
4
|
+
model: sonnet
|
|
5
|
+
effort: high
|
|
6
|
+
maxTurns: 30
|
|
7
|
+
disallowedTools: Edit, Write, Bash
|
|
8
|
+
skills:
|
|
9
|
+
- neohive:launch-team
|
|
10
|
+
- neohive:status
|
|
11
|
+
- neohive:plan
|
|
12
|
+
- neohive:conventions
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
You are a project coordinator. Your job is to plan, delegate, and track work — you NEVER write code.
|
|
16
|
+
|
|
17
|
+
Your tools: `register`, `get_briefing`, `send_message`, `broadcast`, `create_task`, `update_task`, `list_tasks`, `create_workflow`, `advance_workflow`, `workflow_status`, `distribute_prompt`, `start_plan`, `messages`, `kb_write`, `kb_read`, `log_decision`, `get_decisions`, `listen`.
|
|
18
|
+
|
|
19
|
+
Workflow:
|
|
20
|
+
1. Call `register(name="Coordinator")` then `get_briefing()` to load context
|
|
21
|
+
2. Break the user's request into research tasks and coding tasks
|
|
22
|
+
3. Create tasks with `create_task` and assign to available agents
|
|
23
|
+
4. Use `distribute_prompt` to dispatch task instructions to agents
|
|
24
|
+
5. Call `messages(action="check")` to check for agent updates (non-blocking)
|
|
25
|
+
6. Process reports, advance workflows with `advance_workflow`, assign next tasks
|
|
26
|
+
7. Synthesize results and present to user
|
|
27
|
+
8. **Always call `listen()` as the last tool call of every response**
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Neohive Multi-Agent Collaboration
|
|
2
|
+
|
|
3
|
+
You are part of a Neohive multi-agent team. Use the neohive MCP tools to communicate with other agents.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
1. Call `register` with your name to join the team
|
|
7
|
+
2. Call `get_briefing` for project context and active work
|
|
8
|
+
3. Call `listen` to wait for messages from other agents
|
|
9
|
+
|
|
10
|
+
## Conventions
|
|
11
|
+
- Always call `listen()` after every action -- this is how you receive messages
|
|
12
|
+
- Update task status: `update_task(in_progress)` when starting, `update_task(done)` when finishing
|
|
13
|
+
- Lock files before editing: `lock_file()` before, `unlock_file()` after
|
|
14
|
+
- Report completions to the Coordinator with: what changed, files modified, decisions made
|
|
15
|
+
- Keep messages to 2-3 paragraphs max
|
|
16
|
+
- Use `kb_write()` for findings, `kb_read()` to check team knowledge
|
|
17
|
+
- Never work on another agent's task -- check `list_tasks()` first
|
|
18
|
+
|
|
19
|
+
## Available Tools
|
|
20
|
+
- **Messaging:** register, send_message, broadcast, listen, messages, handoff
|
|
21
|
+
- **Tasks:** create_task, update_task, list_tasks
|
|
22
|
+
- **Workflows:** create_workflow, advance_workflow, workflow_status
|
|
23
|
+
- **Knowledge:** kb_write, kb_read, kb_list, log_decision
|
|
24
|
+
- **Coordination:** lock_file, unlock_file, update_progress, get_briefing
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/auto-register.sh",
|
|
10
|
+
"timeout": 10,
|
|
11
|
+
"statusMessage": "Checking Neohive team status..."
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"UserPromptSubmit": [
|
|
17
|
+
{
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/before-prompt.sh",
|
|
22
|
+
"timeout": 5,
|
|
23
|
+
"statusMessage": "Loading Neohive team context..."
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"PreToolUse": [
|
|
29
|
+
{
|
|
30
|
+
"matcher": "Edit|Write",
|
|
31
|
+
"hooks": [
|
|
32
|
+
{
|
|
33
|
+
"type": "command",
|
|
34
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/enforce-locks.sh",
|
|
35
|
+
"timeout": 5,
|
|
36
|
+
"statusMessage": "Checking file locks..."
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"PostToolUse": [
|
|
42
|
+
{
|
|
43
|
+
"matcher": "mcp__neohive__.*",
|
|
44
|
+
"hooks": [
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/track-activity.sh",
|
|
48
|
+
"async": true,
|
|
49
|
+
"timeout": 5
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"matcher": "mcp__neohive__send_message|mcp__neohive__advance_workflow|mcp__neohive__update_task|mcp__neohive__broadcast|mcp__neohive__add_rule|mcp__neohive__remove_rule|mcp__neohive__toggle_rule",
|
|
55
|
+
"hooks": [
|
|
56
|
+
{
|
|
57
|
+
"type": "command",
|
|
58
|
+
"command": "echo '\\n📡 NEOHIVE: Call listen() now to receive your next task. Do not stop without calling listen().'",
|
|
59
|
+
"timeout": 3
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"matcher": "Edit|Write|MultiEdit|mcp__neohive__update_task",
|
|
65
|
+
"hooks": [
|
|
66
|
+
{
|
|
67
|
+
"type": "command",
|
|
68
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.sh",
|
|
69
|
+
"async": true,
|
|
70
|
+
"timeout": 5
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"Stop": [
|
|
76
|
+
{
|
|
77
|
+
"hooks": [
|
|
78
|
+
{
|
|
79
|
+
"type": "command",
|
|
80
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/enforce-listen.sh",
|
|
81
|
+
"timeout": 5
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SessionStart hook: check if Neohive is active for this project.
|
|
3
|
+
# Outputs a dynamic context message — no hardcoded agent names.
|
|
4
|
+
|
|
5
|
+
NEOHIVE_DIR="${CLAUDE_PROJECT_DIR}/.neohive"
|
|
6
|
+
|
|
7
|
+
if [ -d "$NEOHIVE_DIR" ]; then
|
|
8
|
+
# Count registered agents
|
|
9
|
+
AGENT_COUNT=0
|
|
10
|
+
AGENT_NAMES=""
|
|
11
|
+
if [ -f "$NEOHIVE_DIR/agents.json" ]; then
|
|
12
|
+
AGENT_COUNT=$(jq 'length' "$NEOHIVE_DIR/agents.json" 2>/dev/null || echo "0")
|
|
13
|
+
# List alive agent names (dynamic — no hardcoding)
|
|
14
|
+
AGENT_NAMES=$(jq -r '[to_entries[] | select(.value.alive == true) | .key] | join(", ")' \
|
|
15
|
+
"$NEOHIVE_DIR/agents.json" 2>/dev/null || echo "")
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Count active workflows
|
|
19
|
+
WF_COUNT=0
|
|
20
|
+
if [ -f "$NEOHIVE_DIR/workflows.json" ]; then
|
|
21
|
+
WF_COUNT=$(jq '[.[] | select(.status == "active")] | length' \
|
|
22
|
+
"$NEOHIVE_DIR/workflows.json" 2>/dev/null || echo "0")
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Count pending tasks
|
|
26
|
+
PENDING_TASKS=0
|
|
27
|
+
if [ -f "$NEOHIVE_DIR/tasks.json" ]; then
|
|
28
|
+
PENDING_TASKS=$(jq '[.[] | select(.status == "pending" or .status == "in_progress")] | length' \
|
|
29
|
+
"$NEOHIVE_DIR/tasks.json" 2>/dev/null || echo "0")
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Build the names hint (show only if agents are online)
|
|
33
|
+
NAMES_HINT=""
|
|
34
|
+
if [ -n "$AGENT_NAMES" ]; then
|
|
35
|
+
NAMES_HINT=" Online agents: $AGENT_NAMES."
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
cat <<EOF
|
|
39
|
+
{
|
|
40
|
+
"hookSpecificOutput": {
|
|
41
|
+
"hookEventName": "SessionStart"
|
|
42
|
+
},
|
|
43
|
+
"systemMessage": "Neohive is active ($AGENT_COUNT agents registered, $WF_COUNT active workflows, $PENDING_TASKS pending/in-progress tasks).$NAMES_HINT\n\nTo join: call register() with the name you were assigned, then get_briefing(), then listen(). Do NOT invent a name — the user or Coordinator will tell you which name to use."
|
|
44
|
+
}
|
|
45
|
+
EOF
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# UserPromptSubmit hook: inject live neohive team context before every prompt.
|
|
3
|
+
# No hardcoded names — all data read dynamically from .neohive/ files.
|
|
4
|
+
# Exit 0 always (never blocks the prompt).
|
|
5
|
+
|
|
6
|
+
NEOHIVE_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}/.neohive"
|
|
7
|
+
|
|
8
|
+
# Not a neohive project — nothing to inject
|
|
9
|
+
[ -d "$NEOHIVE_DIR" ] || exit 0
|
|
10
|
+
|
|
11
|
+
# Agents: list alive agents with their roles (dynamic, no hardcoded names)
|
|
12
|
+
AGENTS_ONLINE=""
|
|
13
|
+
if [ -f "$NEOHIVE_DIR/agents.json" ]; then
|
|
14
|
+
AGENTS_ONLINE=$(jq -r '
|
|
15
|
+
[to_entries[]
|
|
16
|
+
| select(.value.alive == true)
|
|
17
|
+
| "\(.key)(\(.value.role // "agent"))"]
|
|
18
|
+
| join(", ")
|
|
19
|
+
' "$NEOHIVE_DIR/agents.json" 2>/dev/null || echo "")
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Task counts
|
|
23
|
+
PENDING_COUNT=0
|
|
24
|
+
IN_PROGRESS_COUNT=0
|
|
25
|
+
if [ -f "$NEOHIVE_DIR/tasks.json" ]; then
|
|
26
|
+
PENDING_COUNT=$(jq '[.[] | select(.status == "pending")] | length' \
|
|
27
|
+
"$NEOHIVE_DIR/tasks.json" 2>/dev/null || echo "0")
|
|
28
|
+
IN_PROGRESS_COUNT=$(jq '[.[] | select(.status == "in_progress")] | length' \
|
|
29
|
+
"$NEOHIVE_DIR/tasks.json" 2>/dev/null || echo "0")
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Only inject if there is an active team
|
|
33
|
+
[ -z "$AGENTS_ONLINE" ] && exit 0
|
|
34
|
+
|
|
35
|
+
# Escape for JSON
|
|
36
|
+
AGENTS_ESCAPED=$(printf '%s' "$AGENTS_ONLINE" | sed 's/"/\\"/g')
|
|
37
|
+
|
|
38
|
+
cat <<EOF
|
|
39
|
+
{
|
|
40
|
+
"hookSpecificOutput": {
|
|
41
|
+
"hookEventName": "UserPromptSubmit"
|
|
42
|
+
},
|
|
43
|
+
"systemMessage": "Neohive team status: $AGENTS_ESCAPED | Tasks: $PENDING_COUNT pending, $IN_PROGRESS_COUNT in-progress. Reminder: call listen() after every action."
|
|
44
|
+
}
|
|
45
|
+
EOF
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Stop hook: block agent from stopping if last neohive action wasn't listen()
|
|
3
|
+
# Also auto-reports to coordinator via /api/inject when blocking.
|
|
4
|
+
# Exit 2 = block stop (forces Claude to continue and call listen)
|
|
5
|
+
# Exit 0 = allow stop
|
|
6
|
+
|
|
7
|
+
NEOHIVE_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}/.neohive"
|
|
8
|
+
ACTIVITY_FILE="$NEOHIVE_DIR/activity.jsonl"
|
|
9
|
+
SESSION="${CLAUDE_SESSION_ID:-}"
|
|
10
|
+
NEOHIVE_URL="${NEOHIVE_SERVER_URL:-http://localhost:4321}"
|
|
11
|
+
|
|
12
|
+
# Allow exit: no tool calls this session (user cancelled before any tools ran)
|
|
13
|
+
LOOP_COUNT="${CLAUDE_LOOP_COUNT:-0}"
|
|
14
|
+
[ "$LOOP_COUNT" = "0" ] && exit 0
|
|
15
|
+
|
|
16
|
+
# Allow exit: user aborted — agent had no chance to call listen()
|
|
17
|
+
STOP_STATUS="${CLAUDE_STOP_HOOK_STATUS:-}"
|
|
18
|
+
[ "$STOP_STATUS" = "aborted" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Not a neohive project — allow stop
|
|
21
|
+
[ -f "$ACTIVITY_FILE" ] || exit 0
|
|
22
|
+
|
|
23
|
+
# Get the last neohive tool used in THIS session
|
|
24
|
+
LAST_TOOL=$(tail -100 "$ACTIVITY_FILE" 2>/dev/null | jq -r --arg session "$SESSION" '
|
|
25
|
+
select(.session == $session or $session == "") | .tool
|
|
26
|
+
' | grep "^mcp__neohive__" | tail -1)
|
|
27
|
+
|
|
28
|
+
# No neohive tools used in this session — allow stop
|
|
29
|
+
[ -z "$LAST_TOOL" ] && exit 0
|
|
30
|
+
|
|
31
|
+
# Look up agent name from the last activity
|
|
32
|
+
AGENT_NAME=$(tail -100 "$ACTIVITY_FILE" 2>/dev/null | jq -r --arg session "$SESSION" '
|
|
33
|
+
select(.session == $session or $session == "") | .agent
|
|
34
|
+
' | tail -1)
|
|
35
|
+
AGENT="${AGENT_NAME:-unknown}"
|
|
36
|
+
|
|
37
|
+
# All roles must call listen() — no exemptions
|
|
38
|
+
|
|
39
|
+
# Last action was listen/register/rules management — allow stop
|
|
40
|
+
case "$LAST_TOOL" in
|
|
41
|
+
mcp__neohive__listen|\
|
|
42
|
+
mcp__neohive__register|\
|
|
43
|
+
mcp__neohive__add_rule|mcp__neohive__remove_rule|mcp__neohive__toggle_rule)
|
|
44
|
+
exit 0
|
|
45
|
+
;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
# Last neohive action was something other than listen — report + block
|
|
49
|
+
|
|
50
|
+
# Auto-report to coordinator via neohive /api/inject (HTTP equivalent of send_message)
|
|
51
|
+
# Fire-and-forget: don't fail if dashboard isn't running
|
|
52
|
+
PAYLOAD=$(printf '{"from":"%s","to":"__user__","content":"[STOP HOOK] %s attempted to stop without calling listen(). Last tool: %s. Blocking stop — agent will be prompted to call listen() now.","priority":"normal"}' \
|
|
53
|
+
"$AGENT" "$AGENT" "$LAST_TOOL")
|
|
54
|
+
curl -s -X POST "${NEOHIVE_URL}/api/inject" \
|
|
55
|
+
-H "Content-Type: application/json" \
|
|
56
|
+
-d "$PAYLOAD" \
|
|
57
|
+
--max-time 2 \
|
|
58
|
+
> /dev/null 2>&1 || true
|
|
59
|
+
|
|
60
|
+
cat <<'EOF'
|
|
61
|
+
|
|
62
|
+
⚠️ NEOHIVE — REQUIRED ACTION BEFORE STOPPING:
|
|
63
|
+
|
|
64
|
+
Your last neohive action was not listen(). You MUST call listen() before stopping.
|
|
65
|
+
This keeps you in the receive loop for your next task.
|
|
66
|
+
Your coordinator has been notified via the dashboard.
|
|
67
|
+
|
|
68
|
+
→ Call: listen()
|
|
69
|
+
|
|
70
|
+
Do not respond with text. Call listen() now.
|
|
71
|
+
EOF
|
|
72
|
+
exit 2
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse hook: check if the file being edited is locked by another agent
|
|
3
|
+
# Input: JSON via stdin with tool_input.file_path or tool_input.file
|
|
4
|
+
# Exit 0 = allow (with optional context), Exit 2 = block
|
|
5
|
+
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.file // empty' 2>/dev/null)
|
|
8
|
+
NEOHIVE_DIR="${CLAUDE_PROJECT_DIR}/.neohive"
|
|
9
|
+
LOCKS_FILE="$NEOHIVE_DIR/locks.json"
|
|
10
|
+
|
|
11
|
+
# No file path or no locks file — allow
|
|
12
|
+
if [ -z "$FILE_PATH" ] || [ ! -f "$LOCKS_FILE" ]; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Normalize: make path relative to project dir for matching
|
|
17
|
+
REL_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
|
|
18
|
+
|
|
19
|
+
# Check if file is locked (check both absolute and relative paths)
|
|
20
|
+
LOCKED_BY=$(jq -r --arg fp "$FILE_PATH" --arg rp "$REL_PATH" '(.[$fp].agent // .[$rp].agent) // empty' "$LOCKS_FILE" 2>/dev/null)
|
|
21
|
+
|
|
22
|
+
if [ -n "$LOCKED_BY" ]; then
|
|
23
|
+
# File is locked — soft enforcement (warn but don't block)
|
|
24
|
+
cat <<EOF
|
|
25
|
+
{
|
|
26
|
+
"hookSpecificOutput": {
|
|
27
|
+
"hookEventName": "PreToolUse",
|
|
28
|
+
"additionalContext": "WARNING: File '$REL_PATH' is locked by agent '$LOCKED_BY'. Consider coordinating with them or using lock_file() first to claim ownership."
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
EOF
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
exit 0
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook — three jobs:
|
|
3
|
+
# 1. send_message → __user__: echo message content as a systemMessage in chat,
|
|
4
|
+
# and send the last assistant chat turn as report context to the dashboard.
|
|
5
|
+
# 2. File edits: fire-and-forget report to dashboard.
|
|
6
|
+
# 3. Task updates: fire-and-forget report to dashboard.
|
|
7
|
+
#
|
|
8
|
+
# Works with both Claude Code (mcp__neohive__*) and Cursor (MCP:*) tool name formats.
|
|
9
|
+
|
|
10
|
+
INPUT=$(cat)
|
|
11
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null)
|
|
12
|
+
NEOHIVE_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}/.neohive"
|
|
13
|
+
NEOHIVE_URL="${NEOHIVE_SERVER_URL:-http://localhost:4321}"
|
|
14
|
+
|
|
15
|
+
[ -d "$NEOHIVE_DIR" ] || exit 0
|
|
16
|
+
|
|
17
|
+
# Normalize: strip "MCP:" or "mcp__<server>__" prefix → bare tool name
|
|
18
|
+
BARE=$(echo "$TOOL_NAME" | sed 's/^MCP://; s/^mcp__[^_]*__//')
|
|
19
|
+
|
|
20
|
+
# Resolve agent name from response or env
|
|
21
|
+
AGENT=$(echo "$INPUT" | jq -r '.tool_response.from // empty' 2>/dev/null)
|
|
22
|
+
[ -z "$AGENT" ] && AGENT="${CLAUDE_AGENT_NAME:-unknown}"
|
|
23
|
+
|
|
24
|
+
# ── Helper: extract last assistant text turn from the transcript JSONL ────────
|
|
25
|
+
last_chat_text() {
|
|
26
|
+
local transcript
|
|
27
|
+
transcript=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
28
|
+
[ -z "$transcript" ] && return
|
|
29
|
+
[ -f "$transcript" ] || return
|
|
30
|
+
|
|
31
|
+
# Use python3 (always available on macOS/Linux) to walk backward through the
|
|
32
|
+
# JSONL and find the last assistant turn that has a text content block.
|
|
33
|
+
# role is at the top level; content is inside .message.content or .content.
|
|
34
|
+
python3 - "$transcript" <<'PYEOF'
|
|
35
|
+
import sys, json
|
|
36
|
+
|
|
37
|
+
path = sys.argv[1]
|
|
38
|
+
with open(path, 'rb') as f:
|
|
39
|
+
lines = f.read().splitlines()
|
|
40
|
+
|
|
41
|
+
for line in reversed(lines):
|
|
42
|
+
try:
|
|
43
|
+
obj = json.loads(line)
|
|
44
|
+
except Exception:
|
|
45
|
+
continue
|
|
46
|
+
role = obj.get('role') or (obj.get('message') or {}).get('role', '')
|
|
47
|
+
if role != 'assistant':
|
|
48
|
+
continue
|
|
49
|
+
content = (obj.get('message') or {}).get('content') or obj.get('content') or []
|
|
50
|
+
if not isinstance(content, list):
|
|
51
|
+
continue
|
|
52
|
+
for block in content:
|
|
53
|
+
if isinstance(block, dict) and block.get('type') == 'text':
|
|
54
|
+
text = block.get('text', '').strip()
|
|
55
|
+
if text:
|
|
56
|
+
# Truncate to 500 chars so the JSON payload stays manageable
|
|
57
|
+
print(text[:500], end='')
|
|
58
|
+
sys.exit(0)
|
|
59
|
+
PYEOF
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "$BARE" in
|
|
63
|
+
# ── send_message to __user__ ────────────────────────────────────────────────
|
|
64
|
+
send_message)
|
|
65
|
+
TO=$(echo "$INPUT" | jq -r '.tool_input.to // ""' 2>/dev/null)
|
|
66
|
+
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""' 2>/dev/null)
|
|
67
|
+
|
|
68
|
+
if [ "$TO" = "__user__" ] && [ -n "$CONTENT" ]; then
|
|
69
|
+
# Inject into chat as systemMessage
|
|
70
|
+
CONTENT_ESC=$(echo "$CONTENT" | sed 's/"/\\"/g; s/$/\\n/' | tr -d '\n')
|
|
71
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse"},"systemMessage":"[Neohive → you] %s"}\n' \
|
|
72
|
+
"$CONTENT_ESC"
|
|
73
|
+
|
|
74
|
+
# Also send last chat turn as context to the dashboard
|
|
75
|
+
LAST_CHAT=$(last_chat_text)
|
|
76
|
+
if [ -n "$LAST_CHAT" ]; then
|
|
77
|
+
LAST_ESC=$(printf '%s' "$LAST_CHAT" | sed 's/"/\\"/g')
|
|
78
|
+
REPORT="[REPORT] ${AGENT} sent message. Last chat turn: ${LAST_ESC}"
|
|
79
|
+
REPORT_ESC=$(printf '%s' "$REPORT" | sed 's/"/\\"/g')
|
|
80
|
+
curl -s -X POST "${NEOHIVE_URL}/api/inject" \
|
|
81
|
+
-H "Content-Type: application/json" \
|
|
82
|
+
-d "{\"from\":\"${AGENT}\",\"to\":\"__user__\",\"content\":\"${REPORT_ESC}\",\"priority\":\"normal\"}" \
|
|
83
|
+
--max-time 2 \
|
|
84
|
+
> /dev/null 2>&1 || true
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
exit 0
|
|
88
|
+
;;
|
|
89
|
+
|
|
90
|
+
# ── File edits ───────────────────────────────────────────────────────────────
|
|
91
|
+
Edit|Write|MultiEdit)
|
|
92
|
+
FILE=$(echo "$INPUT" | jq -r \
|
|
93
|
+
'.tool_input.file_path // .tool_input.file // "unknown"' 2>/dev/null)
|
|
94
|
+
FILE="${FILE#$CLAUDE_PROJECT_DIR/}"
|
|
95
|
+
MSG="[POST-TOOL] ${AGENT} edited: ${FILE}"
|
|
96
|
+
;;
|
|
97
|
+
|
|
98
|
+
# ── Task status updates ──────────────────────────────────────────────────────
|
|
99
|
+
update_task)
|
|
100
|
+
TASK_ID=$(echo "$INPUT" | jq -r '.tool_input.task_id // "?"' 2>/dev/null)
|
|
101
|
+
STATUS=$(echo "$INPUT" | jq -r '.tool_input.status // "?"' 2>/dev/null)
|
|
102
|
+
MSG="[POST-TOOL] ${AGENT} updated task ${TASK_ID} → ${STATUS}"
|
|
103
|
+
;;
|
|
104
|
+
|
|
105
|
+
*)
|
|
106
|
+
exit 0
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
[ -z "$MSG" ] && exit 0
|
|
111
|
+
|
|
112
|
+
MSG_ESCAPED=$(printf '%s' "$MSG" | sed 's/"/\\"/g')
|
|
113
|
+
curl -s -X POST "${NEOHIVE_URL}/api/inject" \
|
|
114
|
+
-H "Content-Type: application/json" \
|
|
115
|
+
-d "{\"from\":\"${AGENT}\",\"to\":\"__user__\",\"content\":\"${MSG_ESCAPED}\",\"priority\":\"normal\"}" \
|
|
116
|
+
--max-time 2 \
|
|
117
|
+
> /dev/null 2>&1 || true
|
|
118
|
+
|
|
119
|
+
exit 0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook: log Neohive MCP tool calls for activity analytics
|
|
3
|
+
# Runs async — does not block tool execution
|
|
4
|
+
# Input: JSON via stdin with tool_name, tool_input, tool_response
|
|
5
|
+
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null)
|
|
8
|
+
NEOHIVE_DIR="${CLAUDE_PROJECT_DIR}/.neohive"
|
|
9
|
+
ACTIVITY_FILE="$NEOHIVE_DIR/activity.jsonl"
|
|
10
|
+
|
|
11
|
+
# Only log if neohive data dir exists
|
|
12
|
+
if [ -d "$NEOHIVE_DIR" ]; then
|
|
13
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
14
|
+
INPUT_SIZE=$(echo "$INPUT" | jq -r '.tool_input | tostring | length' 2>/dev/null || echo "0")
|
|
15
|
+
OUTPUT_SIZE=$(echo "$INPUT" | jq -r '.tool_response | tostring | length' 2>/dev/null || echo "0")
|
|
16
|
+
|
|
17
|
+
# Extract agent name from tool response if available
|
|
18
|
+
AGENT=$(echo "$INPUT" | jq -r '.tool_response.from // .tool_input.name // empty' 2>/dev/null)
|
|
19
|
+
|
|
20
|
+
echo "{\"tool\":\"$TOOL_NAME\",\"timestamp\":\"$TIMESTAMP\",\"input_size\":$INPUT_SIZE,\"output_size\":$OUTPUT_SIZE,\"agent\":\"${AGENT:-unknown}\",\"session\":\"${CLAUDE_SESSION_ID:-unknown}\"}" >> "$ACTIVITY_FILE"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
exit 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conventions
|
|
3
|
+
description: Neohive multi-agent collaboration conventions. Automatically loaded when working in a multi-agent team to ensure proper tool usage, communication patterns, and workflow management.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Neohive Collaboration Conventions
|
|
8
|
+
|
|
9
|
+
When working as part of a Neohive multi-agent team:
|
|
10
|
+
|
|
11
|
+
1. **Register and get briefing first** — call `register()` then `get_briefing()` before any other action
|
|
12
|
+
2. **Always call `listen()` as the LAST tool call of every response** — no exceptions, all agents
|
|
13
|
+
3. **Handle `retry: true`** — if `listen()` returns `{retry: true}`, call `listen()` again immediately
|
|
14
|
+
4. **Use `listen()` to complete tasks** — pass outcome params instead of a separate update_task call:
|
|
15
|
+
- `listen(outcome="completed", task_id="...", summary="one line of what was done")`
|
|
16
|
+
- `listen(outcome="blocked", task_id="...", summary="what is blocking you")`
|
|
17
|
+
5. **Update task to in_progress when starting** — call `update_task(id, status="in_progress")` when you pick up a task
|
|
18
|
+
6. **Lock files before editing** — call `lock_file(path)` before editing, `unlock_file(path)` after
|
|
19
|
+
7. **Report completions via send_message** — after finishing a task send the coordinator: what changed, files modified, decisions made
|
|
20
|
+
8. **Use KB for shared knowledge** — `kb_write()` for findings and decisions, `kb_read()` before starting work to avoid duplication
|
|
21
|
+
9. **Never work on another agent's task** — check `list_tasks()` first
|
|
22
|
+
10. **Keep messages concise** — 2–3 paragraphs max
|
|
23
|
+
|
|
24
|
+
### listen() outcome loop
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
register → get_briefing → listen → pick up task → update_task(in_progress)
|
|
28
|
+
→ do work → send_message(coordinator, report) → listen(outcome="completed", task_id, summary)
|
|
29
|
+
↑ always last
|
|
30
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: launch-team
|
|
3
|
+
description: Launch a multi-agent team from a template. Lists available templates and generates prompts for each agent. Use when starting a new multi-agent collaboration, team session, or when the user says "launch team" or "start agents".
|
|
4
|
+
argument-hint: [template-name]
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Launch a multi-agent team using Neohive templates.
|
|
9
|
+
|
|
10
|
+
1. Call `list_agents` to see who's already online
|
|
11
|
+
2. Call `workflow_status` to check if there's an active workflow
|
|
12
|
+
3. If $ARGUMENTS is provided, use it as the template name
|
|
13
|
+
4. If no template specified, list available templates:
|
|
14
|
+
- **pair** — 2 agents for brainstorming or Q&A
|
|
15
|
+
- **team** — Coordinator + Researcher + Coder for complex features
|
|
16
|
+
- **review** — Author + Reviewer for code review pipeline
|
|
17
|
+
- **managed** — Manager with floor control for structured sessions with large teams
|
|
18
|
+
- **debate** — Pro vs Con structured debate between two agents
|
|
19
|
+
5. For the chosen template, generate the launch prompt for each agent role
|
|
20
|
+
6. Each prompt must instruct the agent to:
|
|
21
|
+
- Call `register(name="<AgentName>")` first
|
|
22
|
+
- Call `get_briefing()` to load context
|
|
23
|
+
- Call `listen()` to enter the listen loop
|
|
24
|
+
7. Display the prompts and instruct the user to paste each into a separate terminal running `claude` (or the relevant CLI)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan
|
|
3
|
+
description: Create a multi-agent workflow plan from a natural language description. Breaks down the task into steps, assigns to agents, and creates the workflow. Use when the user describes a feature or task they want the team to build.
|
|
4
|
+
argument-hint: [task description]
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Create a workflow plan for the multi-agent team.
|
|
9
|
+
|
|
10
|
+
1. Call `list_agents` to see available agents and their roles/skills
|
|
11
|
+
2. Call `kb_read` to check for existing decisions or context relevant to this task
|
|
12
|
+
3. Analyze $ARGUMENTS to identify the steps needed
|
|
13
|
+
4. Assign each step to the best-suited agent based on their skills
|
|
14
|
+
5. Call `create_workflow` with the plan:
|
|
15
|
+
- name: derived from the task description
|
|
16
|
+
- steps: array of `{description, assignee, depends_on}`
|
|
17
|
+
- autonomous: true if all agents are online and no human checkpoints needed, false to let Coordinator manage step-by-step
|
|
18
|
+
6. Call `create_task` for each step that can start immediately (no unresolved dependencies)
|
|
19
|
+
7. Call `distribute_prompt` to dispatch task instructions to assigned agents
|
|
20
|
+
8. Call `log_decision` to record the plan breakdown
|
|
21
|
+
9. Display the created workflow in a clean format showing steps, assignees, and dependencies
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: send
|
|
3
|
+
description: Send a message to another agent or broadcast to all agents via Neohive. Use when the user wants to communicate with a specific agent or the whole team.
|
|
4
|
+
argument-hint: [agent-name|all] [message]
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Send a message to an agent or the entire team.
|
|
9
|
+
|
|
10
|
+
1. Parse $ARGUMENTS — first word is agent name (or `all`), rest is the message
|
|
11
|
+
2. If no agent specified, call `list_agents` and ask the user which agent to target
|
|
12
|
+
3. If agent is `all`, call `broadcast(content=<message>)`
|
|
13
|
+
4. Otherwise call `send_message(to=<agent>, content=<message>)`
|
|
14
|
+
5. Call `listen()` after sending to stay in the listen loop
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: status
|
|
3
|
+
description: Show the current status of the Neohive multi-agent team — who's online, active tasks, workflow progress, and recent messages. Use when the user asks about agent status, team progress, or "what's happening".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Check the status of the multi-agent team:
|
|
7
|
+
|
|
8
|
+
1. Call `list_agents` to see who's online/offline with their roles
|
|
9
|
+
2. Call `list_tasks` to see all tasks and their statuses
|
|
10
|
+
3. Call `workflow_status` to see workflow progress and current step
|
|
11
|
+
4. Call `messages(action="check")` to surface any unread messages
|
|
12
|
+
5. Call `get_decisions` to show recent decisions logged by the team
|
|
13
|
+
6. Summarize in a clean format:
|
|
14
|
+
- **Agents:** name, status (online/offline), role, current task
|
|
15
|
+
- **Tasks:** title, assignee, status (`pending` / `in_progress` / `in_review` / `done` / `blocked`)
|
|
16
|
+
- **Workflows:** name, progress, current step, autonomous or managed
|
|
17
|
+
- **Recent decisions:** logged rationale from `get_decisions`
|