wogiflow 1.0.11 → 1.0.13

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 (46) hide show
  1. package/.workflow/specs/architecture.md.template +24 -0
  2. package/.workflow/specs/stack.md.template +33 -0
  3. package/.workflow/specs/testing.md.template +36 -0
  4. package/README.md +90 -1
  5. package/lib/unified-wizard.js +569 -30
  6. package/package.json +1 -1
  7. package/scripts/MEMORY-ARCHITECTURE.md +150 -0
  8. package/scripts/flow +20 -19
  9. package/scripts/flow-auto-context.js +97 -3
  10. package/scripts/flow-conflict-resolver.js +735 -0
  11. package/scripts/flow-context-gatherer.js +520 -0
  12. package/scripts/flow-context-monitor.js +148 -19
  13. package/scripts/flow-damage-control.js +5 -1
  14. package/scripts/flow-export-profile +168 -1
  15. package/scripts/flow-import-profile +257 -6
  16. package/scripts/flow-instruction-richness.js +182 -18
  17. package/scripts/flow-knowledge-router.js +2 -0
  18. package/scripts/flow-knowledge-sync.js +2 -0
  19. package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
  20. package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
  21. package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
  22. package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
  23. package/scripts/flow-memory-db.js +386 -1
  24. package/scripts/flow-memory-sync.js +2 -0
  25. package/scripts/flow-model-adapter.js +53 -29
  26. package/scripts/flow-model-router.js +246 -1
  27. package/scripts/flow-morning.js +94 -0
  28. package/scripts/flow-onboard +223 -10
  29. package/scripts/flow-orchestrate-validation.js +539 -0
  30. package/scripts/flow-orchestrate.js +16 -507
  31. package/scripts/flow-pattern-extractor.js +1265 -0
  32. package/scripts/flow-prompt-composer.js +222 -2
  33. package/scripts/flow-quality-guard.js +594 -0
  34. package/scripts/flow-section-index.js +713 -0
  35. package/scripts/flow-section-resolver.js +484 -0
  36. package/scripts/flow-session-end.js +188 -2
  37. package/scripts/flow-skill-create.js +19 -3
  38. package/scripts/flow-skill-matcher.js +122 -7
  39. package/scripts/flow-statusline-setup.js +218 -0
  40. package/scripts/flow-step-review.js +19 -0
  41. package/scripts/flow-tech-debt.js +734 -0
  42. package/scripts/flow-utils.js +2 -0
  43. package/scripts/hooks/core/long-input-gate.js +293 -0
  44. package/scripts/flow-parallel-detector.js +0 -399
  45. package/scripts/flow-parallel-dispatch.js +0 -987
  46. /package/scripts/{flow-transcript-language.js → flow-long-input-language.js} +0 -0
