ultra-dex 1.7.3 → 2.2.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 (119) hide show
  1. package/README.md +160 -127
  2. package/assets/agents/0-orchestration/orchestrator.md +225 -0
  3. package/assets/agents/00-AGENT_INDEX.md +138 -0
  4. package/assets/agents/1-leadership/cto.md +186 -0
  5. package/assets/agents/1-leadership/planner.md +205 -0
  6. package/assets/agents/1-leadership/research.md +285 -0
  7. package/assets/agents/2-development/backend.md +472 -0
  8. package/assets/agents/2-development/database.md +516 -0
  9. package/assets/agents/2-development/frontend.md +144 -0
  10. package/assets/agents/3-security/auth.md +168 -0
  11. package/assets/agents/3-security/security.md +335 -0
  12. package/assets/agents/4-devops/devops.md +587 -0
  13. package/assets/agents/5-quality/debugger.md +188 -0
  14. package/assets/agents/5-quality/documentation.md +167 -0
  15. package/assets/agents/5-quality/reviewer.md +213 -0
  16. package/assets/agents/5-quality/testing.md +280 -0
  17. package/assets/agents/6-specialist/performance.md +323 -0
  18. package/assets/agents/6-specialist/refactoring.md +343 -0
  19. package/assets/agents/AGENT-INSTRUCTIONS.md +315 -0
  20. package/assets/agents/README.md +232 -0
  21. package/assets/cursor-rules/00-ultra-dex-core.mdc +48 -0
  22. package/assets/cursor-rules/01-database.mdc +50 -0
  23. package/assets/cursor-rules/02-api.mdc +81 -0
  24. package/assets/cursor-rules/03-auth.mdc +70 -0
  25. package/assets/cursor-rules/04-frontend.mdc +92 -0
  26. package/assets/cursor-rules/05-payments.mdc +88 -0
  27. package/assets/cursor-rules/06-testing.mdc +104 -0
  28. package/assets/cursor-rules/07-security.mdc +94 -0
  29. package/assets/cursor-rules/08-deployment.mdc +92 -0
  30. package/assets/cursor-rules/09-error-handling.mdc +137 -0
  31. package/assets/cursor-rules/10-performance.mdc +123 -0
  32. package/assets/cursor-rules/11-nextjs-v15.mdc +307 -0
  33. package/assets/cursor-rules/12-multi-tenancy.mdc +282 -0
  34. package/assets/cursor-rules/README.md +78 -0
  35. package/assets/cursor-rules/load.ps1 +108 -0
  36. package/assets/cursor-rules/load.sh +102 -0
  37. package/assets/docs/BUILD-AUTH-30M.md +113 -0
  38. package/assets/docs/CHECKLIST-21-STEP.md +86 -0
  39. package/assets/docs/CODEMAP.md +229 -0
  40. package/assets/docs/CUSTOMIZATION.md +127 -0
  41. package/assets/docs/LAUNCH-POSTS.md +238 -0
  42. package/assets/docs/QUICK-REFERENCE.md +338 -0
  43. package/assets/docs/README.md +21 -0
  44. package/assets/docs/ROADMAP.md +480 -0
  45. package/assets/docs/TROUBLESHOOTING.md +148 -0
  46. package/assets/docs/TUTORIAL.md +182 -0
  47. package/assets/docs/VERIFICATION.md +108 -0
  48. package/assets/docs/VISION-V2.md +187 -0
  49. package/assets/docs/WORKFLOW-DIAGRAMS.md +463 -0
  50. package/assets/docs/index.html +550 -0
  51. package/assets/live-templates/next15-prisma-clerk/.env.example +3 -0
  52. package/assets/live-templates/next15-prisma-clerk/README.md +10 -0
  53. package/assets/live-templates/next15-prisma-clerk/app/layout.tsx +7 -0
  54. package/assets/live-templates/next15-prisma-clerk/app/page.tsx +8 -0
  55. package/assets/live-templates/next15-prisma-clerk/next.config.js +6 -0
  56. package/assets/live-templates/next15-prisma-clerk/package.json +22 -0
  57. package/assets/live-templates/next15-prisma-clerk/prisma/schema.prisma +34 -0
  58. package/assets/live-templates/remix-supabase/.env.example +2 -0
  59. package/assets/live-templates/remix-supabase/README.md +9 -0
  60. package/assets/live-templates/remix-supabase/app/root.tsx +19 -0
  61. package/assets/live-templates/remix-supabase/app/routes/_index.tsx +8 -0
  62. package/assets/live-templates/remix-supabase/app/utils/supabase.server.ts +6 -0
  63. package/assets/live-templates/remix-supabase/package.json +20 -0
  64. package/assets/live-templates/remix-supabase/remix.config.js +6 -0
  65. package/assets/live-templates/sveltekit-drizzle/.env.example +1 -0
  66. package/assets/live-templates/sveltekit-drizzle/README.md +9 -0
  67. package/assets/live-templates/sveltekit-drizzle/drizzle/schema.ts +7 -0
  68. package/assets/live-templates/sveltekit-drizzle/drizzle.config.ts +5 -0
  69. package/assets/live-templates/sveltekit-drizzle/package.json +21 -0
  70. package/assets/live-templates/sveltekit-drizzle/src/lib/db.ts +5 -0
  71. package/assets/live-templates/sveltekit-drizzle/src/routes/+page.svelte +2 -0
  72. package/assets/live-templates/sveltekit-drizzle/svelte.config.js +5 -0
  73. package/assets/live-templates/sveltekit-drizzle/vite.config.js +5 -0
  74. package/assets/saas-plan/04-Imp-Template.md +5546 -0
  75. package/assets/templates/CASE-STUDY-TEMPLATE.md +139 -0
  76. package/assets/templates/MASTER-PLAN-TEMPLATE.md +647 -0
  77. package/assets/templates/ORDER-TRACKER-TEMPLATE.md +731 -0
  78. package/assets/templates/PHASE-TRACKER-TEMPLATE.md +577 -0
  79. package/assets/templates/README.md +419 -0
  80. package/bin/ultra-dex.js +1078 -422
  81. package/lib/commands/agents.js +154 -0
  82. package/lib/commands/audit.js +135 -0
  83. package/lib/commands/banner.js +21 -0
  84. package/lib/commands/build.js +214 -0
  85. package/lib/commands/examples.js +34 -0
  86. package/lib/commands/fetch.js +186 -0
  87. package/lib/commands/generate.js +217 -0
  88. package/lib/commands/hooks.js +105 -0
  89. package/lib/commands/init.js +337 -0
  90. package/lib/commands/placeholders.js +11 -0
  91. package/lib/commands/review.js +287 -0
  92. package/lib/commands/serve.js +56 -0
  93. package/lib/commands/suggest.js +126 -0
  94. package/lib/commands/validate.js +140 -0
  95. package/lib/commands/workflows.js +185 -0
  96. package/lib/config/paths.js +9 -0
  97. package/lib/config/urls.js +16 -0
  98. package/lib/providers/base.js +82 -0
  99. package/lib/providers/claude.js +177 -0
  100. package/lib/providers/gemini.js +170 -0
  101. package/lib/providers/index.js +93 -0
  102. package/lib/providers/openai.js +163 -0
  103. package/lib/templates/context.js +26 -0
  104. package/lib/templates/embedded.js +141 -0
  105. package/lib/templates/prompts/generate-plan.js +147 -0
  106. package/lib/templates/prompts/review-code.js +57 -0
  107. package/lib/templates/prompts/section-prompts.js +275 -0
  108. package/lib/templates/prompts/system-prompt.md +58 -0
  109. package/lib/templates/quick-start.js +43 -0
  110. package/lib/utils/build-helpers.js +257 -0
  111. package/lib/utils/fallback.js +36 -0
  112. package/lib/utils/files.js +67 -0
  113. package/lib/utils/network.js +18 -0
  114. package/lib/utils/output.js +20 -0
  115. package/lib/utils/parser.js +155 -0
  116. package/lib/utils/prompt-builder.js +93 -0
  117. package/lib/utils/review-helpers.js +334 -0
  118. package/lib/utils/validation.js +34 -0
  119. package/package.json +19 -5
package/bin/ultra-dex.js CHANGED
@@ -1,426 +1,43 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import inquirer from 'inquirer';
5
- import chalk from 'chalk';
6
- import ora from 'ora';
7
- import fs from 'fs/promises';
8
- import path from 'path';
9
- import { fileURLToPath } from 'url';
4
+ import { Command } from 'commander';
10
5
 
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = path.dirname(__filename);
6
+ import { banner } from '../lib/commands/banner.js';
7
+ import { registerInitCommand } from '../lib/commands/init.js';
8
+ import { registerAuditCommand } from '../lib/commands/audit.js';
9
+ import { registerExamplesCommand } from '../lib/commands/examples.js';
10
+ import { registerAgentsCommand, registerPackCommand } from '../lib/commands/agents.js';
11
+ import { registerPlaceholderCommands } from '../lib/commands/placeholders.js';
12
+ import { registerServeCommand } from '../lib/commands/serve.js';
13
+ import { registerWorkflowCommand } from '../lib/commands/workflows.js';
14
+ import { registerSuggestCommand } from '../lib/commands/suggest.js';
15
+ import { registerValidateCommand } from '../lib/commands/validate.js';
16
+ import { registerHooksCommand } from '../lib/commands/hooks.js';
17
+ import { registerFetchCommand } from '../lib/commands/fetch.js';
18
+ import { registerSyncCommand } from '../lib/commands/sync.js';
13
19
 
14
20
  const program = new Command();
