code-puppy 0.0.380__py3-none-any.whl → 0.0.382__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.
@@ -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
- ]