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,970 @@
1
+ /**
2
+ * Gigaspec AI Collaboration Framework
3
+ *
4
+ * This framework provides structure for AI assistants to:
5
+ * 1. Analyze project requirements
6
+ * 2. Ask clarifying questions
7
+ * 3. Make intelligent recommendations
8
+ * 4. Generate complete specifications
9
+ *
10
+ * The AI does the thinking. This framework provides the structure.
11
+ */
12
+
13
+ const fs = require('fs-extra');
14
+ const path = require('path');
15
+ const templates = require('./templates');
16
+
17
+ // Inquirer is ESM-only in v9+, so we need dynamic import
18
+ let inquirer;
19
+ async function getInquirer() {
20
+ if (!inquirer) {
21
+ const mod = await import('inquirer');
22
+ inquirer = mod.default;
23
+ }
24
+ return inquirer;
25
+ }
26
+
27
+ // ============================================================================
28
+ // ANALYSIS PROMPTS - For LLM to analyze requirements
29
+ // ============================================================================
30
+
31
+ const ANALYSIS_PROMPT = `You are a Staff+ Engineer and Technical Architect.
32
+ Your task is to analyze a project description and make intelligent technical decisions.
33
+
34
+ PROJECT DESCRIPTION:
35
+ {{DESCRIPTION}}
36
+
37
+ {{CONTEXT}}
38
+
39
+ Analyze this project deeply. Consider:
40
+
41
+ 1. WHAT is being built? (Core functionality, user flows)
42
+ 2. WHO are the users? (End users, admins, scale)
43
+ 3. WHAT are the constraints? (Performance, security, compliance)
44
+ 4. WHAT are the integrations? (External APIs, services)
45
+ 5. WHAT is the timeline and team size?
46
+
47
+ Based on your analysis, answer these questions:
48
+
49
+ --- ANALYSIS ---
50
+
51
+ Core Purpose: [What is the fundamental problem being solved?]
52
+
53
+ User Types: [Who will use this? How many?]
54
+
55
+ Key Features: [What are the 3-5 most important features?]
56
+
57
+ Technical Challenges: [What will be hardest to build?]
58
+
59
+ Scale Expectations: [How many users? What traffic?]
60
+
61
+ --- TECHNICAL DECISIONS ---
62
+
63
+ Recommended Stack: [Language/Framework with specific version]
64
+ Why: [Explain your reasoning based on the analysis]
65
+
66
+ Alternative Stacks: [2 other options with pros/cons]
67
+
68
+ Database: [Which database and why]
69
+
70
+ Cache Strategy: [What to cache and how]
71
+
72
+ External Services: [What 3rd party services are needed?]
73
+
74
+ Deployment Platform: [Where to deploy and why]
75
+
76
+ --- QUESTIONS FOR USER ---
77
+
78
+ What questions do you have that would help you make better recommendations?
79
+ List 3-5 specific questions.
80
+
81
+ --- CONFIDENCE ---
82
+
83
+ How confident are you in these recommendations? (High/Medium/Low)
84
+ What information would increase your confidence?
85
+ `;
86
+
87
+ const QUESTION_ANSWER_PROMPT = `You previously analyzed this project:
88
+
89
+ {{DESCRIPTION}}
90
+
91
+ And asked these questions:
92
+ {{QUESTIONS}}
93
+
94
+ The user has answered:
95
+ {{ANSWERS}}
96
+
97
+ Based on these answers, refine your technical recommendations:
98
+
99
+ --- REFINED ANALYSIS ---
100
+ [Update your analysis with new information]
101
+
102
+ --- FINAL TECHNICAL STACK ---
103
+ Stack: [Final decision with version]
104
+ Frontend: [If applicable]
105
+ Database: [Final decision]
106
+ Cache: [Final decision]
107
+ Services: [List of external services]
108
+ Deployment: [Final platform]
109
+
110
+ --- RATIONALE ---
111
+ [Explain why you made these specific choices]
112
+
113
+ --- TIMELINE BREAKDOWN ---
114
+ [Break down {{WEEKS}} weeks into phases with milestones]
115
+
116
+ --- RISKS & MITIGATION ---
117
+ [What could go wrong and how to prevent it]
118
+ `;
119
+
120
+ // ============================================================================
121
+ // INTERACTIVE QUESTIONS
122
+ // ============================================================================
123
+
124
+ const PROJECT_QUESTIONS = [
125
+ {
126
+ type: 'input',
127
+ name: 'name',
128
+ message: '📛 Project name:',
129
+ default: 'MyProject',
130
+ validate: (input) => input.trim().length > 0 || 'Project name is required'
131
+ },
132
+ {
133
+ type: 'input',
134
+ name: 'description',
135
+ message: '📝 Brief description:',
136
+ default: (answers) => `${answers.name} - A new software project`
137
+ },
138
+ {
139
+ type: 'list',
140
+ name: 'team',
141
+ message: '👥 Team size:',
142
+ choices: ['Solo (1)', 'Small (2-5)', 'Medium (6-10)', 'Large (10+)'],
143
+ default: 'Small (2-5)'
144
+ },
145
+ {
146
+ type: 'list',
147
+ name: 'weeks',
148
+ message: '⏱️ Timeline:',
149
+ choices: ['4 weeks', '8 weeks', '12 weeks', '16 weeks', '20+ weeks'],
150
+ default: '12 weeks'
151
+ },
152
+ {
153
+ type: 'input',
154
+ name: 'targetUsers',
155
+ message: '🎯 Target users (e.g., "students", "developers", "small businesses"):',
156
+ default: 'general users'
157
+ },
158
+ {
159
+ type: 'confirm',
160
+ name: 'needsRealtime',
161
+ message: '⚡ Does this need real-time features (chat, live updates)?',
162
+ default: false
163
+ },
164
+ {
165
+ type: 'confirm',
166
+ name: 'needsAuth',
167
+ message: '🔐 Does this need user authentication?',
168
+ default: true
169
+ },
170
+ {
171
+ type: 'confirm',
172
+ name: 'needsPayments',
173
+ message: '💳 Does this need payment processing?',
174
+ default: false
175
+ },
176
+ {
177
+ type: 'list',
178
+ name: 'scale',
179
+ message: '📈 Expected scale at launch:',
180
+ choices: ['< 100 users', '100 - 1,000 users', '1,000 - 10,000 users', '10,000+ users'],
181
+ default: '100 - 1,000 users'
182
+ }
183
+ ];
184
+
185
+ const STACK_QUESTIONS = [
186
+ {
187
+ type: 'list',
188
+ name: 'stack',
189
+ message: '🛠️ Backend stack:',
190
+ choices: [
191
+ 'Node.js/Express',
192
+ 'Node.js/NestJS',
193
+ 'Python/FastAPI',
194
+ 'Python/Django',
195
+ 'Elixir/Phoenix',
196
+ 'Go/Gin',
197
+ 'Ruby on Rails',
198
+ 'Java/Spring Boot',
199
+ 'Other'
200
+ ],
201
+ default: 'Node.js/Express'
202
+ },
203
+ {
204
+ type: 'list',
205
+ name: 'frontend',
206
+ message: '🎨 Frontend:',
207
+ choices: [
208
+ 'React/Next.js',
209
+ 'React/Vite',
210
+ 'Vue/Nuxt',
211
+ 'Vue/Vite',
212
+ 'Svelte/SvelteKit',
213
+ 'Angular',
214
+ 'HTMX + Templates',
215
+ 'React Native',
216
+ 'Flutter',
217
+ 'None (API only)'
218
+ ],
219
+ default: 'React/Next.js'
220
+ },
221
+ {
222
+ type: 'list',
223
+ name: 'database',
224
+ message: '💾 Database:',
225
+ choices: [
226
+ 'PostgreSQL',
227
+ 'MySQL',
228
+ 'MongoDB',
229
+ 'SQLite',
230
+ 'Supabase',
231
+ 'Firebase',
232
+ 'DynamoDB'
233
+ ],
234
+ default: 'PostgreSQL'
235
+ },
236
+ {
237
+ type: 'list',
238
+ name: 'cache',
239
+ message: '⚡ Cache:',
240
+ choices: [
241
+ 'Redis',
242
+ 'Memcached',
243
+ 'None needed'
244
+ ],
245
+ default: 'Redis'
246
+ },
247
+ {
248
+ type: 'list',
249
+ name: 'deployment',
250
+ message: '🚀 Deployment platform:',
251
+ choices: [
252
+ 'Vercel',
253
+ 'Railway',
254
+ 'Fly.io',
255
+ 'AWS',
256
+ 'Google Cloud',
257
+ 'Azure',
258
+ 'DigitalOcean',
259
+ 'Heroku',
260
+ 'Self-hosted'
261
+ ],
262
+ default: 'Railway'
263
+ }
264
+ ];
265
+
266
+ // ============================================================================
267
+ // FRAMEWORK API
268
+ // ============================================================================
269
+
270
+ class GigaspecFramework {
271
+ constructor(options = {}) {
272
+ this.outputDir = options.outputDir || '.';
273
+ this.verbose = options.verbose || false;
274
+ this.jsonMode = options.jsonMode || false;
275
+ }
276
+
277
+ /**
278
+ * Output data (text or JSON based on mode)
279
+ */
280
+ output(data) {
281
+ if (this.jsonMode) {
282
+ return data;
283
+ }
284
+ return data;
285
+ }
286
+
287
+ /**
288
+ * Step 1: Create analysis prompt for LLM
289
+ * The AI uses this to analyze requirements
290
+ */
291
+ createAnalysisPrompt(description, context = {}) {
292
+ let prompt = ANALYSIS_PROMPT
293
+ .replace('{{DESCRIPTION}}', description)
294
+ .replace('{{WEEKS}}', context.weeks || '12');
295
+
296
+ if (context.previousAnalysis) {
297
+ prompt = prompt.replace(
298
+ '{{CONTEXT}}',
299
+ `\nPREVIOUS ANALYSIS:\n${context.previousAnalysis}\n`
300
+ );
301
+ } else {
302
+ prompt = prompt.replace('{{CONTEXT}}', '');
303
+ }
304
+
305
+ return prompt;
306
+ }
307
+
308
+ /**
309
+ * Step 2: Create question-answer prompt for LLM
310
+ * After AI asks questions, user answers, AI refines recommendations
311
+ */
312
+ createRefinementPrompt(description, questions, answers, weeks = 12) {
313
+ return QUESTION_ANSWER_PROMPT
314
+ .replace('{{DESCRIPTION}}', description)
315
+ .replace('{{QUESTIONS}}', questions.map((q, i) => `${i + 1}. ${q}`).join('\n'))
316
+ .replace('{{ANSWERS}}', answers.map((a, i) => `${i + 1}. ${a}`).join('\n'))
317
+ .replace('{{WEEKS}}', weeks);
318
+ }
319
+
320
+ /**
321
+ * Step 3: Parse AI analysis output
322
+ * Extract structured data from LLM response
323
+ */
324
+ parseAnalysis(aiResponse) {
325
+ const sections = this._extractSections(aiResponse);
326
+
327
+ return {
328
+ analysis: {
329
+ corePurpose: sections['Core Purpose'] || '',
330
+ userTypes: sections['User Types'] || '',
331
+ keyFeatures: sections['Key Features'] || '',
332
+ technicalChallenges: sections['Technical Challenges'] || '',
333
+ scaleExpectations: sections['Scale Expectations'] || ''
334
+ },
335
+ recommendations: {
336
+ stack: this._extractValue(sections['Recommended Stack']),
337
+ stackReasoning: sections['Why'] || '',
338
+ alternatives: sections['Alternative Stacks'] || '',
339
+ database: this._extractValue(sections['Database']),
340
+ cache: this._extractValue(sections['Cache Strategy']),
341
+ services: this._extractList(sections['External Services']),
342
+ deployment: this._extractValue(sections['Deployment Platform'])
343
+ },
344
+ questions: this._extractList(sections['Questions for User']),
345
+ confidence: this._extractValue(sections['Confidence'])
346
+ };
347
+ }
348
+
349
+ /**
350
+ * Step 4: Parse refined recommendations
351
+ */
352
+ parseRefinement(aiResponse) {
353
+ const sections = this._extractSections(aiResponse);
354
+
355
+ return {
356
+ stack: this._extractValue(sections['Stack']),
357
+ frontend: this._extractValue(sections['Frontend']),
358
+ database: this._extractValue(sections['Database']),
359
+ cache: this._extractValue(sections['Cache']),
360
+ services: this._extractList(sections['Services']),
361
+ deployment: this._extractValue(sections['Deployment']),
362
+ rationale: sections['Rationale'] || '',
363
+ timeline: this._extractPhases(sections['Timeline Breakdown']),
364
+ risks: sections['Risks & Mitigation'] || ''
365
+ };
366
+ }
367
+
368
+ /**
369
+ * Step 5: Interactive project setup
370
+ * Asks user questions to gather requirements
371
+ */
372
+ async interactiveSetup() {
373
+ const inq = await getInquirer();
374
+
375
+ // Step 1: Project basics
376
+ const basics = await inq.prompt(PROJECT_QUESTIONS);
377
+
378
+ // Parse weeks from selection
379
+ const weeksMatch = basics.weeks.match(/(\d+)/);
380
+ const weeks = weeksMatch ? parseInt(weeksMatch[1]) : 12;
381
+
382
+ // Step 2: Stack selection (or AI recommendation)
383
+ const useAI = await inq.prompt([{
384
+ type: 'confirm',
385
+ name: 'useAI',
386
+ message: '🤖 Would you like AI to recommend the tech stack based on your requirements?',
387
+ default: true
388
+ }]);
389
+
390
+ let stackConfig;
391
+
392
+ if (useAI.useAI) {
393
+ // Return partial config for AI analysis
394
+ return {
395
+ mode: 'ai_analysis',
396
+ config: {
397
+ name: basics.name,
398
+ description: basics.description,
399
+ team: basics.team,
400
+ weeks: weeks,
401
+ targetUsers: basics.targetUsers,
402
+ needsRealtime: basics.needsRealtime,
403
+ needsAuth: basics.needsAuth,
404
+ needsPayments: basics.needsPayments,
405
+ scale: basics.scale
406
+ }
407
+ };
408
+ } else {
409
+ // Manual stack selection
410
+ stackConfig = await inq.prompt(STACK_QUESTIONS);
411
+ }
412
+
413
+ // Step 3: Additional services
414
+ const services = [];
415
+ if (basics.needsAuth) services.push('Authentication (Clerk/Auth0)');
416
+ if (basics.needsPayments) services.push('Stripe (payments)');
417
+
418
+ const additionalServices = await inq.prompt([{
419
+ type: 'checkbox',
420
+ name: 'extras',
421
+ message: '📦 Additional services:',
422
+ choices: [
423
+ 'Email (SendGrid/Resend)',
424
+ 'File Storage (S3/R2)',
425
+ 'Analytics (PostHog/Amplitude)',
426
+ 'Error Tracking (Sentry)',
427
+ 'Search (Algolia/Meilisearch)',
428
+ 'CMS (Sanity/Strapi)',
429
+ 'Real-time (Pusher/Ably)'
430
+ ]
431
+ }]);
432
+
433
+ services.push(...additionalServices.extras);
434
+
435
+ return {
436
+ mode: 'complete',
437
+ config: {
438
+ name: basics.name,
439
+ description: basics.description,
440
+ team: basics.team,
441
+ weeks: weeks,
442
+ stack: stackConfig?.stack || 'Node.js/Express',
443
+ frontend: stackConfig?.frontend || 'React/Next.js',
444
+ database: stackConfig?.database || 'PostgreSQL',
445
+ cache: stackConfig?.cache || 'Redis',
446
+ deployment: stackConfig?.deployment || 'Railway',
447
+ services: services,
448
+ requirements: {
449
+ targetUsers: basics.targetUsers,
450
+ needsRealtime: basics.needsRealtime,
451
+ needsAuth: basics.needsAuth,
452
+ needsPayments: basics.needsPayments,
453
+ scale: basics.scale
454
+ }
455
+ }
456
+ };
457
+ }
458
+
459
+ /**
460
+ * Step 6: Generate all specification files
461
+ * Uses AI's recommendations to generate docs
462
+ */
463
+ async generate(config) {
464
+ const outputDir = path.resolve(this.outputDir);
465
+ await fs.ensureDir(outputDir);
466
+
467
+ const files = [];
468
+ const docs = [
469
+ { name: 'AGENT.md', template: 'agent' },
470
+ { name: 'ARCHITECTURE.md', template: 'architecture' },
471
+ { name: 'PLAN.md', template: 'plan' },
472
+ { name: 'STATE.md', template: 'state' },
473
+ { name: 'WORKFLOW.md', template: 'workflow' },
474
+ { name: 'SETUP.md', template: 'setup' },
475
+ { name: 'DEPLOYMENT.md', template: 'deployment' },
476
+ { name: 'ENVIRONMENT.md', template: 'environment' },
477
+ { name: 'GETTING_STARTED.md', template: 'gettingStarted' }
478
+ ];
479
+
480
+ for (const doc of docs) {
481
+ const content = templates[doc.template](config);
482
+ const filePath = path.join(outputDir, doc.name);
483
+ await fs.writeFile(filePath, content);
484
+ files.push(doc.name);
485
+ }
486
+
487
+ // Generate infrastructure
488
+ await this._generateInfrastructure(outputDir, config);
489
+
490
+ return { files, outputDir };
491
+ }
492
+
493
+ /**
494
+ * Create structured output for AI assistant
495
+ */
496
+ createStructuredOutput(analysis, recommendations, config) {
497
+ return {
498
+ version: '4.0.0',
499
+ generatedAt: new Date().toISOString(),
500
+
501
+ project: {
502
+ name: config.name,
503
+ description: config.description,
504
+ team: config.team,
505
+ weeks: config.weeks
506
+ },
507
+
508
+ analysis: analysis,
509
+
510
+ recommendations: {
511
+ stack: recommendations.stack,
512
+ frontend: recommendations.frontend,
513
+ database: recommendations.database,
514
+ cache: recommendations.cache,
515
+ services: recommendations.services,
516
+ deployment: recommendations.deployment,
517
+ rationale: recommendations.rationale
518
+ },
519
+
520
+ timeline: recommendations.timeline,
521
+
522
+ risks: recommendations.risks,
523
+
524
+ nextSteps: [
525
+ 'Review AI analysis and recommendations',
526
+ 'Approve or modify technical stack',
527
+ 'Generate specification files',
528
+ 'Begin development following PLAN.md'
529
+ ]
530
+ };
531
+ }
532
+
533
+ // ============================================================================
534
+ // PRIVATE HELPERS
535
+ // ============================================================================
536
+
537
+ _extractSections(text) {
538
+ const sections = {};
539
+ const lines = text.split('\n');
540
+ let currentSection = null;
541
+ let currentContent = [];
542
+
543
+ for (const line of lines) {
544
+ // Match section headers like "--- Section Name ---" or "-- Section Name --"
545
+ const sectionMatch = line.match(/^---\s*([^-][^\n-]*?)\s*---$/);
546
+
547
+ if (sectionMatch) {
548
+ if (currentSection) {
549
+ sections[currentSection] = currentContent.join('\n').trim();
550
+ }
551
+ currentSection = sectionMatch[1].trim();
552
+ currentContent = [];
553
+ } else if (currentSection && line.trim()) {
554
+ currentContent.push(line);
555
+ }
556
+ }
557
+
558
+ if (currentSection) {
559
+ sections[currentSection] = currentContent.join('\n').trim();
560
+ }
561
+
562
+ return sections;
563
+ }
564
+
565
+ _extractValue(text) {
566
+ if (!text) return '';
567
+ // Extract value after colon or first line
568
+ const match = text.match(/:\s*(.+)/);
569
+ return match ? match[1].trim() : text.split('\n')[0].trim();
570
+ }
571
+
572
+ _extractList(text) {
573
+ if (!text) return [];
574
+ return text
575
+ .split('\n')
576
+ .map(line => line.replace(/^[-*\d.\s]+/, '').trim())
577
+ .filter(line => line.length > 0);
578
+ }
579
+
580
+ _extractPhases(text) {
581
+ if (!text) return [];
582
+ const phases = [];
583
+ const lines = text.split('\n');
584
+
585
+ for (const line of lines) {
586
+ const phaseMatch = line.match(/Phase\s*\d+.*?:\s*(.+)/i);
587
+ if (phaseMatch) {
588
+ phases.push(phaseMatch[1].trim());
589
+ }
590
+ }
591
+
592
+ return phases;
593
+ }
594
+
595
+ async _generateInfrastructure(outputDir, config) {
596
+ // Create directories
597
+ await fs.ensureDir(path.join(outputDir, 'scripts'));
598
+ await fs.ensureDir(path.join(outputDir, '.hooks'));
599
+ await fs.ensureDir(path.join(outputDir, '.github', 'workflows'));
600
+ await fs.ensureDir(path.join(outputDir, 'prompts'));
601
+
602
+ // Validate script
603
+ await fs.writeFile(
604
+ path.join(outputDir, 'scripts', 'validate-state.sh'),
605
+ `#!/bin/bash\nset -e\nif [ ! -f "STATE.md" ]; then\n echo "❌ STATE.md not found"\n exit 1\nfi\necho "✅ STATE.md valid"\n`,
606
+ { mode: 0o755 }
607
+ );
608
+
609
+ // Pre-commit hook
610
+ await fs.writeFile(
611
+ path.join(outputDir, '.hooks', 'pre-commit'),
612
+ `#!/bin/bash\necho "🔍 Running checks..."\nif [ -f "scripts/validate-state.sh" ]; then\n ./scripts/validate-state.sh\nfi\necho "✅ Checks passed"\n`,
613
+ { mode: 0o755 }
614
+ );
615
+
616
+ // GitHub Actions
617
+ await fs.writeFile(
618
+ path.join(outputDir, '.github', 'workflows', 'ai-compliance.yml'),
619
+ `name: Spec Validation\non: [push, pull_request]\njobs:\n validate:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n - run: ./scripts/validate-state.sh\n`
620
+ );
621
+
622
+ // Prompts
623
+ const prompts = {
624
+ 'plan.md': '# Planning Protocol\nRead STATE.md → Identify critical path → Output: Assessment → Chunks → Checklist',
625
+ 'implement.md': '# Implementation Protocol\nPhase A: Interface → Phase B: Logic → Phase C: Fault tolerance',
626
+ 'verify.md': '# Verification Protocol\nStatic analysis → Tests → AGENT.md compliance',
627
+ 'fix.md': '# Fix Protocol\nInput: Violation + Code → Output: Before/After diff'
628
+ };
629
+
630
+ for (const [name, content] of Object.entries(prompts)) {
631
+ await fs.writeFile(path.join(outputDir, 'prompts', name), content);
632
+ }
633
+
634
+ // .cursorrules
635
+ await fs.writeFile(
636
+ path.join(outputDir, '.cursorrules'),
637
+ `# ${config.name} - Cursor Rules\n## ALWAYS\n1. Read STATE.md first\n2. Check AGENT.md constraints\n3. Max 10 lines per function\n## NEVER\n1. Skip verification\n2. Update STATE.md without CONFIRMED\n## COMMANDS\n- "Familiarize yourself" - Read all docs\n- "CONTINUE" - Next task from STATE.md\n- "STATUS" - Current state\n`
638
+ );
639
+
640
+ // CLAUDE.md
641
+ await fs.writeFile(
642
+ path.join(outputDir, 'CLAUDE.md'),
643
+ `# ${config.name} - Claude Code Guide\n## Session Start\n1. Read STATE.md completely\n2. Check AGENT.md constraints\n3. Identify next task\n## Stack: ${config.stack}\n`
644
+ );
645
+
646
+ // .env.example
647
+ await fs.writeFile(
648
+ path.join(outputDir, '.env.example'),
649
+ `# ${config.name} Environment\nDATABASE_URL=postgresql://user:pass@localhost:5432/${config.name.toLowerCase().replace(/\s+/g, '_')}\nSECRET_KEY_BASE=change_in_production\nPORT=4000\n`
650
+ );
651
+
652
+ // .gitignore
653
+ await fs.writeFile(
654
+ path.join(outputDir, '.gitignore'),
655
+ `.ai-temp/\n.env\n.env.local\n.DS_Store\nnode_modules/\n_build/\ndist/\n`
656
+ );
657
+ }
658
+ }
659
+
660
+ // ============================================================================
661
+ // CLI COMMANDS
662
+ // ============================================================================
663
+
664
+ async function initCommand(options, brand) {
665
+ const framework = new GigaspecFramework({
666
+ outputDir: options.output,
667
+ jsonMode: options.json
668
+ });
669
+
670
+ // JSON mode: skip branding, output structured data
671
+ if (!options.json) {
672
+ brand.title();
673
+ brand.header('Gigaspec - AI Collaboration Framework');
674
+ brand.footer();
675
+ }
676
+
677
+ let config;
678
+ let result;
679
+
680
+ // Mode 1: Non-interactive with stack provided (highest priority)
681
+ if (options.stack) {
682
+ if (!options.json) {
683
+ brand.step(1, 3, 'Using provided stack configuration');
684
+ }
685
+
686
+ config = {
687
+ name: options.name || 'MyProject',
688
+ description: options.description || `${options.name || 'MyProject'} - A new software project`,
689
+ stack: options.stack,
690
+ frontend: options.frontend || 'React/Next.js',
691
+ database: options.database || 'PostgreSQL',
692
+ cache: options.cache || 'Redis',
693
+ services: options.services ? options.services.split(',') : [],
694
+ deployment: options.deploy || 'Railway',
695
+ weeks: parseInt(options.weeks) || 12,
696
+ team: options.team || '2-5'
697
+ };
698
+ }
699
+ // Mode 2: Non-interactive with defaults (--yes)
700
+ else if (options.yes) {
701
+ if (!options.json) {
702
+ brand.step(1, 3, 'Using default configuration');
703
+ }
704
+
705
+ config = {
706
+ name: options.name || 'MyProject',
707
+ description: options.description || `${options.name || 'MyProject'} - A new software project`,
708
+ stack: 'Node.js/Express',
709
+ frontend: 'React/Next.js',
710
+ database: 'PostgreSQL',
711
+ cache: 'Redis',
712
+ services: [],
713
+ deployment: 'Railway',
714
+ weeks: parseInt(options.weeks) || 12,
715
+ team: options.team || '2-5'
716
+ };
717
+ }
718
+ // Mode 3: JSON mode with description only (for AI consumption)
719
+ else if (options.json && options.description) {
720
+ const analysisPrompt = framework.createAnalysisPrompt(options.description, {
721
+ weeks: options.weeks || '12'
722
+ });
723
+
724
+ result = {
725
+ status: 'analysis_required',
726
+ message: 'AI analysis needed',
727
+ prompt: analysisPrompt,
728
+ nextSteps: [
729
+ 'Analyze the project requirements',
730
+ 'Ask clarifying questions',
731
+ 'Recommend technical stack',
732
+ 'Run: gigaspec generate --config <config>'
733
+ ]
734
+ };
735
+
736
+ console.log(JSON.stringify(result, null, 2));
737
+ return result;
738
+ }
739
+ // Mode 4: No stack provided - show help
740
+ else if (!options.json && !options.name) {
741
+ // User ran 'gigaspec init' with no options
742
+ // Header already shown above, just show help
743
+ console.log('\n' + brand.bold('🤔 No stack specified!'));
744
+ console.log('\nHere are your options:\n');
745
+
746
+ console.log(brand.cyan('Option 1: Interactive Wizard (Recommended)'));
747
+ console.log(' gigaspec wizard');
748
+ console.log(' → Answer questions, get AI stack recommendation\n');
749
+
750
+ console.log(brand.cyan('Option 2: Analyze First'));
751
+ console.log(' gigaspec analyze "Your project idea" --json');
752
+ console.log(' → Give prompt to AI assistant for stack recommendation');
753
+ console.log(' → Then: gigaspec init --name "MyApp" --stack "RecommendedStack"\n');
754
+
755
+ console.log(brand.cyan('Option 3: Use Defaults'));
756
+ console.log(' gigaspec init --name "MyApp" --yes');
757
+ console.log(' → Uses Node.js/Express with defaults\n');
758
+
759
+ console.log(brand.cyan('Option 4: Specify Stack'));
760
+ console.log(' gigaspec init --name "MyApp" --stack "Node.js/Next.js"');
761
+ console.log(' → Common stacks: Node.js/Next.js, Python/FastAPI, Elixir/Phoenix\n');
762
+
763
+ console.log(brand.bold('💡 Tip:'));
764
+ console.log('If you\'re unsure about your stack, use Option 1 or 2!\n');
765
+
766
+ return { status: 'help_shown' };
767
+ }
768
+ // Mode 5: Interactive mode (has name but no stack/yes)
769
+ else if (!options.json) {
770
+ brand.step(1, 3, 'Interactive project setup');
771
+
772
+ const setup = await framework.interactiveSetup();
773
+
774
+ if (setup.mode === 'ai_analysis') {
775
+ // Generate analysis prompt for AI
776
+ const description = `${setup.config.name}: ${setup.config.description}
777
+ Target users: ${setup.config.targetUsers}
778
+ Needs realtime: ${setup.config.needsRealtime}
779
+ Needs auth: ${setup.config.needsAuth}
780
+ Needs payments: ${setup.config.needsPayments}
781
+ Expected scale: ${setup.config.scale}`;
782
+
783
+ const analysisPrompt = framework.createAnalysisPrompt(description, {
784
+ weeks: setup.config.weeks
785
+ });
786
+
787
+ // Save prompt
788
+ const promptPath = path.join(options.output, '.ai-temp');
789
+ await fs.ensureDir(promptPath);
790
+ await fs.writeFile(
791
+ path.join(promptPath, 'analysis-prompt.txt'),
792
+ analysisPrompt
793
+ );
794
+
795
+ brand.step(2, 3, 'AI analysis required');
796
+ console.log(`\n ${brand.infoColor('Analysis prompt saved to:')} .ai-temp/analysis-prompt.txt`);
797
+ console.log(`\n ${brand.warnColor('Next:')} Provide this prompt to your AI assistant`);
798
+ console.log(` ${brand.infoColor('The AI will analyze and recommend a technical stack')}`);
799
+ console.log(`\n ${brand.infoColor('After AI analysis, run:')}`);
800
+ console.log(` ${brand.cyan('gigaspec generate --config .ai-temp/config.json')}`);
801
+
802
+ // Save partial config for later
803
+ await fs.writeFile(
804
+ path.join(promptPath, 'project-config.json'),
805
+ JSON.stringify(setup.config, null, 2)
806
+ );
807
+
808
+ return {
809
+ status: 'awaiting_ai_analysis',
810
+ config: setup.config,
811
+ promptPath: '.ai-temp/analysis-prompt.txt'
812
+ };
813
+ }
814
+
815
+ config = setup.config;
816
+ }
817
+ // Mode 5: JSON mode without description (error)
818
+ else {
819
+ const error = {
820
+ status: 'error',
821
+ message: 'JSON mode requires either --stack, --yes, or --description for analysis'
822
+ };
823
+ console.log(JSON.stringify(error, null, 2));
824
+ throw new Error(error.message);
825
+ }
826
+
827
+ // Generate files
828
+ if (!options.json) {
829
+ brand.step(3, 3, 'Generating specification files');
830
+ }
831
+
832
+ const genResult = await framework.generate(config);
833
+
834
+ if (options.json) {
835
+ result = {
836
+ status: 'success',
837
+ message: 'Specification framework ready',
838
+ files: genResult.files,
839
+ config: config
840
+ };
841
+ console.log(JSON.stringify(result, null, 2));
842
+ } else {
843
+ brand.divider();
844
+ brand.success('Specification framework ready!');
845
+ brand.divider();
846
+
847
+ console.log('\n' + brand.bold('📁 Generated Files:'));
848
+ genResult.files.forEach(f => console.log(` ${brand.successColor('✓')} ${f}`));
849
+
850
+ console.log('\n' + brand.bold('⭐ START HERE:'));
851
+ console.log(` ${brand.cyan('GETTING_STARTED.md')} - Your step-by-step guide`);
852
+
853
+ console.log('\n' + brand.bold('🤖 For AI Assistants:'));
854
+ console.log(` 1. Read ${brand.cyan('AGENT.md')} for coding standards`);
855
+ console.log(` 2. Check ${brand.cyan('STATE.md')} for current status`);
856
+ console.log(` 3. Run ${brand.cyan('gigaspec status')} to check progress`);
857
+
858
+ console.log('\n' + brand.bold('🚀 Quick Commands:'));
859
+ console.log(` ${brand.cyan('gigaspec status')} - Check what to do next`);
860
+ console.log(` ${brand.cyan('cat STATE.md')} - View current status`);
861
+ console.log(` ${brand.cyan('cat GETTING_STARTED.md')} - Read the full guide`);
862
+
863
+ console.log('\n' + brand.bold('💡 Next Step:'));
864
+ console.log(` Run ${brand.cyan('gigaspec status')} to see your first task!`);
865
+ }
866
+
867
+ return { files: genResult.files, config };
868
+ }
869
+
870
+ async function analyzeCommand(description, options, brand) {
871
+ const framework = new GigaspecFramework({ jsonMode: options.json });
872
+
873
+ const prompt = framework.createAnalysisPrompt(description, {
874
+ weeks: options.weeks || '12'
875
+ });
876
+
877
+ if (options.json) {
878
+ console.log(JSON.stringify({
879
+ status: 'success',
880
+ prompt,
881
+ metadata: {
882
+ description,
883
+ weeks: options.weeks || '12',
884
+ generatedAt: new Date().toISOString()
885
+ }
886
+ }, null, 2));
887
+ } else {
888
+ console.log(prompt);
889
+ }
890
+ }
891
+
892
+ async function generateCommand(options, brand) {
893
+ const framework = new GigaspecFramework({
894
+ outputDir: options.output,
895
+ jsonMode: options.json
896
+ });
897
+
898
+ // Load config from file or options
899
+ let config;
900
+ if (options.config) {
901
+ try {
902
+ config = await fs.readJson(options.config);
903
+ // Ensure all required fields have defaults
904
+ config.name = config.name || 'MyProject';
905
+ config.description = config.description || `${config.name} - A new software project`;
906
+ config.stack = config.stack || 'Node.js/Express';
907
+ config.frontend = config.frontend || 'React/Next.js';
908
+ config.database = config.database || 'PostgreSQL';
909
+ config.cache = config.cache || 'Redis';
910
+ config.services = config.services || [];
911
+ config.deployment = config.deployment || 'Railway';
912
+ config.weeks = config.weeks || 12;
913
+ } catch (err) {
914
+ const error = {
915
+ status: 'error',
916
+ message: `Failed to load config: ${err.message}`
917
+ };
918
+ if (options.json) {
919
+ console.log(JSON.stringify(error, null, 2));
920
+ } else {
921
+ brand.error(error.message);
922
+ }
923
+ process.exit(1);
924
+ return; // For tests that mock process.exit
925
+ }
926
+ } else {
927
+ config = {
928
+ name: options.name || 'MyProject',
929
+ description: options.description || `${options.name || 'MyProject'} - A new software project`,
930
+ stack: options.stack || 'Node.js/Express',
931
+ frontend: options.frontend || 'React/Next.js',
932
+ database: options.database || 'PostgreSQL',
933
+ cache: options.cache || 'Redis',
934
+ services: options.services ? options.services.split(',') : [],
935
+ deployment: options.deploy || 'Railway',
936
+ weeks: parseInt(options.weeks) || 12
937
+ };
938
+ }
939
+
940
+ if (!options.json) {
941
+ brand.step(1, 1, 'Generating specification files');
942
+ }
943
+
944
+ const result = await framework.generate(config);
945
+
946
+ if (options.json) {
947
+ console.log(JSON.stringify({
948
+ status: 'success',
949
+ files: result.files,
950
+ config,
951
+ generatedAt: new Date().toISOString()
952
+ }, null, 2));
953
+ } else {
954
+ brand.divider();
955
+ brand.success('Files generated!');
956
+ brand.divider();
957
+ result.files.forEach(f => console.log(` ${brand.successColor('✓')} ${f}`));
958
+ }
959
+ }
960
+
961
+ module.exports = {
962
+ GigaspecFramework,
963
+ initCommand,
964
+ analyzeCommand,
965
+ generateCommand,
966
+ ANALYSIS_PROMPT,
967
+ QUESTION_ANSWER_PROMPT,
968
+ PROJECT_QUESTIONS,
969
+ STACK_QUESTIONS
970
+ };