15
-
16
- // ASCII Art Banner
17
- const banner = `
18
- ╔═══════════════════════════════════════════════════════════╗
19
- ā•‘ ā•‘
20
- ā•‘ ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘
21
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•— ā•‘
22
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā•‘
23
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ ā•‘
24
- ā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā•‘
25
- ā•‘ ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•‘
26
- ā•‘ ā•‘
27
- ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā•‘
28
- ā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•”ā• ā•‘
29
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā•”ā• ā•‘
30
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā•‘
31
- ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•”ā• ā–ˆā–ˆā•— ā•‘
32
- ā•‘ ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā• ā•‘
33
- ā•‘ ā•‘
34
- ā•‘ From Idea to Production-Ready SaaS ā•‘
35
- ā•‘ ā•‘
36
- ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
37
- `;
38
-
39
- // Template content (embedded)
40
- const QUICK_START_TEMPLATE = `# {{PROJECT_NAME}} - Quick Start
41
-
42
- ## 1. Your Idea (2 sentences max)
43
-
44
- **What:** {{IDEA_WHAT}}
45
- **For whom:** {{IDEA_FOR}}
46
-
47
- ## 2. The Problem (3 bullets)
48
-
49
- - {{PROBLEM_1}}
50
- - {{PROBLEM_2}}
51
- - {{PROBLEM_3}}
52
-
53
- ## 3. Core Production Features (5 max)
54
-
55
- | Feature | Priority | Justification |
56
- |---------|----------|---------------|
57
- | {{FEATURE_1}} | P0 | |
58
- | | P0 | |
59
- | | P1 | |
60
- | | P1 | |
61
- | | P2 | |
62
-
63
- ## 4. Tech Stack
64
-
65
- | Layer | Your Choice |
66
- |-------|-------------|
67
- | Frontend | {{FRONTEND}} |
68
- | Database | {{DATABASE}} |
69
- | Auth | {{AUTH}} |
70
- | Payments | {{PAYMENTS}} |
71
- | Hosting | {{HOSTING}} |
72
-
73
- ## 5. First 3 Tasks
74
-
75
- 1. [ ] Set up project with chosen stack
76
- 2. [ ] Implement core feature #1
77
- 3. [ ] Deploy to staging
78
-
79
- ---
80
-
81
- **Next:** Fill out the full implementation plan using the Ultra-Dex template.
82
- `;
83
-
84
- const CONTEXT_TEMPLATE = `# {{PROJECT_NAME}} - Context
85
-
86
- ## Project Overview
87
- **Name:** {{PROJECT_NAME}}
88
- **Started:** {{DATE}}
89
- **Status:** Planning
90
-
91
- ## Quick Summary
92
- {{IDEA_WHAT}} for {{IDEA_FOR}}.
93
-
94
- ## Key Decisions
95
- - Frontend: {{FRONTEND}}
96
- - Database: {{DATABASE}}
97
- - Auth: {{AUTH}}
98
- - Payments: {{PAYMENTS}}
99
- - Hosting: {{HOSTING}}
100
-
101
- ## Current Focus
102
- Setting up the implementation plan.
103
-
104
- ## Resources
105
- - [Ultra-Dex Template](https://github.com/Srujan0798/Ultra-Dex)
106
- - [TaskFlow Example](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/Examples/TaskFlow-Complete.md)
107
- `;
21
+ program.banner = banner;
108
22
 
109
23
  program
110
24
  .name('ultra-dex')
111
25
  .description('CLI for Ultra-Dex SaaS Implementation Framework')