@@ -91,10 +91,15 @@ class UnifiedWizard {
91
91
  this.rl = null;
92
92
  this.config = {
93
93
  projectName: '',
94
- projectType: '', // 'new' | 'existing'
94
+ description: '',
95
95
  cli: 'claude',
96
+ projectState: '',
97
+ goals: [],
98
+ documentation: [], // Array of {type, inputType, content, summary}
99
+ planningDocs: [], // Roadmap, issue tracker, etc.
100
+ // Legacy fields for backward compatibility
101
+ projectType: '',
96
102
  strictMode: true,
97
- importPath: null,
98
103
  stackSelections: null,
99
104
  scanFindings: null
100
105
  };
@@ -103,6 +108,7 @@ class UnifiedWizard {
103
108
 
104
109
  /**
105
110
  * Run the unified wizard
111
+ * New flow: CLI first → basics → docs → state → goals → planning → summary → AI handoff
106
112
  */
107
113
  async run() {
108
114
  this.rl = readline.createInterface({
@@ -114,44 +120,33 @@ class UnifiedWizard {
114
120
  // Step 1: Welcome
115
121
  this.printWelcome();
116
122
 
117
- // Step 2: Project name
118
- await this.askProjectName();
123
+ // Step 2: CLI selection (FIRST)
124
+ await this.askCLI();
119
125
 
120
- // Step 3: New or existing project?
121
- await this.askProjectType();
126
+ // Step 3: Project name
127
+ await this.askProjectName();
122
128
 
123
- // Step 4: Import config option (for solo devs)
124
- const imported = await this.askImportConfig();
125
- if (imported) {
126
- await this.finalizeSetup();
127
- return this.config;
128
- }
129
+ // Step 4: Description (short or documentation)
130
+ await this.askDescription();
129
131
 
130
- // Step 5: Branch based on project type
131
- if (this.config.projectType === 'new') {
132
- await this.runStackWizard();
133
- } else {
134
- await this.runProjectScanner();
135
- }
132
+ // Step 5: Project state
133
+ await this.askProjectState();
136
134
 
137
- // Step 6: CLI preference
138
- await this.askCLIPreference();
135
+ // Step 6: Goals (multi-select)
136
+ await this.askGoals();
139
137
 
140
- // Step 7: Strict mode
141
- await this.askStrictMode();
138
+ // Step 7: Planning documents
139
+ await this.askPlanningDocs();
142
140
 
143
141
  // Step 8: Show summary and confirm
144
- const confirmed = await this.showSummaryAndConfirm();
142
+ const confirmed = await this.showSummary();
145
143
  if (!confirmed) {
146
- console.log(c('yellow', '\nSetup cancelled. Run `flow init` to try again.\n'));
144
+ console.log(c('yellow', '\nSetup cancelled. Run `npx flow onboard` to try again.\n'));
147
145
  return null;
148
146
  }
149
147
 
150
- // Step 9: Create everything
151
- await this.finalizeSetup();
152
-
153
- // Step 10: Success
154
- this.printSuccess();
148
+ // Step 9: Create structure and handoff to AI
149
+ await this.finalizeAndHandoff();
155
150
 
156
151
  return this.config;
157
152
 
@@ -315,10 +310,301 @@ class UnifiedWizard {
315
310
  });
316
311
  }
317
312
 
313
+ /**
314
+ * Single-select with arrow key navigation
315
+ * @param {string} question - The question to ask
316
+ * @param {Array} options - Array of {key, label, description?} objects
317
+ * @param {string} defaultKey - Default selected key
318
+ * @returns {Promise<string>} Selected key
319
+ */
320
+ askSingleSelect(question, options, defaultKey = null) {
321
+ return new Promise((resolve) => {
322
+ // Fallback if no TTY
323
+ if (!process.stdin.isTTY || !process.stdin.setRawMode) {
324
+ console.log(`\n${question}\n`);
325
+ options.forEach((opt, i) => {
326
+ const marker = opt.key === defaultKey ? '>' : ' ';
327
+ console.log(` ${marker} (${i + 1}) ${opt.label}${opt.description ? ` - ${opt.description}` : ''}`);
328
+ });
329
+ return this.ask('Your choice (number)', '1').then(answer => {
330
+ const num = parseInt(answer, 10);
331
+ if (num >= 1 && num <= options.length) {
332
+ resolve(options[num - 1].key);
333
+ } else {
334
+ resolve(defaultKey || options[0].key);
335
+ }
336
+ });
337
+ }
338
+
339
+ let selectedIndex = defaultKey ? options.findIndex(o => o.key === defaultKey) : 0;
340
+ if (selectedIndex < 0) selectedIndex = 0;
341
+
342
+ const render = () => {
343
+ // Move cursor up to redraw (except first render)
344
+ process.stdout.write(`\x1b[${options.length}A\x1b[J`);
345
+ options.forEach((opt, i) => {
346
+ const selected = i === selectedIndex;
347
+ const marker = selected ? c('cyan', '❯') : ' ';
348
+ const radio = selected ? c('green', '●') : '○';
349
+ const label = selected ? c('bold', opt.label) : opt.label;
350
+ const desc = opt.description ? c('dim', ` - ${opt.description}`) : '';
351
+ console.log(` ${marker} ${radio} ${label}${desc}`);
352
+ });
353
+ process.stdout.write(c('dim', '\n [↑↓ to move, Enter to select]'));
354
+ };
355
+
356
+ console.log(`\n${question}\n`);
357
+ // Initial render (print blank lines first)
358
+ options.forEach(() => console.log(''));
359
+ console.log('');
360
+ render();
361
+
362
+ this.rl.pause();
363
+ process.stdin.setRawMode(true);
364
+ process.stdin.resume();
365
+
366
+ const cleanup = () => {
367
+ process.stdin.setRawMode(false);
368
+ process.stdin.removeListener('data', onKey);
369
+ this.rl.resume();
370
+ };
371
+
372
+ const onKey = (key) => {
373
+ const seq = key.toString();
374
+
375
+ // Arrow up
376
+ if (seq === '\x1b[A' || seq === 'k') {
377
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
378
+ render();
379
+ return;
380
+ }
381
+
382
+ // Arrow down
383
+ if (seq === '\x1b[B' || seq === 'j') {
384
+ selectedIndex = (selectedIndex + 1) % options.length;
385
+ render();
386
+ return;
387
+ }
388
+
389
+ // Enter
390
+ if (seq === '\r' || seq === '\n') {
391
+ cleanup();
392
+ process.stdout.write('\x1b[K\n'); // Clear hint line
393
+ resolve(options[selectedIndex].key);
394
+ return;
395
+ }
396
+
397
+ // Ctrl+C
398
+ if (seq === '\x03') {
399
+ cleanup();
400
+ process.stdout.write('\n');
401
+ process.exit();
402
+ }
403
+ };
404
+
405
+ process.stdin.on('data', onKey);
406
+ });
407
+ }
408
+
409
+ /**
410
+ * Multi-select with checkbox toggle (space to toggle, enter to confirm)
411
+ * @param {string} question - The question to ask
412
+ * @param {Array} options - Array of {key, label, description?, default?} objects
413
+ * @returns {Promise<string[]>} Array of selected keys
414
+ */
415
+ askMultiSelect(question, options) {
416
+ return new Promise((resolve) => {
417
+ // Fallback if no TTY
418
+ if (!process.stdin.isTTY || !process.stdin.setRawMode) {
419
+ console.log(`\n${question}\n`);
420
+ options.forEach((opt, i) => {
421
+ const checked = opt.default ? '[x]' : '[ ]';
422
+ console.log(` ${checked} (${i + 1}) ${opt.label}${opt.description ? ` - ${opt.description}` : ''}`);
423
+ });
424
+ return this.ask('Select (comma-separated numbers)', '').then(answer => {
425
+ if (!answer) {
426
+ resolve(options.filter(o => o.default).map(o => o.key));
427
+ return;
428
+ }
429
+ const nums = answer.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n));
430
+ resolve(nums.filter(n => n >= 1 && n <= options.length).map(n => options[n - 1].key));
431
+ });
432
+ }
433
+
434
+ let cursorIndex = 0;
435
+ const selected = new Set(options.filter(o => o.default).map(o => o.key));
436
+
437
+ const render = () => {
438
+ // Move cursor up to redraw
439
+ process.stdout.write(`\x1b[${options.length}A\x1b[J`);
440
+ options.forEach((opt, i) => {
441
+ const isCursor = i === cursorIndex;
442
+ const isSelected = selected.has(opt.key);
443
+ const cursor = isCursor ? c('cyan', '❯') : ' ';
444
+ const checkbox = isSelected ? c('green', '☑') : '☐';
445
+ const label = isCursor ? c('bold', opt.label) : opt.label;
446
+ const desc = opt.description ? c('dim', ` - ${opt.description}`) : '';
447
+ console.log(` ${cursor} ${checkbox} ${label}${desc}`);
448
+ });
449
+ process.stdout.write(c('dim', '\n [↑↓ move, Space toggle, Enter done]'));
450
+ };
451
+
452
+ console.log(`\n${question}\n`);
453
+ // Initial render
454
+ options.forEach(() => console.log(''));
455
+ console.log('');
456
+ render();
457
+
458
+ this.rl.pause();
459
+ process.stdin.setRawMode(true);
460
+ process.stdin.resume();
461
+
462
+ const cleanup = () => {
463
+ process.stdin.setRawMode(false);
464
+ process.stdin.removeListener('data', onKey);
465
+ this.rl.resume();
466
+ };
467
+
468
+ const onKey = (key) => {
469
+ const seq = key.toString();
470
+
471
+ // Arrow up
472
+ if (seq === '\x1b[A' || seq === 'k') {
473
+ cursorIndex = (cursorIndex - 1 + options.length) % options.length;
474
+ render();
475
+ return;
476
+ }
477
+
478
+ // Arrow down
479
+ if (seq === '\x1b[B' || seq === 'j') {
480
+ cursorIndex = (cursorIndex + 1) % options.length;
481
+ render();
482
+ return;
483
+ }
484
+
485
+ // Space - toggle
486
+ if (seq === ' ') {
487
+ const key = options[cursorIndex].key;
488
+ if (selected.has(key)) {
489
+ selected.delete(key);
490
+ } else {
491
+ selected.add(key);
492
+ }
493
+ render();
494
+ return;
495
+ }
496
+
497
+ // Enter - confirm
498
+ if (seq === '\r' || seq === '\n') {
499
+ cleanup();
500
+ process.stdout.write('\x1b[K\n');
501
+ resolve(Array.from(selected));
502
+ return;
503
+ }
504
+
505
+ // Ctrl+C
506
+ if (seq === '\x03') {
507
+ cleanup();
508
+ process.stdout.write('\n');
509
+ process.exit();
510
+ }
511
+ };
512
+
513
+ process.stdin.on('data', onKey);
514
+ });
515
+ }
516
+
517
+ /**
518
+ * Ask for document input - either paste content or link to file
519
+ * @param {string} docType - Name of the document type (e.g., "PRD", "README")
520
+ * @returns {Promise<{type: 'paste'|'link', content: string, summary: string}>}
521
+ */
522
+ async askDocumentInput(docType) {
523
+ // First ask: paste or link?
524
+ const inputType = await this.askSingleSelect(
525
+ `How do you want to provide the ${docType}?`,
526
+ [
527
+ { key: 'paste', label: 'Paste content', description: 'Paste the document content directly' },
528
+ { key: 'link', label: 'Link to file', description: 'Provide a file path' }
529
+ ],
530
+ 'paste'
531
+ );
532
+
533
+ if (inputType === 'link') {
534
+ const filePath = await this.ask(`File path for ${docType}`);
535
+ if (!filePath) {
536
+ return { type: 'link', content: '', summary: '[no file provided]' };
537
+ }
538
+
539
+ // Validate file exists
540
+ const resolvedPath = path.resolve(this.projectRoot, filePath);
541
+ if (!isPathWithinProject(resolvedPath, this.projectRoot)) {
542
+ console.log(c('yellow', ' Path must be within project directory'));
543
+ return { type: 'link', content: '', summary: '[invalid path]' };
544
+ }
545
+
546
+ try {
547
+ fs.accessSync(resolvedPath, fs.constants.R_OK);
548
+ return { type: 'link', content: resolvedPath, summary: `[linked - ${filePath}]` };
549
+ } catch {
550
+ console.log(c('yellow', ' File not found or not readable'));
551
+ return { type: 'link', content: '', summary: '[file not found]' };
552
+ }
553
+ }
554
+
555
+ // Paste mode - multi-line input
556
+ console.log(c('dim', `\nPaste ${docType} content (press Enter twice on empty line when done):\n`));
557
+
558
+ return new Promise((resolve) => {
559
+ const lines = [];
560
+ let emptyLineCount = 0;
561
+
562
+ const lineHandler = (line) => {
563
+ if (line === '') {
564
+ emptyLineCount++;
565
+ if (emptyLineCount >= 2) {
566
+ this.rl.removeListener('line', lineHandler);
567
+ const content = lines.join('\n');
568
+ const lineCount = lines.length;
569
+ const summary = `[pasted - ${lineCount} line${lineCount !== 1 ? 's' : ''}]`;
570
+ console.log(c('green', ` ${summary}\n`));
571
+ resolve({ type: 'paste', content, summary });
572
+ return;
573
+ }
574
+ } else {
575
+ emptyLineCount = 0;
576
+ }
577
+ lines.push(line);
578
+ };
579
+
580
+ this.rl.on('line', lineHandler);
581
+ });
582
+ }
583
+
318
584
  // ============================================
319
- // STEP IMPLEMENTATIONS
585
+ // STEP IMPLEMENTATIONS (New Flow)
320
586
  // ============================================
321
587
 
588
+ /**
589
+ * Step 2: Ask CLI selection (FIRST question)
590
+ */
591
+ async askCLI() {
592
+ const cliOptions = Object.entries(SUPPORTED_CLIS).map(([key, value]) => ({
593
+ key,
594
+ label: value.name,
595
+ description: value.description
596
+ }));
597
+
598
+ this.config.cli = await this.askSingleSelect(
599
+ 'Which AI CLI are you using?',
600
+ cliOptions,
601
+ 'claude'
602
+ );
603
+ }
604
+
605
+ /**
606
+ * Step 3: Ask project name
607
+ */
322
608
  async askProjectName() {
323
609
  // Try to detect from package.json
324
610
  const packageJsonPath = path.join(this.projectRoot, 'package.json');
@@ -341,6 +627,259 @@ class UnifiedWizard {
341
627
  this.config.projectName = await this.askWithPlaceholder('Project name', detectedName);
342
628
  }
343
629
 
630
+ /**
631
+ * Step 4: Ask for description (short or documentation)
632
+ */
633
+ async askDescription() {
634
+ const hasDocsChoice = await this.askSingleSelect(
635
+ 'How would you like to describe your project?',
636
+ [
637
+ { key: 'short', label: 'Short description', description: '1-2 sentences' },
638
+ { key: 'docs', label: 'I have documentation', description: 'PRD, README, specs, etc.' }
639
+ ],
640
+ 'short'
641
+ );
642
+
643
+ if (hasDocsChoice === 'short') {
644
+ this.config.description = await this.ask('What does this project do? (1-2 sentences)');
645
+ return;
646
+ }
647
+
648
+ // Documentation flow
649
+ const docTypes = await this.askMultiSelect(
650
+ 'What documentation do you have?',
651
+ [
652
+ { key: 'prd', label: 'PRD / Product Spec' },
653
+ { key: 'readme', label: 'README' },
654
+ { key: 'architecture', label: 'Architecture docs' },
655
+ { key: 'api', label: 'API documentation' },
656
+ { key: 'other', label: 'Other' }
657
+ ]
658
+ );
659
+
660
+ if (docTypes.length === 0) {
661
+ // Fallback to short description
662
+ this.config.description = await this.ask('What does this project do? (1-2 sentences)');
663
+ return;
664
+ }
665
+
666
+ // For each selected doc type, get input
667
+ const docTypeNames = {
668
+ prd: 'PRD / Product Spec',
669
+ readme: 'README',
670
+ architecture: 'Architecture docs',
671
+ api: 'API documentation',
672
+ other: 'Other documentation'
673
+ };
674
+
675
+ for (const docType of docTypes) {
676
+ const docName = docTypeNames[docType] || docType;
677
+ const input = await this.askDocumentInput(docName);
678
+ this.config.documentation.push({
679
+ type: docType,
680
+ name: docName,
681
+ inputType: input.type,
682
+ content: input.content,
683
+ summary: input.summary
684
+ });
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Step 5: Ask project state
690
+ */
691
+ async askProjectState() {
692
+ this.config.projectState = await this.askSingleSelect(
693
+ "What's your project's current state?",
694
+ [
695
+ { key: 'new', label: 'New / early development', description: 'Just starting out' },
696
+ { key: 'mvp', label: 'MVP / working prototype', description: 'Core features working' },
697
+ { key: 'production', label: 'Production with users', description: 'Live and serving users' },
698
+ { key: 'maintenance', label: 'Maintenance mode', description: 'Stable, minimal changes' }
699
+ ],
700
+ 'mvp'
701
+ );
702
+ }
703
+
704
+ /**
705
+ * Step 6: Ask goals (multi-select)
706
+ */
707
+ async askGoals() {
708
+ this.config.goals = await this.askMultiSelect(
709
+ 'What are you trying to accomplish with AI assistance?',
710
+ [
711
+ { key: 'features', label: 'Add new features', default: true },
712
+ { key: 'bugs', label: 'Fix bugs', default: true },
713
+ { key: 'refactor', label: 'Refactor / improve code quality' },
714
+ { key: 'tests', label: 'Add tests' },
715
+ { key: 'docs', label: 'Documentation' },
716
+ { key: 'performance', label: 'Performance optimization' },
717
+ { key: 'security', label: 'Security improvements' }
718
+ ]
719
+ );
720
+ }
721
+
722
+ /**
723
+ * Step 7: Ask about planning documents
724
+ */
725
+ async askPlanningDocs() {
726
+ const planningTypes = await this.askMultiSelect(
727
+ 'Do you have any existing planning documents the AI should analyze?',
728
+ [
729
+ { key: 'roadmap', label: 'Roadmap / backlog' },
730
+ { key: 'issues', label: 'Issue tracker export' },
731
+ { key: 'techdebt', label: 'Technical debt notes' },
732
+ { key: 'none', label: 'None of these', default: true }
733
+ ]
734
+ );
735
+
736
+ // Filter out 'none' and store
737
+ this.config.planningDocs = planningTypes.filter(t => t !== 'none');
738
+
739
+ if (this.config.planningDocs.length > 0) {
740
+ console.log(c('dim', '\n These will be analyzed when the AI scans your project.\n'));
741
+ }
742
+ }
743
+
744
+ /**
745
+ * Step 8: Show summary
746
+ */
747
+ async showSummary() {
748
+ const cli = SUPPORTED_CLIS[this.config.cli];
749
+
750
+ console.log('\n' + c('cyan', '═'.repeat(60)));
751
+ console.log(c('cyan', ' Setup Summary'));
752
+ console.log(c('cyan', '═'.repeat(60)) + '\n');
753
+
754
+ console.log(` Project: ${c('bold', this.config.projectName)}`);
755
+ console.log(` CLI: ${c('bold', cli.name)}`);
756
+ console.log(` State: ${c('bold', this.config.projectState || 'Not specified')}`);
757
+
758
+ if (this.config.goals.length > 0) {
759
+ console.log(` Goals: ${c('bold', this.config.goals.join(', '))}`);
760
+ }
761
+
762
+ if (this.config.documentation.length > 0) {
763
+ console.log(` Documentation: ${c('bold', this.config.documentation.length + ' file(s)')}`);
764
+ this.config.documentation.forEach(doc => {
765
+ console.log(` - ${doc.name}: ${c('dim', doc.summary)}`);
766
+ });
767
+ } else if (this.config.description) {
768
+ console.log(` Description: ${c('dim', this.config.description.substring(0, 50) + (this.config.description.length > 50 ? '...' : ''))}`);
769
+ }
770
+
771
+ console.log('\n' + c('bold', 'Ready to let the AI analyze your project?'));
772
+ console.log(c('dim', 'This will create the workflow structure and prepare for AI analysis.\n'));
773
+
774
+ return await this.askYesNo('Proceed?', true);
775
+ }
776
+
777
+ /**
778
+ * Step 9: Finalize and handoff to AI
779
+ */
780
+ async finalizeAndHandoff() {
781
+ console.log(c('dim', '\nCreating project structure...\n'));
782
+
783
+ // Create minimal structure
784
+ await this.createWorkflowStructure();
785
+ await this.createCLIConfig();
786
+
787
+ // Save onboarding data for AI to read
788
+ await this.saveOnboardingData();
789
+
790
+ // Determine if running inside AI session
791
+ const inSession = this.detectAISession();
792
+
793
+ if (inSession) {
794
+ // Already in AI session - output trigger for AI to continue
795
+ this.printAITrigger();
796
+ } else {
797
+ // Not in session - show instructions
798
+ this.printHandoffInstructions();
799
+ }
800
+ }
801
+
802
+ /**
803
+ * Detect if running inside an AI CLI session
804
+ */
805
+ detectAISession() {
806
+ // Check for common environment indicators
807
+ return !!(
808
+ process.env.CLAUDE_CODE ||
809
+ process.env.ANTHROPIC_API_KEY ||
810
+ process.env.GEMINI_CLI ||
811
+ process.env.OPENCODE_SESSION ||
812
+ // Check if parent process looks like an AI CLI
813
+ (process.env._ && (
814
+ process.env._.includes('claude') ||
815
+ process.env._.includes('gemini') ||
816
+ process.env._.includes('opencode')
817
+ ))
818
+ );
819
+ }
820
+
821
+ /**
822
+ * Print trigger for AI to continue (when in session)
823
+ */
824
+ printAITrigger() {
825
+ console.log('\n' + c('green', '═'.repeat(60)));
826
+ console.log(c('green', ' ✅ Structure created!'));
827
+ console.log(c('green', '═'.repeat(60)) + '\n');
828
+
829
+ console.log('The AI can now analyze your project.\n');
830
+ console.log(c('yellow', 'AI: Please run /wogi-onboard to complete the setup by:'));
831
+ console.log(' • Scanning the codebase for patterns and structure');
832
+ console.log(' • Detecting frameworks, testing, and coding styles');
833
+ console.log(' • Generating component map and architecture docs');
834
+ console.log(' • Creating coding rules based on detected patterns\n');
835
+ }
836
+
837
+ /**
838
+ * Print handoff instructions (when not in session)
839
+ */
840
+ printHandoffInstructions() {
841
+ const cli = SUPPORTED_CLIS[this.config.cli];
842
+ const cliCommand = this.config.cli; // 'claude', 'gemini', 'opencode'
843
+
844
+ console.log('\n' + c('cyan', '╔════════════════════════════════════════════════════════════╗'));
845
+ console.log(c('cyan', '║') + ' ' + c('bold', 'Almost done! Start your AI CLI to complete setup.') + ' ' + c('cyan', '║'));
846
+ console.log(c('cyan', '╚════════════════════════════════════════════════════════════╝') + '\n');
847
+
848
+ console.log(` Run: ${c('yellow', cliCommand)}\n`);
849
+
850
+ console.log(' The AI will:');
851
+ console.log(' • Scan your codebase for patterns');
852
+ console.log(' • Detect frameworks and coding styles');
853
+ console.log(' • Generate component map and architecture docs');
854
+ console.log(' • Ask clarifying questions if needed\n');
855
+ }
856
+
857
+ /**
858
+ * Save onboarding data for AI to read
859
+ */
860
+ async saveOnboardingData() {
861
+ const onboardingData = {
862
+ version: PACKAGE_VERSION,
863
+ timestamp: new Date().toISOString(),
864
+ projectName: this.config.projectName,
865
+ description: this.config.description,
866
+ cli: this.config.cli,
867
+ projectState: this.config.projectState,
868
+ goals: this.config.goals,
869
+ documentation: this.config.documentation,
870
+ planningDocs: this.config.planningDocs,
871
+ status: 'pending_ai_analysis'
872
+ };
873
+
874
+ const onboardingPath = path.join(this.projectRoot, '.workflow', 'state', 'onboarding.json');
875
+ fs.writeFileSync(onboardingPath, JSON.stringify(onboardingData, null, 2));
876
+ console.log(' ' + c('green', '✓') + ' Saved onboarding data for AI analysis');
877
+ }
878
+
879
+ // ============================================
880
+ // LEGACY METHODS (kept for backward compatibility)
881
+ // ============================================
882
+
344
883
  async askProjectType() {
345
884
  const choice = await this.askChoice(
346
885
  'Is this a new project or an existing codebase?',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {