code-puppy 0.0.380__py3-none-any.whl → 0.0.381__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/callbacks.py +70 -0
- code_puppy/cli_runner.py +93 -1
- code_puppy/command_line/core_commands.py +61 -0
- code_puppy/command_line/motd.py +26 -3
- code_puppy/command_line/wiggum_state.py +78 -0
- code_puppy/model_factory.py +43 -0
- code_puppy/tools/browser/browser_manager.py +66 -5
- {code_puppy-0.0.380.dist-info → code_puppy-0.0.381.dist-info}/METADATA +1 -1
- {code_puppy-0.0.380.dist-info → code_puppy-0.0.381.dist-info}/RECORD +14 -21
- code_puppy/plugins/ralph/__init__.py +0 -13
- code_puppy/plugins/ralph/agents.py +0 -433
- code_puppy/plugins/ralph/commands.py +0 -208
- code_puppy/plugins/ralph/loop_controller.py +0 -289
- code_puppy/plugins/ralph/models.py +0 -125
- code_puppy/plugins/ralph/register_callbacks.py +0 -140
- code_puppy/plugins/ralph/state_manager.py +0 -322
- code_puppy/plugins/ralph/tools.py +0 -451
- {code_puppy-0.0.380.data → code_puppy-0.0.381.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.380.data → code_puppy-0.0.381.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.380.dist-info → code_puppy-0.0.381.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.380.dist-info → code_puppy-0.0.381.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.380.dist-info → code_puppy-0.0.381.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
"""Ralph plugin tools - registered via the register_tools callback."""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from typing import List
|
|
6
|
-
|
|
7
|
-
from pydantic_ai import RunContext
|
|
8
|
-
|
|
9
|
-
from .models import ProgressEntry
|
|
10
|
-
from .state_manager import get_state_manager
|
|
11
|
-
|
|
12
|
-
# ============================================================================
|
|
13
|
-
# TOOL OUTPUT TYPES
|
|
14
|
-
# ============================================================================
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@dataclass
|
|
18
|
-
class RalphStoryOutput:
|
|
19
|
-
"""Output for getting the current story."""
|
|
20
|
-
|
|
21
|
-
story_id: str | None
|
|
22
|
-
title: str | None
|
|
23
|
-
description: str | None
|
|
24
|
-
acceptance_criteria: List[str]
|
|
25
|
-
priority: int | None
|
|
26
|
-
requires_ui_verification: bool
|
|
27
|
-
all_complete: bool
|
|
28
|
-
error: str | None = None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@dataclass
|
|
32
|
-
class RalphStatusOutput:
|
|
33
|
-
"""Output for status checks."""
|
|
34
|
-
|
|
35
|
-
success: bool
|
|
36
|
-
message: str
|
|
37
|
-
progress_summary: str | None = None
|
|
38
|
-
stories_remaining: int = 0
|
|
39
|
-
all_complete: bool = False
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@dataclass
|
|
43
|
-
class RalphPRDOutput:
|
|
44
|
-
"""Output for reading the full PRD."""
|
|
45
|
-
|
|
46
|
-
success: bool
|
|
47
|
-
project: str | None = None
|
|
48
|
-
branch_name: str | None = None
|
|
49
|
-
description: str | None = None
|
|
50
|
-
stories: List[dict] | None = None
|
|
51
|
-
progress_summary: str | None = None
|
|
52
|
-
error: str | None = None
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@dataclass
|
|
56
|
-
class RalphPatternsOutput:
|
|
57
|
-
"""Output for reading codebase patterns."""
|
|
58
|
-
|
|
59
|
-
patterns: str
|
|
60
|
-
has_patterns: bool
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# ============================================================================
|
|
64
|
-
# TOOL REGISTRATION FUNCTIONS
|
|
65
|
-
# ============================================================================
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def register_ralph_get_current_story(agent) -> None:
|
|
69
|
-
"""Register the tool to get the current story to work on."""
|
|
70
|
-
|
|
71
|
-
@agent.tool
|
|
72
|
-
def ralph_get_current_story(context: RunContext) -> RalphStoryOutput:
|
|
73
|
-
"""Get the next user story to work on from prd.json.
|
|
74
|
-
|
|
75
|
-
This tool reads the prd.json file and returns the highest-priority
|
|
76
|
-
story that hasn't been completed yet (passes=false).
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
79
|
-
RalphStoryOutput containing:
|
|
80
|
-
- story_id: The story ID (e.g., "US-001")
|
|
81
|
-
- title: Story title
|
|
82
|
-
- description: Full story description
|
|
83
|
-
- acceptance_criteria: List of criteria to satisfy
|
|
84
|
-
- priority: Story priority (lower = higher priority)
|
|
85
|
-
- requires_ui_verification: True if story needs browser testing
|
|
86
|
-
- all_complete: True if ALL stories are done
|
|
87
|
-
- error: Error message if something went wrong
|
|
88
|
-
"""
|
|
89
|
-
manager = get_state_manager()
|
|
90
|
-
|
|
91
|
-
if not manager.prd_exists():
|
|
92
|
-
return RalphStoryOutput(
|
|
93
|
-
story_id=None,
|
|
94
|
-
title=None,
|
|
95
|
-
description=None,
|
|
96
|
-
acceptance_criteria=[],
|
|
97
|
-
priority=None,
|
|
98
|
-
requires_ui_verification=False,
|
|
99
|
-
all_complete=False,
|
|
100
|
-
error="No prd.json found in current directory. Create one first with /ralph prd",
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if manager.all_stories_complete():
|
|
104
|
-
return RalphStoryOutput(
|
|
105
|
-
story_id=None,
|
|
106
|
-
title=None,
|
|
107
|
-
description=None,
|
|
108
|
-
acceptance_criteria=[],
|
|
109
|
-
priority=None,
|
|
110
|
-
requires_ui_verification=False,
|
|
111
|
-
all_complete=True,
|
|
112
|
-
error=None,
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
story = manager.get_next_story()
|
|
116
|
-
if story is None:
|
|
117
|
-
return RalphStoryOutput(
|
|
118
|
-
story_id=None,
|
|
119
|
-
title=None,
|
|
120
|
-
description=None,
|
|
121
|
-
acceptance_criteria=[],
|
|
122
|
-
priority=None,
|
|
123
|
-
requires_ui_verification=False,
|
|
124
|
-
all_complete=True,
|
|
125
|
-
error=None,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
return RalphStoryOutput(
|
|
129
|
-
story_id=story.id,
|
|
130
|
-
title=story.title,
|
|
131
|
-
description=story.description,
|
|
132
|
-
acceptance_criteria=story.acceptance_criteria,
|
|
133
|
-
priority=story.priority,
|
|
134
|
-
requires_ui_verification=story.has_ui_verification(),
|
|
135
|
-
all_complete=False,
|
|
136
|
-
error=None,
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def register_ralph_mark_story_complete(agent) -> None:
|
|
141
|
-
"""Register the tool to mark a story as complete."""
|
|
142
|
-
|
|
143
|
-
@agent.tool
|
|
144
|
-
def ralph_mark_story_complete(
|
|
145
|
-
context: RunContext,
|
|
146
|
-
story_id: str,
|
|
147
|
-
notes: str | None = None,
|
|
148
|
-
) -> RalphStatusOutput:
|
|
149
|
-
"""Mark a user story as complete (passes=true) in prd.json.
|
|
150
|
-
|
|
151
|
-
Call this AFTER you have:
|
|
152
|
-
1. Implemented all the acceptance criteria
|
|
153
|
-
2. Verified the code compiles/typechecks
|
|
154
|
-
3. Run any required tests
|
|
155
|
-
4. Committed the changes
|
|
156
|
-
|
|
157
|
-
For UI stories, also ensure browser verification passed.
|
|
158
|
-
|
|
159
|
-
Args:
|
|
160
|
-
story_id: The story ID to mark complete (e.g., "US-001")
|
|
161
|
-
notes: Optional notes about the implementation
|
|
162
|
-
|
|
163
|
-
Returns:
|
|
164
|
-
RalphStatusOutput with success status and message
|
|
165
|
-
"""
|
|
166
|
-
manager = get_state_manager()
|
|
167
|
-
|
|
168
|
-
success, message = manager.mark_story_complete(story_id, notes or "")
|
|
169
|
-
|
|
170
|
-
prd = manager.read_prd()
|
|
171
|
-
remaining = 0
|
|
172
|
-
all_complete = False
|
|
173
|
-
progress = None
|
|
174
|
-
|
|
175
|
-
if prd:
|
|
176
|
-
remaining = sum(1 for s in prd.user_stories if not s.passes)
|
|
177
|
-
all_complete = prd.all_complete()
|
|
178
|
-
progress = prd.get_progress_summary()
|
|
179
|
-
|
|
180
|
-
return RalphStatusOutput(
|
|
181
|
-
success=success,
|
|
182
|
-
message=message,
|
|
183
|
-
progress_summary=progress,
|
|
184
|
-
stories_remaining=remaining,
|
|
185
|
-
all_complete=all_complete,
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def register_ralph_log_progress(agent) -> None:
|
|
190
|
-
"""Register the tool to log progress to progress.txt."""
|
|
191
|
-
|
|
192
|
-
@agent.tool
|
|
193
|
-
def ralph_log_progress(
|
|
194
|
-
context: RunContext,
|
|
195
|
-
story_id: str,
|
|
196
|
-
summary: str,
|
|
197
|
-
files_changed: List[str] | None = None,
|
|
198
|
-
learnings: List[str] | None = None,
|
|
199
|
-
) -> RalphStatusOutput:
|
|
200
|
-
"""Append a progress entry to progress.txt.
|
|
201
|
-
|
|
202
|
-
Call this after completing a story to record:
|
|
203
|
-
- What was implemented
|
|
204
|
-
- Which files were changed
|
|
205
|
-
- Any learnings for future iterations
|
|
206
|
-
|
|
207
|
-
The learnings are especially important for helping future iterations
|
|
208
|
-
understand patterns and avoid mistakes.
|
|
209
|
-
|
|
210
|
-
Args:
|
|
211
|
-
story_id: The story ID that was completed
|
|
212
|
-
summary: Brief summary of what was implemented
|
|
213
|
-
files_changed: List of files that were modified
|
|
214
|
-
learnings: List of learnings/patterns discovered
|
|
215
|
-
|
|
216
|
-
Returns:
|
|
217
|
-
RalphStatusOutput with success status
|
|
218
|
-
"""
|
|
219
|
-
manager = get_state_manager()
|
|
220
|
-
|
|
221
|
-
entry = ProgressEntry(
|
|
222
|
-
timestamp=datetime.now(),
|
|
223
|
-
story_id=story_id,
|
|
224
|
-
summary=summary,
|
|
225
|
-
files_changed=files_changed or [],
|
|
226
|
-
learnings=learnings or [],
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
success = manager.append_progress(entry)
|
|
230
|
-
|
|
231
|
-
return RalphStatusOutput(
|
|
232
|
-
success=success,
|
|
233
|
-
message="Progress logged successfully"
|
|
234
|
-
if success
|
|
235
|
-
else "Failed to log progress",
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def register_ralph_check_all_complete(agent) -> None:
|
|
240
|
-
"""Register the tool to check if all stories are complete."""
|
|
241
|
-
|
|
242
|
-
@agent.tool
|
|
243
|
-
def ralph_check_all_complete(context: RunContext) -> RalphStatusOutput:
|
|
244
|
-
"""Check if all user stories in prd.json are complete.
|
|
245
|
-
|
|
246
|
-
Use this to determine if the Ralph loop should exit.
|
|
247
|
-
When all stories are complete, you should output:
|
|
248
|
-
<promise>COMPLETE</promise>
|
|
249
|
-
|
|
250
|
-
Returns:
|
|
251
|
-
RalphStatusOutput with all_complete flag
|
|
252
|
-
"""
|
|
253
|
-
manager = get_state_manager()
|
|
254
|
-
|
|
255
|
-
if not manager.prd_exists():
|
|
256
|
-
return RalphStatusOutput(
|
|
257
|
-
success=False,
|
|
258
|
-
message="No prd.json found",
|
|
259
|
-
all_complete=False,
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
prd = manager.read_prd()
|
|
263
|
-
if prd is None:
|
|
264
|
-
return RalphStatusOutput(
|
|
265
|
-
success=False,
|
|
266
|
-
message="Failed to read prd.json",
|
|
267
|
-
all_complete=False,
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
all_complete = prd.all_complete()
|
|
271
|
-
remaining = sum(1 for s in prd.user_stories if not s.passes)
|
|
272
|
-
|
|
273
|
-
return RalphStatusOutput(
|
|
274
|
-
success=True,
|
|
275
|
-
message="All stories complete!"
|
|
276
|
-
if all_complete
|
|
277
|
-
else f"{remaining} stories remaining",
|
|
278
|
-
progress_summary=prd.get_progress_summary(),
|
|
279
|
-
stories_remaining=remaining,
|
|
280
|
-
all_complete=all_complete,
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
def register_ralph_read_prd(agent) -> None:
|
|
285
|
-
"""Register the tool to read the full PRD."""
|
|
286
|
-
|
|
287
|
-
@agent.tool
|
|
288
|
-
def ralph_read_prd(context: RunContext) -> RalphPRDOutput:
|
|
289
|
-
"""Read the full prd.json file and return its contents.
|
|
290
|
-
|
|
291
|
-
Use this to understand the overall project and see all stories.
|
|
292
|
-
|
|
293
|
-
Returns:
|
|
294
|
-
RalphPRDOutput with project details and all stories
|
|
295
|
-
"""
|
|
296
|
-
manager = get_state_manager()
|
|
297
|
-
|
|
298
|
-
if not manager.prd_exists():
|
|
299
|
-
return RalphPRDOutput(
|
|
300
|
-
success=False,
|
|
301
|
-
error="No prd.json found in current directory",
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
prd = manager.read_prd()
|
|
305
|
-
if prd is None:
|
|
306
|
-
return RalphPRDOutput(
|
|
307
|
-
success=False,
|
|
308
|
-
error="Failed to parse prd.json",
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
return RalphPRDOutput(
|
|
312
|
-
success=True,
|
|
313
|
-
project=prd.project,
|
|
314
|
-
branch_name=prd.branch_name,
|
|
315
|
-
description=prd.description,
|
|
316
|
-
stories=[s.to_dict() for s in prd.user_stories],
|
|
317
|
-
progress_summary=prd.get_progress_summary(),
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
def register_ralph_read_patterns(agent) -> None:
|
|
322
|
-
"""Register the tool to read codebase patterns from progress.txt."""
|
|
323
|
-
|
|
324
|
-
@agent.tool
|
|
325
|
-
def ralph_read_patterns(context: RunContext) -> RalphPatternsOutput:
|
|
326
|
-
"""Read the Codebase Patterns section from progress.txt.
|
|
327
|
-
|
|
328
|
-
These patterns were discovered by previous iterations and contain
|
|
329
|
-
important context about the codebase. Read this BEFORE starting
|
|
330
|
-
work on a new story.
|
|
331
|
-
|
|
332
|
-
Returns:
|
|
333
|
-
RalphPatternsOutput with patterns text
|
|
334
|
-
"""
|
|
335
|
-
manager = get_state_manager()
|
|
336
|
-
patterns = manager.read_codebase_patterns()
|
|
337
|
-
|
|
338
|
-
return RalphPatternsOutput(
|
|
339
|
-
patterns=patterns if patterns else "No patterns recorded yet.",
|
|
340
|
-
has_patterns=bool(patterns),
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
def register_ralph_add_pattern(agent) -> None:
|
|
345
|
-
"""Register the tool to add a codebase pattern."""
|
|
346
|
-
|
|
347
|
-
@agent.tool
|
|
348
|
-
def ralph_add_pattern(context: RunContext, pattern: str) -> RalphStatusOutput:
|
|
349
|
-
"""Add a reusable pattern to the Codebase Patterns section.
|
|
350
|
-
|
|
351
|
-
Only add patterns that are GENERAL and REUSABLE, not story-specific.
|
|
352
|
-
|
|
353
|
-
Good examples:
|
|
354
|
-
- "Use `sql<number>` template for aggregations"
|
|
355
|
-
- "Always use `IF NOT EXISTS` for migrations"
|
|
356
|
-
- "Export types from actions.ts for UI components"
|
|
357
|
-
|
|
358
|
-
Bad examples (too specific):
|
|
359
|
-
- "Added login button to header" (story-specific)
|
|
360
|
-
- "Fixed bug in user.ts" (not a pattern)
|
|
361
|
-
|
|
362
|
-
Args:
|
|
363
|
-
pattern: The pattern to record
|
|
364
|
-
|
|
365
|
-
Returns:
|
|
366
|
-
RalphStatusOutput with success status
|
|
367
|
-
"""
|
|
368
|
-
manager = get_state_manager()
|
|
369
|
-
success = manager.add_codebase_pattern(pattern)
|
|
370
|
-
|
|
371
|
-
return RalphStatusOutput(
|
|
372
|
-
success=success,
|
|
373
|
-
message="Pattern added" if success else "Failed to add pattern",
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
def register_ralph_run_loop(agent) -> None:
|
|
378
|
-
"""Register the tool to run the Ralph autonomous loop."""
|
|
379
|
-
|
|
380
|
-
@agent.tool
|
|
381
|
-
async def ralph_run_loop(
|
|
382
|
-
context: RunContext,
|
|
383
|
-
max_iterations: int = 10,
|
|
384
|
-
) -> RalphStatusOutput:
|
|
385
|
-
"""Start the Ralph autonomous loop to implement all pending stories.
|
|
386
|
-
|
|
387
|
-
This runs the full Ralph loop, executing one story per iteration
|
|
388
|
-
until all stories are complete or max_iterations is reached.
|
|
389
|
-
|
|
390
|
-
Each iteration:
|
|
391
|
-
1. Gets the next pending story from prd.json
|
|
392
|
-
2. Invokes the ralph-orchestrator agent with a fresh session
|
|
393
|
-
3. The orchestrator implements and commits the story
|
|
394
|
-
4. Checks if all stories are complete
|
|
395
|
-
|
|
396
|
-
Args:
|
|
397
|
-
max_iterations: Maximum number of iterations (default 10)
|
|
398
|
-
|
|
399
|
-
Returns:
|
|
400
|
-
RalphStatusOutput with success status and final progress
|
|
401
|
-
"""
|
|
402
|
-
from .loop_controller import run_ralph_loop
|
|
403
|
-
|
|
404
|
-
try:
|
|
405
|
-
result = await run_ralph_loop(max_iterations=max_iterations)
|
|
406
|
-
|
|
407
|
-
return RalphStatusOutput(
|
|
408
|
-
success=result.get("success", False),
|
|
409
|
-
message=result.get("message", "Loop completed"),
|
|
410
|
-
progress_summary=result.get("message"),
|
|
411
|
-
stories_remaining=0 if result.get("all_complete") else -1,
|
|
412
|
-
all_complete=result.get("all_complete", False),
|
|
413
|
-
)
|
|
414
|
-
except Exception as e:
|
|
415
|
-
return RalphStatusOutput(
|
|
416
|
-
success=False,
|
|
417
|
-
message=f"Loop failed: {str(e)}",
|
|
418
|
-
all_complete=False,
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
# ============================================================================
|
|
423
|
-
# TOOL PROVIDER FOR CALLBACK
|
|
424
|
-
# ============================================================================
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
def get_ralph_tools() -> List[dict]:
|
|
428
|
-
"""Get all Ralph tools for registration via the register_tools callback.
|
|
429
|
-
|
|
430
|
-
Returns:
|
|
431
|
-
List of tool definitions with name and register_func.
|
|
432
|
-
"""
|
|
433
|
-
return [
|
|
434
|
-
{
|
|
435
|
-
"name": "ralph_get_current_story",
|
|
436
|
-
"register_func": register_ralph_get_current_story,
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
"name": "ralph_mark_story_complete",
|
|
440
|
-
"register_func": register_ralph_mark_story_complete,
|
|
441
|
-
},
|
|
442
|
-
{"name": "ralph_log_progress", "register_func": register_ralph_log_progress},
|
|
443
|
-
{
|
|
444
|
-
"name": "ralph_check_all_complete",
|
|
445
|
-
"register_func": register_ralph_check_all_complete,
|
|
446
|
-
},
|
|
447
|
-
{"name": "ralph_read_prd", "register_func": register_ralph_read_prd},
|
|
448
|
-
{"name": "ralph_read_patterns", "register_func": register_ralph_read_patterns},
|
|
449
|
-
{"name": "ralph_add_pattern", "register_func": register_ralph_add_pattern},
|
|
450
|
-
{"name": "ralph_run_loop", "register_func": register_ralph_run_loop},
|
|
451
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|