112
- .version('1.7.1');
113
-
114
- program
115
- .command('init')
116
- .description('Initialize a new Ultra-Dex project')
117
- .option('-n, --name <name>', 'Project name')
118
- .option('-d, --dir <directory>', 'Output directory', '.')
119
- .action(async (options) => {
120
- console.log(chalk.cyan(banner));
121
- console.log(chalk.bold('\nWelcome to Ultra-Dex! Let\'s plan your SaaS.\n'));
122
-
123
- // Gather project info
124
- const answers = await inquirer.prompt([
125
- {
126
- type: 'input',
127
- name: 'projectName',
128
- message: 'What\'s your project name?',
129
- default: options.name || 'my-saas',
130
- validate: (input) => input.length > 0 || 'Project name is required',
131
- },
132
- {
133
- type: 'input',
134
- name: 'ideaWhat',
135
- message: 'What are you building? (1 sentence)',
136
- validate: (input) => input.length > 0 || 'Please describe your idea',
137
- },
138
- {
139
- type: 'input',
140
- name: 'ideaFor',
141
- message: 'Who is it for?',
142
- validate: (input) => input.length > 0 || 'Please specify your target users',
143
- },
144
- {
145
- type: 'input',
146
- name: 'problem1',
147
- message: 'Problem #1 you\'re solving:',
148
- default: '',
149
- },
150
- {
151
- type: 'input',
152
- name: 'problem2',
153
- message: 'Problem #2 you\'re solving:',
154
- default: '',
155
- },
156
- {
157
- type: 'input',
158
- name: 'problem3',
159
- message: 'Problem #3 you\'re solving:',
160
- default: '',
161
- },
162
- {
163
- type: 'input',
164
- name: 'feature1',
165
- message: 'Critical production feature:',
166
- default: '',
167
- },
168
- {
169
- type: 'list',
170
- name: 'frontend',
171
- message: 'Frontend framework:',
172
- choices: ['Next.js', 'Remix', 'SvelteKit', 'Nuxt', 'Other'],
173
- },
174
- {
175
- type: 'list',
176
- name: 'database',
177
- message: 'Database:',
178
- choices: ['PostgreSQL', 'Supabase', 'MongoDB', 'PlanetScale', 'Other'],
179
- },
180
- {
181
- type: 'list',
182
- name: 'auth',
183
- message: 'Authentication:',
184
- choices: ['NextAuth', 'Clerk', 'Auth0', 'Supabase Auth', 'Other'],
185
- },
186
- {
187
- type: 'list',
188
- name: 'payments',
189
- message: 'Payments:',
190
- choices: ['Stripe', 'Lemonsqueezy', 'Paddle', 'None (free)', 'Other'],
191
- },
192
- {
193
- type: 'list',
194
- name: 'hosting',
195
- message: 'Hosting:',
196
- choices: ['Vercel', 'Railway', 'Fly.io', 'AWS', 'Other'],
197
- },
198
- {
199
- type: 'confirm',
200
- name: 'includeCursorRules',
201
- message: 'Include cursor-rules for AI assistants? (Cursor, Copilot)',
202
- default: true,
203
- },
204
- {
205
- type: 'confirm',
206
- name: 'includeFullTemplate',
207
- message: 'Copy full 34-section template locally?',
208
- default: false,
209
- },
210
- {
211
- type: 'confirm',
212
- name: 'includeDocs',
213
- message: 'Copy VERIFICATION.md & AGENT-INSTRUCTIONS.md to docs/?',
214
- default: true,
215
- },
216
- {
217
- type: 'confirm',
218
- name: 'includeAgents',
219
- message: 'Include AI agent prompts? (.agents/ folder)',
220
- default: true,
221
- },
222
- ]);
223
-
224
- const spinner = ora('Creating project files...').start();
225
-
226
- try {
227
- const outputDir = path.resolve(options.dir, answers.projectName);
228
-
229
- // Create directories
230
- await fs.mkdir(outputDir, { recursive: true });
231
- await fs.mkdir(path.join(outputDir, 'docs'), { recursive: true });
232
-
233
- // Replace placeholders
234
- const replacements = {
235
- '{{PROJECT_NAME}}': answers.projectName,
236
- '{{DATE}}': new Date().toISOString().split('T')[0],
237
- '{{IDEA_WHAT}}': answers.ideaWhat,
238
- '{{IDEA_FOR}}': answers.ideaFor,
239
- '{{PROBLEM_1}}': answers.problem1 || 'Problem 1',
240
- '{{PROBLEM_2}}': answers.problem2 || 'Problem 2',
241
- '{{PROBLEM_3}}': answers.problem3 || 'Problem 3',
242
- '{{FEATURE_1}}': answers.feature1 || 'Core feature',
243
- '{{FRONTEND}}': answers.frontend,
244
- '{{DATABASE}}': answers.database,
245
- '{{AUTH}}': answers.auth,
246
- '{{PAYMENTS}}': answers.payments,
247
- '{{HOSTING}}': answers.hosting,
248
- };
249
-
250
- let quickStart = QUICK_START_TEMPLATE;
251
- let context = CONTEXT_TEMPLATE;
252
-
253
- for (const [key, value] of Object.entries(replacements)) {
254
- quickStart = quickStart.replace(new RegExp(key, 'g'), value);
255
- context = context.replace(new RegExp(key, 'g'), value);
256
- }
257
-
258
- // Write files
259
- await fs.writeFile(path.join(outputDir, 'QUICK-START.md'), quickStart);
260
- await fs.writeFile(path.join(outputDir, 'CONTEXT.md'), context);
261
-
262
- // Create empty implementation plan
263
- const planContent = `# ${answers.projectName} - Implementation Plan
264
-
265
- > Generated with Ultra-Dex CLI
266
-
267
- ## Overview
268
-
269
- ${answers.ideaWhat} for ${answers.ideaFor}.
270
-
271
- ---
272
-
273
- ## Next Steps
274
-
275
- 1. Open QUICK-START.md and complete the remaining sections
276
- 2. Copy sections from the full Ultra-Dex template as needed
277
- 3. Use the TaskFlow example as reference
278
- 4. Start building!
279
-
280
- ## Resources
281
-
282
- - [Full Template](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/04-Imp-Template.md)
283
- - [TaskFlow Example](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/Examples/TaskFlow-Complete.md)
284
- - [Methodology](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/03-METHODOLOGY.md)
285
- `;
286
-
287
- await fs.writeFile(path.join(outputDir, 'IMPLEMENTATION-PLAN.md'), planContent);
288
-
289
- // Copy cursor-rules if requested
290
- if (answers.includeCursorRules) {
291
- const rulesDir = path.join(outputDir, '.cursor', 'rules');
292
- await fs.mkdir(rulesDir, { recursive: true });
293
-
294
- const cursorRulesPath = path.resolve(__dirname, '../../cursor-rules');
295
- try {
296
- const ruleFiles = await fs.readdir(cursorRulesPath);
297
- for (const file of ruleFiles.filter(f => f.endsWith('.mdc'))) {
298
- await fs.copyFile(
299
- path.join(cursorRulesPath, file),
300
- path.join(rulesDir, file)
301
- );
302
- }
303
- // Also generate .github/copilot-instructions.md for Copilot users
304
- const coreRulePath = path.join(cursorRulesPath, '00-ultra-dex-core.mdc');
305
- try {
306
- const coreContent = await fs.readFile(coreRulePath, 'utf-8');
307
- const dotGithub = path.join(outputDir, '.github');
308
- await fs.mkdir(dotGithub, { recursive: true });
309
- await fs.writeFile(path.join(dotGithub, 'copilot-instructions.md'), coreContent);
310
- } catch (e) {
311
- // Core rule not available - skip Copilot setup
312
- }
313
- } catch (err) {
314
- // Cursor rules not bundled - intentional design choice for npm package size
315
- console.log(chalk.gray('\n šŸ“¦ Cursor rules not bundled (keeps npm package <50KB)'));
316
- console.log(chalk.cyan(' Quick setup: npx degit Srujan0798/Ultra-Dex/cursor-rules .cursor/rules'));
317
- console.log(chalk.blue(' Or download: https://github.com/Srujan0798/Ultra-Dex/tree/main/cursor-rules'));
318
- }
319
- }
320
-
321
- // Copy full template if requested
322
- if (answers.includeFullTemplate) {
323
- const templatePath = path.resolve(__dirname, '../../@ Ultra DeX/Saas plan/04-Imp-Template.md');
324
- try {
325
- await fs.copyFile(templatePath, path.join(outputDir, 'docs', 'MASTER-PLAN.md'));
326
- } catch (err) {
327
- // Template not bundled - keeps npm package lightweight
328
- console.log(chalk.gray('\n šŸ“¦ Full template not bundled (5,500 lines - too large for npm)'));
329
- console.log(chalk.cyan(' Quick fetch: curl -O https://raw.githubusercontent.com/Srujan0798/Ultra-Dex/main/%40%20Ultra%20DeX/Saas%20plan/04-Imp-Template.md'));
330
- console.log(chalk.blue(' Or view: https://github.com/Srujan0798/Ultra-Dex/blob/main/%40%20Ultra%20DeX/Saas%20plan/04-Imp-Template.md'));
331
- }
332
- }
333
-
334
- // Copy docs if requested
335
- if (answers.includeDocs) {
336
- const verificationPath = path.resolve(__dirname, '../../docs/VERIFICATION.md');
337
- const agentPath = path.resolve(__dirname, '../../agents/AGENT-INSTRUCTIONS.md');
338
- try {
339
- await fs.copyFile(verificationPath, path.join(outputDir, 'docs', 'CHECKLIST.md'));
340
- await fs.copyFile(agentPath, path.join(outputDir, 'docs', 'AI-PROMPTS.md'));
341
- } catch (err) {
342
- // Docs not bundled - intentional for npm package size
343
- console.log(chalk.gray('\n šŸ“¦ Verification & instructions not bundled (npm size optimization)'));
344
- console.log(chalk.cyan(' Quick setup: npx degit Srujan0798/Ultra-Dex/docs docs'));
345
- console.log(chalk.blue(' Or view: https://github.com/Srujan0798/Ultra-Dex/tree/main/docs'));
346
- }
347
- }
348
-
349
- // Copy agents if requested
350
- if (answers.includeAgents) {
351
- const agentsDir = path.join(outputDir, '.agents');
352
- await fs.mkdir(agentsDir, { recursive: true });
353
-
354
- const agentsSourcePath = path.resolve(__dirname, '../../agents');
355
- try {
356
- // Copy tier directories and agent files
357
- const tiers = ['1-leadership', '2-development', '3-security', '4-devops', '5-quality', '6-specialist'];
358
- for (const tier of tiers) {
359
- const tierDir = path.join(agentsDir, tier);
360
- await fs.mkdir(tierDir, { recursive: true });
361
-
362
- const tierPath = path.join(agentsSourcePath, tier);
363
- const tierFiles = await fs.readdir(tierPath);
364
- for (const file of tierFiles.filter(f => f.endsWith('.md'))) {
365
- await fs.copyFile(
366
- path.join(tierPath, file),
367
- path.join(tierDir, file)
368
- );
369
- }
370
- }
371
-
372
- // Copy agent index and README
373
- await fs.copyFile(
374
- path.join(agentsSourcePath, '00-AGENT_INDEX.md'),
375
- path.join(agentsDir, '00-AGENT_INDEX.md')
376
- );
377
- await fs.copyFile(
378
- path.join(agentsSourcePath, 'README.md'),
379
- path.join(agentsDir, 'README.md')
380
- );
381
- } catch (err) {
382
- // Agents not bundled - intentional for npm package size
383
- console.log(chalk.gray('\n šŸ“¦ Agent prompts not bundled (npm size optimization)'));
384
- console.log(chalk.cyan(' Quick setup: npx degit Srujan0798/Ultra-Dex/agents .agents'));
385
- console.log(chalk.blue(' Or view: https://github.com/Srujan0798/Ultra-Dex/tree/main/agents'));
386
- }
387
- }
388
-
389
- spinner.succeed(chalk.green('Project created successfully!'));
390
-
391
- console.log('\n' + chalk.bold('Files created:'));
392
- console.log(chalk.gray(` ${outputDir}/`));
393
- console.log(chalk.gray(' ā”œā”€ā”€ QUICK-START.md'));
394
- console.log(chalk.gray(' ā”œā”€ā”€ CONTEXT.md'));
395
- console.log(chalk.gray(' ā”œā”€ā”€ IMPLEMENTATION-PLAN.md'));
396
- if (answers.includeFullTemplate) {
397
- console.log(chalk.gray(' ā”œā”€ā”€ docs/MASTER-PLAN.md (34 sections)'));
398
- }
399
- if (answers.includeDocs) {
400
- console.log(chalk.gray(' ā”œā”€ā”€ docs/CHECKLIST.md'));
401
- console.log(chalk.gray(' ā”œā”€ā”€ docs/AI-PROMPTS.md'));
402
- }
403
- if (answers.includeCursorRules) {
404
- console.log(chalk.gray(' ā”œā”€ā”€ .cursor/rules/ (11 AI rule files)'));
405
- }
406
- if (answers.includeAgents) {
407
- console.log(chalk.gray(' └── .agents/ (15 AI agent prompts in 6 tiers)'));
408
- }
409
-
410
- console.log('\n' + chalk.bold('Next steps:'));
411
- console.log(chalk.cyan(` 1. cd ${answers.projectName}`));
412
- console.log(chalk.cyan(' 2. Open QUICK-START.md and complete it'));
413
- console.log(chalk.cyan(' 3. Start building! šŸš€'));
414
-
415
- console.log('\n' + chalk.gray('Full Ultra-Dex repo:'));
416
- console.log(chalk.blue(' https://github.com/Srujan0798/Ultra-Dex'));
417
-
418
- } catch (error) {
419
- spinner.fail(chalk.red('Failed to create project'));
420
- console.error(error);
421
- process.exit(1);
422
- }
423
- });
26
+ .version('2.0.1');
27
+
28
+ registerInitCommand(program);
29
+ registerAuditCommand(program);
30
+ registerExamplesCommand(program);
31
+ registerAgentsCommand(program);
32
+ registerPlaceholderCommands(program);
33
+ registerServeCommand(program);
34
+ registerPackCommand(program);
35
+ registerWorkflowCommand(program);
36
+ registerSuggestCommand(program);
37
+ registerValidateCommand(program);
38
+ registerHooksCommand(program);
39
+ registerFetchCommand(program);
40
+ registerSyncCommand(program);
424
41
 
425
42
  program
426
43
  .command('audit')
@@ -634,6 +251,662 @@ program
634
251
  console.log('\n' + chalk.gray('Agent Index: https://github.com/Srujan0798/Ultra-Dex/blob/main/agents/00-AGENT_INDEX.md\n'));
635
252
  });
636
253
 
