wiggum-cli 0.1.0

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 (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +341 -0
  3. package/bin/ralph.js +8 -0
  4. package/dist/ai/enhancer.d.ts +100 -0
  5. package/dist/ai/enhancer.d.ts.map +1 -0
  6. package/dist/ai/enhancer.js +233 -0
  7. package/dist/ai/enhancer.js.map +1 -0
  8. package/dist/ai/index.d.ts +8 -0
  9. package/dist/ai/index.d.ts.map +1 -0
  10. package/dist/ai/index.js +11 -0
  11. package/dist/ai/index.js.map +1 -0
  12. package/dist/ai/prompts.d.ts +26 -0
  13. package/dist/ai/prompts.d.ts.map +1 -0
  14. package/dist/ai/prompts.js +201 -0
  15. package/dist/ai/prompts.js.map +1 -0
  16. package/dist/ai/providers.d.ts +35 -0
  17. package/dist/ai/providers.d.ts.map +1 -0
  18. package/dist/ai/providers.js +104 -0
  19. package/dist/ai/providers.js.map +1 -0
  20. package/dist/cli.d.ts +6 -0
  21. package/dist/cli.d.ts.map +1 -0
  22. package/dist/cli.js +196 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/commands/init.d.ts +16 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +124 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/monitor.d.ts +17 -0
  29. package/dist/commands/monitor.d.ts.map +1 -0
  30. package/dist/commands/monitor.js +342 -0
  31. package/dist/commands/monitor.js.map +1 -0
  32. package/dist/commands/new.d.ts +19 -0
  33. package/dist/commands/new.d.ts.map +1 -0
  34. package/dist/commands/new.js +272 -0
  35. package/dist/commands/new.js.map +1 -0
  36. package/dist/commands/run.d.ts +16 -0
  37. package/dist/commands/run.d.ts.map +1 -0
  38. package/dist/commands/run.js +175 -0
  39. package/dist/commands/run.js.map +1 -0
  40. package/dist/generator/config.d.ts +59 -0
  41. package/dist/generator/config.d.ts.map +1 -0
  42. package/dist/generator/config.js +68 -0
  43. package/dist/generator/config.js.map +1 -0
  44. package/dist/generator/index.d.ts +64 -0
  45. package/dist/generator/index.d.ts.map +1 -0
  46. package/dist/generator/index.js +147 -0
  47. package/dist/generator/index.js.map +1 -0
  48. package/dist/generator/templates.d.ts +70 -0
  49. package/dist/generator/templates.d.ts.map +1 -0
  50. package/dist/generator/templates.js +296 -0
  51. package/dist/generator/templates.js.map +1 -0
  52. package/dist/generator/writer.d.ts +93 -0
  53. package/dist/generator/writer.d.ts.map +1 -0
  54. package/dist/generator/writer.js +213 -0
  55. package/dist/generator/writer.js.map +1 -0
  56. package/dist/index.d.ts +12 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +17 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/scanner/detectors/core/framework.d.ts +11 -0
  61. package/dist/scanner/detectors/core/framework.d.ts.map +1 -0
  62. package/dist/scanner/detectors/core/framework.js +275 -0
  63. package/dist/scanner/detectors/core/framework.js.map +1 -0
  64. package/dist/scanner/detectors/core/packageManager.d.ts +11 -0
  65. package/dist/scanner/detectors/core/packageManager.d.ts.map +1 -0
  66. package/dist/scanner/detectors/core/packageManager.js +74 -0
  67. package/dist/scanner/detectors/core/packageManager.js.map +1 -0
  68. package/dist/scanner/detectors/core/styling.d.ts +12 -0
  69. package/dist/scanner/detectors/core/styling.d.ts.map +1 -0
  70. package/dist/scanner/detectors/core/styling.js +230 -0
  71. package/dist/scanner/detectors/core/styling.js.map +1 -0
  72. package/dist/scanner/detectors/core/testing.d.ts +12 -0
  73. package/dist/scanner/detectors/core/testing.d.ts.map +1 -0
  74. package/dist/scanner/detectors/core/testing.js +190 -0
  75. package/dist/scanner/detectors/core/testing.js.map +1 -0
  76. package/dist/scanner/detectors/data/api.d.ts +12 -0
  77. package/dist/scanner/detectors/data/api.d.ts.map +1 -0
  78. package/dist/scanner/detectors/data/api.js +261 -0
  79. package/dist/scanner/detectors/data/api.js.map +1 -0
  80. package/dist/scanner/detectors/data/database.d.ts +12 -0
  81. package/dist/scanner/detectors/data/database.d.ts.map +1 -0
  82. package/dist/scanner/detectors/data/database.js +213 -0
  83. package/dist/scanner/detectors/data/database.js.map +1 -0
  84. package/dist/scanner/detectors/data/orm.d.ts +12 -0
  85. package/dist/scanner/detectors/data/orm.d.ts.map +1 -0
  86. package/dist/scanner/detectors/data/orm.js +160 -0
  87. package/dist/scanner/detectors/data/orm.js.map +1 -0
  88. package/dist/scanner/detectors/frontend/formHandling.d.ts +12 -0
  89. package/dist/scanner/detectors/frontend/formHandling.d.ts.map +1 -0
  90. package/dist/scanner/detectors/frontend/formHandling.js +211 -0
  91. package/dist/scanner/detectors/frontend/formHandling.js.map +1 -0
  92. package/dist/scanner/detectors/frontend/stateManagement.d.ts +12 -0
  93. package/dist/scanner/detectors/frontend/stateManagement.d.ts.map +1 -0
  94. package/dist/scanner/detectors/frontend/stateManagement.js +221 -0
  95. package/dist/scanner/detectors/frontend/stateManagement.js.map +1 -0
  96. package/dist/scanner/detectors/frontend/uiComponents.d.ts +12 -0
  97. package/dist/scanner/detectors/frontend/uiComponents.d.ts.map +1 -0
  98. package/dist/scanner/detectors/frontend/uiComponents.js +285 -0
  99. package/dist/scanner/detectors/frontend/uiComponents.js.map +1 -0
  100. package/dist/scanner/detectors/infra/deployment.d.ts +12 -0
  101. package/dist/scanner/detectors/infra/deployment.d.ts.map +1 -0
  102. package/dist/scanner/detectors/infra/deployment.js +301 -0
  103. package/dist/scanner/detectors/infra/deployment.js.map +1 -0
  104. package/dist/scanner/detectors/infra/monorepo.d.ts +12 -0
  105. package/dist/scanner/detectors/infra/monorepo.d.ts.map +1 -0
  106. package/dist/scanner/detectors/infra/monorepo.js +219 -0
  107. package/dist/scanner/detectors/infra/monorepo.js.map +1 -0
  108. package/dist/scanner/detectors/mcp/mcpProject.d.ts +12 -0
  109. package/dist/scanner/detectors/mcp/mcpProject.d.ts.map +1 -0
  110. package/dist/scanner/detectors/mcp/mcpProject.js +154 -0
  111. package/dist/scanner/detectors/mcp/mcpProject.js.map +1 -0
  112. package/dist/scanner/detectors/mcp/mcpServers.d.ts +17 -0
  113. package/dist/scanner/detectors/mcp/mcpServers.d.ts.map +1 -0
  114. package/dist/scanner/detectors/mcp/mcpServers.js +193 -0
  115. package/dist/scanner/detectors/mcp/mcpServers.js.map +1 -0
  116. package/dist/scanner/detectors/services/analytics.d.ts +12 -0
  117. package/dist/scanner/detectors/services/analytics.d.ts.map +1 -0
  118. package/dist/scanner/detectors/services/analytics.js +236 -0
  119. package/dist/scanner/detectors/services/analytics.js.map +1 -0
  120. package/dist/scanner/detectors/services/auth.d.ts +12 -0
  121. package/dist/scanner/detectors/services/auth.d.ts.map +1 -0
  122. package/dist/scanner/detectors/services/auth.js +217 -0
  123. package/dist/scanner/detectors/services/auth.js.map +1 -0
  124. package/dist/scanner/detectors/services/email.d.ts +12 -0
  125. package/dist/scanner/detectors/services/email.d.ts.map +1 -0
  126. package/dist/scanner/detectors/services/email.js +211 -0
  127. package/dist/scanner/detectors/services/email.js.map +1 -0
  128. package/dist/scanner/detectors/services/payments.d.ts +12 -0
  129. package/dist/scanner/detectors/services/payments.d.ts.map +1 -0
  130. package/dist/scanner/detectors/services/payments.js +185 -0
  131. package/dist/scanner/detectors/services/payments.js.map +1 -0
  132. package/dist/scanner/detectors/utils.d.ts +160 -0
  133. package/dist/scanner/detectors/utils.d.ts.map +1 -0
  134. package/dist/scanner/detectors/utils.js +222 -0
  135. package/dist/scanner/detectors/utils.js.map +1 -0
  136. package/dist/scanner/index.d.ts +42 -0
  137. package/dist/scanner/index.d.ts.map +1 -0
  138. package/dist/scanner/index.js +282 -0
  139. package/dist/scanner/index.js.map +1 -0
  140. package/dist/scanner/registry.d.ts +43 -0
  141. package/dist/scanner/registry.d.ts.map +1 -0
  142. package/dist/scanner/registry.js +243 -0
  143. package/dist/scanner/registry.js.map +1 -0
  144. package/dist/scanner/types.d.ts +112 -0
  145. package/dist/scanner/types.d.ts.map +1 -0
  146. package/dist/scanner/types.js +6 -0
  147. package/dist/scanner/types.js.map +1 -0
  148. package/dist/templates/config/ralph.config.js.tmpl +38 -0
  149. package/dist/templates/guides/AGENTS.md.tmpl +100 -0
  150. package/dist/templates/guides/FRONTEND.md.tmpl +523 -0
  151. package/dist/templates/guides/PERFORMANCE.md.tmpl +264 -0
  152. package/dist/templates/guides/SECURITY.md.tmpl +100 -0
  153. package/dist/templates/prompts/PROMPT.md.tmpl +77 -0
  154. package/dist/templates/prompts/PROMPT_e2e.md.tmpl +234 -0
  155. package/dist/templates/prompts/PROMPT_feature.md.tmpl +83 -0
  156. package/dist/templates/prompts/PROMPT_review.md.tmpl +167 -0
  157. package/dist/templates/prompts/PROMPT_verify.md.tmpl +72 -0
  158. package/dist/templates/root/.gitignore.tmpl +5 -0
  159. package/dist/templates/root/LEARNINGS.md.tmpl +24 -0
  160. package/dist/templates/root/README.md.tmpl +61 -0
  161. package/dist/templates/scripts/feature-loop.sh.tmpl +267 -0
  162. package/dist/templates/scripts/loop.sh.tmpl +59 -0
  163. package/dist/templates/scripts/ralph-monitor.sh.tmpl +244 -0
  164. package/dist/templates/specs/README.md.tmpl +57 -0
  165. package/dist/templates/specs/_example.md.tmpl +71 -0
  166. package/dist/utils/config.d.ts +95 -0
  167. package/dist/utils/config.d.ts.map +1 -0
  168. package/dist/utils/config.js +148 -0
  169. package/dist/utils/config.js.map +1 -0
  170. package/dist/utils/header.d.ts +5 -0
  171. package/dist/utils/header.d.ts.map +1 -0
  172. package/dist/utils/header.js +15 -0
  173. package/dist/utils/header.js.map +1 -0
  174. package/dist/utils/logger.d.ts +11 -0
  175. package/dist/utils/logger.d.ts.map +1 -0
  176. package/dist/utils/logger.js +24 -0
  177. package/dist/utils/logger.js.map +1 -0
  178. package/package.json +44 -0
  179. package/src/ai/enhancer.ts +350 -0
  180. package/src/ai/index.ts +38 -0
  181. package/src/ai/prompts.ts +217 -0
  182. package/src/ai/providers.ts +136 -0
  183. package/src/cli.ts +255 -0
  184. package/src/commands/init.ts +149 -0
  185. package/src/commands/monitor.ts +412 -0
  186. package/src/commands/new.ts +312 -0
  187. package/src/commands/run.ts +214 -0
  188. package/src/generator/config.ts +116 -0
  189. package/src/generator/index.ts +227 -0
  190. package/src/generator/templates.ts +412 -0
  191. package/src/generator/writer.ts +293 -0
  192. package/src/index.ts +41 -0
  193. package/src/scanner/detectors/core/framework.ts +332 -0
  194. package/src/scanner/detectors/core/packageManager.ts +91 -0
  195. package/src/scanner/detectors/core/styling.ts +261 -0
  196. package/src/scanner/detectors/core/testing.ts +221 -0
  197. package/src/scanner/detectors/data/api.ts +303 -0
  198. package/src/scanner/detectors/data/database.ts +245 -0
  199. package/src/scanner/detectors/data/orm.ts +180 -0
  200. package/src/scanner/detectors/frontend/formHandling.ts +244 -0
  201. package/src/scanner/detectors/frontend/stateManagement.ts +261 -0
  202. package/src/scanner/detectors/frontend/uiComponents.ts +328 -0
  203. package/src/scanner/detectors/infra/deployment.ts +343 -0
  204. package/src/scanner/detectors/infra/monorepo.ts +251 -0
  205. package/src/scanner/detectors/mcp/mcpProject.ts +176 -0
  206. package/src/scanner/detectors/mcp/mcpServers.ts +237 -0
  207. package/src/scanner/detectors/services/analytics.ts +273 -0
  208. package/src/scanner/detectors/services/auth.ts +254 -0
  209. package/src/scanner/detectors/services/email.ts +244 -0
  210. package/src/scanner/detectors/services/payments.ts +213 -0
  211. package/src/scanner/detectors/utils.ts +251 -0
  212. package/src/scanner/index.ts +354 -0
  213. package/src/scanner/registry.ts +301 -0
  214. package/src/scanner/types.ts +152 -0
  215. package/src/templates/config/ralph.config.js.tmpl +38 -0
  216. package/src/templates/guides/AGENTS.md.tmpl +100 -0
  217. package/src/templates/guides/FRONTEND.md.tmpl +523 -0
  218. package/src/templates/guides/PERFORMANCE.md.tmpl +264 -0
  219. package/src/templates/guides/SECURITY.md.tmpl +100 -0
  220. package/src/templates/prompts/PROMPT.md.tmpl +77 -0
  221. package/src/templates/prompts/PROMPT_e2e.md.tmpl +234 -0
  222. package/src/templates/prompts/PROMPT_feature.md.tmpl +83 -0
  223. package/src/templates/prompts/PROMPT_review.md.tmpl +167 -0
  224. package/src/templates/prompts/PROMPT_verify.md.tmpl +72 -0
  225. package/src/templates/root/.gitignore.tmpl +5 -0
  226. package/src/templates/root/LEARNINGS.md.tmpl +24 -0
  227. package/src/templates/root/README.md.tmpl +61 -0
  228. package/src/templates/scripts/feature-loop.sh.tmpl +267 -0
  229. package/src/templates/scripts/loop.sh.tmpl +59 -0
  230. package/src/templates/scripts/ralph-monitor.sh.tmpl +244 -0
  231. package/src/templates/specs/README.md.tmpl +57 -0
  232. package/src/templates/specs/_example.md.tmpl +71 -0
  233. package/src/utils/config.ts +221 -0
  234. package/src/utils/header.ts +15 -0
  235. package/src/utils/logger.ts +28 -0
  236. package/tsconfig.json +19 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * AI Analysis Prompts
3
+ * Defines prompts for codebase analysis and enhancement
4
+ */
5
+
6
+ import type { ScanResult, DetectedStack } from '../scanner/types.js';
7
+
8
+ /**
9
+ * Format the detected stack for inclusion in prompts
10
+ */
11
+ export function formatStackForPrompt(stack: DetectedStack): string {
12
+ const sections: string[] = [];
13
+
14
+ // Core
15
+ if (stack.framework) {
16
+ const variant = stack.framework.variant ? ` (${stack.framework.variant})` : '';
17
+ sections.push(`Framework: ${stack.framework.name}${variant}`);
18
+ }
19
+ if (stack.packageManager) {
20
+ sections.push(`Package Manager: ${stack.packageManager.name}`);
21
+ }
22
+ if (stack.testing?.unit) {
23
+ sections.push(`Unit Testing: ${stack.testing.unit.name}`);
24
+ }
25
+ if (stack.testing?.e2e) {
26
+ sections.push(`E2E Testing: ${stack.testing.e2e.name}`);
27
+ }
28
+ if (stack.styling) {
29
+ sections.push(`Styling: ${stack.styling.name}`);
30
+ }
31
+
32
+ // Data Layer
33
+ if (stack.database) {
34
+ sections.push(`Database: ${stack.database.name}`);
35
+ }
36
+ if (stack.orm) {
37
+ sections.push(`ORM: ${stack.orm.name}`);
38
+ }
39
+ if (stack.api && stack.api.length > 0) {
40
+ sections.push(`API Patterns: ${stack.api.map(a => a.name).join(', ')}`);
41
+ }
42
+
43
+ // Frontend
44
+ if (stack.stateManagement) {
45
+ sections.push(`State Management: ${stack.stateManagement.name}`);
46
+ }
47
+ if (stack.uiComponents && stack.uiComponents.length > 0) {
48
+ sections.push(`UI Components: ${stack.uiComponents.map(u => u.name).join(', ')}`);
49
+ }
50
+ if (stack.formHandling && stack.formHandling.length > 0) {
51
+ sections.push(`Form Handling: ${stack.formHandling.map(f => f.name).join(', ')}`);
52
+ }
53
+
54
+ // Services
55
+ if (stack.auth) {
56
+ sections.push(`Auth: ${stack.auth.name}`);
57
+ }
58
+ if (stack.analytics && stack.analytics.length > 0) {
59
+ sections.push(`Analytics: ${stack.analytics.map(a => a.name).join(', ')}`);
60
+ }
61
+ if (stack.payments) {
62
+ sections.push(`Payments: ${stack.payments.name}`);
63
+ }
64
+ if (stack.email) {
65
+ sections.push(`Email: ${stack.email.name}`);
66
+ }
67
+
68
+ // Infrastructure
69
+ if (stack.deployment && stack.deployment.length > 0) {
70
+ sections.push(`Deployment: ${stack.deployment.map(d => d.name).join(', ')}`);
71
+ }
72
+ if (stack.monorepo) {
73
+ sections.push(`Monorepo: ${stack.monorepo.name}`);
74
+ }
75
+
76
+ // MCP
77
+ if (stack.mcp) {
78
+ if (stack.mcp.isProject) {
79
+ sections.push('MCP: This is an MCP server project');
80
+ }
81
+ if (stack.mcp.detected && stack.mcp.detected.length > 0) {
82
+ sections.push(`MCP Servers: ${stack.mcp.detected.map(m => m.name).join(', ')}`);
83
+ }
84
+ }
85
+
86
+ return sections.join('\n');
87
+ }
88
+
89
+ /**
90
+ * System prompt for codebase analysis
91
+ */
92
+ export const SYSTEM_PROMPT = `You are an expert software architect analyzing a codebase to provide insights for AI-assisted development.
93
+
94
+ Your role is to:
95
+ 1. Analyze the detected tech stack and provide deeper insights
96
+ 2. Identify architectural patterns and coding conventions
97
+ 3. Suggest improvements to the detection results
98
+ 4. Recommend MCP servers that would benefit this project
99
+ 5. Generate custom prompt suggestions for AI coding assistants
100
+
101
+ Be concise and actionable. Focus on practical insights that help developers work more effectively with AI tools.
102
+
103
+ Respond in valid JSON format only.`;
104
+
105
+ /**
106
+ * Create the codebase analysis prompt
107
+ */
108
+ export function createAnalysisPrompt(scanResult: ScanResult): string {
109
+ const stackInfo = formatStackForPrompt(scanResult.stack);
110
+
111
+ return `Analyze this codebase and provide enhanced insights.
112
+
113
+ Project Root: ${scanResult.projectRoot}
114
+
115
+ Detected Stack:
116
+ ${stackInfo || 'No technologies detected'}
117
+
118
+ Based on this stack, provide analysis in the following JSON format:
119
+ {
120
+ "frameworkInsights": {
121
+ "variant": "more specific variant if detectable (e.g., 'app-router', 'pages-router', 'spa', 'ssr')",
122
+ "confidence": "high/medium/low",
123
+ "notes": "any additional observations about framework usage"
124
+ },
125
+ "architecturalPatterns": [
126
+ {
127
+ "pattern": "pattern name",
128
+ "confidence": "high/medium/low",
129
+ "evidence": "why you think this pattern is used"
130
+ }
131
+ ],
132
+ "codingConventions": [
133
+ {
134
+ "convention": "convention name",
135
+ "suggestion": "how to follow this convention"
136
+ }
137
+ ],
138
+ "recommendedMcpServers": [
139
+ {
140
+ "name": "server name",
141
+ "reason": "why this would be useful"
142
+ }
143
+ ],
144
+ "customPromptSuggestions": [
145
+ "Specific prompt suggestions tailored to this codebase"
146
+ ],
147
+ "additionalDetections": {
148
+ "possibleMissed": ["technologies that might be in use but weren't detected"],
149
+ "refinements": ["suggestions to improve existing detections"]
150
+ }
151
+ }
152
+
153
+ Only include sections where you have meaningful insights. Keep responses focused and actionable.`;
154
+ }
155
+
156
+ /**
157
+ * Prompt for validating and improving scanner results
158
+ */
159
+ export function createValidationPrompt(scanResult: ScanResult): string {
160
+ const stackInfo = formatStackForPrompt(scanResult.stack);
161
+
162
+ return `Review and validate these scanner detection results for accuracy.
163
+
164
+ Detected Stack:
165
+ ${stackInfo || 'No technologies detected'}
166
+
167
+ For each detection, assess if it seems accurate based on common project patterns.
168
+ Identify any likely false positives or missing detections.
169
+
170
+ Respond in JSON format:
171
+ {
172
+ "validations": [
173
+ {
174
+ "technology": "name",
175
+ "status": "confirmed/uncertain/likely-false-positive",
176
+ "notes": "explanation if uncertain or false positive"
177
+ }
178
+ ],
179
+ "likelyMissed": [
180
+ {
181
+ "technology": "name",
182
+ "reason": "why this might be in use"
183
+ }
184
+ ]
185
+ }`;
186
+ }
187
+
188
+ /**
189
+ * Prompt for generating stack-specific recommendations
190
+ */
191
+ export function createRecommendationsPrompt(scanResult: ScanResult): string {
192
+ const stackInfo = formatStackForPrompt(scanResult.stack);
193
+
194
+ return `Based on this tech stack, provide specific recommendations for AI-assisted development.
195
+
196
+ Detected Stack:
197
+ ${stackInfo || 'No technologies detected'}
198
+
199
+ Provide recommendations in JSON format:
200
+ {
201
+ "mcpServers": [
202
+ {
203
+ "name": "server identifier",
204
+ "priority": "high/medium/low",
205
+ "reason": "why this is recommended"
206
+ }
207
+ ],
208
+ "aiToolingTips": [
209
+ "Tips for working with AI tools on this codebase"
210
+ ],
211
+ "contextSuggestions": [
212
+ "Important context an AI should know about this stack"
213
+ ]
214
+ }
215
+
216
+ Focus on practical, actionable recommendations.`;
217
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * AI Provider Configuration
3
+ * Configures AI providers (Anthropic, OpenAI, OpenRouter) for the enhancer
4
+ */
5
+
6
+ import { createAnthropic } from '@ai-sdk/anthropic';
7
+ import { createOpenAI } from '@ai-sdk/openai';
8
+ import type { LanguageModel } from 'ai';
9
+
10
+ /**
11
+ * Supported AI providers
12
+ */
13
+ export type AIProvider = 'anthropic' | 'openai' | 'openrouter';
14
+
15
+ /**
16
+ * Provider configuration result
17
+ */
18
+ export interface ProviderConfig {
19
+ model: LanguageModel;
20
+ provider: AIProvider;
21
+ modelId: string;
22
+ }
23
+
24
+ /**
25
+ * Environment variable names for each provider
26
+ */
27
+ const API_KEY_ENV_VARS: Record<AIProvider, string> = {
28
+ anthropic: 'ANTHROPIC_API_KEY',
29
+ openai: 'OPENAI_API_KEY',
30
+ openrouter: 'OPENROUTER_API_KEY',
31
+ };
32
+
33
+ /**
34
+ * Default models for each provider
35
+ * Using fast/cost-effective models for speed
36
+ */
37
+ const DEFAULT_MODELS: Record<AIProvider, string> = {
38
+ anthropic: 'claude-sonnet-4-20250514',
39
+ openai: 'gpt-4o-mini',
40
+ openrouter: 'anthropic/claude-3.5-sonnet',
41
+ };
42
+
43
+ /**
44
+ * Check if an API key is available for a provider
45
+ */
46
+ export function hasApiKey(provider: AIProvider): boolean {
47
+ const envVar = API_KEY_ENV_VARS[provider];
48
+ return !!process.env[envVar];
49
+ }
50
+
51
+ /**
52
+ * Get the API key for a provider
53
+ * @throws Error if API key is not set
54
+ */
55
+ function getApiKey(provider: AIProvider): string {
56
+ const envVar = API_KEY_ENV_VARS[provider];
57
+ const apiKey = process.env[envVar];
58
+
59
+ if (!apiKey) {
60
+ throw new Error(
61
+ `API key not found. Set ${envVar} environment variable to use ${provider} provider.`
62
+ );
63
+ }
64
+
65
+ return apiKey;
66
+ }
67
+
68
+ /**
69
+ * Get a configured AI model for the specified provider
70
+ */
71
+ export function getModel(provider: AIProvider = 'anthropic'): ProviderConfig {
72
+ const modelId = DEFAULT_MODELS[provider];
73
+
74
+ switch (provider) {
75
+ case 'anthropic': {
76
+ const anthropic = createAnthropic({
77
+ apiKey: getApiKey('anthropic'),
78
+ });
79
+ return {
80
+ model: anthropic(modelId),
81
+ provider,
82
+ modelId,
83
+ };
84
+ }
85
+
86
+ case 'openai': {
87
+ const openai = createOpenAI({
88
+ apiKey: getApiKey('openai'),
89
+ });
90
+ return {
91
+ model: openai(modelId),
92
+ provider,
93
+ modelId,
94
+ };
95
+ }
96
+
97
+ case 'openrouter': {
98
+ // OpenRouter uses OpenAI-compatible API
99
+ const openrouter = createOpenAI({
100
+ apiKey: getApiKey('openrouter'),
101
+ baseURL: 'https://openrouter.ai/api/v1',
102
+ });
103
+ return {
104
+ model: openrouter(modelId),
105
+ provider,
106
+ modelId,
107
+ };
108
+ }
109
+
110
+ default:
111
+ throw new Error(`Unknown provider: ${provider}`);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Get the first available provider with an API key
117
+ * Checks in order: anthropic, openai, openrouter
118
+ */
119
+ export function getAvailableProvider(): AIProvider | null {
120
+ const providers: AIProvider[] = ['anthropic', 'openai', 'openrouter'];
121
+
122
+ for (const provider of providers) {
123
+ if (hasApiKey(provider)) {
124
+ return provider;
125
+ }
126
+ }
127
+
128
+ return null;
129
+ }
130
+
131
+ /**
132
+ * Get the environment variable name for a provider's API key
133
+ */
134
+ export function getApiKeyEnvVar(provider: AIProvider): string {
135
+ return API_KEY_ENV_VARS[provider];
136
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,255 @@
1
+ import { Command } from 'commander';
2
+ import { displayHeader } from './utils/header.js';
3
+ import { initCommand } from './commands/init.js';
4
+ import { runCommand, type RunOptions } from './commands/run.js';
5
+ import { monitorCommand, type MonitorOptions } from './commands/monitor.js';
6
+ import { newCommand, type NewOptions } from './commands/new.js';
7
+ import { logger } from './utils/logger.js';
8
+
9
+ /**
10
+ * Set up and configure the CLI
11
+ */
12
+ export function createCli(): Command {
13
+ const program = new Command();
14
+
15
+ program
16
+ .name('ralph')
17
+ .description(
18
+ 'AI-powered feature development loop CLI.\n\n' +
19
+ 'Ralph auto-detects your tech stack and generates an intelligent\n' +
20
+ 'development environment for AI-driven feature implementation.'
21
+ )
22
+ .version('0.1.0')
23
+ .hook('preAction', () => {
24
+ displayHeader();
25
+ })
26
+ .addHelpText(
27
+ 'after',
28
+ `
29
+ Examples:
30
+ $ ralph init Initialize Ralph in your project
31
+ $ ralph init --ai Initialize with AI-enhanced analysis
32
+ $ ralph new my-feature Create a new feature specification
33
+ $ ralph run my-feature Run the feature development loop
34
+ $ ralph monitor my-feature Monitor progress in real-time
35
+
36
+ Documentation:
37
+ https://github.com/your-org/ralph-cli#readme
38
+ `
39
+ );
40
+
41
+ // ralph init
42
+ program
43
+ .command('init')
44
+ .description(
45
+ 'Initialize Ralph in the current project.\n\n' +
46
+ 'Scans your codebase to detect the tech stack (framework, testing,\n' +
47
+ 'database, auth, etc.) and generates configuration files in .ralph/'
48
+ )
49
+ .option('--ai', 'Enable AI-enhanced analysis for deeper project insights')
50
+ .option(
51
+ '--provider <name>',
52
+ 'AI provider to use (anthropic, openai, openrouter)',
53
+ 'anthropic'
54
+ )
55
+ .option('-y, --yes', 'Accept defaults and skip all confirmation prompts')
56
+ .addHelpText(
57
+ 'after',
58
+ `
59
+ Examples:
60
+ $ ralph init Basic initialization
61
+ $ ralph init --ai With AI-enhanced analysis (Anthropic)
62
+ $ ralph init --ai --provider openai With OpenAI provider
63
+ $ ralph init --yes Non-interactive mode
64
+
65
+ Environment Variables:
66
+ ANTHROPIC_API_KEY Required for --ai with anthropic provider
67
+ OPENAI_API_KEY Required for --ai with openai provider
68
+ OPENROUTER_API_KEY Required for --ai with openrouter provider
69
+ `
70
+ )
71
+ .action(async (options) => {
72
+ try {
73
+ await initCommand(options);
74
+ } catch (error) {
75
+ handleCommandError(error);
76
+ }
77
+ });
78
+
79
+ // ralph run <feature>
80
+ program
81
+ .command('run <feature>')
82
+ .description(
83
+ 'Run the feature development loop for a specific feature.\n\n' +
84
+ 'Executes the AI-driven implementation workflow using the feature\n' +
85
+ 'spec in .ralph/specs/<feature>.md'
86
+ )
87
+ .option(
88
+ '--worktree',
89
+ 'Use git worktree for isolation (enables parallel execution of multiple features)'
90
+ )
91
+ .option(
92
+ '--resume',
93
+ 'Resume an interrupted loop (reuses existing branch and worktree)'
94
+ )
95
+ .option(
96
+ '--model <model>',
97
+ 'Claude model to use for implementation (opus, sonnet)'
98
+ )
99
+ .option(
100
+ '--max-iterations <n>',
101
+ 'Maximum number of implementation iterations (default: 50)',
102
+ parseInt
103
+ )
104
+ .option(
105
+ '--max-e2e-attempts <n>',
106
+ 'Maximum E2E test retry attempts before giving up (default: 3)',
107
+ parseInt
108
+ )
109
+ .addHelpText(
110
+ 'after',
111
+ `
112
+ Examples:
113
+ $ ralph run user-auth Run the user-auth feature
114
+ $ ralph run payment --worktree Run in isolated worktree
115
+ $ ralph run payment --resume Resume interrupted session
116
+ $ ralph run my-feature --model opus Use Claude Opus model
117
+ $ ralph run my-feature --max-iterations 30 --max-e2e-attempts 5
118
+
119
+ Notes:
120
+ - Create a feature spec first with: ralph new <feature>
121
+ - The spec file should be at: .ralph/specs/<feature>.md
122
+ - Use --worktree to run multiple features in parallel
123
+ `
124
+ )
125
+ .action(async (feature: string, options) => {
126
+ try {
127
+ const runOptions: RunOptions = {
128
+ worktree: options.worktree,
129
+ resume: options.resume,
130
+ model: options.model,
131
+ maxIterations: options.maxIterations,
132
+ maxE2eAttempts: options.maxE2eAttempts,
133
+ };
134
+ await runCommand(feature, runOptions);
135
+ } catch (error) {
136
+ handleCommandError(error);
137
+ }
138
+ });
139
+
140
+ // ralph monitor <feature>
141
+ program
142
+ .command('monitor <feature>')
143
+ .description(
144
+ 'Launch the monitoring dashboard for a feature.\n\n' +
145
+ 'Displays real-time progress including iteration count, phase,\n' +
146
+ 'task completion, token usage, and E2E test status.'
147
+ )
148
+ .option(
149
+ '--bash',
150
+ 'Use the bash script monitor instead of the built-in dashboard'
151
+ )
152
+ .option('--python', 'Use the Python TUI monitor (if available)')
153
+ .option(
154
+ '--interval <seconds>',
155
+ 'Dashboard refresh interval in seconds (default: 5)',
156
+ parseInt,
157
+ 5
158
+ )
159
+ .addHelpText(
160
+ 'after',
161
+ `
162
+ Examples:
163
+ $ ralph monitor my-feature Monitor with built-in dashboard
164
+ $ ralph monitor my-feature --interval 2 Refresh every 2 seconds
165
+ $ ralph monitor my-feature --bash Use bash script monitor
166
+
167
+ Dashboard Shows:
168
+ - Current phase (Planning, Implementation, E2E Testing, etc.)
169
+ - Iteration progress
170
+ - Task completion status
171
+ - Token usage (input/output)
172
+ - Git branch information
173
+ `
174
+ )
175
+ .action(async (feature: string, options) => {
176
+ try {
177
+ const monitorOptions: MonitorOptions = {
178
+ bash: options.bash,
179
+ python: options.python,
180
+ interval: options.interval,
181
+ };
182
+ await monitorCommand(feature, monitorOptions);
183
+ } catch (error) {
184
+ handleCommandError(error);
185
+ }
186
+ });
187
+
188
+ // ralph new <feature>
189
+ program
190
+ .command('new <feature>')
191
+ .description(
192
+ 'Create a new feature specification from template.\n\n' +
193
+ 'Generates a markdown spec file with sections for requirements,\n' +
194
+ 'acceptance criteria, technical notes, and more.'
195
+ )
196
+ .option('-e, --edit', 'Open the spec in your editor after creation')
197
+ .option(
198
+ '--editor <editor>',
199
+ 'Editor command to use (defaults to $EDITOR or "code")'
200
+ )
201
+ .option('-y, --yes', 'Skip confirmation prompts')
202
+ .option('-f, --force', 'Overwrite existing spec file without prompting')
203
+ .addHelpText(
204
+ 'after',
205
+ `
206
+ Examples:
207
+ $ ralph new user-dashboard Create spec with prompts
208
+ $ ralph new user-dashboard --edit Create and open in editor
209
+ $ ralph new user-dashboard -e --editor vim Open in vim
210
+ $ ralph new user-dashboard --yes Skip confirmations
211
+ $ ralph new user-dashboard --force Overwrite if exists
212
+
213
+ Output:
214
+ Creates: .ralph/specs/<feature>.md
215
+
216
+ Template includes sections for:
217
+ - Purpose and user stories
218
+ - Functional and non-functional requirements
219
+ - Technical notes and dependencies
220
+ - Visual requirements (for UI features)
221
+ - API endpoints
222
+ - Acceptance criteria
223
+ `
224
+ )
225
+ .action(async (feature: string, options) => {
226
+ try {
227
+ const newOptions: NewOptions = {
228
+ edit: options.edit,
229
+ editor: options.editor,
230
+ yes: options.yes,
231
+ force: options.force,
232
+ };
233
+ await newCommand(feature, newOptions);
234
+ } catch (error) {
235
+ handleCommandError(error);
236
+ }
237
+ });
238
+
239
+ return program;
240
+ }
241
+
242
+ /**
243
+ * Handle command errors with user-friendly output
244
+ */
245
+ function handleCommandError(error: unknown): void {
246
+ if (error instanceof Error) {
247
+ logger.error(error.message);
248
+ if (process.env.DEBUG) {
249
+ console.error(error.stack);
250
+ }
251
+ } else {
252
+ logger.error(String(error));
253
+ }
254
+ process.exit(1);
255
+ }