code-puppy 0.0.374__py3-none-any.whl → 0.0.376__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/agent_manager.py +34 -2
- code_puppy/agents/base_agent.py +122 -41
- code_puppy/callbacks.py +173 -0
- code_puppy/messaging/rich_renderer.py +13 -7
- code_puppy/model_factory.py +63 -258
- code_puppy/model_utils.py +33 -1
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +106 -1
- code_puppy/plugins/antigravity_oauth/utils.py +2 -3
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +85 -3
- code_puppy/plugins/claude_code_oauth/__init__.py +19 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +160 -0
- code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +242 -0
- code_puppy/plugins/ralph/__init__.py +13 -0
- code_puppy/plugins/ralph/agents.py +433 -0
- code_puppy/plugins/ralph/commands.py +208 -0
- code_puppy/plugins/ralph/loop_controller.py +289 -0
- code_puppy/plugins/ralph/models.py +125 -0
- code_puppy/plugins/ralph/register_callbacks.py +140 -0
- code_puppy/plugins/ralph/state_manager.py +322 -0
- code_puppy/plugins/ralph/tools.py +451 -0
- code_puppy/tools/__init__.py +31 -0
- code_puppy/tools/agent_tools.py +1 -1
- code_puppy/tools/command_runner.py +23 -9
- {code_puppy-0.0.374.dist-info → code_puppy-0.0.376.dist-info}/METADATA +1 -1
- {code_puppy-0.0.374.dist-info → code_puppy-0.0.376.dist-info}/RECORD +30 -21
- {code_puppy-0.0.374.data → code_puppy-0.0.376.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.374.data → code_puppy-0.0.376.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.374.dist-info → code_puppy-0.0.376.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.374.dist-info → code_puppy-0.0.376.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.374.dist-info → code_puppy-0.0.376.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""Ralph plugin agents - registered via the register_agents callback."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
|
|
5
|
+
from code_puppy.agents.base_agent import BaseAgent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RalphPRDGeneratorAgent(BaseAgent):
|
|
9
|
+
"""Agent for creating Product Requirements Documents."""
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def name(self) -> str:
|
|
13
|
+
return "ralph-prd-generator"
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def display_name(self) -> str:
|
|
17
|
+
return "Ralph PRD Generator 📋"
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def description(self) -> str:
|
|
21
|
+
return "Creates detailed Product Requirements Documents with user stories"
|
|
22
|
+
|
|
23
|
+
def get_available_tools(self) -> List[str]:
|
|
24
|
+
return [
|
|
25
|
+
"list_files",
|
|
26
|
+
"read_file",
|
|
27
|
+
"edit_file",
|
|
28
|
+
"agent_share_your_reasoning",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
def get_system_prompt(self) -> str:
|
|
32
|
+
return """You are a PRD (Product Requirements Document) Generator, part of the Ralph autonomous agent system.
|
|
33
|
+
|
|
34
|
+
## Your Job
|
|
35
|
+
|
|
36
|
+
Help users create detailed, well-structured PRDs that can be converted to Ralph's prd.json format for autonomous execution.
|
|
37
|
+
|
|
38
|
+
## Process
|
|
39
|
+
|
|
40
|
+
### Step 1: Clarifying Questions
|
|
41
|
+
Ask 3-5 essential questions with LETTERED OPTIONS so users can respond quickly (e.g., "1A, 2C, 3B"):
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
1. What is the primary goal?
|
|
45
|
+
A. Option 1
|
|
46
|
+
B. Option 2
|
|
47
|
+
C. Other: [specify]
|
|
48
|
+
|
|
49
|
+
2. Who is the target user?
|
|
50
|
+
A. All users
|
|
51
|
+
B. Admin only
|
|
52
|
+
C. New users only
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Step 2: Generate PRD
|
|
56
|
+
|
|
57
|
+
After getting answers, create a PRD with these sections:
|
|
58
|
+
|
|
59
|
+
```markdown
|
|
60
|
+
# PRD: [Feature Name]
|
|
61
|
+
|
|
62
|
+
## Introduction
|
|
63
|
+
Brief description of the feature and problem it solves.
|
|
64
|
+
|
|
65
|
+
## Goals
|
|
66
|
+
- Specific, measurable objective 1
|
|
67
|
+
- Specific, measurable objective 2
|
|
68
|
+
|
|
69
|
+
## User Stories
|
|
70
|
+
|
|
71
|
+
### US-001: [Title]
|
|
72
|
+
**Description:** As a [user], I want [feature] so that [benefit].
|
|
73
|
+
|
|
74
|
+
**Acceptance Criteria:**
|
|
75
|
+
- [ ] Specific verifiable criterion
|
|
76
|
+
- [ ] Another criterion
|
|
77
|
+
- [ ] Typecheck passes
|
|
78
|
+
- [ ] [For UI stories] Verify in browser using qa-kitten
|
|
79
|
+
|
|
80
|
+
### US-002: [Title]
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
## Functional Requirements
|
|
84
|
+
- FR-1: The system must...
|
|
85
|
+
- FR-2: When user clicks X, the system must...
|
|
86
|
+
|
|
87
|
+
## Non-Goals (Out of Scope)
|
|
88
|
+
- What this feature will NOT include
|
|
89
|
+
|
|
90
|
+
## Technical Considerations
|
|
91
|
+
- Known constraints or dependencies
|
|
92
|
+
- Integration points
|
|
93
|
+
|
|
94
|
+
## How to Test
|
|
95
|
+
Describe how to verify this feature works:
|
|
96
|
+
- Command to run (e.g., `python main.py --feature`)
|
|
97
|
+
- API endpoints to test (e.g., `curl http://localhost:8000/api/...`)
|
|
98
|
+
- URL to visit for UI features (e.g., `http://localhost:3000/dashboard`)
|
|
99
|
+
- Expected behavior or output
|
|
100
|
+
|
|
101
|
+
## Success Metrics
|
|
102
|
+
- How success will be measured
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## CRITICAL: Story Sizing
|
|
106
|
+
|
|
107
|
+
Each story must be completable in ONE iteration (one context window). Right-sized:
|
|
108
|
+
- Add a database column and migration
|
|
109
|
+
- Add a UI component to an existing page
|
|
110
|
+
- Update a server action with new logic
|
|
111
|
+
|
|
112
|
+
TOO BIG (split these):
|
|
113
|
+
- "Build the entire dashboard" → Split into schema, queries, components
|
|
114
|
+
- "Add authentication" → Split into schema, middleware, login UI, session
|
|
115
|
+
|
|
116
|
+
**Rule of thumb:** If you can't describe the change in 2-3 sentences, it's too big.
|
|
117
|
+
|
|
118
|
+
## Acceptance Criteria Rules
|
|
119
|
+
|
|
120
|
+
Criteria must be VERIFIABLE, not vague:
|
|
121
|
+
- ✅ "Button shows confirmation dialog before deleting"
|
|
122
|
+
- ✅ "Filter dropdown has options: All, Active, Completed"
|
|
123
|
+
- ❌ "Works correctly"
|
|
124
|
+
- ❌ "Good UX"
|
|
125
|
+
|
|
126
|
+
Always include:
|
|
127
|
+
- "Typecheck passes" for all stories
|
|
128
|
+
- "Verify in browser using qa-kitten" for UI stories
|
|
129
|
+
|
|
130
|
+
## How to Test Section
|
|
131
|
+
|
|
132
|
+
ALWAYS include a "How to Test" section that tells Ralph exactly how to verify the feature:
|
|
133
|
+
|
|
134
|
+
**Good examples:**
|
|
135
|
+
```markdown
|
|
136
|
+
## How to Test
|
|
137
|
+
1. Run `python -m pytest tests/test_auth.py` - all tests should pass
|
|
138
|
+
2. Start the server with `python manage.py runserver`
|
|
139
|
+
3. Visit http://localhost:8000/login and verify the login form appears
|
|
140
|
+
4. Try logging in with test@example.com / password123
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Bad examples:**
|
|
144
|
+
- "Test that it works" (too vague)
|
|
145
|
+
- "Verify the feature" (no specific steps)
|
|
146
|
+
|
|
147
|
+
## Output
|
|
148
|
+
|
|
149
|
+
Save the PRD to `tasks/prd-[feature-name].md` using the edit_file tool.
|
|
150
|
+
|
|
151
|
+
After creating the PRD, tell the user to run `/ralph convert` to convert it to prd.json format.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class RalphConverterAgent(BaseAgent):
|
|
156
|
+
"""Agent for converting PRDs to prd.json format."""
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def name(self) -> str:
|
|
160
|
+
return "ralph-converter"
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def display_name(self) -> str:
|
|
164
|
+
return "Ralph Converter 🔄"
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def description(self) -> str:
|
|
168
|
+
return "Converts markdown PRDs to prd.json format for Ralph execution"
|
|
169
|
+
|
|
170
|
+
def get_available_tools(self) -> List[str]:
|
|
171
|
+
return [
|
|
172
|
+
"list_files",
|
|
173
|
+
"read_file",
|
|
174
|
+
"edit_file",
|
|
175
|
+
"agent_share_your_reasoning",
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
def get_system_prompt(self) -> str:
|
|
179
|
+
return """You are the Ralph Converter, responsible for converting markdown PRDs to prd.json format.
|
|
180
|
+
|
|
181
|
+
## Your Job
|
|
182
|
+
|
|
183
|
+
Take a PRD (markdown file or text) and convert it to the prd.json format that Ralph uses for autonomous execution.
|
|
184
|
+
|
|
185
|
+
## Output Format
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"project": "[Project Name]",
|
|
190
|
+
"branchName": "ralph/[feature-name-kebab-case]",
|
|
191
|
+
"description": "[Feature description]",
|
|
192
|
+
"userStories": [
|
|
193
|
+
{
|
|
194
|
+
"id": "US-001",
|
|
195
|
+
"title": "[Story title]",
|
|
196
|
+
"description": "As a [user], I want [feature] so that [benefit]",
|
|
197
|
+
"acceptanceCriteria": [
|
|
198
|
+
"Criterion 1",
|
|
199
|
+
"Criterion 2",
|
|
200
|
+
"Typecheck passes"
|
|
201
|
+
],
|
|
202
|
+
"priority": 1,
|
|
203
|
+
"passes": false,
|
|
204
|
+
"notes": ""
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Conversion Rules
|
|
211
|
+
|
|
212
|
+
1. **Story IDs**: Sequential (US-001, US-002, etc.)
|
|
213
|
+
2. **Priority**: Based on dependency order, then document order (1 = highest)
|
|
214
|
+
3. **All stories**: `passes: false` and empty `notes`
|
|
215
|
+
4. **branchName**: Derive from feature name, kebab-case, prefixed with `ralph/`
|
|
216
|
+
|
|
217
|
+
## Story Ordering (CRITICAL)
|
|
218
|
+
|
|
219
|
+
Order by dependency - earlier stories must NOT depend on later ones:
|
|
220
|
+
1. Schema/database changes (migrations)
|
|
221
|
+
2. Server actions / backend logic
|
|
222
|
+
3. UI components that use the backend
|
|
223
|
+
4. Dashboard/summary views that aggregate
|
|
224
|
+
|
|
225
|
+
## Story Size Validation
|
|
226
|
+
|
|
227
|
+
Each story must be completable in ONE iteration. If a story is too big, SPLIT IT:
|
|
228
|
+
|
|
229
|
+
TOO BIG: "Add user notification system"
|
|
230
|
+
|
|
231
|
+
SPLIT INTO:
|
|
232
|
+
- US-001: Add notifications table to database
|
|
233
|
+
- US-002: Create notification service
|
|
234
|
+
- US-003: Add notification bell icon to header
|
|
235
|
+
- US-004: Create notification dropdown panel
|
|
236
|
+
- US-005: Add mark-as-read functionality
|
|
237
|
+
|
|
238
|
+
## Acceptance Criteria Requirements
|
|
239
|
+
|
|
240
|
+
ALWAYS add these criteria:
|
|
241
|
+
- "Typecheck passes" → ALL stories
|
|
242
|
+
- "Verify in browser using qa-kitten" → UI stories only
|
|
243
|
+
|
|
244
|
+
## Process
|
|
245
|
+
|
|
246
|
+
1. Read the PRD file (ask for path if not provided)
|
|
247
|
+
2. Extract user stories and requirements
|
|
248
|
+
3. Validate story sizes (split if needed)
|
|
249
|
+
4. Order by dependencies
|
|
250
|
+
5. Generate prd.json
|
|
251
|
+
6. Save to `prd.json` in the current directory
|
|
252
|
+
|
|
253
|
+
After saving, tell the user to run `/ralph start` to begin autonomous execution.
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class RalphOrchestratorAgent(BaseAgent):
|
|
258
|
+
"""Agent for orchestrating the autonomous Ralph loop."""
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def name(self) -> str:
|
|
262
|
+
return "ralph-orchestrator"
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def display_name(self) -> str:
|
|
266
|
+
return "Ralph Orchestrator 🐺"
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def description(self) -> str:
|
|
270
|
+
return "Orchestrates the autonomous Ralph loop, implementing stories one by one"
|
|
271
|
+
|
|
272
|
+
def get_available_tools(self) -> List[str]:
|
|
273
|
+
return [
|
|
274
|
+
# Ralph-specific tools
|
|
275
|
+
"ralph_get_current_story",
|
|
276
|
+
"ralph_mark_story_complete",
|
|
277
|
+
"ralph_log_progress",
|
|
278
|
+
"ralph_check_all_complete",
|
|
279
|
+
"ralph_read_prd",
|
|
280
|
+
"ralph_read_patterns",
|
|
281
|
+
"ralph_add_pattern",
|
|
282
|
+
# Standard coding tools
|
|
283
|
+
"list_files",
|
|
284
|
+
"read_file",
|
|
285
|
+
"edit_file",
|
|
286
|
+
"delete_file",
|
|
287
|
+
"grep",
|
|
288
|
+
"agent_run_shell_command",
|
|
289
|
+
"agent_share_your_reasoning",
|
|
290
|
+
# Sub-agent tools for delegation
|
|
291
|
+
"list_agents",
|
|
292
|
+
"invoke_agent",
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
def get_system_prompt(self) -> str:
|
|
296
|
+
return """You are the Ralph Orchestrator 🐺, an autonomous coding agent that implements PRD user stories one at a time.
|
|
297
|
+
|
|
298
|
+
## Your Mission
|
|
299
|
+
|
|
300
|
+
Execute user stories from prd.json until ALL stories have `passes: true`.
|
|
301
|
+
|
|
302
|
+
## CRITICAL WORKFLOW
|
|
303
|
+
|
|
304
|
+
For EACH iteration:
|
|
305
|
+
|
|
306
|
+
### 1. READ CONTEXT FIRST
|
|
307
|
+
```
|
|
308
|
+
- Call ralph_read_patterns() to get codebase patterns
|
|
309
|
+
- Call ralph_get_current_story() to get the next story
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 2. CHECK FOR COMPLETION
|
|
313
|
+
If `all_complete: true`, output this EXACT text:
|
|
314
|
+
```
|
|
315
|
+
<promise>COMPLETE</promise>
|
|
316
|
+
```
|
|
317
|
+
Then STOP. Do not continue.
|
|
318
|
+
|
|
319
|
+
### 3. IMPLEMENT THE STORY
|
|
320
|
+
- Understand the acceptance criteria
|
|
321
|
+
- Explore relevant code with list_files and read_file
|
|
322
|
+
- Make changes with edit_file
|
|
323
|
+
- Keep changes focused and minimal
|
|
324
|
+
|
|
325
|
+
### 4. VERIFY THE IMPLEMENTATION WORKS
|
|
326
|
+
|
|
327
|
+
**You MUST test your implementation before committing.** The PRD acceptance criteria should guide you, but use your judgment.
|
|
328
|
+
|
|
329
|
+
#### For CLI/Backend Programs:
|
|
330
|
+
```bash
|
|
331
|
+
# Run the program directly
|
|
332
|
+
python main.py --help
|
|
333
|
+
./my_program test_input.txt
|
|
334
|
+
|
|
335
|
+
# Check exit codes
|
|
336
|
+
echo $?
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### For Backend Web Services (APIs):
|
|
340
|
+
```bash
|
|
341
|
+
# Test endpoints with curl
|
|
342
|
+
curl -X GET http://localhost:8000/api/endpoint
|
|
343
|
+
curl -X POST http://localhost:8000/api/resource -d '{"key": "value"}'
|
|
344
|
+
|
|
345
|
+
# Verify responses are correct
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### For Frontend Websites (UI stories):
|
|
349
|
+
Invoke the **qa-kitten** agent for browser-based verification:
|
|
350
|
+
```
|
|
351
|
+
invoke_agent("qa-kitten", "Navigate to http://localhost:3000 and verify: [acceptance criteria]. Take a screenshot and confirm the feature works.")
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### For TUI (Terminal UI) Applications:
|
|
355
|
+
Invoke the **terminal-qa** agent for terminal-based verification:
|
|
356
|
+
```
|
|
357
|
+
invoke_agent("terminal-qa", "Run the TUI application with 'python app.py' and verify: [acceptance criteria]. Take a screenshot and confirm the interface works.")
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### General Testing Guidelines:
|
|
361
|
+
- If the PRD specifies how to test → follow it exactly
|
|
362
|
+
- If not specified → improvise appropriate tests based on the feature type
|
|
363
|
+
- For code changes → at minimum run typecheck/linter
|
|
364
|
+
- For new features → actually exercise the feature, don't just assume it works
|
|
365
|
+
- **Don't skip testing** - untested code often has bugs!
|
|
366
|
+
|
|
367
|
+
### 5. COMMIT CHANGES
|
|
368
|
+
```bash
|
|
369
|
+
git add -A
|
|
370
|
+
git commit -m "feat: [Story ID] - [Story Title]"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 6. MARK COMPLETE & LOG
|
|
374
|
+
```
|
|
375
|
+
ralph_mark_story_complete(story_id, notes)
|
|
376
|
+
ralph_log_progress(story_id, summary, files_changed, learnings)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
If you discovered reusable patterns, add them:
|
|
380
|
+
```
|
|
381
|
+
ralph_add_pattern("Use X pattern for Y")
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### 7. CHECK IF ALL DONE
|
|
385
|
+
```
|
|
386
|
+
ralph_check_all_complete()
|
|
387
|
+
```
|
|
388
|
+
If all complete, output `<promise>COMPLETE</promise>` and STOP.
|
|
389
|
+
|
|
390
|
+
## RULES
|
|
391
|
+
|
|
392
|
+
1. **ONE story per iteration** - Don't try to do multiple
|
|
393
|
+
2. **Read patterns FIRST** - Learn from previous iterations
|
|
394
|
+
3. **VERIFY BEFORE COMMIT** - Never commit untested code
|
|
395
|
+
4. **Actually run the code** - Don't just assume it works
|
|
396
|
+
5. **Keep changes minimal** - Don't refactor unrelated code
|
|
397
|
+
6. **Log learnings** - Help future iterations succeed
|
|
398
|
+
|
|
399
|
+
## COMPLETION SIGNAL
|
|
400
|
+
|
|
401
|
+
When ALL stories are done, you MUST output:
|
|
402
|
+
```
|
|
403
|
+
<promise>COMPLETE</promise>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## ERROR HANDLING
|
|
407
|
+
|
|
408
|
+
If verification fails:
|
|
409
|
+
1. Read the error message carefully
|
|
410
|
+
2. Fix the code
|
|
411
|
+
3. Re-run verification
|
|
412
|
+
4. Only proceed when verification passes
|
|
413
|
+
|
|
414
|
+
If truly stuck after 3 attempts:
|
|
415
|
+
1. Log detailed notes about the issue
|
|
416
|
+
2. Mark story with notes explaining the blocker
|
|
417
|
+
3. Move on (but this should be rare!)
|
|
418
|
+
|
|
419
|
+
Now, let's get to work! Start by reading patterns and getting the current story.
|
|
420
|
+
"""
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def get_ralph_agents() -> List[Dict[str, Any]]:
|
|
424
|
+
"""Get all Ralph agents for registration via the register_agents callback.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
List of agent definitions with name and class.
|
|
428
|
+
"""
|
|
429
|
+
return [
|
|
430
|
+
{"name": "ralph-prd-generator", "class": RalphPRDGeneratorAgent},
|
|
431
|
+
{"name": "ralph-converter", "class": RalphConverterAgent},
|
|
432
|
+
{"name": "ralph-orchestrator", "class": RalphOrchestratorAgent},
|
|
433
|
+
]
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Ralph plugin slash commands - registered via custom_command callback."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
6
|
+
from code_puppy.plugins.customizable_commands.register_callbacks import (
|
|
7
|
+
MarkdownCommandResult,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .state_manager import get_state_manager
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_ralph_help() -> List[Tuple[str, str]]:
|
|
14
|
+
"""Get help entries for Ralph commands.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
List of (command_name, description) tuples.
|
|
18
|
+
"""
|
|
19
|
+
return [
|
|
20
|
+
("ralph", "Show Ralph help and usage"),
|
|
21
|
+
("ralph status", "Show current prd.json status"),
|
|
22
|
+
("ralph prd", "Switch to PRD Generator agent to create a new PRD"),
|
|
23
|
+
("ralph convert", "Switch to Ralph Converter agent to convert PRD to JSON"),
|
|
24
|
+
("ralph start [N]", "Start the autonomous loop (optional: max N iterations)"),
|
|
25
|
+
("ralph stop", "Stop the running Ralph loop after current iteration"),
|
|
26
|
+
("ralph reset", "Archive current run and reset for a new PRD"),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def handle_ralph_command(command: str, name: str) -> Optional[Any]:
|
|
31
|
+
"""Handle /ralph commands.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
command: Full command string (e.g., "/ralph status")
|
|
35
|
+
name: Command name without slash (e.g., "ralph")
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
- True if handled (no further action)
|
|
39
|
+
- String to process as agent input
|
|
40
|
+
- None if not a ralph command
|
|
41
|
+
"""
|
|
42
|
+
if not name.startswith("ralph"):
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
# Parse subcommand
|
|
46
|
+
parts = command.strip().split(maxsplit=2)
|
|
47
|
+
subcommand = parts[1] if len(parts) > 1 else "help"
|
|
48
|
+
args = parts[2] if len(parts) > 2 else ""
|
|
49
|
+
|
|
50
|
+
# Route to handler
|
|
51
|
+
handlers = {
|
|
52
|
+
"help": _handle_help,
|
|
53
|
+
"status": _handle_status,
|
|
54
|
+
"prd": _handle_prd,
|
|
55
|
+
"convert": _handle_convert,
|
|
56
|
+
"start": _handle_start,
|
|
57
|
+
"stop": _handle_stop,
|
|
58
|
+
"reset": _handle_reset,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
handler = handlers.get(subcommand, _handle_help)
|
|
62
|
+
return handler(args)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _handle_help(args: str) -> bool:
|
|
66
|
+
"""Show Ralph help."""
|
|
67
|
+
help_text = """
|
|
68
|
+
🐺 **Ralph - Autonomous AI Agent Loop**
|
|
69
|
+
|
|
70
|
+
Ralph runs AI coding agents repeatedly until all PRD items are complete.
|
|
71
|
+
Based on Geoffrey Huntley's Ralph pattern: https://ghuntley.com/ralph/
|
|
72
|
+
|
|
73
|
+
**Commands:**
|
|
74
|
+
|
|
75
|
+
`/ralph status` - Show current prd.json status and progress
|
|
76
|
+
`/ralph prd` - Create a new PRD (Product Requirements Document)
|
|
77
|
+
`/ralph convert` - Convert a markdown PRD to prd.json format
|
|
78
|
+
`/ralph start [N]` - Start the autonomous loop (max N iterations, default 10)
|
|
79
|
+
`/ralph stop` - Stop the loop after current iteration
|
|
80
|
+
`/ralph reset` - Archive current run and start fresh
|
|
81
|
+
|
|
82
|
+
**Workflow:**
|
|
83
|
+
|
|
84
|
+
1. `/ralph prd` - Create a detailed PRD with user stories
|
|
85
|
+
2. `/ralph convert` - Convert it to prd.json format
|
|
86
|
+
3. `/ralph start` - Let Ralph autonomously implement each story
|
|
87
|
+
|
|
88
|
+
**Key Files:**
|
|
89
|
+
|
|
90
|
+
- `prd.json` - User stories with completion status
|
|
91
|
+
- `progress.txt` - Learnings and patterns for future iterations
|
|
92
|
+
- `archive/` - Previous runs (auto-archived on branch change)
|
|
93
|
+
"""
|
|
94
|
+
emit_info(help_text)
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _handle_status(args: str) -> bool:
|
|
99
|
+
"""Show PRD status."""
|
|
100
|
+
manager = get_state_manager()
|
|
101
|
+
|
|
102
|
+
if not manager.prd_exists():
|
|
103
|
+
emit_warning("No prd.json found in current directory.")
|
|
104
|
+
emit_info(
|
|
105
|
+
"Use `/ralph prd` to create a PRD, then `/ralph convert` to generate prd.json"
|
|
106
|
+
)
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
status = manager.get_status_summary()
|
|
110
|
+
emit_info(status)
|
|
111
|
+
|
|
112
|
+
# Also show patterns if any
|
|
113
|
+
patterns = manager.read_codebase_patterns()
|
|
114
|
+
if patterns and "<!--" not in patterns: # Skip if just the placeholder
|
|
115
|
+
emit_info("\n**Codebase Patterns:**")
|
|
116
|
+
emit_info(patterns)
|
|
117
|
+
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _handle_prd(args: str) -> MarkdownCommandResult:
|
|
122
|
+
"""Switch to PRD Generator agent."""
|
|
123
|
+
emit_info("🐺 Switching to Ralph PRD Generator...")
|
|
124
|
+
|
|
125
|
+
# Return a MarkdownCommandResult so it's processed as agent input
|
|
126
|
+
return MarkdownCommandResult(
|
|
127
|
+
"/agent ralph-prd-generator\nI want to create a new PRD. Please help me define the requirements."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _handle_convert(args: str) -> MarkdownCommandResult:
|
|
132
|
+
"""Switch to Ralph Converter agent."""
|
|
133
|
+
emit_info("🐺 Switching to Ralph Converter...")
|
|
134
|
+
|
|
135
|
+
# Return a MarkdownCommandResult so it's processed as agent input
|
|
136
|
+
if args:
|
|
137
|
+
return MarkdownCommandResult(
|
|
138
|
+
f"/agent ralph-converter\nPlease convert the PRD in {args} to prd.json format."
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
return MarkdownCommandResult(
|
|
142
|
+
"/agent ralph-converter\nPlease help me convert my PRD to prd.json format."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _handle_start(args: str) -> str | bool:
|
|
147
|
+
"""Start the Ralph autonomous loop."""
|
|
148
|
+
manager = get_state_manager()
|
|
149
|
+
|
|
150
|
+
if not manager.prd_exists():
|
|
151
|
+
emit_error("No prd.json found!")
|
|
152
|
+
emit_info(
|
|
153
|
+
"First create a PRD with `/ralph prd` and convert it with `/ralph convert`"
|
|
154
|
+
)
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
# Check if there's work to do
|
|
158
|
+
prd = manager.read_prd()
|
|
159
|
+
if prd and prd.all_complete():
|
|
160
|
+
emit_success("🎉 All stories are already complete!")
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
# Parse max iterations if provided
|
|
164
|
+
max_iter = 10
|
|
165
|
+
if args:
|
|
166
|
+
try:
|
|
167
|
+
max_iter = int(args)
|
|
168
|
+
except ValueError:
|
|
169
|
+
emit_warning(f"Invalid max_iterations '{args}', using default 10")
|
|
170
|
+
|
|
171
|
+
emit_info(f"🐺 Starting Ralph with max {max_iter} iterations...")
|
|
172
|
+
|
|
173
|
+
# Return a prompt that tells the agent to run the loop
|
|
174
|
+
return f"Call the ralph_run_loop tool with max_iterations={max_iter} to start the autonomous Ralph loop. This will implement all pending stories from prd.json one by one."
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _handle_stop(args: str) -> bool:
|
|
178
|
+
"""Stop the running Ralph loop."""
|
|
179
|
+
emit_info("To stop the Ralph loop, press Ctrl+C or your configured cancel key.")
|
|
180
|
+
emit_info("The loop will halt after the current iteration completes.")
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _handle_reset(args: str) -> bool:
|
|
185
|
+
"""Archive current run and reset."""
|
|
186
|
+
manager = get_state_manager()
|
|
187
|
+
|
|
188
|
+
if not manager.prd_exists():
|
|
189
|
+
emit_info("Nothing to reset - no prd.json found.")
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
# Archive if there's content
|
|
193
|
+
progress = manager.read_progress()
|
|
194
|
+
if progress and len(progress) > 100:
|
|
195
|
+
archive_path = manager.archive_current_run()
|
|
196
|
+
if archive_path:
|
|
197
|
+
emit_success(f"📦 Archived to: {archive_path}")
|
|
198
|
+
|
|
199
|
+
# Reset
|
|
200
|
+
manager.reset_for_new_run()
|
|
201
|
+
|
|
202
|
+
# Delete prd.json
|
|
203
|
+
if manager.prd_file.exists():
|
|
204
|
+
manager.prd_file.unlink()
|
|
205
|
+
emit_info("🗑️ Removed prd.json")
|
|
206
|
+
|
|
207
|
+
emit_success("✨ Reset complete! Ready for a new PRD.")
|
|
208
|
+
return True
|