254
+ // ========================================
255
+ // GENERATE COMMAND - AI-Powered Plan Generation (v2.0)
256
+ // ========================================
257
+ const GENERATE_PROVIDERS = {
258
+ claude: {
259
+ name: 'Claude (Anthropic)',
260
+ envKey: 'ANTHROPIC_API_KEY',
261
+ model: 'claude-sonnet-4-20250514',
262
+ endpoint: 'https://api.anthropic.com/v1/messages'
263
+ },
264
+ openai: {
265
+ name: 'GPT-4 (OpenAI)',
266
+ envKey: 'OPENAI_API_KEY',
267
+ model: 'gpt-4o',
268
+ endpoint: 'https://api.openai.com/v1/chat/completions'
269
+ },
270
+ gemini: {
271
+ name: 'Gemini (Google)',
272
+ envKey: 'GOOGLE_AI_KEY',
273
+ model: 'gemini-2.0-flash',
274
+ endpoint: 'https://generativelanguage.googleapis.com/v1beta/models'
275
+ }
276
+ };
277
+
278
+ const GENERATE_SECTIONS = [
279
+ '1. Project Identity', '2. Vision & Problem', '3. Target Users', '4. Core Value Proposition',
280
+ '5. Feature Set (MVP)', '6. Feature Set (Future)', '7. User Journeys', '8. Tech Stack',
281
+ '9. System Architecture', '10. Database Schema', '11. API Design', '12. Authentication',
282
+ '13. Security Requirements', '14. Performance Requirements', '15. UI/UX Guidelines',
283
+ '16. Component Library', '17. State Management', '18. Error Handling', '19. Testing Strategy',
284
+ '20. CI/CD Pipeline', '21. Deployment Strategy', '22. Monitoring & Logging', '23. Cost Estimation',
285
+ '24. Timeline & Milestones', '25. Team Structure', '26. Risk Assessment', '27. Compliance',
286
+ '28. Documentation Plan', '29. Launch Checklist', '30. Success Metrics', '31. Scaling Plan',
287
+ '32. Support Strategy', '33. Iteration Roadmap', '34. Project Metadata'
288
+ ];
289
+
290
+ const GENERATE_SYSTEM_PROMPT = `You are an expert SaaS architect. Generate a comprehensive implementation plan.
291
+
292
+ For the given idea, create detailed content for ALL 34 sections:
293
+ ${GENERATE_SECTIONS.map(s => `- ${s}`).join('\n')}
294
+
295
+ Output format - use EXACTLY this markdown structure:
296
+ ## 1. Project Identity
297
+ [content]
298
+
299
+ ## 2. Vision & Problem
300
+ [content]
301
+
302
+ ... continue for all 34 sections ...
303
+
304
+ Be specific, actionable, and production-ready. Include real code examples, database schemas, API endpoints.`;
305
+
306
+ async function generateWithClaude(idea, apiKey, model) {
307
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
308
+ method: 'POST',
309
+ headers: {
310
+ 'Content-Type': 'application/json',
311
+ 'x-api-key': apiKey,
312
+ 'anthropic-version': '2023-06-01'
313
+ },
314
+ body: JSON.stringify({
315
+ model: model,
316
+ max_tokens: 8192,
317
+ messages: [{ role: 'user', content: `${GENERATE_SYSTEM_PROMPT}\n\nIdea: ${idea}` }]
318
+ })
319
+ });
320
+ if (!response.ok) throw new Error(`Claude API error: ${response.status}`);
321
+ const data = await response.json();
322
+ return data.content[0].text;
323
+ }
324
+
325
+ async function generateWithOpenAI(idea, apiKey, model) {
326
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
327
+ method: 'POST',
328
+ headers: {
329
+ 'Content-Type': 'application/json',
330
+ 'Authorization': `Bearer ${apiKey}`
331
+ },
332
+ body: JSON.stringify({
333
+ model: model,
334
+ messages: [
335
+ { role: 'system', content: GENERATE_SYSTEM_PROMPT },
336
+ { role: 'user', content: `Idea: ${idea}` }
337
+ ],
338
+ max_tokens: 8192
339
+ })
340
+ });
341
+ if (!response.ok) throw new Error(`OpenAI API error: ${response.status}`);
342
+ const data = await response.json();
343
+ return data.choices[0].message.content;
344
+ }
345
+
346
+ async function generateWithGemini(idea, apiKey, model) {
347
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
348
+ const response = await fetch(url, {
349
+ method: 'POST',
350
+ headers: { 'Content-Type': 'application/json' },
351
+ body: JSON.stringify({
352
+ contents: [{ parts: [{ text: `${GENERATE_SYSTEM_PROMPT}\n\nIdea: ${idea}` }] }],
353
+ generationConfig: { maxOutputTokens: 8192 }
354
+ })
355
+ });
356
+ if (!response.ok) throw new Error(`Gemini API error: ${response.status}`);
357
+ const data = await response.json();
358
+ return data.candidates[0].content.parts[0].text;
359
+ }
360
+
361
+ program
362
+ .command('generate')
363
+ .description('Generate a full implementation plan from an idea using AI')
364
+ .argument('[idea]', 'Your SaaS idea in one sentence')
365
+ .option('-p, --provider <provider>', 'AI provider: claude, openai, gemini', 'claude')
366
+ .option('-m, --model <model>', 'Specific model to use')
367
+ .option('-o, --output <dir>', 'Output directory', '.')
368
+ .option('-k, --key <key>', 'API key (or use environment variable)')
369
+ .option('--dry-run', 'Show what would be generated without calling AI')
370
+ .action(async (idea, options) => {
371
+ console.log(chalk.cyan('\n' + banner));
372
+ console.log(chalk.bold.green('✨ Ultra-Dex Generate - AI-Powered Plan Creation\n'));
373
+
374
+ // Get idea if not provided
375
+ if (!idea) {
376
+ const response = await inquirer.prompt([{
377
+ type: 'input',
378
+ name: 'idea',
379
+ message: 'Describe your SaaS idea in one sentence:',
380
+ validate: (input) => input.length > 10 || 'Please provide more detail'
381
+ }]);
382
+ idea = response.idea;
383
+ }
384
+
385
+ console.log(chalk.gray(`\nIdea: "${idea}"\n`));
386
+
387
+ // Validate provider
388
+ const provider = options.provider.toLowerCase();
389
+ if (!GENERATE_PROVIDERS[provider]) {
390
+ console.log(chalk.red(`Unknown provider: ${provider}`));
391
+ console.log(chalk.gray(`Available: ${Object.keys(GENERATE_PROVIDERS).join(', ')}`));
392
+ return;
393
+ }
394
+
395
+ const providerConfig = GENERATE_PROVIDERS[provider];
396
+ const apiKey = options.key || process.env[providerConfig.envKey];
397
+ const model = options.model || providerConfig.model;
398
+
399
+ if (!apiKey && !options.dryRun) {
400
+ console.log(chalk.red(`\nāŒ No API key found for ${providerConfig.name}`));
401
+ console.log(chalk.gray(`Set ${providerConfig.envKey} environment variable or use --key flag`));
402
+ return;
403
+ }
404
+
405
+ console.log(chalk.cyan(`Provider: ${providerConfig.name}`));
406
+ console.log(chalk.cyan(`Model: ${model}`));
407
+ console.log(chalk.cyan(`Output: ${options.output}\n`));
408
+
409
+ if (options.dryRun) {
410
+ console.log(chalk.yellow('šŸ” Dry run mode - showing plan structure:\n'));
411
+ GENERATE_SECTIONS.forEach((section, i) => {
412
+ console.log(chalk.gray(` ${section}`));
413
+ });
414
+ console.log(chalk.green('\nāœ“ Would generate IMPLEMENTATION-PLAN.md with all 34 sections'));
415
+ return;
416
+ }
417
+
418
+ const spinner = ora('Generating implementation plan...').start();
419
+
420
+ try {
421
+ let result;
422
+ spinner.text = `Calling ${providerConfig.name} API...`;
423
+
424
+ if (provider === 'claude') {
425
+ result = await generateWithClaude(idea, apiKey, model);
426
+ } else if (provider === 'openai') {
427
+ result = await generateWithOpenAI(idea, apiKey, model);
428
+ } else if (provider === 'gemini') {
429
+ result = await generateWithGemini(idea, apiKey, model);
430
+ }
431
+
432
+ spinner.succeed('AI generation complete!');
433
+
434
+ // Create output files
435
+ const outputDir = path.resolve(options.output);
436
+ await fs.mkdir(outputDir, { recursive: true });
437
+
438
+ // Write IMPLEMENTATION-PLAN.md
439
+ const planPath = path.join(outputDir, 'IMPLEMENTATION-PLAN.md');
440
+ const planContent = `# Implementation Plan
441
+
442
+ > Generated by Ultra-Dex AI • ${new Date().toISOString().split('T')[0]}
443
+ > Idea: "${idea}"
444
+
445
+ ---
446
+
447
+ ${result}
448
+ `;
449
+ await fs.writeFile(planPath, planContent);
450
+ console.log(chalk.green(`\nāœ“ Created: ${planPath}`));
451
+
452
+ // Write QUICK-START.md
453
+ const quickStartPath = path.join(outputDir, 'QUICK-START.md');
454
+ const quickStartContent = `# Quick Start Guide
455
+
456
+ > Generated from: ${idea}
457
+
458
+ ## Getting Started
459
+
460
+ 1. Review \`IMPLEMENTATION-PLAN.md\` for full architecture
461
+ 2. Run \`ultra-dex build\` to start development with AI assistance
462
+ 3. Use \`ultra-dex review\` to check implementation against plan
463
+
464
+ ## Next Steps
465
+
466
+ \`\`\`bash
467
+ # Start building with AI assistance
468
+ ultra-dex build --agent planner
469
+
470
+ # Review your progress
471
+ ultra-dex review
472
+ \`\`\`
473
+
474
+ ---
475
+ *Generated by Ultra-Dex v2.1.0*
476
+ `;
477
+ await fs.writeFile(quickStartPath, quickStartContent);
478
+ console.log(chalk.green(`āœ“ Created: ${quickStartPath}`));
479
+
480
+ console.log(chalk.bold.green('\nšŸŽ‰ Plan generation complete!'));
481
+ console.log(chalk.gray('Next: Review the plan and run `ultra-dex build` to start development\n'));
482
+
483
+ } catch (error) {
484
+ spinner.fail('Generation failed');
485
+ console.log(chalk.red(`\nError: ${error.message}`));
486
+ if (error.message.includes('401')) {
487
+ console.log(chalk.yellow('Check your API key is valid'));
488
+ }
489
+ }
490
+ });
491
+
492
+ // ========================================
493
+ // BUILD COMMAND - AI-Assisted Development (v2.1)
494
+ // ========================================
495
+ const BUILD_AGENTS = {
496
+ planner: { name: '@Planner', tier: 'Leadership', task: 'Break down features into atomic tasks' },
497
+ cto: { name: '@CTO', tier: 'Leadership', task: 'Architecture decisions' },
498
+ backend: { name: '@Backend', tier: 'Development', task: 'API endpoints and business logic' },
499
+ frontend: { name: '@Frontend', tier: 'Development', task: 'UI components and pages' },
500
+ database: { name: '@Database', tier: 'Development', task: 'Schema design and migrations' },
501
+ auth: { name: '@Auth', tier: 'Security', task: 'Authentication and authorization' },
502
+ security: { name: '@Security', tier: 'Security', task: 'Security audit' },
503
+ testing: { name: '@Testing', tier: 'Quality', task: 'Write and run tests' },
504
+ reviewer: { name: '@Reviewer', tier: 'Quality', task: 'Code review' },
505
+ devops: { name: '@DevOps', tier: 'DevOps', task: 'Deployment and CI/CD' },
506
+ };
507
+
508
+ program
509
+ .command('build')
510
+ .description('Start AI-assisted development with auto-loaded context')
511
+ .option('-a, --agent <agent>', 'Agent to use (planner, backend, frontend, etc.)')
512
+ .option('-t, --task <task>', 'Specific task to work on')
513
+ .option('--copy', 'Copy prompt to clipboard instead of displaying')
514
+ .action(async (options) => {
515
+ console.log(chalk.cyan('\nšŸ”§ Ultra-Dex Build Mode\n'));
516
+
517
+ async function fileExists(fp) {
518
+ try { await fs.access(fp); return true; } catch { return false; }
519
+ }
520
+ async function readFileSafe(fp) {
521
+ try { return await fs.readFile(fp, 'utf-8'); } catch { return null; }
522
+ }
523
+
524
+ const hasContext = await fileExists('CONTEXT.md');
525
+ const hasPlan = await fileExists('IMPLEMENTATION-PLAN.md');
526
+
527
+ if (!hasContext && !hasPlan) {
528
+ console.log(chalk.yellow('āš ļø No Ultra-Dex project found in current directory.\n'));
529
+ console.log(chalk.white('Run one of these first:'));
530
+ console.log(chalk.gray(' npx ultra-dex init # Create from template'));
531
+ console.log(chalk.gray(' npx ultra-dex generate # Generate from idea\n'));
532
+ return;
533
+ }
534
+
535
+ const context = await readFileSafe('CONTEXT.md');
536
+ const plan = await readFileSafe('IMPLEMENTATION-PLAN.md');
537
+
538
+ console.log(chalk.green('āœ“ Context loaded'));
539
+
540
+ let agent = options.agent;
541
+ if (!agent) {
542
+ const { selectedAgent } = await inquirer.prompt([{
543
+ type: 'list',
544
+ name: 'selectedAgent',
545
+ message: 'Select an agent:',
546
+ choices: [
547
+ new inquirer.Separator('── Leadership ──'),
548
+ { name: 'šŸ“‹ @Planner - Break down tasks', value: 'planner' },
549
+ { name: 'šŸ—ļø @CTO - Architecture decisions', value: 'cto' },
550
+ new inquirer.Separator('── Development ──'),
551
+ { name: 'āš™ļø @Backend - API endpoints', value: 'backend' },
552
+ { name: 'šŸŽØ @Frontend - UI components', value: 'frontend' },
553
+ { name: 'šŸ—„ļø @Database - Schema design', value: 'database' },
554
+ new inquirer.Separator('── Security ──'),
555
+ { name: 'šŸ” @Auth - Authentication', value: 'auth' },
556
+ { name: 'šŸ›”ļø @Security - Security audit', value: 'security' },
557
+ new inquirer.Separator('── Quality ──'),
558
+ { name: '🧪 @Testing - Write tests', value: 'testing' },
559
+ { name: 'šŸ‘ļø @Reviewer - Code review', value: 'reviewer' },
560
+ new inquirer.Separator('── DevOps ──'),
561
+ { name: 'šŸš€ @DevOps - Deployment', value: 'devops' },
562
+ ],
563
+ }]);
564
+ agent = selectedAgent;
565
+ }
566
+
567
+ const agentConfig = BUILD_AGENTS[agent];
568
+ if (!agentConfig) {
569
+ console.log(chalk.red(`Unknown agent: ${agent}`));
570
+ console.log(chalk.gray(`Available: ${Object.keys(BUILD_AGENTS).join(', ')}`));
571
+ return;
572
+ }
573
+
574
+ let task = options.task;
575
+ if (!task) {
576
+ const { taskInput } = await inquirer.prompt([{
577
+ type: 'input',
578
+ name: 'taskInput',
579
+ message: `What should ${agentConfig.name} do?`,
580
+ default: agentConfig.task,
581
+ }]);
582
+ task = taskInput;
583
+ }
584
+
585
+ const contextSection = context ? `## Project Context\n${context}\n` : '';
586
+ const planSection = plan ? `## Implementation Plan (Summary)\n${plan.slice(0, 8000)}...\n[Full plan in IMPLEMENTATION-PLAN.md]\n` : '';
587
+
588
+ const prompt = `# ${agentConfig.name} Agent Session
589
+
590
+ You are acting as ${agentConfig.name} for this project.
591
+
592
+ ${contextSection}
593
+ ${planSection}
594
+
595
+ ## Your Task
596
+ ${task}
597
+
598
+ ## Instructions
599
+ 1. Read the context and plan carefully
600
+ 2. Focus ONLY on your assigned task
601
+ 3. Follow Ultra-Dex 21-step verification for any code changes
602
+ 4. Document your work in a way the next agent can continue
603
+
604
+ ## Output Format
605
+ - For code: Include full file paths and production-ready code
606
+ - For plans: Use atomic tasks (4-9 hours each)
607
+ - For reviews: Use severity levels (critical, warning, info)
608
+
609
+ Begin working on: ${task}
610
+ `;
611
+
612
+ console.log(chalk.green(`\nāœ… ${agentConfig.name} prompt ready\n`));
613
+
614
+ if (options.copy) {
615
+ try {
616
+ const { exec } = await import('child_process');
617
+ const { promisify } = await import('util');
618
+ const execAsync = promisify(exec);
619
+ const platform = process.platform;
620
+ if (platform === 'darwin') {
621
+ await execAsync(`echo ${JSON.stringify(prompt)} | pbcopy`);
622
+ console.log(chalk.cyan('šŸ“‹ Prompt copied to clipboard!'));
623
+ } else {
624
+ console.log(chalk.yellow('Clipboard not supported. Displaying instead:'));
625
+ console.log(chalk.gray('─'.repeat(60)));
626
+ console.log(prompt);
627
+ console.log(chalk.gray('─'.repeat(60)));
628
+ }
629
+ } catch {
630
+ console.log(chalk.gray('─'.repeat(60)));
631
+ console.log(prompt);
632
+ console.log(chalk.gray('─'.repeat(60)));
633
+ }
634
+ } else {
635
+ console.log(chalk.gray('─'.repeat(60)));
636
+ console.log(prompt);
637
+ console.log(chalk.gray('─'.repeat(60)));
638
+ console.log(chalk.cyan('\nšŸ“‹ Copy this prompt into your AI tool'));
639
+ console.log(chalk.gray('Or use --copy to copy to clipboard\n'));
640
+ }
641
+ });
642
+
643
+ // ========================================
644
+ // REVIEW COMMAND - Code vs Plan Alignment (v2.2)
645
+ // ========================================
646
+ program
647
+ .command('review')
648
+ .description('Review code against the implementation plan')
649
+ .option('-d, --dir <directory>', 'Directory to review', '.')
650
+ .option('--quick', 'Quick review without AI (checks file structure only)')
651
+ .action(async (options) => {
652
+ console.log(chalk.cyan('\nšŸ” Ultra-Dex Code Review\n'));
653
+
654
+ const reviewDir = path.resolve(options.dir);
655
+
656
+ async function readFileSafe(fp) {
657
+ try { return await fs.readFile(fp, 'utf-8'); } catch { return null; }
658
+ }
659
+ async function fileExists(fp) {
660
+ try { await fs.access(fp); return true; } catch { return false; }
661
+ }
662
+
663
+ const planPath = path.join(reviewDir, 'IMPLEMENTATION-PLAN.md');
664
+ const plan = await readFileSafe(planPath);
665
+
666
+ if (!plan) {
667
+ console.log(chalk.yellow('āš ļø No IMPLEMENTATION-PLAN.md found.\n'));
668
+ console.log(chalk.white('Run one of these first:'));
669
+ console.log(chalk.gray(' npx ultra-dex init'));
670
+ console.log(chalk.gray(' npx ultra-dex generate\n'));
671
+ return;
672
+ }
673
+
674
+ async function getDirectoryStructure(dir, depth = 3, prefix = '') {
675
+ let structure = '';
676
+ try {
677
+ const entries = await fs.readdir(dir, { withFileTypes: true });
678
+ for (const entry of entries) {
679
+ if (['node_modules', '.git', '.next', 'dist', 'build'].includes(entry.name)) continue;
680
+ const entryPath = path.join(dir, entry.name);
681
+ if (entry.isDirectory()) {
682
+ structure += `${prefix}šŸ“ ${entry.name}/\n`;
683
+ if (depth > 1) structure += await getDirectoryStructure(entryPath, depth - 1, prefix + ' ');
684
+ } else {
685
+ structure += `${prefix}šŸ“„ ${entry.name}\n`;
686
+ }
687
+ }
688
+ } catch { /* ignore errors */ }
689
+ return structure;
690
+ }
691
+
692
+ const spinner = ora('Scanning codebase...').start();
693
+ const structure = await getDirectoryStructure(reviewDir);
694
+ spinner.succeed('Codebase scanned');
695
+
696
+ console.log(chalk.white('\nšŸ“ Project Structure:\n'));
697
+ console.log(chalk.gray(structure));
698
+
699
+ const checks = [
700
+ { name: 'IMPLEMENTATION-PLAN.md', path: 'IMPLEMENTATION-PLAN.md' },
701
+ { name: 'CONTEXT.md', path: 'CONTEXT.md' },
702
+ { name: 'package.json', path: 'package.json' },
703
+ { name: 'Database schema', path: 'prisma/schema.prisma' },
704
+ { name: 'Tests', path: 'tests' },
705
+ ];
706
+
707
+ console.log(chalk.white('šŸ“‹ Structure Checks:\n'));
708
+ for (const check of checks) {
709
+ const exists = await fileExists(path.join(reviewDir, check.path));
710
+ const icon = exists ? chalk.green('āœ…') : chalk.red('āŒ');
711
+ console.log(` ${icon} ${check.name}`);
712
+ }
713
+
714
+ // Check for key sections in plan
715
+ console.log(chalk.white('\nšŸ“‹ Plan Completeness:\n'));
716
+ const planChecks = [
717
+ { name: 'Database schema (Section 10)', pattern: /section 10|data model/i },
718
+ { name: 'API endpoints (Section 11)', pattern: /section 11|api blueprint/i },
719
+ { name: 'Tech stack (Section 15)', pattern: /section 15|tech stack/i },
720
+ { name: 'Implementation tasks (Section 16)', pattern: /section 16|implementation plan/i },
721
+ ];
722
+ for (const check of planChecks) {
723
+ const found = check.pattern.test(plan);
724
+ const icon = found ? chalk.green('āœ…') : chalk.yellow('āš ļø');
725
+ console.log(` ${icon} ${check.name}`);
726
+ }
727
+
728
+ console.log(chalk.cyan('\nšŸ’” Tips:'));
729
+ console.log(chalk.gray(' • Use "npx ultra-dex audit" for a detailed score'));
730
+ console.log(chalk.gray(' • Use "npx ultra-dex build" to start AI-assisted development\n'));
731
+ });
732
+
733
+ // ========================================
734
+ // MCP SERVER (Enhanced Context over HTTP)
735
+ // ========================================
736
+ program
737
+ .command('serve')
738
+ .description('Serve Ultra-Dex context over HTTP (MCP-compatible)')
739
+ .option('-p, --port <port>', 'Port to listen on', '3001')
740
+ .option('-w, --watch', 'Watch for file changes and notify clients')
741
+ .action(async (options) => {
742
+ const port = Number.parseInt(options.port, 10);
743
+ if (Number.isNaN(port)) {
744
+ console.log(chalk.red('Invalid port. Use a numeric value.'));
745
+ process.exit(1);
746
+ }
747
+
748
+ async function readFileSafe(filePath, label) {
749
+ try {
750
+ const content = await fs.readFile(filePath, 'utf-8');
751
+ return { label, content, exists: true };
752
+ } catch {
753
+ return { label, content: '', exists: false };
754
+ }
755
+ }
756
+
757
+ async function getProjectState() {
758
+ const [context, plan, quickStart] = await Promise.all([
759
+ readFileSafe('CONTEXT.md', 'CONTEXT.md'),
760
+ readFileSafe('IMPLEMENTATION-PLAN.md', 'IMPLEMENTATION-PLAN.md'),
761
+ readFileSafe('QUICK-START.md', 'QUICK-START.md'),
762
+ ]);
763
+
764
+ // Calculate quick stats
765
+ let planSections = 0;
766
+ if (plan.content) {
767
+ const matches = plan.content.match(/## (SECTION \d+|Section \d+)/gi);
768
+ planSections = matches ? matches.length : 0;
769
+ }
770
+
771
+ return {
772
+ meta: {
773
+ protocol: 'ultra-dex-mcp',
774
+ version: '2.1.0',
775
+ timestamp: new Date().toISOString(),
776
+ },
777
+ stats: {
778
+ hasContext: context.exists,
779
+ hasPlan: plan.exists,
780
+ hasQuickStart: quickStart.exists,
781
+ planSections,
782
+ },
783
+ files: [context, plan, quickStart].filter(f => f.exists),
784
+ };
785
+ }
786
+
787
+ // Available agents for quick reference
788
+ const agentsList = Object.entries(BUILD_AGENTS).map(([id, cfg]) => ({
789
+ id,
790
+ name: cfg.name,
791
+ tier: cfg.tier,
792
+ task: cfg.task,
793
+ }));
794
+
795
+ const server = http.createServer(async (req, res) => {
796
+ // CORS headers for browser access
797
+ res.setHeader('Access-Control-Allow-Origin', '*');
798
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
799
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
800
+
801
+ if (req.method === 'OPTIONS') {
802
+ res.writeHead(204);
803
+ res.end();
804
+ return;
805
+ }
806
+
807
+ const url = new URL(req.url, `http://localhost:${port}`);
808
+
809
+ // Health check
810
+ if (url.pathname === '/' || url.pathname === '/health') {
811
+ res.writeHead(200, { 'Content-Type': 'application/json' });
812
+ res.end(JSON.stringify({
813
+ status: 'ok',
814
+ service: 'ultra-dex-mcp',
815
+ version: '2.1.0',
816
+ endpoints: ['/context', '/agents', '/plan', '/state'],
817
+ }));
818
+ return;
819
+ }
820
+
821
+ // Full context (all files)
822
+ if (url.pathname === '/context') {
823
+ const state = await getProjectState();
824
+ res.writeHead(200, { 'Content-Type': 'application/json' });
825
+ res.end(JSON.stringify(state));
826
+ return;
827
+ }
828
+
829
+ // Just the plan
830
+ if (url.pathname === '/plan') {
831
+ const plan = await readFileSafe('IMPLEMENTATION-PLAN.md', 'IMPLEMENTATION-PLAN.md');
832
+ res.writeHead(200, { 'Content-Type': 'application/json' });
833
+ res.end(JSON.stringify({ plan: plan.content, exists: plan.exists }));
834
+ return;
835
+ }
836
+
837
+ // Project state (quick check)
838
+ if (url.pathname === '/state') {
839
+ const state = await getProjectState();
840
+ res.writeHead(200, { 'Content-Type': 'application/json' });
841
+ res.end(JSON.stringify({ meta: state.meta, stats: state.stats }));
842
+ return;
843
+ }
844
+
845
+ // List agents
846
+ if (url.pathname === '/agents') {
847
+ res.writeHead(200, { 'Content-Type': 'application/json' });
848
+ res.end(JSON.stringify({ agents: agentsList }));
849
+ return;
850
+ }
851
+
852
+ // Get specific agent prompt
853
+ if (url.pathname.startsWith('/agents/')) {
854
+ const agentId = url.pathname.split('/')[2];
855
+ const agentConfig = BUILD_AGENTS[agentId];
856
+ if (!agentConfig) {
857
+ res.writeHead(404, { 'Content-Type': 'application/json' });
858
+ res.end(JSON.stringify({ error: 'Agent not found', available: Object.keys(BUILD_AGENTS) }));
859
+ return;
860
+ }
861
+
862
+ // Build prompt with context
863
+ const context = await readFileSafe('CONTEXT.md', 'CONTEXT.md');
864
+ const plan = await readFileSafe('IMPLEMENTATION-PLAN.md', 'IMPLEMENTATION-PLAN.md');
865
+
866
+ const prompt = `# ${agentConfig.name} Agent Session
867
+
868
+ You are acting as ${agentConfig.name} for this project.
869
+
870
+ ${context.content ? `## Project Context\n${context.content}\n` : ''}
871
+ ${plan.content ? `## Implementation Plan\n${plan.content.slice(0, 10000)}\n` : ''}
872
+
873
+ ## Default Task
874
+ ${agentConfig.task}
875
+
876
+ ## Instructions
877
+ 1. Read the context and plan carefully
878
+ 2. Focus ONLY on your assigned task
879
+ 3. Follow Ultra-Dex 21-step verification for code changes
880
+ `;
881
+
882
+ res.writeHead(200, { 'Content-Type': 'application/json' });
883
+ res.end(JSON.stringify({
884
+ agent: agentConfig,
885
+ prompt,
886
+ contextLength: context.content.length,
887
+ planLength: plan.content.length,
888
+ }));
889
+ return;
890
+ }
891
+
892
+ res.writeHead(404, { 'Content-Type': 'application/json' });
893
+ res.end(JSON.stringify({ error: 'Not found' }));
894
+ });
895
+
896
+ server.listen(port, () => {
897
+ console.log(chalk.green(`\nāœ… Ultra-Dex MCP Server v2.1.0`));
898
+ console.log(chalk.cyan(` Running on http://localhost:${port}\n`));
899
+ console.log(chalk.white(' Endpoints:'));
900
+ console.log(chalk.gray(' GET / - Health check + endpoint list'));
901
+ console.log(chalk.gray(' GET /context - Full context (all files)'));
902
+ console.log(chalk.gray(' GET /plan - Just IMPLEMENTATION-PLAN.md'));
903
+ console.log(chalk.gray(' GET /state - Quick project state'));
904
+ console.log(chalk.gray(' GET /agents - List all agents'));
905
+ console.log(chalk.gray(' GET /agents/:id - Get agent prompt with context\n'));
906
+ console.log(chalk.yellow(' Example: curl http://localhost:' + port + '/agents/backend\n'));
907
+ });
908
+ });
909
+
637
910
  program
638
911
  .command('agent <name>')
639
912
  .description('Show a specific agent prompt')
@@ -649,7 +922,7 @@ program
649
922
  }
650
923
 
651
924
  // Try to read agent file
652
- const agentPath = path.resolve(__dirname, '../../agents', agent.file);
925
+ const agentPath = path.join(ASSETS_ROOT, 'agents', agent.file);
653
926
  try {
654
927
  const content = await fs.readFile(agentPath, 'utf-8');
655
928
  console.log(chalk.bold(`\nšŸ¤– ${agent.name.toUpperCase()} Agent\n`));
@@ -658,10 +931,19 @@ program
658
931
  console.log(chalk.gray('─'.repeat(60)));
659
932
  console.log(chalk.bold('\nšŸ“‹ Copy the above prompt and paste into your AI tool.\n'));
660
933
  } catch (err) {
661
- // Agent file not bundled (npm package) - show URL instead
662
- console.log(chalk.bold(`\nšŸ¤– ${agent.name.toUpperCase()} Agent\n`));
663
- console.log(chalk.gray('View full prompt on GitHub:'));
664
- console.log(chalk.blue(` https://github.com/Srujan0798/Ultra-Dex/blob/main/agents/${agent.file}\n`));
934
+ const fallbackPath = path.join(ROOT_FALLBACK, 'agents', agent.file);
935
+ try {
936
+ const content = await fs.readFile(fallbackPath, 'utf-8');
937
+ console.log(chalk.bold(`\nšŸ¤– ${agent.name.toUpperCase()} Agent\n`));
938
+ console.log(chalk.gray('─'.repeat(60)));
939
+ console.log(content);
940
+ console.log(chalk.gray('─'.repeat(60)));
941
+ console.log(chalk.bold('\nšŸ“‹ Copy the above prompt and paste into your AI tool.\n'));
942
+ } catch (fallbackErr) {
943
+ console.log(chalk.bold(`\nšŸ¤– ${agent.name.toUpperCase()} Agent\n`));
944
+ console.log(chalk.gray('View full prompt on GitHub:'));
945
+ console.log(chalk.blue(` https://github.com/Srujan0798/Ultra-Dex/blob/main/agents/${agent.file}\n`));
946
+ }
665
947
  }
666
948
  });
