ralphx 0.3.4__py3-none-any.whl → 0.4.0__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.
Files changed (48) hide show
  1. ralphx/__init__.py +1 -1
  2. ralphx/adapters/base.py +10 -2
  3. ralphx/adapters/claude_cli.py +222 -82
  4. ralphx/api/routes/auth.py +780 -98
  5. ralphx/api/routes/config.py +3 -56
  6. ralphx/api/routes/export_import.py +6 -9
  7. ralphx/api/routes/loops.py +4 -4
  8. ralphx/api/routes/planning.py +882 -19
  9. ralphx/api/routes/resources.py +528 -6
  10. ralphx/api/routes/stream.py +58 -56
  11. ralphx/api/routes/templates.py +2 -2
  12. ralphx/api/routes/workflows.py +258 -47
  13. ralphx/cli.py +4 -1
  14. ralphx/core/auth.py +372 -172
  15. ralphx/core/database.py +588 -164
  16. ralphx/core/executor.py +170 -19
  17. ralphx/core/loop.py +15 -2
  18. ralphx/core/loop_templates.py +29 -3
  19. ralphx/core/planning_iteration_executor.py +633 -0
  20. ralphx/core/planning_service.py +119 -24
  21. ralphx/core/preview.py +9 -25
  22. ralphx/core/project_db.py +864 -121
  23. ralphx/core/project_export.py +1 -5
  24. ralphx/core/project_import.py +14 -29
  25. ralphx/core/resources.py +28 -2
  26. ralphx/core/sample_project.py +1 -5
  27. ralphx/core/templates.py +9 -9
  28. ralphx/core/workflow_executor.py +32 -3
  29. ralphx/core/workflow_export.py +4 -7
  30. ralphx/core/workflow_import.py +3 -27
  31. ralphx/mcp/__init__.py +6 -2
  32. ralphx/mcp/registry.py +3 -3
  33. ralphx/mcp/tools/diagnostics.py +1 -1
  34. ralphx/mcp/tools/monitoring.py +10 -16
  35. ralphx/mcp/tools/workflows.py +115 -33
  36. ralphx/mcp_server.py +6 -2
  37. ralphx/static/assets/index-BuLI7ffn.css +1 -0
  38. ralphx/static/assets/index-DWvlqOTb.js +264 -0
  39. ralphx/static/assets/index-DWvlqOTb.js.map +1 -0
  40. ralphx/static/index.html +2 -2
  41. ralphx/templates/loop_templates/consumer.md +2 -2
  42. {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/METADATA +33 -12
  43. {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/RECORD +45 -44
  44. ralphx/static/assets/index-CcRDyY3b.css +0 -1
  45. ralphx/static/assets/index-CcxfTosc.js +0 -251
  46. ralphx/static/assets/index-CcxfTosc.js.map +0 -1
  47. {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/WHEEL +0 -0
  48. {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/entry_points.txt +0 -0
@@ -64,25 +64,109 @@ This format is machine-parsed. Non-compliance breaks the system.
64
64
  # =============================================================================
65
65
 
66
66
  PLANNING_BEHAVIOR = '''<behavior>
67
- You are a product planning assistant helping users design software products.
68
-
69
- Your role is to:
70
- 1. Understand what the user wants to build
71
- 2. Ask clarifying questions about requirements, users, and scope
72
- 3. Help them think through the architecture and design
73
- 4. Eventually produce a comprehensive design document
74
-
75
- Guidelines:
76
- - Ask focused, specific questions (2-3 at a time max)
77
- - Summarize your understanding periodically
78
- - Be conversational but efficient
79
- - Focus on: problem space, target users, core features, technical constraints
80
- - When the user is ready, offer to generate the design document
81
-
82
- Do NOT:
83
- - Write code unless explicitly asked
84
- - Make assumptions without confirming
85
- - Overwhelm with too many questions at once
67
+ You are an expert product architect and technical consultant helping users design
68
+ software products. You combine deep technical knowledge with strong product sense.
69
+
70
+ ## Your Core Mission
71
+
72
+ Transform vague ideas into comprehensive, implementable design documents through
73
+ collaborative discovery. You're not just a Q&A bot—you're a thinking partner who
74
+ challenges assumptions, identifies blind spots, and brings industry expertise.
75
+
76
+ ## Discovery Phases
77
+
78
+ Work through these phases naturally (you don't need to announce them):
79
+
80
+ ### Phase 1: Problem Space Understanding
81
+ - What problem are we solving? Why does it matter?
82
+ - Who experiences this problem? (specific personas, not generic "users")
83
+ - What do they currently do? What's broken about that?
84
+ - What would success look like for them?
85
+
86
+ ### Phase 2: Solution Requirements
87
+ - Core features (MVP vs nice-to-have)
88
+ - User workflows and key interactions
89
+ - Data the system needs to handle
90
+ - Integration points with other systems
91
+ - Constraints: budget, timeline, team skills, existing infrastructure
92
+
93
+ ### Phase 3: Technical Architecture
94
+ - System components and how they communicate
95
+ - Data model and storage choices
96
+ - API design (if applicable)
97
+ - Authentication and authorization approach
98
+ - Third-party services and dependencies
99
+
100
+ ### Phase 4: Infrastructure & Operations
101
+ - Hosting environment (cloud provider, on-prem, hybrid)
102
+ - Deployment strategy (containers, serverless, VMs)
103
+ - Scaling considerations
104
+ - Monitoring and observability
105
+ - Backup and disaster recovery
106
+
107
+ ### Phase 5: Security & Compliance
108
+ - Data sensitivity and protection requirements
109
+ - Authentication mechanisms
110
+ - Compliance requirements (GDPR, HIPAA, SOC2, etc.)
111
+ - Threat model considerations
112
+
113
+ ## How to Ask Questions
114
+
115
+ Ask 2-4 focused questions at a time. For each question:
116
+ - Explain WHY you're asking (what decision it informs)
117
+ - Offer concrete options when helpful, not just open-ended questions
118
+ - Share your initial thinking or recommendation if you have one
119
+
120
+ Good: "For authentication, are you thinking OAuth (Google/GitHub login) for simplicity,
121
+ or do you need custom username/password? OAuth is faster to implement and more secure,
122
+ but custom auth gives you more control over the user experience."
123
+
124
+ Bad: "How do you want to handle authentication?"
125
+
126
+ ## Using Web Search
127
+
128
+ When you have web search available, use it strategically:
129
+ - Research industry best practices for the specific domain
130
+ - Look up current pricing/capabilities of services you might recommend
131
+ - Find examples of similar products for inspiration
132
+ - Verify technical approaches are current (technologies evolve fast)
133
+ - Look for common pitfalls in this type of application
134
+
135
+ IMPORTANT: Always tell the user when you're searching and summarize what you learned.
136
+ This builds trust and shows you're doing real research, not just making things up.
137
+
138
+ ## Progressive Refinement
139
+
140
+ As you learn more:
141
+ - Periodically summarize your understanding ("Here's what I have so far...")
142
+ - Explicitly call out assumptions you're making
143
+ - Revisit earlier decisions if new information changes things
144
+ - Be willing to push back if something doesn't make sense
145
+
146
+ ## Re-engagement Support
147
+
148
+ If the user is returning to continue or update an existing design doc:
149
+ - Acknowledge what exists and ask what they want to change
150
+ - Don't re-ask questions that are already answered in the doc
151
+ - Focus on the delta—what's new, changed, or needs refinement
152
+
153
+ ## Offering to Generate
154
+
155
+ When you have enough information for a solid design document:
156
+ - Summarize the key decisions that have been made
157
+ - List any important questions that remain unanswered
158
+ - Offer to generate the design doc (the user will click a button)
159
+
160
+ The user can generate the document at ANY time—it doesn't have to be "complete."
161
+ Better to generate something and iterate than to wait forever for perfection.
162
+
163
+ ## What NOT To Do
164
+
165
+ - Don't write code or implementation details (that's for later steps)
166
+ - Don't make major assumptions without confirming
167
+ - Don't be a yes-person—challenge ideas that seem problematic
168
+ - Don't overwhelm with too many questions
169
+ - Don't be generic—tailor advice to their specific situation
86
170
  </behavior>'''
87
171
 
88
172
  ARTIFACT_BEHAVIOR = '''<behavior>
@@ -153,10 +237,17 @@ class PlanningService:
153
237
  "format_rules", "request", "design_doc", "guardrails",
154
238
  ]
155
239
  for tag in dangerous_tags:
156
- # Escape opening tags: <tag> or <tag ...>
240
+ # Escape opening tags with attributes: <tag ...>
241
+ sanitized = re.sub(
242
+ rf'<\s*{tag}\s([^>]*)>',
243
+ rf'&lt;{tag} \1&gt;',
244
+ sanitized,
245
+ flags=re.IGNORECASE,
246
+ )
247
+ # Escape opening tags without attributes: <tag>
157
248
  sanitized = re.sub(
158
- rf'<\s*{tag}(\s|>)',
159
- rf'&lt;{tag}\1',
249
+ rf'<\s*{tag}\s*>',
250
+ rf'&lt;{tag}&gt;',
160
251
  sanitized,
161
252
  flags=re.IGNORECASE,
162
253
  )
@@ -264,6 +355,8 @@ Start your response with <design_doc> immediately.
264
355
  self,
265
356
  messages: list[dict],
266
357
  model: str = "sonnet",
358
+ tools: Optional[list[str]] = None,
359
+ timeout: int = 180,
267
360
  ) -> AsyncIterator[StreamEvent]:
268
361
  """Stream Claude's response to the conversation.
269
362
 
@@ -272,6 +365,8 @@ Start your response with <design_doc> immediately.
272
365
  Args:
273
366
  messages: Conversation history.
274
367
  model: Model to use (sonnet, opus, haiku).
368
+ tools: Optional list of tools to enable (e.g., ['WebSearch', 'WebFetch']).
369
+ timeout: Timeout in seconds (default 180 for web search).
275
370
 
276
371
  Yields:
277
372
  StreamEvent objects as Claude responds.
@@ -286,8 +381,8 @@ Start your response with <design_doc> immediately.
286
381
  async for event in adapter.stream(
287
382
  prompt=prompt,
288
383
  model=model,
289
- tools=None, # No tools for planning chat
290
- timeout=120,
384
+ tools=tools,
385
+ timeout=timeout,
291
386
  ):
292
387
  yield event
293
388
 
ralphx/core/preview.py CHANGED
@@ -337,16 +337,14 @@ class PromptPreviewEngine:
337
337
  title = item.get("title") or ""
338
338
  metadata = item.get("metadata")
339
339
  metadata_json = json.dumps(metadata) if metadata else "{}"
340
- namespace = item.get("namespace", "unknown")
340
+ workflow_id = item.get("workflow_id", "unknown")
341
341
 
342
342
  # Substitution order: most specific first
343
343
  result = template.replace("{{input_item.metadata}}", metadata_json)
344
344
  result = result.replace("{{input_item.content}}", content)
345
345
  result = result.replace("{{input_item.title}}", title)
346
346
  result = result.replace("{{input_item}}", content)
347
- result = result.replace("{{namespace}}", namespace)
348
- # Backward compatibility
349
- result = result.replace("{{source_loop}}", namespace)
347
+ result = result.replace("{{workflow_id}}", workflow_id)
350
348
 
351
349
  return result
352
350
 
@@ -372,28 +370,17 @@ class PromptPreviewEngine:
372
370
  return False
373
371
  return bool(self.config.item_types.input.source)
374
372
 
375
- def _get_source_namespace(self) -> Optional[str]:
376
- """Get the source namespace for consumer loops.
377
-
378
- For consumer loops, this is the source loop name used as the namespace.
379
- """
380
- if not self._is_consumer_loop():
381
- return None
382
- return self.config.item_types.input.source
383
-
384
373
  def _get_sample_item(self) -> Optional[dict]:
385
- """Get a sample item from the source namespace for preview.
374
+ """Get a sample item for preview (for consumer loops).
386
375
 
387
376
  Returns:
388
377
  Sample item dict or None.
389
378
  """
390
- namespace = self._get_source_namespace()
391
- if not namespace:
379
+ if not self._is_consumer_loop():
392
380
  return None
393
381
 
394
- # Get first completed item from namespace
382
+ # Get first completed item
395
383
  items, _ = self.db.list_work_items(
396
- namespace=namespace,
397
384
  status="completed",
398
385
  limit=1,
399
386
  )
@@ -403,7 +390,6 @@ class PromptPreviewEngine:
403
390
 
404
391
  # Fallback: get any pending item
405
392
  items, _ = self.db.list_work_items(
406
- namespace=namespace,
407
393
  status="pending",
408
394
  limit=1,
409
395
  )
@@ -414,8 +400,8 @@ class PromptPreviewEngine:
414
400
  # Return a placeholder
415
401
  return {
416
402
  "id": "sample-001",
417
- "content": "[Sample item content - no items in namespace]",
418
- "namespace": namespace,
403
+ "content": "[Sample item content - no items available]",
404
+ "source": source_name,
419
405
  }
420
406
 
421
407
  def _explain_strategy(self) -> str:
@@ -472,15 +458,13 @@ class PromptPreviewEngine:
472
458
  variables["{{input_item.content}}"] = sample_item.get("content", "")[:50] + "..."
473
459
  variables["{{input_item.title}}"] = sample_item.get("title", "")
474
460
  variables["{{input_item.metadata}}"] = "{...}"
475
- variables["{{namespace}}"] = sample_item.get("namespace", "")
476
- variables["{{source_loop}}"] = sample_item.get("namespace", "") # Backward compat
461
+ variables["{{workflow_id}}"] = sample_item.get("workflow_id", "")
477
462
  else:
478
463
  variables["{{input_item}}"] = "[Claimed item content]"
479
464
  variables["{{input_item.content}}"] = "[Claimed item content]"
480
465
  variables["{{input_item.title}}"] = "[Claimed item title]"
481
466
  variables["{{input_item.metadata}}"] = "[Claimed item metadata as JSON]"
482
- variables["{{namespace}}"] = self._get_source_namespace() or ""
483
- variables["{{source_loop}}"] = self._get_source_namespace() or "" # Backward compat
467
+ variables["{{workflow_id}}"] = "[Workflow ID]"
484
468
 
485
469
  # Design doc variable (if configured)
486
470
  if self.config.context and self.config.context.design_doc: