popeye-cli 1.0.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 (209) hide show
  1. package/.env.example +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/dist/adapters/claude.d.ts +82 -0
  5. package/dist/adapters/claude.d.ts.map +1 -0
  6. package/dist/adapters/claude.js +230 -0
  7. package/dist/adapters/claude.js.map +1 -0
  8. package/dist/adapters/openai.d.ts +48 -0
  9. package/dist/adapters/openai.d.ts.map +1 -0
  10. package/dist/adapters/openai.js +257 -0
  11. package/dist/adapters/openai.js.map +1 -0
  12. package/dist/auth/claude.d.ts +44 -0
  13. package/dist/auth/claude.d.ts.map +1 -0
  14. package/dist/auth/claude.js +139 -0
  15. package/dist/auth/claude.js.map +1 -0
  16. package/dist/auth/index.d.ts +61 -0
  17. package/dist/auth/index.d.ts.map +1 -0
  18. package/dist/auth/index.js +141 -0
  19. package/dist/auth/index.js.map +1 -0
  20. package/dist/auth/keychain.d.ts +66 -0
  21. package/dist/auth/keychain.d.ts.map +1 -0
  22. package/dist/auth/keychain.js +125 -0
  23. package/dist/auth/keychain.js.map +1 -0
  24. package/dist/auth/openai-entry.d.ts +9 -0
  25. package/dist/auth/openai-entry.d.ts.map +1 -0
  26. package/dist/auth/openai-entry.js +410 -0
  27. package/dist/auth/openai-entry.js.map +1 -0
  28. package/dist/auth/openai.d.ts +71 -0
  29. package/dist/auth/openai.d.ts.map +1 -0
  30. package/dist/auth/openai.js +212 -0
  31. package/dist/auth/openai.js.map +1 -0
  32. package/dist/auth/server.d.ts +32 -0
  33. package/dist/auth/server.d.ts.map +1 -0
  34. package/dist/auth/server.js +213 -0
  35. package/dist/auth/server.js.map +1 -0
  36. package/dist/cli/commands/auth.d.ts +10 -0
  37. package/dist/cli/commands/auth.d.ts.map +1 -0
  38. package/dist/cli/commands/auth.js +162 -0
  39. package/dist/cli/commands/auth.js.map +1 -0
  40. package/dist/cli/commands/config.d.ts +10 -0
  41. package/dist/cli/commands/config.d.ts.map +1 -0
  42. package/dist/cli/commands/config.js +215 -0
  43. package/dist/cli/commands/config.js.map +1 -0
  44. package/dist/cli/commands/create.d.ts +10 -0
  45. package/dist/cli/commands/create.d.ts.map +1 -0
  46. package/dist/cli/commands/create.js +240 -0
  47. package/dist/cli/commands/create.js.map +1 -0
  48. package/dist/cli/commands/index.d.ts +10 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -0
  50. package/dist/cli/commands/index.js +10 -0
  51. package/dist/cli/commands/index.js.map +1 -0
  52. package/dist/cli/commands/resume.d.ts +18 -0
  53. package/dist/cli/commands/resume.d.ts.map +1 -0
  54. package/dist/cli/commands/resume.js +241 -0
  55. package/dist/cli/commands/resume.js.map +1 -0
  56. package/dist/cli/commands/status.d.ts +18 -0
  57. package/dist/cli/commands/status.d.ts.map +1 -0
  58. package/dist/cli/commands/status.js +154 -0
  59. package/dist/cli/commands/status.js.map +1 -0
  60. package/dist/cli/index.d.ts +17 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +71 -0
  63. package/dist/cli/index.js.map +1 -0
  64. package/dist/cli/interactive.d.ts +9 -0
  65. package/dist/cli/interactive.d.ts.map +1 -0
  66. package/dist/cli/interactive.js +330 -0
  67. package/dist/cli/interactive.js.map +1 -0
  68. package/dist/cli/output.d.ts +182 -0
  69. package/dist/cli/output.d.ts.map +1 -0
  70. package/dist/cli/output.js +355 -0
  71. package/dist/cli/output.js.map +1 -0
  72. package/dist/config/defaults.d.ts +57 -0
  73. package/dist/config/defaults.d.ts.map +1 -0
  74. package/dist/config/defaults.js +103 -0
  75. package/dist/config/defaults.js.map +1 -0
  76. package/dist/config/index.d.ts +138 -0
  77. package/dist/config/index.d.ts.map +1 -0
  78. package/dist/config/index.js +244 -0
  79. package/dist/config/index.js.map +1 -0
  80. package/dist/config/schema.d.ts +220 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +141 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/generators/index.d.ts +101 -0
  85. package/dist/generators/index.d.ts.map +1 -0
  86. package/dist/generators/index.js +200 -0
  87. package/dist/generators/index.js.map +1 -0
  88. package/dist/generators/python.d.ts +48 -0
  89. package/dist/generators/python.d.ts.map +1 -0
  90. package/dist/generators/python.js +262 -0
  91. package/dist/generators/python.js.map +1 -0
  92. package/dist/generators/templates/index.d.ts +6 -0
  93. package/dist/generators/templates/index.d.ts.map +1 -0
  94. package/dist/generators/templates/index.js +6 -0
  95. package/dist/generators/templates/index.js.map +1 -0
  96. package/dist/generators/templates/python.d.ts +53 -0
  97. package/dist/generators/templates/python.d.ts.map +1 -0
  98. package/dist/generators/templates/python.js +454 -0
  99. package/dist/generators/templates/python.js.map +1 -0
  100. package/dist/generators/templates/typescript.d.ts +53 -0
  101. package/dist/generators/templates/typescript.d.ts.map +1 -0
  102. package/dist/generators/templates/typescript.js +394 -0
  103. package/dist/generators/templates/typescript.js.map +1 -0
  104. package/dist/generators/typescript.d.ts +64 -0
  105. package/dist/generators/typescript.d.ts.map +1 -0
  106. package/dist/generators/typescript.js +271 -0
  107. package/dist/generators/typescript.js.map +1 -0
  108. package/dist/index.d.ts +7 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +12 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/state/index.d.ts +168 -0
  113. package/dist/state/index.d.ts.map +1 -0
  114. package/dist/state/index.js +338 -0
  115. package/dist/state/index.js.map +1 -0
  116. package/dist/state/persistence.d.ts +91 -0
  117. package/dist/state/persistence.d.ts.map +1 -0
  118. package/dist/state/persistence.js +201 -0
  119. package/dist/state/persistence.js.map +1 -0
  120. package/dist/types/cli.d.ts +132 -0
  121. package/dist/types/cli.d.ts.map +1 -0
  122. package/dist/types/cli.js +17 -0
  123. package/dist/types/cli.js.map +1 -0
  124. package/dist/types/consensus.d.ts +111 -0
  125. package/dist/types/consensus.d.ts.map +1 -0
  126. package/dist/types/consensus.js +29 -0
  127. package/dist/types/consensus.js.map +1 -0
  128. package/dist/types/index.d.ts +9 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +13 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/project.d.ts +73 -0
  133. package/dist/types/project.d.ts.map +1 -0
  134. package/dist/types/project.js +55 -0
  135. package/dist/types/project.js.map +1 -0
  136. package/dist/types/workflow.d.ts +236 -0
  137. package/dist/types/workflow.d.ts.map +1 -0
  138. package/dist/types/workflow.js +74 -0
  139. package/dist/types/workflow.js.map +1 -0
  140. package/dist/workflow/consensus.d.ts +89 -0
  141. package/dist/workflow/consensus.d.ts.map +1 -0
  142. package/dist/workflow/consensus.js +220 -0
  143. package/dist/workflow/consensus.js.map +1 -0
  144. package/dist/workflow/execution-mode.d.ts +82 -0
  145. package/dist/workflow/execution-mode.d.ts.map +1 -0
  146. package/dist/workflow/execution-mode.js +346 -0
  147. package/dist/workflow/execution-mode.js.map +1 -0
  148. package/dist/workflow/index.d.ts +110 -0
  149. package/dist/workflow/index.d.ts.map +1 -0
  150. package/dist/workflow/index.js +283 -0
  151. package/dist/workflow/index.js.map +1 -0
  152. package/dist/workflow/plan-mode.d.ts +83 -0
  153. package/dist/workflow/plan-mode.d.ts.map +1 -0
  154. package/dist/workflow/plan-mode.js +241 -0
  155. package/dist/workflow/plan-mode.js.map +1 -0
  156. package/dist/workflow/test-runner.d.ts +87 -0
  157. package/dist/workflow/test-runner.d.ts.map +1 -0
  158. package/dist/workflow/test-runner.js +273 -0
  159. package/dist/workflow/test-runner.js.map +1 -0
  160. package/eslint.config.js +25 -0
  161. package/package.json +66 -0
  162. package/src/adapters/claude.ts +298 -0
  163. package/src/adapters/openai.ts +300 -0
  164. package/src/auth/claude.ts +166 -0
  165. package/src/auth/index.ts +171 -0
  166. package/src/auth/keychain.ts +138 -0
  167. package/src/auth/openai-entry.ts +410 -0
  168. package/src/auth/openai.ts +260 -0
  169. package/src/auth/server.ts +252 -0
  170. package/src/cli/commands/auth.ts +194 -0
  171. package/src/cli/commands/config.ts +241 -0
  172. package/src/cli/commands/create.ts +308 -0
  173. package/src/cli/commands/index.ts +10 -0
  174. package/src/cli/commands/resume.ts +304 -0
  175. package/src/cli/commands/status.ts +189 -0
  176. package/src/cli/index.ts +90 -0
  177. package/src/cli/interactive.ts +418 -0
  178. package/src/cli/output.ts +410 -0
  179. package/src/config/defaults.ts +114 -0
  180. package/src/config/index.ts +315 -0
  181. package/src/config/schema.ts +164 -0
  182. package/src/generators/index.ts +251 -0
  183. package/src/generators/python.ts +318 -0
  184. package/src/generators/templates/index.ts +6 -0
  185. package/src/generators/templates/python.ts +465 -0
  186. package/src/generators/templates/typescript.ts +417 -0
  187. package/src/generators/typescript.ts +340 -0
  188. package/src/index.ts +13 -0
  189. package/src/state/index.ts +454 -0
  190. package/src/state/persistence.ts +230 -0
  191. package/src/types/cli.ts +146 -0
  192. package/src/types/consensus.ts +116 -0
  193. package/src/types/index.ts +64 -0
  194. package/src/types/project.ts +85 -0
  195. package/src/types/workflow.ts +149 -0
  196. package/src/workflow/consensus.ts +299 -0
  197. package/src/workflow/execution-mode.ts +517 -0
  198. package/src/workflow/index.ts +396 -0
  199. package/src/workflow/plan-mode.ts +356 -0
  200. package/src/workflow/test-runner.ts +345 -0
  201. package/tests/adapters/openai.test.ts +145 -0
  202. package/tests/config/config.test.ts +208 -0
  203. package/tests/generators/generators.test.ts +185 -0
  204. package/tests/types/consensus.test.ts +152 -0
  205. package/tests/types/project.test.ts +134 -0
  206. package/tests/workflow/consensus.test.ts +221 -0
  207. package/tests/workflow/test-runner.test.ts +214 -0
  208. package/tsconfig.json +25 -0
  209. package/vitest.config.ts +22 -0
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Claude Agent SDK adapter
3
+ * Wraps the Claude Agent SDK for code execution and generation
4
+ */
5
+
6
+ import { query, type SDKMessage } from '@anthropic-ai/claude-agent-sdk';
7
+
8
+ /**
9
+ * Options for executing a prompt through Claude
10
+ */
11
+ export interface ClaudeExecuteOptions {
12
+ cwd?: string;
13
+ allowedTools?: string[];
14
+ permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
15
+ systemPrompt?: string;
16
+ timeout?: number;
17
+ onMessage?: (message: SDKMessage) => void;
18
+ }
19
+
20
+ /**
21
+ * Result from Claude execution
22
+ */
23
+ export interface ClaudeExecuteResult {
24
+ success: boolean;
25
+ response: string;
26
+ toolCalls: ToolCallRecord[];
27
+ error?: string;
28
+ }
29
+
30
+ /**
31
+ * Record of a tool call made by Claude
32
+ */
33
+ export interface ToolCallRecord {
34
+ tool: string;
35
+ input: Record<string, unknown>;
36
+ output?: string;
37
+ }
38
+
39
+ /**
40
+ * Default allowed tools for autonomous operation
41
+ */
42
+ export const DEFAULT_ALLOWED_TOOLS = [
43
+ 'Read',
44
+ 'Write',
45
+ 'Edit',
46
+ 'MultiEdit',
47
+ 'Bash',
48
+ 'Grep',
49
+ 'Glob',
50
+ 'LS',
51
+ 'TodoRead',
52
+ 'TodoWrite',
53
+ ];
54
+
55
+ /**
56
+ * Execute a prompt through the Claude Agent SDK
57
+ *
58
+ * @param prompt - The prompt to execute
59
+ * @param options - Execution options
60
+ * @returns The execution result
61
+ */
62
+ export async function executePrompt(
63
+ prompt: string,
64
+ options: ClaudeExecuteOptions = {}
65
+ ): Promise<ClaudeExecuteResult> {
66
+ const {
67
+ cwd,
68
+ allowedTools = DEFAULT_ALLOWED_TOOLS,
69
+ permissionMode = 'bypassPermissions',
70
+ systemPrompt,
71
+ onMessage,
72
+ } = options;
73
+
74
+ const toolCalls: ToolCallRecord[] = [];
75
+ let response = '';
76
+ let error: string | undefined;
77
+
78
+ try {
79
+ const result = query({
80
+ prompt,
81
+ options: {
82
+ cwd,
83
+ allowedTools,
84
+ permissionMode,
85
+ systemPrompt: systemPrompt || { type: 'preset', preset: 'claude_code' },
86
+ },
87
+ });
88
+
89
+ for await (const message of result) {
90
+ // Call the message handler if provided
91
+ if (onMessage) {
92
+ onMessage(message);
93
+ }
94
+
95
+ // Process different message types
96
+ if (message.type === 'assistant') {
97
+ const assistantMessage = message as { type: 'assistant'; message: { content: string | Array<{ type: string; text?: string }> } };
98
+ if (typeof assistantMessage.message.content === 'string') {
99
+ response += assistantMessage.message.content;
100
+ } else if (Array.isArray(assistantMessage.message.content)) {
101
+ for (const block of assistantMessage.message.content) {
102
+ if (block.type === 'text' && block.text) {
103
+ response += block.text;
104
+ }
105
+ }
106
+ }
107
+ } else if (message.type === 'result') {
108
+ // Handle result messages which may contain tool information
109
+ const resultMessage = message as { type: 'result'; result?: string; error?: { message: string } };
110
+ if (resultMessage.error) {
111
+ error = resultMessage.error.message;
112
+ }
113
+ }
114
+ }
115
+
116
+ return {
117
+ success: !error,
118
+ response: response.trim(),
119
+ toolCalls,
120
+ error,
121
+ };
122
+ } catch (err) {
123
+ return {
124
+ success: false,
125
+ response: '',
126
+ toolCalls,
127
+ error: err instanceof Error ? err.message : 'Unknown error executing prompt',
128
+ };
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Execute code generation for a specific task
134
+ *
135
+ * @param task - The task description
136
+ * @param context - Additional context about the project
137
+ * @param options - Execution options
138
+ */
139
+ export async function generateCode(
140
+ task: string,
141
+ context: string,
142
+ options: ClaudeExecuteOptions = {}
143
+ ): Promise<ClaudeExecuteResult> {
144
+ const prompt = `
145
+ ## Task
146
+ ${task}
147
+
148
+ ## Project Context
149
+ ${context}
150
+
151
+ ## Instructions
152
+ 1. Implement the task following best practices
153
+ 2. Write clean, well-documented code
154
+ 3. Include appropriate error handling
155
+ 4. Follow the project's existing patterns and style
156
+
157
+ Please implement this task now.
158
+ `.trim();
159
+
160
+ return executePrompt(prompt, {
161
+ ...options,
162
+ allowedTools: options.allowedTools || [
163
+ ...DEFAULT_ALLOWED_TOOLS,
164
+ 'WebSearch',
165
+ 'WebFetch',
166
+ ],
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Run tests and capture results
172
+ *
173
+ * @param testCommand - The test command to run
174
+ * @param cwd - Working directory
175
+ */
176
+ export async function runTests(
177
+ testCommand: string,
178
+ cwd?: string
179
+ ): Promise<ClaudeExecuteResult> {
180
+ const prompt = `
181
+ Run the following test command and report the results:
182
+
183
+ \`\`\`bash
184
+ ${testCommand}
185
+ \`\`\`
186
+
187
+ After running the tests:
188
+ 1. Report the total number of tests
189
+ 2. Report how many passed and failed
190
+ 3. If there are failures, summarize what failed
191
+ 4. Provide any relevant error messages
192
+ `.trim();
193
+
194
+ return executePrompt(prompt, {
195
+ cwd,
196
+ allowedTools: ['Bash', 'Read'],
197
+ permissionMode: 'bypassPermissions',
198
+ });
199
+ }
200
+
201
+ /**
202
+ * Analyze codebase to understand structure and patterns
203
+ *
204
+ * @param cwd - Working directory of the project
205
+ */
206
+ export async function analyzeCodebase(cwd: string): Promise<ClaudeExecuteResult> {
207
+ const prompt = `
208
+ Analyze this codebase and provide:
209
+
210
+ 1. **Project Structure**: Overview of directories and their purposes
211
+ 2. **Key Technologies**: Languages, frameworks, and major dependencies
212
+ 3. **Architecture Patterns**: Design patterns and architectural decisions observed
213
+ 4. **Code Style**: Naming conventions, formatting, and documentation style
214
+ 5. **Test Setup**: Testing framework and test organization
215
+ 6. **Build/Deploy**: Build tools and deployment configuration
216
+
217
+ Be concise but thorough in your analysis.
218
+ `.trim();
219
+
220
+ return executePrompt(prompt, {
221
+ cwd,
222
+ allowedTools: ['Read', 'Glob', 'Grep', 'LS'],
223
+ permissionMode: 'default', // Read-only analysis
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Create a development plan from a specification
229
+ *
230
+ * @param specification - The project specification
231
+ * @param context - Additional context (existing code, etc.)
232
+ */
233
+ export async function createPlan(
234
+ specification: string,
235
+ context: string = ''
236
+ ): Promise<ClaudeExecuteResult> {
237
+ const prompt = `
238
+ Create a detailed development plan for the following specification:
239
+
240
+ ## Specification
241
+ ${specification}
242
+
243
+ ${context ? `## Additional Context\n${context}` : ''}
244
+
245
+ ## Required Plan Sections
246
+
247
+ 1. **Background & Context**: Summarize the project requirements
248
+ 2. **Goals & Objectives**: List measurable objectives
249
+ 3. **Milestones**: Break down into major phases
250
+ 4. **Tasks**: Detail specific tasks for each milestone
251
+ 5. **Test Plan**: Define tests for each task
252
+ 6. **Risks & Mitigations**: Identify potential issues
253
+
254
+ Format the plan as markdown with clear sections and bullet points.
255
+ `.trim();
256
+
257
+ return executePrompt(prompt, {
258
+ allowedTools: ['Read', 'Glob'],
259
+ permissionMode: 'plan',
260
+ });
261
+ }
262
+
263
+ /**
264
+ * Revise a plan based on feedback
265
+ *
266
+ * @param originalPlan - The original plan
267
+ * @param feedback - Feedback to incorporate
268
+ * @param concerns - Specific concerns to address
269
+ */
270
+ export async function revisePlan(
271
+ originalPlan: string,
272
+ feedback: string,
273
+ concerns: string[]
274
+ ): Promise<ClaudeExecuteResult> {
275
+ const prompt = `
276
+ Revise the following plan based on the feedback provided:
277
+
278
+ ## Original Plan
279
+ ${originalPlan}
280
+
281
+ ## Feedback
282
+ ${feedback}
283
+
284
+ ## Specific Concerns to Address
285
+ ${concerns.map((c, i) => `${i + 1}. ${c}`).join('\n')}
286
+
287
+ ## Instructions
288
+ 1. Address each concern specifically
289
+ 2. Maintain the same plan structure
290
+ 3. Note what changed from the original
291
+ 4. Ensure the revised plan is complete and actionable
292
+ `.trim();
293
+
294
+ return executePrompt(prompt, {
295
+ allowedTools: [],
296
+ permissionMode: 'plan',
297
+ });
298
+ }
@@ -0,0 +1,300 @@
1
+ /**
2
+ * OpenAI API adapter
3
+ * Handles consensus reviews and plan validation
4
+ */
5
+
6
+ import OpenAI from 'openai';
7
+ import type { ConsensusResult, ConsensusConfig, OpenAIModel } from '../types/index.js';
8
+ import { getOpenAIToken } from '../auth/index.js';
9
+ import { DEFAULT_CONSENSUS_CONFIG } from '../types/consensus.js';
10
+
11
+ /**
12
+ * Create an OpenAI client with stored credentials
13
+ */
14
+ export async function createClient(): Promise<OpenAI> {
15
+ const apiKey = await getOpenAIToken();
16
+
17
+ if (!apiKey) {
18
+ throw new Error('OpenAI API key not found. Run: popeye-cli auth openai');
19
+ }
20
+
21
+ return new OpenAI({ apiKey });
22
+ }
23
+
24
+ /**
25
+ * Request consensus review from OpenAI
26
+ *
27
+ * @param plan - The development plan to review
28
+ * @param context - Project context
29
+ * @param config - Consensus configuration
30
+ * @returns Consensus result
31
+ */
32
+ export async function requestConsensus(
33
+ plan: string,
34
+ context: string,
35
+ config: Partial<ConsensusConfig> = {}
36
+ ): Promise<ConsensusResult> {
37
+ const {
38
+ openaiModel = DEFAULT_CONSENSUS_CONFIG.openaiModel,
39
+ temperature = DEFAULT_CONSENSUS_CONFIG.temperature,
40
+ maxTokens = DEFAULT_CONSENSUS_CONFIG.maxTokens,
41
+ } = config;
42
+
43
+ const client = await createClient();
44
+
45
+ // Build the consensus review prompt (matches spec section 11.1)
46
+ const prompt = buildConsensusPrompt(plan, context);
47
+
48
+ try {
49
+ const completion = await client.chat.completions.create({
50
+ model: openaiModel,
51
+ messages: [{ role: 'user', content: prompt }],
52
+ temperature,
53
+ max_tokens: maxTokens,
54
+ });
55
+
56
+ const response = completion.choices[0]?.message?.content || '';
57
+ return parseConsensusResponse(response);
58
+ } catch (error) {
59
+ if (error instanceof OpenAI.RateLimitError) {
60
+ // Implement exponential backoff retry
61
+ await sleep(5000);
62
+ return requestConsensus(plan, context, config);
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Build the consensus review prompt
70
+ * Follows spec section 11.1 format
71
+ */
72
+ function buildConsensusPrompt(plan: string, context: string): string {
73
+ return `You are a senior software architect reviewing a development plan.
74
+ Analyze the following plan for completeness, correctness, and feasibility.
75
+
76
+ PROJECT CONTEXT:
77
+ ${context}
78
+
79
+ PROPOSED PLAN:
80
+ ${plan}
81
+
82
+ Please provide:
83
+ 1. ANALYSIS: Detailed review of the plan
84
+ 2. STRENGTHS: What works well
85
+ 3. CONCERNS: Issues or gaps identified
86
+ 4. RECOMMENDATIONS: Specific improvements
87
+ 5. CONSENSUS SCORE: A percentage (0-100%) indicating your agreement
88
+ - 95-100%: Ready for execution
89
+ - 80-94%: Minor revisions needed
90
+ - 60-79%: Significant revisions needed
91
+ - Below 60%: Major rework required
92
+
93
+ Format your consensus score as: CONSENSUS: [X]%
94
+
95
+ Be thorough but constructive in your feedback.`;
96
+ }
97
+
98
+ /**
99
+ * Parse the consensus response from OpenAI
100
+ * Extracts score, analysis, concerns, etc.
101
+ */
102
+ export function parseConsensusResponse(response: string): ConsensusResult {
103
+ // Extract consensus score
104
+ const scoreMatch = response.match(/CONSENSUS:\s*(\d+)%/i);
105
+ const score = scoreMatch ? parseInt(scoreMatch[1], 10) : 0;
106
+
107
+ // Extract sections
108
+ const analysis = extractSection(response, 'ANALYSIS', 'STRENGTHS');
109
+ const strengthsText = extractSection(response, 'STRENGTHS', 'CONCERNS');
110
+ const concernsText = extractSection(response, 'CONCERNS', 'RECOMMENDATIONS');
111
+ const recommendationsText = extractSection(response, 'RECOMMENDATIONS', 'CONSENSUS');
112
+
113
+ // Parse lists from sections
114
+ const strengths = parseList(strengthsText);
115
+ const concerns = parseList(concernsText);
116
+ const recommendations = parseList(recommendationsText);
117
+
118
+ return {
119
+ score,
120
+ analysis: analysis.trim(),
121
+ strengths,
122
+ concerns,
123
+ recommendations,
124
+ approved: score >= 95,
125
+ rawResponse: response,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Extract a section from the response between two headers
131
+ */
132
+ function extractSection(response: string, startHeader: string, endHeader: string): string {
133
+ const startPattern = new RegExp(`${startHeader}[:\\s]*`, 'i');
134
+ const endPattern = new RegExp(`${endHeader}[:\\s]*`, 'i');
135
+
136
+ const startMatch = response.match(startPattern);
137
+ if (!startMatch) return '';
138
+
139
+ const startIndex = startMatch.index! + startMatch[0].length;
140
+ const endMatch = response.slice(startIndex).match(endPattern);
141
+
142
+ if (!endMatch) {
143
+ return response.slice(startIndex).trim();
144
+ }
145
+
146
+ return response.slice(startIndex, startIndex + endMatch.index!).trim();
147
+ }
148
+
149
+ /**
150
+ * Parse a bulleted or numbered list from text
151
+ */
152
+ function parseList(text: string): string[] {
153
+ if (!text) return [];
154
+
155
+ const lines = text.split('\n');
156
+ const items: string[] = [];
157
+
158
+ for (const line of lines) {
159
+ // Match bullets (-, *, +) or numbers (1., 2., etc.)
160
+ const match = line.match(/^[\s]*[-*+][\s]+(.+)$|^[\s]*\d+\.[\s]+(.+)$/);
161
+ if (match) {
162
+ const item = (match[1] || match[2]).trim();
163
+ if (item) {
164
+ items.push(item);
165
+ }
166
+ } else if (line.trim() && !line.match(/^[A-Z]+:/)) {
167
+ // Non-empty line that's not a header
168
+ items.push(line.trim());
169
+ }
170
+ }
171
+
172
+ return items;
173
+ }
174
+
175
+ /**
176
+ * Helper sleep function for rate limiting
177
+ */
178
+ function sleep(ms: number): Promise<void> {
179
+ return new Promise((resolve) => setTimeout(resolve, ms));
180
+ }
181
+
182
+ /**
183
+ * Validate that a model is available
184
+ */
185
+ export async function validateModel(model: OpenAIModel): Promise<boolean> {
186
+ try {
187
+ const client = await createClient();
188
+ const models = await client.models.list();
189
+ return models.data.some((m) => m.id === model);
190
+ } catch {
191
+ return false;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * List available models
197
+ */
198
+ export async function listAvailableModels(): Promise<string[]> {
199
+ try {
200
+ const client = await createClient();
201
+ const models = await client.models.list();
202
+
203
+ return models.data
204
+ .filter(
205
+ (m) =>
206
+ m.id.includes('gpt-4') ||
207
+ m.id.includes('gpt-3.5') ||
208
+ m.id.startsWith('o1')
209
+ )
210
+ .map((m) => m.id)
211
+ .sort();
212
+ } catch {
213
+ return [];
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Expand a brief idea into a full specification using OpenAI
219
+ *
220
+ * @param idea - The brief project idea
221
+ * @param language - Target programming language
222
+ * @returns Expanded specification
223
+ */
224
+ export async function expandIdea(
225
+ idea: string,
226
+ language: 'python' | 'typescript'
227
+ ): Promise<string> {
228
+ const client = await createClient();
229
+
230
+ const prompt = `You are a senior software architect. A user wants to build a project with the following idea:
231
+
232
+ "${idea}"
233
+
234
+ The project will be implemented in ${language === 'python' ? 'Python' : 'TypeScript'}.
235
+
236
+ Expand this into a complete software specification including:
237
+
238
+ 1. **Project Overview**: A clear description of what will be built
239
+ 2. **Core Features**: List of main features and functionality
240
+ 3. **Technical Requirements**:
241
+ - Language and framework choices
242
+ - Database requirements (if any)
243
+ - External APIs or services needed
244
+ - Authentication requirements (if any)
245
+ 4. **Architecture Overview**: High-level system design
246
+ 5. **API Specification** (if applicable): Key endpoints and their purposes
247
+ 6. **Data Models**: Key entities and their relationships
248
+ 7. **Non-Functional Requirements**: Performance, security, scalability considerations
249
+ 8. **Deployment**: Docker configuration and deployment approach
250
+
251
+ Be specific and actionable. The specification should be detailed enough that a developer could implement it without further clarification.`;
252
+
253
+ const completion = await client.chat.completions.create({
254
+ model: 'gpt-4o',
255
+ messages: [{ role: 'user', content: prompt }],
256
+ temperature: 0.7,
257
+ max_tokens: 4096,
258
+ });
259
+
260
+ return completion.choices[0]?.message?.content || idea;
261
+ }
262
+
263
+ /**
264
+ * Get feedback on generated code
265
+ *
266
+ * @param code - The code to review
267
+ * @param context - Context about what the code should do
268
+ */
269
+ export async function reviewCode(code: string, context: string): Promise<ConsensusResult> {
270
+ const client = await createClient();
271
+
272
+ const prompt = `You are a senior software engineer reviewing code. Review the following code:
273
+
274
+ CONTEXT:
275
+ ${context}
276
+
277
+ CODE:
278
+ \`\`\`
279
+ ${code}
280
+ \`\`\`
281
+
282
+ Provide:
283
+ 1. ANALYSIS: Overall code quality assessment
284
+ 2. STRENGTHS: What's done well
285
+ 3. CONCERNS: Issues, bugs, or improvements needed
286
+ 4. RECOMMENDATIONS: Specific fixes or enhancements
287
+ 5. CONSENSUS SCORE: A percentage (0-100%) of how production-ready this code is
288
+
289
+ Format your score as: CONSENSUS: [X]%`;
290
+
291
+ const completion = await client.chat.completions.create({
292
+ model: 'gpt-4o',
293
+ messages: [{ role: 'user', content: prompt }],
294
+ temperature: 0.3,
295
+ max_tokens: 2048,
296
+ });
297
+
298
+ const response = completion.choices[0]?.message?.content || '';
299
+ return parseConsensusResponse(response);
300
+ }