667
949
 
@@ -683,12 +965,18 @@ program
683
965
  let output = '';
684
966
 
685
967
  // 1. Read Agent Prompt
686
- const agentPath = path.resolve(__dirname, '../../agents', agent.file);
968
+ const agentPath = path.join(ASSETS_ROOT, 'agents', agent.file);
687
969
  try {
688
970
  const agentPrompt = await fs.readFile(agentPath, 'utf-8');
689
971
  output += agentPrompt + '\n\n';
690
972
  } catch (err) {
691
- output += `# ${agent.name.toUpperCase()} Agent\n\nSee: https://github.com/Srujan0798/Ultra-Dex/blob/main/agents/${agent.file}\n\n`;
973
+ const fallbackPath = path.join(ROOT_FALLBACK, 'agents', agent.file);
974
+ try {
975
+ const agentPrompt = await fs.readFile(fallbackPath, 'utf-8');
976
+ output += agentPrompt + '\n\n';
977
+ } catch (fallbackErr) {
978
+ output += `# ${agent.name.toUpperCase()} Agent\n\nSee: https://github.com/Srujan0798/Ultra-Dex/blob/main/agents/${agent.file}\n\n`;
979
+ }
692
980
  }
693
981
 
694
982
  output += '---\n\n';
@@ -1373,11 +1661,10 @@ program
1373
1661
  spinner.text = 'Fetching agent prompts...';
