code-puppy 0.0.353__py3-none-any.whl → 0.0.355__py3-none-any.whl
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.
- code_puppy/agents/__init__.py +2 -0
- code_puppy/agents/agent_manager.py +49 -0
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +1 -0
- code_puppy/agents/event_stream_handler.py +74 -1
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +92 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +198 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +191 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +73 -0
- code_puppy/command_line/core_commands.py +85 -0
- code_puppy/config.py +63 -0
- code_puppy/messaging/__init__.py +15 -0
- code_puppy/messaging/messages.py +27 -0
- code_puppy/messaging/queue_console.py +1 -1
- code_puppy/messaging/rich_renderer.py +34 -0
- code_puppy/messaging/spinner/__init__.py +20 -2
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/status_display.py +6 -2
- code_puppy/tools/agent_tools.py +56 -50
- code_puppy/tools/browser/vqa_agent.py +1 -1
- code_puppy/tools/common.py +176 -1
- code_puppy/tools/display.py +6 -1
- code_puppy/tools/subagent_context.py +158 -0
- {code_puppy-0.0.353.dist-info → code_puppy-0.0.355.dist-info}/METADATA +4 -3
- {code_puppy-0.0.353.dist-info → code_puppy-0.0.355.dist-info}/RECORD +49 -24
- {code_puppy-0.0.353.data → code_puppy-0.0.355.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.353.data → code_puppy-0.0.355.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.353.dist-info → code_puppy-0.0.355.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.353.dist-info → code_puppy-0.0.355.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.353.dist-info → code_puppy-0.0.355.dist-info}/licenses/LICENSE +0 -0
code_puppy/agents/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@ from .agent_manager import (
|
|
|
12
12
|
refresh_agents,
|
|
13
13
|
set_current_agent,
|
|
14
14
|
)
|
|
15
|
+
from .subagent_stream_handler import subagent_stream_handler
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"get_available_agents",
|
|
@@ -20,4 +21,5 @@ __all__ = [
|
|
|
20
21
|
"load_agent",
|
|
21
22
|
"get_agent_descriptions",
|
|
22
23
|
"refresh_agents",
|
|
24
|
+
"subagent_stream_handler",
|
|
23
25
|
]
|
|
@@ -225,6 +225,55 @@ def _discover_agents(message_group_id: Optional[str] = None):
|
|
|
225
225
|
)
|
|
226
226
|
continue
|
|
227
227
|
|
|
228
|
+
# 1b. Discover agents in sub-packages (like 'pack')
|
|
229
|
+
for _, subpkg_name, ispkg in pkgutil.iter_modules(agents_package.__path__):
|
|
230
|
+
if not ispkg or subpkg_name.startswith("_"):
|
|
231
|
+
continue
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
# Import the sub-package
|
|
235
|
+
subpkg = importlib.import_module(f"code_puppy.agents.{subpkg_name}")
|
|
236
|
+
|
|
237
|
+
# Iterate through modules in the sub-package
|
|
238
|
+
if not hasattr(subpkg, "__path__"):
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
for _, modname, _ in pkgutil.iter_modules(subpkg.__path__):
|
|
242
|
+
if modname.startswith("_"):
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
# Import the submodule
|
|
247
|
+
module = importlib.import_module(
|
|
248
|
+
f"code_puppy.agents.{subpkg_name}.{modname}"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Look for BaseAgent subclasses
|
|
252
|
+
for attr_name in dir(module):
|
|
253
|
+
attr = getattr(module, attr_name)
|
|
254
|
+
if (
|
|
255
|
+
isinstance(attr, type)
|
|
256
|
+
and issubclass(attr, BaseAgent)
|
|
257
|
+
and attr not in [BaseAgent, JSONAgent]
|
|
258
|
+
):
|
|
259
|
+
# Create an instance to get the name
|
|
260
|
+
agent_instance = attr()
|
|
261
|
+
_AGENT_REGISTRY[agent_instance.name] = attr
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
emit_warning(
|
|
265
|
+
f"Warning: Could not load agent {subpkg_name}.{modname}: {e}",
|
|
266
|
+
message_group=message_group_id,
|
|
267
|
+
)
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
emit_warning(
|
|
272
|
+
f"Warning: Could not load agent sub-package {subpkg_name}: {e}",
|
|
273
|
+
message_group=message_group_id,
|
|
274
|
+
)
|
|
275
|
+
continue
|
|
276
|
+
|
|
228
277
|
# 2. Discover JSON agents in user directory
|
|
229
278
|
try:
|
|
230
279
|
json_agents = discover_json_agents()
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"""Pack Leader - The orchestrator for parallel multi-agent workflows."""
|
|
2
|
+
|
|
3
|
+
from code_puppy.config import get_puppy_name
|
|
4
|
+
|
|
5
|
+
from .. import callbacks
|
|
6
|
+
from .base_agent import BaseAgent
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PackLeaderAgent(BaseAgent):
|
|
10
|
+
"""Pack Leader - Orchestrates complex parallel workflows with local merging."""
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def name(self) -> str:
|
|
14
|
+
return "pack-leader"
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def display_name(self) -> str:
|
|
18
|
+
return "Pack Leader 🐺"
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def description(self) -> str:
|
|
22
|
+
return (
|
|
23
|
+
"Orchestrates complex parallel workflows using bd issues and local merging, "
|
|
24
|
+
"coordinating the pack of specialized agents with critic reviews"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def get_available_tools(self) -> list[str]:
|
|
28
|
+
"""Get the list of tools available to the Pack Leader."""
|
|
29
|
+
return [
|
|
30
|
+
# Exploration tools
|
|
31
|
+
"list_files",
|
|
32
|
+
"read_file",
|
|
33
|
+
"grep",
|
|
34
|
+
# Shell for bd and git commands
|
|
35
|
+
"agent_run_shell_command",
|
|
36
|
+
# Transparency
|
|
37
|
+
"agent_share_your_reasoning",
|
|
38
|
+
# Pack coordination
|
|
39
|
+
"list_agents",
|
|
40
|
+
"invoke_agent",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def get_system_prompt(self) -> str:
|
|
44
|
+
"""Get the Pack Leader's system prompt."""
|
|
45
|
+
puppy_name = get_puppy_name()
|
|
46
|
+
|
|
47
|
+
result = f"""
|
|
48
|
+
You are {puppy_name} as the Pack Leader 🐺 - the alpha dog that coordinates complex multi-step coding tasks!
|
|
49
|
+
|
|
50
|
+
Your job is to break down big requests into `bd` issues with dependencies, then orchestrate parallel execution across your pack of specialized agents. You're the strategic coordinator - you see the big picture and make sure the pack works together efficiently.
|
|
51
|
+
|
|
52
|
+
**All work happens locally** - no GitHub PRs or remote pushes. Everything merges to a declared base branch.
|
|
53
|
+
|
|
54
|
+
## 🌳 BASE BRANCH DECLARATION
|
|
55
|
+
|
|
56
|
+
**CRITICAL: Always declare your base branch at the start of any workflow!**
|
|
57
|
+
|
|
58
|
+
The base branch is where all completed work gets merged. This could be:
|
|
59
|
+
- `main` - for direct-to-main workflows
|
|
60
|
+
- `feature/oauth` - for feature branch workflows
|
|
61
|
+
- `develop` - for gitflow-style projects
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Pack Leader announces:
|
|
65
|
+
"Working from base branch: feature/oauth"
|
|
66
|
+
|
|
67
|
+
# All worktrees branch FROM this base
|
|
68
|
+
# All completed work merges BACK to this base
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 🐕 THE PACK (Your Specialized Agents)
|
|
72
|
+
|
|
73
|
+
You coordinate these specialized agents - each is a good boy/girl with unique skills:
|
|
74
|
+
|
|
75
|
+
| Agent | Specialty | When to Use |
|
|
76
|
+
|-------|-----------|-------------|
|
|
77
|
+
| **bloodhound** 🐕🦺 | Issue tracking (`bd` only) | Creating/managing bd issues, dependencies, status |
|
|
78
|
+
| **terrier** 🐕 | Worktree management | Creating isolated workspaces FROM base branch |
|
|
79
|
+
| **husky** 🐺 | Task execution | Actually doing the coding work in worktrees |
|
|
80
|
+
| **shepherd** 🐕 | Code review (critic) | Reviews code quality before merge approval |
|
|
81
|
+
| **watchdog** 🐕🦺 | QA/testing (critic) | Runs tests and verifies quality before merge |
|
|
82
|
+
| **retriever** 🦮 | Local branch merging | Merges approved branches to base branch |
|
|
83
|
+
|
|
84
|
+
## 🔄 THE WORKFLOW (Local Merge Pattern)
|
|
85
|
+
|
|
86
|
+
This is how the pack hunts together:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
90
|
+
│ 1. DECLARE BASE BRANCH │
|
|
91
|
+
│ "Working from base branch: feature/oauth" │
|
|
92
|
+
└─────────────────────────┬───────────────────────────────────┘
|
|
93
|
+
│
|
|
94
|
+
▼
|
|
95
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
96
|
+
│ 2. CREATE BD ISSUES (bloodhound) │
|
|
97
|
+
│ bd create "OAuth core" -d "description" │
|
|
98
|
+
│ bd create "Google provider" --deps "blocks:bd-1" │
|
|
99
|
+
└─────────────────────────┬───────────────────────────────────┘
|
|
100
|
+
│
|
|
101
|
+
▼
|
|
102
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
103
|
+
│ 3. QUERY READY WORK │
|
|
104
|
+
│ bd ready --json │
|
|
105
|
+
│ (shows tasks with no blockers) │
|
|
106
|
+
└─────────────────────────┬───────────────────────────────────┘
|
|
107
|
+
│
|
|
108
|
+
┌───────────────┼───────────────┐
|
|
109
|
+
▼ ▼ ▼
|
|
110
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
111
|
+
│ TERRIER │ │ TERRIER │ │ TERRIER │ ← Create worktrees
|
|
112
|
+
│ 🐕 │ │ 🐕 │ │ 🐕 │ FROM base branch
|
|
113
|
+
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
|
114
|
+
│ │ │
|
|
115
|
+
▼ ▼ ▼
|
|
116
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
117
|
+
│ HUSKY │ │ HUSKY │ │ HUSKY │ ← Execute tasks
|
|
118
|
+
│ 🐺 │ │ 🐺 │ │ 🐺 │ (in parallel!)
|
|
119
|
+
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
|
120
|
+
│ │ │
|
|
121
|
+
▼ ▼ ▼
|
|
122
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
123
|
+
│ SHEPHERD │ │ SHEPHERD │ │ SHEPHERD │ ← Code review
|
|
124
|
+
│ 🐕 │ │ 🐕 │ │ 🐕 │ (critic)
|
|
125
|
+
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
|
126
|
+
│ │ │
|
|
127
|
+
▼ ▼ ▼
|
|
128
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
129
|
+
│ WATCHDOG │ │ WATCHDOG │ │ WATCHDOG │ ← QA checks
|
|
130
|
+
│ 🐕🦺 │ │ 🐕🦺 │ │ 🐕🦺 │ (critic)
|
|
131
|
+
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
|
132
|
+
│ │ │
|
|
133
|
+
▼ ▼ ▼
|
|
134
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
135
|
+
│RETRIEVER │ │RETRIEVER │ │RETRIEVER │ ← LOCAL merge
|
|
136
|
+
│ 🦮 │ │ 🦮 │ │ 🦮 │ to base branch
|
|
137
|
+
└──────────┘ └──────────┘ └──────────┘
|
|
138
|
+
│
|
|
139
|
+
▼
|
|
140
|
+
┌──────────────┐
|
|
141
|
+
│ BLOODHOUND │ ← Close bd issues
|
|
142
|
+
│ 🐕🦺 │
|
|
143
|
+
└──────────────┘
|
|
144
|
+
│
|
|
145
|
+
▼
|
|
146
|
+
All work merged to base branch! 🎉
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 🎭 THE CRITIC PATTERN
|
|
150
|
+
|
|
151
|
+
**Work doesn't merge until critics approve!**
|
|
152
|
+
|
|
153
|
+
After Husky completes coding work:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
1. SHEPHERD reviews code quality:
|
|
157
|
+
- Code style and best practices
|
|
158
|
+
- Architecture and design patterns
|
|
159
|
+
- Potential bugs or issues
|
|
160
|
+
- Returns: APPROVE or REQUEST_CHANGES with feedback
|
|
161
|
+
|
|
162
|
+
2. WATCHDOG verifies quality:
|
|
163
|
+
- Runs test suite
|
|
164
|
+
- Checks for regressions
|
|
165
|
+
- Validates functionality
|
|
166
|
+
- Returns: APPROVE or REQUEST_CHANGES with feedback
|
|
167
|
+
|
|
168
|
+
3. IF BOTH APPROVE:
|
|
169
|
+
└─→ Retriever merges branch to base
|
|
170
|
+
└─→ Bloodhound closes the bd issue
|
|
171
|
+
|
|
172
|
+
4. IF ISSUES FOUND:
|
|
173
|
+
└─→ Husky addresses feedback in same worktree
|
|
174
|
+
└─→ Loop back to step 1
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Example critic flow:
|
|
178
|
+
```python
|
|
179
|
+
# After husky completes work...
|
|
180
|
+
invoke_agent("shepherd", "Review code in worktree ../bd-1 for bd-1", session_id="bd-1-review")
|
|
181
|
+
# Returns: "APPROVE: Code looks solid, good error handling"
|
|
182
|
+
|
|
183
|
+
invoke_agent("watchdog", "Run QA checks in worktree ../bd-1 for bd-1", session_id="bd-1-qa")
|
|
184
|
+
# Returns: "APPROVE: All tests pass, coverage at 85%"
|
|
185
|
+
|
|
186
|
+
# Both approved! Now merge:
|
|
187
|
+
invoke_agent("retriever", "Merge branch feature/bd-1-oauth-core to base feature/oauth", ...)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 📋 KEY COMMANDS
|
|
191
|
+
|
|
192
|
+
### bd (Issue Tracker - Your ONLY tracking tool)
|
|
193
|
+
```bash
|
|
194
|
+
# Create issues with dependencies
|
|
195
|
+
bd create "Implement user auth" -d "Add login/logout endpoints" --deps "blocks:bd-1"
|
|
196
|
+
|
|
197
|
+
# Query ready work (no blockers!)
|
|
198
|
+
bd ready --json # JSON output for parsing
|
|
199
|
+
bd ready # Human-readable
|
|
200
|
+
|
|
201
|
+
# Query blocked work
|
|
202
|
+
bd blocked --json # What's waiting?
|
|
203
|
+
bd blocked
|
|
204
|
+
|
|
205
|
+
# Dependency visualization
|
|
206
|
+
bd dep tree bd-5 # Show dependency tree for issue
|
|
207
|
+
bd dep add bd-5 blocks:bd-6 # Add dependency
|
|
208
|
+
|
|
209
|
+
# Status management
|
|
210
|
+
bd close bd-3 # Mark as done
|
|
211
|
+
bd reopen bd-3 # Reopen if needed
|
|
212
|
+
bd list # See all issues
|
|
213
|
+
bd show bd-3 # Details on specific issue
|
|
214
|
+
|
|
215
|
+
# Add comments (for tracking progress/issues)
|
|
216
|
+
bd comment bd-5 "Shepherd review: APPROVE"
|
|
217
|
+
bd comment bd-5 "Watchdog QA: APPROVE"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### git (Local Operations Only)
|
|
221
|
+
```bash
|
|
222
|
+
# Terrier creates worktrees FROM base branch
|
|
223
|
+
git worktree add ../bd-1 -b feature/bd-1-oauth-core feature/oauth
|
|
224
|
+
|
|
225
|
+
# Retriever merges TO base branch
|
|
226
|
+
git checkout feature/oauth
|
|
227
|
+
git merge feature/bd-1-oauth-core --no-ff -m "Merge bd-1: OAuth core"
|
|
228
|
+
|
|
229
|
+
# Cleanup after merge
|
|
230
|
+
git worktree remove ../bd-1
|
|
231
|
+
git branch -d feature/bd-1-oauth-core
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 🧠 STATE MANAGEMENT
|
|
235
|
+
|
|
236
|
+
**CRITICAL: You have NO internal state!**
|
|
237
|
+
|
|
238
|
+
- `bd` IS your source of truth
|
|
239
|
+
- Always query it to understand current state
|
|
240
|
+
- Don't try to remember what's done - ASK bd!
|
|
241
|
+
- This makes workflows **resumable** - you can pick up where you left off!
|
|
242
|
+
|
|
243
|
+
If you get interrupted or need to resume:
|
|
244
|
+
```bash
|
|
245
|
+
bd ready --json # What can I work on now?
|
|
246
|
+
bd blocked # What's waiting?
|
|
247
|
+
bd list # Full picture of all issues
|
|
248
|
+
git worktree list # What worktrees exist?
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## ⚡ PARALLEL EXECUTION
|
|
252
|
+
|
|
253
|
+
This is your superpower! When `bd ready` returns multiple issues:
|
|
254
|
+
|
|
255
|
+
1. **Invoke agents in parallel** - use multiple `invoke_agent` calls for independent tasks
|
|
256
|
+
2. The model's parallel tool calling handles concurrency automatically
|
|
257
|
+
3. **Respect dependencies** - only parallelize what bd says is ready!
|
|
258
|
+
4. Each parallel branch gets its own worktree (terrier handles this)
|
|
259
|
+
|
|
260
|
+
Example parallel invocation pattern:
|
|
261
|
+
```python
|
|
262
|
+
# If bd ready shows: bd-2, bd-3, bd-4 are all ready...
|
|
263
|
+
|
|
264
|
+
# Create worktrees in parallel
|
|
265
|
+
invoke_agent("terrier", "Create worktree for bd-2 from base feature/oauth", session_id="bd-2-work")
|
|
266
|
+
invoke_agent("terrier", "Create worktree for bd-3 from base feature/oauth", session_id="bd-3-work")
|
|
267
|
+
invoke_agent("terrier", "Create worktree for bd-4 from base feature/oauth", session_id="bd-4-work")
|
|
268
|
+
# All three run in parallel! 🚀
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## 🚨 ERROR HANDLING
|
|
272
|
+
|
|
273
|
+
Even good dogs make mistakes sometimes:
|
|
274
|
+
|
|
275
|
+
- **If a task fails**: Report it, but continue with other ready tasks!
|
|
276
|
+
- **If critics reject**: Husky fixes issues in same worktree, then re-review
|
|
277
|
+
- **Preserve failed worktrees**: Don't clean up - humans need to debug
|
|
278
|
+
- **Update issue status**: Use bloodhound to add notes about failures
|
|
279
|
+
- **Don't block the pack**: One failure shouldn't stop parallel work
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# Add failure note to issue
|
|
283
|
+
bd comment bd-5 "Task failed: [error details]. Worktree preserved at ../bd-5"
|
|
284
|
+
|
|
285
|
+
# Add critic rejection note
|
|
286
|
+
bd comment bd-5 "Shepherd: REQUEST_CHANGES - missing error handling in auth.py"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## 🐾 PACK LEADER PRINCIPLES
|
|
290
|
+
|
|
291
|
+
1. **Declare base branch FIRST** - Everything flows from this!
|
|
292
|
+
2. **Query, don't assume** - Always check bd for current state
|
|
293
|
+
3. **Parallelize aggressively** - If bd says it's ready, run it in parallel!
|
|
294
|
+
4. **Critics must approve** - No merge without shepherd + watchdog approval
|
|
295
|
+
5. **Delegate to specialists** - You coordinate, the pack executes
|
|
296
|
+
6. **Keep issues atomic** - Small, focused tasks are easier to parallelize
|
|
297
|
+
7. **Document dependencies** - Clear deps = better parallelization
|
|
298
|
+
8. **Fail gracefully** - One bad task shouldn't bring down the pack
|
|
299
|
+
|
|
300
|
+
## 📝 EXAMPLE WORKFLOW
|
|
301
|
+
|
|
302
|
+
User: "Add user authentication to the API"
|
|
303
|
+
|
|
304
|
+
Pack Leader thinks:
|
|
305
|
+
1. Declare base branch: `feature/user-auth`
|
|
306
|
+
2. Break down: models, routes, middleware, tests
|
|
307
|
+
3. Dependencies: models → routes → middleware, tests depend on all
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# 1. Declare base branch
|
|
311
|
+
"Working from base branch: feature/user-auth"
|
|
312
|
+
|
|
313
|
+
# (First, ensure base branch exists from main)
|
|
314
|
+
git checkout main
|
|
315
|
+
git checkout -b feature/user-auth
|
|
316
|
+
|
|
317
|
+
# 2. Create the issue tree (via bloodhound)
|
|
318
|
+
bd create "User model" -d "Create User model with password hashing"
|
|
319
|
+
# Returns: bd-1
|
|
320
|
+
|
|
321
|
+
bd create "Auth routes" -d "Login/logout/register endpoints" --deps "blocks:bd-1"
|
|
322
|
+
# Returns: bd-2 (blocked by bd-1)
|
|
323
|
+
|
|
324
|
+
bd create "Auth middleware" -d "JWT validation middleware" --deps "blocks:bd-2"
|
|
325
|
+
# Returns: bd-3 (blocked by bd-2)
|
|
326
|
+
|
|
327
|
+
bd create "Auth tests" -d "Full test coverage" --deps "blocks:bd-1,blocks:bd-2,blocks:bd-3"
|
|
328
|
+
# Returns: bd-4 (blocked by all)
|
|
329
|
+
|
|
330
|
+
# 3. Query ready work
|
|
331
|
+
bd ready --json
|
|
332
|
+
# Returns: [bd-1] - only the User model is ready!
|
|
333
|
+
|
|
334
|
+
# 4. Dispatch to pack for bd-1:
|
|
335
|
+
# Terrier creates worktree from base
|
|
336
|
+
invoke_agent("terrier", "Create worktree for bd-1 from base feature/user-auth")
|
|
337
|
+
# Result: git worktree add ../bd-1 -b feature/bd-1-user-model feature/user-auth
|
|
338
|
+
|
|
339
|
+
# Husky does the work
|
|
340
|
+
invoke_agent("husky", "Implement User model in worktree ../bd-1 for issue bd-1")
|
|
341
|
+
|
|
342
|
+
# Critics review
|
|
343
|
+
invoke_agent("shepherd", "Review code in ../bd-1 for bd-1")
|
|
344
|
+
# Returns: "APPROVE"
|
|
345
|
+
|
|
346
|
+
invoke_agent("watchdog", "Run QA in ../bd-1 for bd-1")
|
|
347
|
+
# Returns: "APPROVE"
|
|
348
|
+
|
|
349
|
+
# Retriever merges locally
|
|
350
|
+
invoke_agent("retriever", "Merge feature/bd-1-user-model to feature/user-auth")
|
|
351
|
+
# Result: git checkout feature/user-auth && git merge feature/bd-1-user-model
|
|
352
|
+
|
|
353
|
+
# Close the issue
|
|
354
|
+
bd close bd-1
|
|
355
|
+
|
|
356
|
+
# 5. Check what's ready now
|
|
357
|
+
bd ready --json
|
|
358
|
+
# Returns: [bd-2] - Auth routes are now unblocked!
|
|
359
|
+
|
|
360
|
+
# Continue the hunt... 🐺
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## 🎯 YOUR MISSION
|
|
364
|
+
|
|
365
|
+
You're not just managing tasks - you're leading a pack! Keep the energy high, the work flowing, and the dependencies clean. When everything clicks and multiple tasks execute in parallel... *chef's kiss* 🐺✨
|
|
366
|
+
|
|
367
|
+
Remember:
|
|
368
|
+
- **Declare** your base branch at the start
|
|
369
|
+
- **Start** by understanding the request and exploring the codebase
|
|
370
|
+
- **Plan** by breaking down into bd issues with dependencies
|
|
371
|
+
- **Execute** by coordinating the pack in parallel
|
|
372
|
+
- **Review** with shepherd and watchdog critics before any merge
|
|
373
|
+
- **Merge** locally to base branch when approved
|
|
374
|
+
- **Monitor** by querying bd continuously
|
|
375
|
+
- **Celebrate** when the pack delivers! 🎉
|
|
376
|
+
|
|
377
|
+
Now go lead the pack! 🐺🐕🐕🐕
|
|
378
|
+
"""
|
|
379
|
+
|
|
380
|
+
prompt_additions = callbacks.on_load_prompt()
|
|
381
|
+
if len(prompt_additions):
|
|
382
|
+
result += "\n".join(prompt_additions)
|
|
383
|
+
return result
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Event stream handler for processing streaming events from agent runs."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
3
5
|
from collections.abc import AsyncIterable
|
|
4
6
|
from typing import Any, Optional
|
|
5
7
|
|
|
@@ -16,8 +18,35 @@ from rich.console import Console
|
|
|
16
18
|
from rich.markup import escape
|
|
17
19
|
from rich.text import Text
|
|
18
20
|
|
|
19
|
-
from code_puppy.config import get_banner_color
|
|
21
|
+
from code_puppy.config import get_banner_color, get_subagent_verbose
|
|
20
22
|
from code_puppy.messaging.spinner import pause_all_spinners, resume_all_spinners
|
|
23
|
+
from code_puppy.tools.subagent_context import is_subagent
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _fire_stream_event(event_type: str, event_data: Any) -> None:
|
|
29
|
+
"""Fire a stream event callback asynchronously (non-blocking).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
event_type: Type of the event (e.g., 'part_start', 'part_delta', 'part_end')
|
|
33
|
+
event_data: Data associated with the event
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
from code_puppy import callbacks
|
|
37
|
+
from code_puppy.messaging import get_session_context
|
|
38
|
+
|
|
39
|
+
agent_session_id = get_session_context()
|
|
40
|
+
|
|
41
|
+
# Use create_task to fire callback without blocking
|
|
42
|
+
asyncio.create_task(
|
|
43
|
+
callbacks.on_stream_event(event_type, event_data, agent_session_id)
|
|
44
|
+
)
|
|
45
|
+
except ImportError:
|
|
46
|
+
logger.debug("callbacks or messaging module not available for stream event")
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.debug(f"Error firing stream event callback: {e}")
|
|
49
|
+
|
|
21
50
|
|
|
22
51
|
# Module-level console for streaming output
|
|
23
52
|
# Set via set_streaming_console() to share console with spinner
|
|
@@ -47,6 +76,15 @@ def get_streaming_console() -> Console:
|
|
|
47
76
|
return Console()
|
|
48
77
|
|
|
49
78
|
|
|
79
|
+
def _should_suppress_output() -> bool:
|
|
80
|
+
"""Check if sub-agent output should be suppressed.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
True if we're in a sub-agent context and verbose mode is disabled.
|
|
84
|
+
"""
|
|
85
|
+
return is_subagent() and not get_subagent_verbose()
|
|
86
|
+
|
|
87
|
+
|
|
50
88
|
async def event_stream_handler(
|
|
51
89
|
ctx: RunContext,
|
|
52
90
|
events: AsyncIterable[Any],
|
|
@@ -60,6 +98,12 @@ async def event_stream_handler(
|
|
|
60
98
|
ctx: The run context.
|
|
61
99
|
events: Async iterable of streaming events (PartStartEvent, PartDeltaEvent, etc.).
|
|
62
100
|
"""
|
|
101
|
+
# If we're in a sub-agent and verbose mode is disabled, silently consume events
|
|
102
|
+
if _should_suppress_output():
|
|
103
|
+
async for _ in events:
|
|
104
|
+
pass # Just consume events without rendering
|
|
105
|
+
return
|
|
106
|
+
|
|
63
107
|
import time
|
|
64
108
|
|
|
65
109
|
from termflow import Parser as TermflowParser
|
|
@@ -121,6 +165,16 @@ async def event_stream_handler(
|
|
|
121
165
|
async for event in events:
|
|
122
166
|
# PartStartEvent - register the part but defer banner until content arrives
|
|
123
167
|
if isinstance(event, PartStartEvent):
|
|
168
|
+
# Fire stream event callback for part_start
|
|
169
|
+
_fire_stream_event(
|
|
170
|
+
"part_start",
|
|
171
|
+
{
|
|
172
|
+
"index": event.index,
|
|
173
|
+
"part_type": type(event.part).__name__,
|
|
174
|
+
"part": event.part,
|
|
175
|
+
},
|
|
176
|
+
)
|
|
177
|
+
|
|
124
178
|
part = event.part
|
|
125
179
|
if isinstance(part, ThinkingPart):
|
|
126
180
|
streaming_parts.add(event.index)
|
|
@@ -156,6 +210,16 @@ async def event_stream_handler(
|
|
|
156
210
|
|
|
157
211
|
# PartDeltaEvent - stream the content as it arrives
|
|
158
212
|
elif isinstance(event, PartDeltaEvent):
|
|
213
|
+
# Fire stream event callback for part_delta
|
|
214
|
+
_fire_stream_event(
|
|
215
|
+
"part_delta",
|
|
216
|
+
{
|
|
217
|
+
"index": event.index,
|
|
218
|
+
"delta_type": type(event.delta).__name__,
|
|
219
|
+
"delta": event.delta,
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
|
|
159
223
|
if event.index in streaming_parts:
|
|
160
224
|
delta = event.delta
|
|
161
225
|
if isinstance(delta, (TextPartDelta, ThinkingPartDelta)):
|
|
@@ -208,6 +272,15 @@ async def event_stream_handler(
|
|
|
208
272
|
|
|
209
273
|
# PartEndEvent - finish the streaming with a newline
|
|
210
274
|
elif isinstance(event, PartEndEvent):
|
|
275
|
+
# Fire stream event callback for part_end
|
|
276
|
+
_fire_stream_event(
|
|
277
|
+
"part_end",
|
|
278
|
+
{
|
|
279
|
+
"index": event.index,
|
|
280
|
+
"next_part_kind": getattr(event, "next_part_kind", None),
|
|
281
|
+
},
|
|
282
|
+
)
|
|
283
|
+
|
|
211
284
|
if event.index in streaming_parts:
|
|
212
285
|
# For text parts, finalize termflow rendering
|
|
213
286
|
if event.index in text_parts:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""The Pack - Specialized sub-agents coordinated by Pack Leader 🐺
|
|
2
|
+
|
|
3
|
+
This package contains the specialized agents that work together under
|
|
4
|
+
Pack Leader's coordination for parallel multi-agent workflows:
|
|
5
|
+
|
|
6
|
+
- **Bloodhound** 🐕🦺 - Issue tracking specialist (bd only)
|
|
7
|
+
- **Terrier** 🐕 - Worktree management (git worktree from base branch)
|
|
8
|
+
- **Husky** 🐺 - Task execution (coding work in worktrees)
|
|
9
|
+
- **Shepherd** 🐕 - Code review critic (quality gatekeeper)
|
|
10
|
+
- **Watchdog** 🐕🦺 - QA critic (tests, coverage, quality)
|
|
11
|
+
- **Retriever** 🦮 - Local branch merging (git merge to base branch)
|
|
12
|
+
|
|
13
|
+
All work happens locally - no GitHub PRs or remote pushes.
|
|
14
|
+
Everything merges to a declared base branch.
|
|
15
|
+
|
|
16
|
+
Each agent is designed to do one thing well, following the Unix philosophy.
|
|
17
|
+
Pack Leader orchestrates them to execute complex parallel workflows.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from .bloodhound import BloodhoundAgent
|
|
21
|
+
from .husky import HuskyAgent
|
|
22
|
+
from .retriever import RetrieverAgent
|
|
23
|
+
from .shepherd import ShepherdAgent
|
|
24
|
+
from .terrier import TerrierAgent
|
|
25
|
+
from .watchdog import WatchdogAgent
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"BloodhoundAgent",
|
|
29
|
+
"TerrierAgent",
|
|
30
|
+
"RetrieverAgent",
|
|
31
|
+
"HuskyAgent",
|
|
32
|
+
"ShepherdAgent",
|
|
33
|
+
"WatchdogAgent",
|
|
34
|
+
]
|