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.
- package/.cursorrules +12 -0
- package/.env.example +4 -0
- package/AGENT.md +114 -0
- package/ARCHITECTURE.md +71 -0
- package/CLAUDE.md +6 -0
- package/DEPLOYMENT.md +70 -0
- package/ENVIRONMENT.md +40 -0
- package/EXAMPLE.md +390 -0
- package/GETTING_STARTED.md +153 -0
- package/IMPLEMENTATION_SUMMARY.md +261 -0
- package/LICENSE +201 -0
- package/PLAN.md +37 -0
- package/README.md +286 -0
- package/SETUP.md +64 -0
- package/STATE.md +53 -0
- package/WORKFLOW.md +41 -0
- package/bin/gigaspec.js +459 -0
- package/bin/mcp-server.js +393 -0
- package/lib/ai-workflow.js +423 -0
- package/lib/framework.js +970 -0
- package/lib/index.js +19 -0
- package/lib/templates.js +1034 -0
- package/logo.svg +55 -0
- package/package.json +46 -0
- package/prompts/fix.md +2 -0
- package/prompts/implement.md +2 -0
- package/prompts/plan.md +2 -0
- package/prompts/verify.md +2 -0
|
@@ -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 };
|