1374
1662
  const agentsDir = path.join(targetDir, 'agents');
1375
1663
 
1376
- const agentPaths = [
1664
+ const agentPaths = [
1377
1665
  '00-AGENT_INDEX.md',
1378
1666
  'README.md',
1379
1667
  'AGENT-INSTRUCTIONS.md',
1380
- '0-orchestration/orchestrator.md',
1381
1668
  '1-leadership/cto.md',
1382
1669
  '1-leadership/planner.md',
1383
1670
  '1-leadership/research.md',
@@ -1474,4 +1761,373 @@ program
1474
1761
  console.log(chalk.gray(' No GitHub access needed after fetch.\n'));
1475
1762
  });
1476
1763
 
1764
+ // ========================================
1765
+ // ALIGN COMMAND - Quick Alignment Score (v2.2)
1766
+ // ========================================
1767
+ program
1768
+ .command('align')
1769
+ .description('Quick alignment score - check how well code matches plan')
1770
+ .option('-d, --dir <directory>', 'Directory to check', '.')
1771
+ .option('--json', 'Output as JSON')
1772
+ .action(async (options) => {
1773
+ const checkDir = path.resolve(options.dir);
1774
+
1775
+ async function readFileSafe(fp) {
1776
+ try { return await fs.readFile(fp, 'utf-8'); } catch { return null; }
1777
+ }
1778
+ async function fileExists(fp) {
1779
+ try { await fs.access(fp); return true; } catch { return false; }
1780
+ }
1781
+ async function countFiles(dir, ext) {
1782
+ try {
1783
+ const entries = await fs.readdir(dir, { withFileTypes: true, recursive: true });
1784
+ return entries.filter(e => e.isFile() && e.name.endsWith(ext)).length;
1785
+ } catch { return 0; }
1786
+ }
1787
+
1788
+ const plan = await readFileSafe(path.join(checkDir, 'IMPLEMENTATION-PLAN.md'));
1789
+ if (!plan) {
1790
+ if (options.json) {
1791
+ console.log(JSON.stringify({ error: 'No IMPLEMENTATION-PLAN.md found', score: 0 }));
1792
+ } else {
1793
+ console.log(chalk.red('\nāŒ No IMPLEMENTATION-PLAN.md found'));
1794
+ console.log(chalk.gray('Run `ultra-dex generate` or `ultra-dex init` first\n'));
1795
+ }
1796
+ return;
1797
+ }
1798
+
1799
+ const spinner = ora('Calculating alignment...').start();
1800
+
1801
+ // Score components
1802
+ let score = 0;
1803
+ const checks = [];
1804
+
1805
+ // 1. Core files exist (30 points)
1806
+ const coreFiles = [
1807
+ { file: 'IMPLEMENTATION-PLAN.md', points: 10, name: 'Implementation Plan' },
1808
+ { file: 'CONTEXT.md', points: 5, name: 'Context File' },
1809
+ { file: 'package.json', points: 5, name: 'Package Config' },
1810
+ { file: 'README.md', points: 5, name: 'Documentation' },
1811
+ { file: '.gitignore', points: 5, name: 'Git Config' },
1812
+ ];
1813
+ for (const cf of coreFiles) {
1814
+ const exists = await fileExists(path.join(checkDir, cf.file));
1815
+ if (exists) score += cf.points;
1816
+ checks.push({ name: cf.name, status: exists ? 'pass' : 'fail', points: exists ? cf.points : 0 });
1817
+ }
1818
+
1819
+ // 2. Plan completeness - check for key sections (30 points)
1820
+ const sections = [
1821
+ { pattern: /## 1\.|project identity/i, name: 'Project Identity', points: 3 },
1822
+ { pattern: /## 5\.|feature set|mvp/i, name: 'Feature Set', points: 5 },
1823
+ { pattern: /## 8\.|tech stack/i, name: 'Tech Stack', points: 3 },
1824
+ { pattern: /## 10\.|database schema/i, name: 'Database Schema', points: 5 },
1825
+ { pattern: /## 11\.|api design/i, name: 'API Design', points: 5 },
1826
+ { pattern: /## 12\.|authentication/i, name: 'Authentication', points: 4 },
1827
+ { pattern: /## 19\.|testing/i, name: 'Testing Strategy', points: 5 },
1828
+ ];
1829
+ for (const sec of sections) {
1830
+ const found = sec.pattern.test(plan);
1831
+ if (found) score += sec.points;
1832
+ checks.push({ name: sec.name, status: found ? 'pass' : 'missing', points: found ? sec.points : 0 });
1833
+ }
1834
+
1835
+ // 3. Code structure (20 points)
1836
+ const codeStructure = [
1837
+ { path: 'src', points: 5, name: 'Source Directory' },
1838
+ { path: 'tests', points: 5, name: 'Tests Directory' },
1839
+ { path: 'prisma/schema.prisma', points: 5, name: 'Database Schema' },
1840
+ { path: '.env.example', points: 5, name: 'Environment Template' },
1841
+ ];
1842
+ for (const cs of codeStructure) {
1843
+ const exists = await fileExists(path.join(checkDir, cs.path));
1844
+ if (exists) score += cs.points;
1845
+ checks.push({ name: cs.name, status: exists ? 'pass' : 'missing', points: exists ? cs.points : 0 });
1846
+ }
1847
+
1848
+ // 4. Code presence (20 points)
1849
+ const jsFiles = await countFiles(checkDir, '.js') + await countFiles(checkDir, '.ts');
1850
+ const testFiles = await countFiles(path.join(checkDir, 'tests'), '.js') +
1851
+ await countFiles(path.join(checkDir, 'tests'), '.ts') +
1852
+ await countFiles(path.join(checkDir, '__tests__'), '.js');
1853
+
1854
+ if (jsFiles > 0) { score += 10; checks.push({ name: 'Source Code', status: 'pass', points: 10 }); }
1855
+ else { checks.push({ name: 'Source Code', status: 'missing', points: 0 }); }
1856
+
1857
+ if (testFiles > 0) { score += 10; checks.push({ name: 'Test Files', status: 'pass', points: 10 }); }
1858
+ else { checks.push({ name: 'Test Files', status: 'missing', points: 0 }); }
1859
+
1860
+ spinner.succeed('Alignment calculated');
1861
+
1862
+ // Determine grade
1863
+ let grade, color;
1864
+ if (score >= 90) { grade = 'A'; color = chalk.green; }
1865
+ else if (score >= 80) { grade = 'B'; color = chalk.green; }
1866
+ else if (score >= 70) { grade = 'C'; color = chalk.yellow; }
1867
+ else if (score >= 60) { grade = 'D'; color = chalk.yellow; }
1868
+ else { grade = 'F'; color = chalk.red; }
1869
+
1870
+ if (options.json) {
1871
+ console.log(JSON.stringify({ score, grade, checks }, null, 2));
1872
+ return;
1873
+ }
1874
+
1875
+ console.log(chalk.cyan('\n╔═════════════════════════════════════╗'));
1876
+ console.log(chalk.cyan('ā•‘') + chalk.bold(' Ultra-Dex Alignment Score ') + chalk.cyan('ā•‘'));
1877
+ console.log(chalk.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n'));
1878
+
1879
+ console.log(color.bold(` Score: ${score}/100 (${grade})\n`));
1880
+
1881
+ // Group by status
1882
+ const passed = checks.filter(c => c.status === 'pass');
1883
+ const failed = checks.filter(c => c.status !== 'pass');
1884
+
1885
+ if (passed.length > 0) {
1886
+ console.log(chalk.green(' āœ… Implemented:'));
1887
+ passed.forEach(c => console.log(chalk.gray(` • ${c.name} (+${c.points})`)));
1888
+ }
1889
+
1890
+ if (failed.length > 0) {
1891
+ console.log(chalk.yellow('\n āš ļø Missing:'));
1892
+ failed.forEach(c => console.log(chalk.gray(` • ${c.name}`)));
1893
+ }
1894
+
1895
+ console.log(chalk.gray('\n ─────────────────────────────────'));
1896
+ console.log(chalk.cyan(' šŸ’” Run `ultra-dex review` for detailed analysis'));
1897
+ console.log(chalk.cyan(' šŸ’” Run `ultra-dex build` to continue development\n'));
1898
+ });
1899
+
1900
+ // ========================================
1901
+ // WATCH COMMAND - Live Context Sync
1902
+ // ========================================
1903
+ program
1904
+ .command('watch')
1905
+ .description('Watch for file changes and keep context up to date')
1906
+ .option('-d, --dir <directory>', 'Directory to watch', '.')
1907
+ .option('--serve', 'Also start the MCP server')
1908
+ .option('-p, --port <port>', 'Port for MCP server', '3001')
1909
+ .action(async (options) => {
1910
+ console.log(chalk.cyan('\nšŸ‘ļø Ultra-Dex Watch Mode\n'));
1911
+
1912
+ const watchDir = path.resolve(options.dir);
1913
+
1914
+ async function readFileSafe(fp) {
1915
+ try { return await fs.readFile(fp, 'utf-8'); } catch { return null; }
1916
+ }
1917
+
1918
+ async function getStats() {
1919
+ const plan = await readFileSafe(path.join(watchDir, 'IMPLEMENTATION-PLAN.md'));
1920
+ const context = await readFileSafe(path.join(watchDir, 'CONTEXT.md'));
1921
+
1922
+ let stats = {
1923
+ hasPlan: !!plan,
1924
+ hasContext: !!context,
1925
+ planSections: 0,
1926
+ lastUpdate: new Date().toISOString(),
1927
+ };
1928
+
1929
+ if (plan) {
1930
+ const matches = plan.match(/## (SECTION|Section) \d+/gi);
1931
+ stats.planSections = matches ? matches.length : 0;
1932
+ }
1933
+
1934
+ return stats;
1935
+ }
1936
+
1937
+ // Initial state
1938
+ let lastStats = await getStats();
1939
+ console.log(chalk.green('āœ“ Watching for changes...'));
1940
+ console.log(chalk.gray(` Directory: ${watchDir}`));
1941
+ console.log(chalk.gray(` Plan sections: ${lastStats.planSections}`));
1942
+ console.log(chalk.gray(` Press Ctrl+C to stop\n`));
1943
+
1944
+ // Track changes
1945
+ let debounceTimer = null;
1946
+
1947
+ fsWatch(watchDir, { recursive: true }, async (eventType, filename) => {
1948
+ // Ignore certain files
1949
+ if (!filename ||
1950
+ filename.includes('node_modules') ||
1951
+ filename.includes('.git') ||
1952
+ filename.startsWith('.')) {
1953
+ return;
1954
+ }
1955
+
1956
+ // Debounce
1957
+ if (debounceTimer) clearTimeout(debounceTimer);
1958
+ debounceTimer = setTimeout(async () => {
1959
+ const newStats = await getStats();
1960
+
1961
+ // Check what changed
1962
+ const changes = [];
1963
+ if (newStats.hasPlan !== lastStats.hasPlan) {
1964
+ changes.push(newStats.hasPlan ? 'šŸ“„ Plan created' : 'āŒ Plan removed');
1965
+ }
1966
+ if (newStats.planSections !== lastStats.planSections) {
1967
+ const diff = newStats.planSections - lastStats.planSections;
1968
+ changes.push(`šŸ“Š Sections: ${lastStats.planSections} → ${newStats.planSections} (${diff > 0 ? '+' : ''}${diff})`);
1969
+ }
1970
+
1971
+ if (changes.length > 0 || filename.endsWith('.md')) {
1972
+ const time = new Date().toLocaleTimeString();
1973
+ console.log(chalk.cyan(`[${time}]`) + chalk.gray(` ${filename}`));
1974
+ changes.forEach(c => console.log(chalk.yellow(` ${c}`)));
1975
+ }
1976
+
1977
+ lastStats = newStats;
1978
+ }, 500);
1979
+ });
1980
+
1981
+ // Keep process alive
1982
+ process.on('SIGINT', () => {
1983
+ console.log(chalk.yellow('\n\nšŸ‘‹ Watch mode stopped.\n'));
1984
+ process.exit(0);
1985
+ });
1986
+
1987
+ // If --serve flag, also start MCP server
1988
+ if (options.serve) {
1989
+ const port = Number.parseInt(options.port, 10);
1990
+ console.log(chalk.green(`\n🌐 Also starting MCP server on port ${port}...`));
1991
+ console.log(chalk.gray(` Access context at: http://localhost:${port}/context\n`));
1992
+
1993
+ const server = http.createServer(async (req, res) => {
1994
+ res.setHeader('Access-Control-Allow-Origin', '*');
1995
+
1996
+ if (req.url === '/') {
1997
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1998
+ res.end(JSON.stringify({ status: 'ok', mode: 'watch', stats: await getStats() }));
1999
+ return;
2000
+ }
2001
+
2002
+ if (req.url === '/context') {
2003
+ const [context, plan, quickStart] = await Promise.all([
2004
+ readFileSafe(path.join(watchDir, 'CONTEXT.md')),
2005
+ readFileSafe(path.join(watchDir, 'IMPLEMENTATION-PLAN.md')),
2006
+ readFileSafe(path.join(watchDir, 'QUICK-START.md')),
2007
+ ]);
2008
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2009
+ res.end(JSON.stringify({
2010
+ meta: { timestamp: new Date().toISOString(), mode: 'watch' },
2011
+ files: [
2012
+ { label: 'CONTEXT.md', content: context || '' },
2013
+ { label: 'IMPLEMENTATION-PLAN.md', content: plan || '' },
2014
+ { label: 'QUICK-START.md', content: quickStart || '' },
2015
+ ],
2016
+ }));
2017
+ return;
2018
+ }
2019
+
2020
+ res.writeHead(404);
2021
+ res.end(JSON.stringify({ error: 'Not found' }));
2022
+ });
2023
+
2024
+ server.listen(port);
2025
+ }
2026
+
2027
+ // Keep process running
2028
+ await new Promise(() => {});
2029
+ });
2030
+
2031
+ // ========================================
2032
+ // STATE COMMAND - Machine-Readable State
2033
+ // ========================================
2034
+ program
2035
+ .command('state')
2036
+ .description('Output machine-readable project state (.ultra/state.json)')
2037
+ .option('-d, --dir <directory>', 'Project directory', '.')
2038
+ .option('--update', 'Update .ultra/state.json')
2039
+ .option('--json', 'Output state to stdout')
2040
+ .action(async (options) => {
2041
+ const projectDir = path.resolve(options.dir);
2042
+ const ultraDir = path.join(projectDir, '.ultra');
2043
+ const statePath = path.join(ultraDir, 'state.json');
2044
+
2045
+ async function readFileSafe(fp) {
2046
+ try { return await fs.readFile(fp, 'utf-8'); } catch { return null; }
2047
+ }
2048
+ async function fileExists(fp) {
2049
+ try { await fs.access(fp); return true; } catch { return false; }
2050
+ }
2051
+
2052
+ // Gather state
2053
+ const plan = await readFileSafe(path.join(projectDir, 'IMPLEMENTATION-PLAN.md'));
2054
+ const context = await readFileSafe(path.join(projectDir, 'CONTEXT.md'));
2055
+ const pkg = await readFileSafe(path.join(projectDir, 'package.json'));
2056
+
2057
+ // Parse plan sections
2058
+ let sections = [];
2059
+ if (plan) {
2060
+ const sectionMatches = plan.matchAll(/## (SECTION \d+|Section \d+)[:\s]*([^\n]*)/gi);
2061
+ for (const match of sectionMatches) {
2062
+ sections.push({ number: match[1], title: match[2].trim() });
2063
+ }
2064
+ }
2065
+
2066
+ // Parse package.json
2067
+ let projectInfo = { name: 'unknown', version: '0.0.0' };
2068
+ if (pkg) {
2069
+ try {
2070
+ const parsed = JSON.parse(pkg);
2071
+ projectInfo = { name: parsed.name, version: parsed.version };
2072
+ } catch {}
2073
+ }
2074
+
2075
+ const state = {
2076
+ version: '2.1.0',
2077
+ timestamp: new Date().toISOString(),
2078
+ project: projectInfo,
2079
+ files: {
2080
+ plan: { exists: !!plan, sections: sections.length },
2081
+ context: { exists: !!context },
2082
+ package: { exists: !!pkg },
2083
+ tests: { exists: await fileExists(path.join(projectDir, 'tests')) },
2084
+ prisma: { exists: await fileExists(path.join(projectDir, 'prisma/schema.prisma')) },
2085
+ },
2086
+ sections,
2087
+ score: {
2088
+ total: sections.length,
2089
+ target: 34,
2090
+ percentage: Math.round((sections.length / 34) * 100),
2091
+ },
2092
+ };
2093
+
2094
+ if (options.json) {
2095
+ console.log(JSON.stringify(state, null, 2));
2096
+ return;
2097
+ }
2098
+
2099
+ if (options.update) {
2100
+ await fs.mkdir(ultraDir, { recursive: true });
2101
+ await fs.writeFile(statePath, JSON.stringify(state, null, 2));
2102
+ console.log(chalk.green(`\nāœ… Updated ${statePath}`));
2103
+ console.log(chalk.gray(` Sections: ${state.sections.length}/34`));
2104
+ console.log(chalk.gray(` Score: ${state.score.percentage}%\n`));
2105
+ return;
2106
+ }
2107
+
2108
+ // Display state
2109
+ console.log(chalk.cyan('\nšŸ“Š Ultra-Dex Project State\n'));
2110
+ console.log(chalk.white(` Project: ${state.project.name} v${state.project.version}`));
2111
+ console.log(chalk.white(` Sections: ${state.sections.length}/34 (${state.score.percentage}%)`));
2112
+
2113
+ console.log(chalk.white('\n Files:'));
2114
+ for (const [name, info] of Object.entries(state.files)) {
2115
+ const icon = info.exists ? chalk.green('āœ…') : chalk.red('āŒ');
2116
+ console.log(` ${icon} ${name}`);
2117
+ }
2118
+
2119
+ if (state.sections.length > 0) {
2120
+ console.log(chalk.white('\n Completed Sections:'));
2121
+ state.sections.slice(0, 5).forEach(s => {
2122
+ console.log(chalk.gray(` • ${s.number}: ${s.title}`));
2123
+ });
2124
+ if (state.sections.length > 5) {
2125
+ console.log(chalk.gray(` ... and ${state.sections.length - 5} more`));
2126
+ }
2127
+ }
2128
+
2129
+ console.log(chalk.cyan('\n šŸ’” Run with --update to save to .ultra/state.json'));
2130
+ console.log(chalk.cyan(' šŸ’” Run with --json for machine output\n'));
2131
+ });
2132
+
1477
2133
  program.parse();