gigaspec 4.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.
@@ -0,0 +1,423 @@
1
+ /**
2
+ * Gigaspec AI-Native Workflow Engine
3
+ *
4
+ * This module handles the AI-driven workflow where:
5
+ * - AI assistant runs commands
6
+ * - Gigaspec outputs structured data for AI to present to human
7
+ * - Human responds via AI
8
+ * - AI feeds responses back to Gigaspec
9
+ */
10
+
11
+ const fs = require('fs-extra');
12
+ const path = require('path');
13
+ const { execSync } = require('child_process');
14
+
15
+ // Interview questions for new projects
16
+ const INTERVIEW_QUESTIONS = [
17
+ {
18
+ id: 'purpose',
19
+ text: 'What is the main purpose of this application?',
20
+ type: 'open_ended',
21
+ example: 'Help remote teams track tasks and deadlines',
22
+ category: 'discovery'
23
+ },
24
+ {
25
+ id: 'users',
26
+ text: 'Who are the primary users and how many do you expect?',
27
+ type: 'open_ended',
28
+ example: 'Small teams of 5-20 people',
29
+ category: 'discovery'
30
+ },
31
+ {
32
+ id: 'key_features',
33
+ text: 'What are the 3-5 most important features?',
34
+ type: 'open_ended',
35
+ example: 'Task creation, assignments, deadlines, comments, notifications',
36
+ category: 'discovery'
37
+ },
38
+ {
39
+ id: 'realtime',
40
+ text: 'Do you need real-time features (live updates, chat, collaboration)?',
41
+ type: 'boolean',
42
+ category: 'technical'
43
+ },
44
+ {
45
+ id: 'auth',
46
+ text: 'Do you need user authentication?',
47
+ type: 'multiple_choice',
48
+ options: ['No auth needed', 'Email/password', 'Social login (Google/GitHub)', 'SSO/Enterprise'],
49
+ category: 'technical'
50
+ },
51
+ {
52
+ id: 'payments',
53
+ text: 'Will this have paid features or subscriptions?',
54
+ type: 'boolean',
55
+ category: 'business'
56
+ },
57
+ {
58
+ id: 'scale',
59
+ text: 'What scale do you expect at launch and in 1 year?',
60
+ type: 'multiple_choice',
61
+ options: ['< 100 users', '100 - 1,000 users', '1,000 - 10,000 users', '10,000+ users'],
62
+ category: 'technical'
63
+ },
64
+ {
65
+ id: 'team_expertise',
66
+ text: 'What technologies is your team most comfortable with?',
67
+ type: 'multiple_select',
68
+ options: ['JavaScript/TypeScript', 'Python', 'Go', 'Rust', 'Ruby', 'Java', 'Elixir', 'No preference'],
69
+ category: 'team'
70
+ }
71
+ ];
72
+
73
+ // Stack recommendations based on requirements
74
+ const STACK_RECOMMENDATIONS = {
75
+ 'high_realtime': {
76
+ primary: { stack: 'Elixir/Phoenix', reason: 'Best-in-class real-time capabilities' },
77
+ alternative: { stack: 'Node.js/Socket.io', reason: 'Good real-time, larger ecosystem' }
78
+ },
79
+ 'rapid_development': {
80
+ primary: { stack: 'Python/Django', reason: 'Fastest development, built-in admin' },
81
+ alternative: { stack: 'Ruby on Rails', reason: 'Convention over configuration' }
82
+ },
83
+ 'modern_fullstack': {
84
+ primary: { stack: 'Node.js/Next.js', reason: 'Modern React, excellent DX, TypeScript' },
85
+ alternative: { stack: 'Python/FastAPI + React', reason: 'Fast API + flexible frontend' }
86
+ },
87
+ 'high_performance': {
88
+ primary: { stack: 'Go/Gin', reason: 'Fast, efficient, great for APIs' },
89
+ alternative: { stack: 'Rust/Axum', reason: 'Maximum performance, type safety' }
90
+ }
91
+ };
92
+
93
+ class AIWorkflowEngine {
94
+ constructor(options = {}) {
95
+ this.outputDir = options.outputDir || '.';
96
+ this.interviewState = null;
97
+ }
98
+
99
+ /**
100
+ * Start the new project workflow
101
+ * Returns JSON for AI to present menu
102
+ */
103
+ async startNewProject() {
104
+ return {
105
+ status: 'started',
106
+ workflow: 'new_project',
107
+ phase: 'interview',
108
+ message: 'Starting new project discovery',
109
+ next_action: 'begin_interview',
110
+ first_question: INTERVIEW_QUESTIONS[0]
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Start the existing project workflow
116
+ * Analyzes codebase and returns findings
117
+ */
118
+ async startExistingProject(projectPath) {
119
+ const absolutePath = path.resolve(projectPath);
120
+
121
+ if (!await fs.pathExists(absolutePath)) {
122
+ return {
123
+ status: 'error',
124
+ message: `Path does not exist: ${projectPath}`
125
+ };
126
+ }
127
+
128
+ const analysis = await this.analyzeCodebase(absolutePath);
129
+
130
+ return {
131
+ status: 'analysis_complete',
132
+ workflow: 'existing_project',
133
+ detected: analysis,
134
+ gaps: this.identifyGaps(analysis),
135
+ interview_questions: this.generateQuestionsFromAnalysis(analysis),
136
+ next_action: 'conduct_interview_for_gaps'
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Process interview answer and return next question or results
142
+ */
143
+ async processInterviewAnswer(questionId, answer, allAnswers = {}) {
144
+ // Store answer
145
+ allAnswers[questionId] = answer;
146
+
147
+ // Find next question
148
+ const currentIndex = INTERVIEW_QUESTIONS.findIndex(q => q.id === questionId);
149
+ const nextQuestion = INTERVIEW_QUESTIONS[currentIndex + 1];
150
+
151
+ if (nextQuestion) {
152
+ return {
153
+ status: 'interview_continue',
154
+ progress: `${currentIndex + 1}/${INTERVIEW_QUESTIONS.length}`,
155
+ question: nextQuestion,
156
+ answers_so_far: allAnswers
157
+ };
158
+ }
159
+
160
+ // Interview complete - analyze and recommend
161
+ return this.analyzeRequirements(allAnswers);
162
+ }
163
+
164
+ /**
165
+ * Analyze requirements and recommend stack
166
+ */
167
+ analyzeRequirements(answers) {
168
+ const analysis = {
169
+ purpose: answers.purpose || 'Application',
170
+ users: answers.users || 'General users',
171
+ key_features: (answers.key_features || '').split(',').map(f => f.trim()).filter(Boolean),
172
+ needs_realtime: answers.realtime === true || answers.realtime === 'true',
173
+ auth_type: answers.auth || 'None',
174
+ needs_payments: answers.payments === true || answers.payments === 'true',
175
+ expected_scale: answers.scale || '< 100 users',
176
+ team_expertise: Array.isArray(answers.team_expertise)
177
+ ? answers.team_expertise
178
+ : (answers.team_expertise || 'No preference').split(',').map(e => e.trim())
179
+ };
180
+
181
+ // Determine recommendation profile
182
+ let profile = 'modern_fullstack'; // default
183
+
184
+ if (analysis.needs_realtime && analysis.expected_scale.includes('10,000')) {
185
+ profile = 'high_realtime';
186
+ } else if (analysis.team_expertise.includes('Python') && !analysis.needs_realtime) {
187
+ profile = 'rapid_development';
188
+ } else if (analysis.expected_scale.includes('10,000+')) {
189
+ profile = 'high_performance';
190
+ }
191
+
192
+ const recommendation = STACK_RECOMMENDATIONS[profile];
193
+
194
+ // Add alternatives based on team expertise
195
+ const alternatives = [recommendation.alternative];
196
+
197
+ if (analysis.team_expertise.includes('JavaScript/TypeScript')) {
198
+ alternatives.push({ stack: 'Node.js/Next.js', reason: 'Your team knows JavaScript' });
199
+ }
200
+ if (analysis.team_expertise.includes('Python')) {
201
+ alternatives.push({ stack: 'Python/FastAPI', reason: 'Your team knows Python' });
202
+ }
203
+
204
+ return {
205
+ status: 'interview_complete',
206
+ analysis: analysis,
207
+ recommendations: {
208
+ primary: {
209
+ stack: recommendation.primary.stack,
210
+ reasoning: recommendation.primary.reason,
211
+ detailed_reasoning: this.generateDetailedReasoning(analysis, recommendation.primary.stack),
212
+ pros: this.getProsForStack(recommendation.primary.stack),
213
+ cons: this.getConsForStack(recommendation.primary.stack)
214
+ },
215
+ alternatives: alternatives.slice(0, 2).map(alt => ({
216
+ stack: alt.stack,
217
+ reasoning: alt.reason
218
+ }))
219
+ },
220
+ next_action: 'present_options_and_get_selection'
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Analyze existing codebase
226
+ */
227
+ async analyzeCodebase(projectPath) {
228
+ const analysis = {
229
+ languages: [],
230
+ frontend: {},
231
+ backend: {},
232
+ database: null,
233
+ testing: {},
234
+ deployment: {},
235
+ architecture: 'unknown'
236
+ };
237
+
238
+ // Check for package.json (Node.js)
239
+ const packageJsonPath = path.join(projectPath, 'package.json');
240
+ if (await fs.pathExists(packageJsonPath)) {
241
+ try {
242
+ const packageJson = await fs.readJson(packageJsonPath);
243
+ analysis.languages.push('JavaScript/TypeScript');
244
+
245
+ // Detect frontend
246
+ if (packageJson.dependencies?.next) {
247
+ analysis.frontend.framework = 'Next.js';
248
+ analysis.frontend.version = packageJson.dependencies.next;
249
+ } else if (packageJson.dependencies?.react) {
250
+ analysis.frontend.framework = 'React';
251
+ analysis.frontend.build_tool = packageJson.devDependencies?.vite ? 'Vite' : 'CRA/Other';
252
+ } else if (packageJson.dependencies?.vue) {
253
+ analysis.frontend.framework = 'Vue';
254
+ }
255
+
256
+ // Detect backend in same package
257
+ if (packageJson.dependencies?.express) {
258
+ analysis.backend.framework = 'Express.js';
259
+ } else if (packageJson.dependencies?.['@nestjs/core']) {
260
+ analysis.backend.framework = 'NestJS';
261
+ }
262
+
263
+ // Detect testing
264
+ if (packageJson.devDependencies?.jest || packageJson.dependencies?.jest) {
265
+ analysis.testing.framework = 'Jest';
266
+ } else if (packageJson.devDependencies?.vitest) {
267
+ analysis.testing.framework = 'Vitest';
268
+ }
269
+ } catch (e) {
270
+ // Ignore parse errors
271
+ }
272
+ }
273
+
274
+ // Check for Python
275
+ const requirementsPath = path.join(projectPath, 'requirements.txt');
276
+ const pyprojectPath = path.join(projectPath, 'pyproject.toml');
277
+ if (await fs.pathExists(requirementsPath) || await fs.pathExists(pyprojectPath)) {
278
+ analysis.languages.push('Python');
279
+
280
+ // Try to detect framework
281
+ if (await fs.pathExists(requirementsPath)) {
282
+ const reqs = await fs.readFile(requirementsPath, 'utf-8');
283
+ if (reqs.includes('fastapi')) analysis.backend.framework = 'FastAPI';
284
+ else if (reqs.includes('django')) analysis.backend.framework = 'Django';
285
+ else if (reqs.includes('flask')) analysis.backend.framework = 'Flask';
286
+ }
287
+ }
288
+
289
+ // Check for database
290
+ const prismaPath = path.join(projectPath, 'prisma', 'schema.prisma');
291
+ if (await fs.pathExists(prismaPath)) {
292
+ analysis.database = { orm: 'Prisma' };
293
+ const schema = await fs.readFile(prismaPath, 'utf-8');
294
+ if (schema.includes('postgresql')) analysis.database.type = 'PostgreSQL';
295
+ else if (schema.includes('mysql')) analysis.database.type = 'MySQL';
296
+ else if (schema.includes('sqlite')) analysis.database.type = 'SQLite';
297
+ }
298
+
299
+ // Check for Dockerfile
300
+ const dockerfilePath = path.join(projectPath, 'Dockerfile');
301
+ const dockerComposePath = path.join(projectPath, 'docker-compose.yml');
302
+ if (await fs.pathExists(dockerfilePath) || await fs.pathExists(dockerComposePath)) {
303
+ analysis.deployment.docker = true;
304
+ analysis.deployment.method = await fs.pathExists(dockerComposePath) ? 'Docker Compose' : 'Docker';
305
+ }
306
+
307
+ // Detect architecture patterns
308
+ const srcPath = path.join(projectPath, 'src');
309
+ const appPath = path.join(projectPath, 'app');
310
+ const pagesPath = path.join(projectPath, 'pages');
311
+
312
+ if (await fs.pathExists(pagesPath)) {
313
+ analysis.architecture = 'Next.js Pages Router';
314
+ } else if (await fs.pathExists(appPath)) {
315
+ analysis.architecture = 'Next.js App Router';
316
+ } else if (await fs.pathExists(srcPath)) {
317
+ analysis.architecture = 'Src-based';
318
+ }
319
+
320
+ return analysis;
321
+ }
322
+
323
+ /**
324
+ * Identify gaps in existing project
325
+ */
326
+ identifyGaps(analysis) {
327
+ const gaps = [];
328
+
329
+ if (!analysis.testing.framework) {
330
+ gaps.push('No testing framework detected');
331
+ }
332
+
333
+ if (!analysis.deployment.docker && !analysis.deployment.ci) {
334
+ gaps.push('No deployment configuration detected');
335
+ }
336
+
337
+ if (!analysis.database) {
338
+ gaps.push('No database configuration detected');
339
+ }
340
+
341
+ return gaps;
342
+ }
343
+
344
+ /**
345
+ * Generate interview questions based on analysis
346
+ */
347
+ generateQuestionsFromAnalysis(analysis) {
348
+ const questions = [];
349
+
350
+ // Always ask about purpose
351
+ questions.push({
352
+ id: 'purpose',
353
+ text: 'What is the main purpose of this application?',
354
+ reason: 'Cannot determine from code alone'
355
+ });
356
+
357
+ // Ask about scale if not clear
358
+ if (!analysis.deployment.method) {
359
+ questions.push({
360
+ id: 'scale',
361
+ text: 'What scale do you expect? (users, traffic)',
362
+ reason: 'Helps determine deployment strategy'
363
+ });
364
+ }
365
+
366
+ return questions;
367
+ }
368
+
369
+ /**
370
+ * Helper: Generate detailed reasoning
371
+ */
372
+ generateDetailedReasoning(analysis, stack) {
373
+ const reasons = [];
374
+
375
+ if (analysis.needs_realtime) {
376
+ reasons.push(`${stack} has good real-time capabilities for your collaboration features`);
377
+ }
378
+
379
+ if (analysis.expected_scale.includes('10,000')) {
380
+ reasons.push(`Can handle ${analysis.expected_scale} as you grow`);
381
+ }
382
+
383
+ if (analysis.team_expertise.some(e => stack.toLowerCase().includes(e.toLowerCase()))) {
384
+ reasons.push(`Matches your team's existing expertise`);
385
+ }
386
+
387
+ if (analysis.needs_payments) {
388
+ reasons.push(`Strong ecosystem for payment integration (Stripe, etc.)`);
389
+ }
390
+
391
+ return reasons.join('. ');
392
+ }
393
+
394
+ /**
395
+ * Helper: Get pros for stack
396
+ */
397
+ getProsForStack(stack) {
398
+ const pros = {
399
+ 'Node.js/Next.js': ['Excellent DX', 'Full-stack TypeScript', 'Huge ecosystem', 'Easy deployment'],
400
+ 'Elixir/Phoenix': ['Best real-time', 'High concurrency', 'Fault-tolerant', 'LiveView'],
401
+ 'Python/Django': ['Rapid development', 'Built-in admin', 'Mature ecosystem', 'Great for MVPs'],
402
+ 'Python/FastAPI': ['Fast performance', 'Automatic API docs', 'Type hints', 'Async support'],
403
+ 'Go/Gin': ['High performance', 'Low memory', 'Fast compile', 'Great for APIs']
404
+ };
405
+ return pros[stack] || ['Mature ecosystem', 'Good documentation'];
406
+ }
407
+
408
+ /**
409
+ * Helper: Get cons for stack
410
+ */
411
+ getConsForStack(stack) {
412
+ const cons = {
413
+ 'Node.js/Next.js': ['JavaScript fatigue', 'Callback complexity'],
414
+ 'Elixir/Phoenix': ['Smaller talent pool', 'Learning curve'],
415
+ 'Python/Django': ['Can be slow', 'Monolithic'],
416
+ 'Python/FastAPI': ['Newer ecosystem', 'Less built-in'],
417
+ 'Go/Gin': ['Verbose code', 'No generics (pre-1.18)']
418
+ };
419
+ return cons[stack] || ['Learning curve'];
420
+ }
421
+ }
422
+
423
+ module.exports = { AIWorkflowEngine, INTERVIEW_QUESTIONS };