ultra-dex 1.8.0 → 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.
- package/README.md +162 -143
- package/bin/ultra-dex.js +1005 -777
- package/lib/commands/agents.js +154 -0
- package/lib/commands/audit.js +135 -0
- package/lib/commands/banner.js +21 -0
- package/lib/commands/build.js +214 -0
- package/lib/commands/examples.js +34 -0
- package/lib/commands/fetch.js +186 -0
- package/lib/commands/generate.js +217 -0
- package/lib/commands/hooks.js +105 -0
- package/lib/commands/init.js +337 -0
- package/lib/commands/placeholders.js +11 -0
- package/lib/commands/review.js +287 -0
- package/lib/commands/serve.js +56 -0
- package/lib/commands/suggest.js +126 -0
- package/lib/commands/validate.js +140 -0
- package/lib/commands/workflows.js +185 -0
- package/lib/config/paths.js +9 -0
- package/lib/config/urls.js +16 -0
- package/lib/providers/base.js +82 -0
- package/lib/providers/claude.js +177 -0
- package/lib/providers/gemini.js +170 -0
- package/lib/providers/index.js +93 -0
- package/lib/providers/openai.js +163 -0
- package/lib/templates/context.js +26 -0
- package/lib/templates/embedded.js +141 -0
- package/lib/templates/prompts/generate-plan.js +147 -0
- package/lib/templates/prompts/review-code.js +57 -0
- package/lib/templates/prompts/section-prompts.js +275 -0
- package/lib/templates/prompts/system-prompt.md +58 -0
- package/lib/templates/quick-start.js +43 -0
- package/lib/utils/build-helpers.js +257 -0
- package/lib/utils/fallback.js +36 -0
- package/lib/utils/files.js +67 -0
- package/lib/utils/network.js +18 -0
- package/lib/utils/output.js +20 -0
- package/lib/utils/parser.js +155 -0
- package/lib/utils/prompt-builder.js +93 -0
- package/lib/utils/review-helpers.js +334 -0
- package/lib/utils/validation.js +34 -0
- package/package.json +17 -3
package/bin/ultra-dex.js
CHANGED
|
@@ -1,757 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import ora from 'ora';
|
|
7
|
-
import fs from 'fs/promises';
|
|
8
|
-
import { watch as fsWatch } from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
|
-
import http from 'http';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
const ASSETS_ROOT = path.resolve(__dirname, '../assets');
|
|
16
|
-
const ROOT_FALLBACK = path.resolve(__dirname, '../../');
|
|
17
|
-
|
|
18
|
-
const program = new Command();
|
|
19
|
-
|
|
20
|
-
// ASCII Art Banner
|
|
21
|
-
const banner = `
|
|
22
|
-
╔═══════════════════════════════════════════════════════════╗
|
|
23
|
-
║ ║
|
|
24
|
-
║ ██╗ ██╗██╗ ████████╗██████╗ █████╗ ║
|
|
25
|
-
║ ██║ ██║██║ ╚══██╔══╝██╔══██╗██╔══██╗ ║
|
|
26
|
-
║ ██║ ██║██║ ██║ ██████╔╝███████║ ║
|
|
27
|
-
║ ██║ ██║██║ ██║ ██╔══██╗██╔══██║ ║
|
|
28
|
-
║ ╚██████╔╝███████╗██║ ██║ ██║██║ ██║ ║
|
|
29
|
-
║ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ║
|
|
30
|
-
║ ║
|
|
31
|
-
║ ██████╗ ███████╗██╗ ██╗ ║
|
|
32
|
-
║ ██╔══██╗██╔════╝╚██╗██╔╝ ║
|
|
33
|
-
║ ██║ ██║█████╗ ╚███╔╝ ║
|
|
34
|
-
║ ██║ ██║██╔══╝ ██╔██╗ ║
|
|
35
|
-
║ ██████╔╝███████╗██╔╝ ██╗ ║
|
|
36
|
-
║ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ║
|
|
37
|
-
║ ║
|
|
38
|
-
║ From Idea to Production-Ready SaaS ║
|
|
39
|
-
║ ║
|
|
40
|
-
╚═══════════════════════════════════════════════════════════╝
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
// Template content (embedded)
|
|
44
|
-
const QUICK_START_TEMPLATE = `# {{PROJECT_NAME}} - Quick Start
|
|
45
|
-
|
|
46
|
-
## 1. Your Idea (2 sentences max)
|
|
47
|
-
|
|
48
|
-
**What:** {{IDEA_WHAT}}
|
|
49
|
-
**For whom:** {{IDEA_FOR}}
|
|
50
|
-
|
|
51
|
-
## 2. The Problem (3 bullets)
|
|
52
|
-
|
|
53
|
-
- {{PROBLEM_1}}
|
|
54
|
-
- {{PROBLEM_2}}
|
|
55
|
-
- {{PROBLEM_3}}
|
|
56
|
-
|
|
57
|
-
## 3. Core Production Features (5 max)
|
|
58
|
-
|
|
59
|
-
| Feature | Priority | Justification |
|
|
60
|
-
|---------|----------|---------------|
|
|
61
|
-
| {{FEATURE_1}} | P0 | |
|
|
62
|
-
| | P0 | |
|
|
63
|
-
| | P1 | |
|
|
64
|
-
| | P1 | |
|
|
65
|
-
| | P2 | |
|
|
66
|
-
|
|
67
|
-
## 4. Tech Stack
|
|
68
|
-
|
|
69
|
-
| Layer | Your Choice |
|
|
70
|
-
|-------|-------------|
|
|
71
|
-
| Frontend | {{FRONTEND}} |
|
|
72
|
-
| Database | {{DATABASE}} |
|
|
73
|
-
| Auth | {{AUTH}} |
|
|
74
|
-
| Payments | {{PAYMENTS}} |
|
|
75
|
-
| Hosting | {{HOSTING}} |
|
|
76
|
-
|
|
77
|
-
## 5. First 3 Tasks
|
|
78
|
-
|
|
79
|
-
1. [ ] Set up project with chosen stack
|
|
80
|
-
2. [ ] Implement core feature #1
|
|
81
|
-
3. [ ] Deploy to staging
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
**Next:** Fill out the full implementation plan using the Ultra-Dex template.
|
|
86
|
-
`;
|
|
87
|
-
|
|
88
|
-
const CONTEXT_TEMPLATE = `# {{PROJECT_NAME}} - Context
|
|
89
|
-
|
|
90
|
-
## Project Overview
|
|
91
|
-
**Name:** {{PROJECT_NAME}}
|
|
92
|
-
**Started:** {{DATE}}
|
|
93
|
-
**Status:** Planning
|
|
94
|
-
|
|
95
|
-
## Quick Summary
|
|
96
|
-
{{IDEA_WHAT}} for {{IDEA_FOR}}.
|
|
97
|
-
|
|
98
|
-
## Key Decisions
|
|
99
|
-
- Frontend: {{FRONTEND}}
|
|
100
|
-
- Database: {{DATABASE}}
|
|
101
|
-
- Auth: {{AUTH}}
|
|
102
|
-
- Payments: {{PAYMENTS}}
|
|
103
|
-
- Hosting: {{HOSTING}}
|
|
104
|
-
|
|
105
|
-
## Current Focus
|
|
106
|
-
Setting up the implementation plan.
|
|
107
|
-
|
|
108
|
-
## Resources
|
|
109
|
-
- [Ultra-Dex Template](https://github.com/Srujan0798/Ultra-Dex)
|
|
110
|
-
- [TaskFlow Example](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/Examples/TaskFlow-Complete.md)
|
|
111
|
-
`;
|
|
112
|
-
|
|
113
|
-
// ===========================================
|
|
114
|
-
// EMBEDDED ASSETS (bundled for offline use)
|
|
115
|
-
// ===========================================
|
|
116
|
-
|
|
117
|
-
const CORE_CURSOR_RULE = `# Ultra-Dex Core Rules
|
|
118
|
-
|
|
119
|
-
> Load this as your base ruleset. Add domain-specific rules as needed.
|
|
120
|
-
|
|
121
|
-
## Project Philosophy
|
|
122
|
-
|
|
123
|
-
- Build production-ready from day 1
|
|
124
|
-
- Every task: 4-9 hours with clear acceptance criteria
|
|
125
|
-
- 21-step verification for features (simplified for fixes)
|
|
126
|
-
- Code > Documentation (but document decisions)
|
|
127
|
-
|
|
128
|
-
## Code Standards
|
|
129
|
-
|
|
130
|
-
- TypeScript strict mode always
|
|
131
|
-
- Zod validation at all API boundaries
|
|
132
|
-
- Error handling: never swallow errors silently
|
|
133
|
-
- Logging: structured JSON, include request IDs
|
|
134
|
-
- Tests: minimum 80% coverage for business logic
|
|
135
|
-
|
|
136
|
-
## Architecture Defaults
|
|
137
|
-
|
|
138
|
-
- Next.js App Router (or specified framework)
|
|
139
|
-
- PostgreSQL with Prisma ORM
|
|
140
|
-
- NextAuth.js for authentication
|
|
141
|
-
- Stripe for payments
|
|
142
|
-
- Vercel for deployment
|
|
143
|
-
|
|
144
|
-
## Task Completion Checklist (Quick 5-Step)
|
|
145
|
-
|
|
146
|
-
1. Does it work? (Manual test)
|
|
147
|
-
2. Are there tests? (Automated)
|
|
148
|
-
3. Is it secure? (No secrets exposed, inputs validated)
|
|
149
|
-
4. Is it documented? (Code comments for complex logic)
|
|
150
|
-
5. Is it deployable? (No breaking changes)
|
|
151
|
-
|
|
152
|
-
## When to Use Full 21-Step
|
|
153
|
-
|
|
154
|
-
- New features affecting multiple files
|
|
155
|
-
- Security-sensitive changes
|
|
156
|
-
- Database schema changes
|
|
157
|
-
- API contract changes
|
|
158
|
-
|
|
159
|
-
## File Naming
|
|
160
|
-
|
|
161
|
-
- Components: PascalCase (UserProfile.tsx)
|
|
162
|
-
- Utilities: camelCase (formatDate.ts)
|
|
163
|
-
- API routes: kebab-case (/api/user-profile)
|
|
164
|
-
- Database: snake_case (user_profiles)
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
|
-
const AGENT_INSTRUCTIONS_EMBEDDED = `# Ultra-Dex AI Agent Quick Reference
|
|
168
|
-
|
|
169
|
-
## Agent Selection
|
|
170
|
-
|
|
171
|
-
| Task | Agent | Use When |
|
|
172
|
-
|------|-------|----------|
|
|
173
|
-
| Architecture decisions | @CTO | Tech stack, scaling, trade-offs |
|
|
174
|
-
| Task breakdown | @Planner | Feature to atomic tasks |
|
|
175
|
-
| API endpoints | @Backend | REST/GraphQL, middleware |
|
|
176
|
-
| React components | @Frontend | UI, state, forms |
|
|
177
|
-
| Schema design | @Database | Models, migrations, queries |
|
|
178
|
-
| Auth flows | @Security | Login, sessions, permissions |
|
|
179
|
-
| CI/CD setup | @DevOps | Deploy, monitoring, infra |
|
|
180
|
-
| Code review | @Reviewer | PR review, quality gates |
|
|
181
|
-
| Test coverage | @Testing | Unit, integration, E2E |
|
|
182
|
-
| Bug fixing | @Debugger | Root cause, fixes |
|
|
183
|
-
|
|
184
|
-
## Quick Start Prompts
|
|
185
|
-
|
|
186
|
-
### @Backend - API Endpoint
|
|
187
|
-
Act as @Backend. Context: [paste CONTEXT.md]
|
|
188
|
-
Task: Create POST /api/users endpoint with validation.
|
|
189
|
-
Requirements: Zod schema, error handling, rate limiting.
|
|
190
|
-
|
|
191
|
-
### @Database - Schema Design
|
|
192
|
-
Act as @Database. Context: [paste CONTEXT.md]
|
|
193
|
-
Task: Design User and Organization tables with relationships.
|
|
194
|
-
Requirements: Prisma schema, indexes, soft deletes.
|
|
195
|
-
|
|
196
|
-
### @Frontend - Component
|
|
197
|
-
Act as @Frontend. Context: [paste CONTEXT.md]
|
|
198
|
-
Task: Create UserProfile component with edit form.
|
|
199
|
-
Requirements: React Hook Form, Zod validation, loading states.
|
|
200
|
-
|
|
201
|
-
## 21-Step Verification (Quick 5)
|
|
202
|
-
|
|
203
|
-
1. Does it work? (Manual test)
|
|
204
|
-
2. Are there tests? (80%+ coverage)
|
|
205
|
-
3. Is it secure? (Inputs validated, no secrets)
|
|
206
|
-
4. Is it documented? (Complex logic commented)
|
|
207
|
-
5. Is it deployable? (No breaking changes)
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
Full agents: https://github.com/Srujan0798/Ultra-Dex/tree/main/agents
|
|
211
|
-
`;
|
|
212
|
-
|
|
213
|
-
const VERIFICATION_CHECKLIST = `# Ultra-Dex 21-Step Verification Checklist
|
|
214
|
-
|
|
215
|
-
## Quick 5 (Every Task)
|
|
216
|
-
- [ ] 1. Does it work? (Manual test)
|
|
217
|
-
- [ ] 2. Are there tests? (Unit tests passing)
|
|
218
|
-
- [ ] 3. Is it secure? (No secrets, inputs validated)
|
|
219
|
-
- [ ] 4. Is it documented? (Comments for complex logic)
|
|
220
|
-
- [ ] 5. Is it deployable? (No breaking changes)
|
|
221
|
-
|
|
222
|
-
## Full 21 (New Features)
|
|
223
|
-
|
|
224
|
-
### Understanding (1-4)
|
|
225
|
-
- [ ] 1. Requirements clear?
|
|
226
|
-
- [ ] 2. Assumptions documented?
|
|
227
|
-
- [ ] 3. Logic flow mapped?
|
|
228
|
-
- [ ] 4. Subtasks identified?
|
|
229
|
-
|
|
230
|
-
### Implementation (5-10)
|
|
231
|
-
- [ ] 5. Setup complete?
|
|
232
|
-
- [ ] 6. Code written?
|
|
233
|
-
- [ ] 7. Comments added?
|
|
234
|
-
- [ ] 8. Unit tests passing?
|
|
235
|
-
- [ ] 9. Bugs fixed?
|
|
236
|
-
- [ ] 10. Integration verified?
|
|
237
|
-
|
|
238
|
-
### Quality (11-16)
|
|
239
|
-
- [ ] 11. Acceptance criteria met?
|
|
240
|
-
- [ ] 12. UX acceptable?
|
|
241
|
-
- [ ] 13. Performance acceptable?
|
|
242
|
-
- [ ] 14. Security reviewed?
|
|
243
|
-
- [ ] 15. Code refactored?
|
|
244
|
-
- [ ] 16. Errors handled?
|
|
245
|
-
|
|
246
|
-
### Delivery (17-21)
|
|
247
|
-
- [ ] 17. API documented?
|
|
248
|
-
- [ ] 18. Committed?
|
|
249
|
-
- [ ] 19. Build passing?
|
|
250
|
-
- [ ] 20. Deploy ready?
|
|
251
|
-
- [ ] 21. Final verified?
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
Use Quick 5 for bug fixes. Use Full 21 for new features.
|
|
255
|
-
`;
|
|
256
|
-
|
|
257
|
-
const GITHUB_RAW = 'https://raw.githubusercontent.com/Srujan0798/Ultra-Dex/main';
|
|
258
|
-
const CURSOR_RULE_FILES = [
|
|
259
|
-
'00-ultra-dex-core.mdc',
|
|
260
|
-
'01-database.mdc',
|
|
261
|
-
'02-api.mdc',
|
|
262
|
-
'03-auth.mdc',
|
|
263
|
-
'04-frontend.mdc',
|
|
264
|
-
'05-payments.mdc',
|
|
265
|
-
'06-testing.mdc',
|
|
266
|
-
'07-security.mdc',
|
|
267
|
-
'08-deployment.mdc',
|
|
268
|
-
'09-error-handling.mdc',
|
|
269
|
-
'10-performance.mdc',
|
|
270
|
-
'11-nextjs-v15.mdc',
|
|
271
|
-
'12-multi-tenancy.mdc',
|
|
272
|
-
];
|
|
273
|
-
const AGENT_PATHS = [
|
|
274
|
-
'00-AGENT_INDEX.md',
|
|
275
|
-
'README.md',
|
|
276
|
-
'AGENT-INSTRUCTIONS.md',
|
|
277
|
-
'1-leadership/cto.md',
|
|
278
|
-
'1-leadership/planner.md',
|
|
279
|
-
'1-leadership/research.md',
|
|
280
|
-
'2-development/backend.md',
|
|
281
|
-
'2-development/frontend.md',
|
|
282
|
-
'2-development/database.md',
|
|
283
|
-
'3-security/auth.md',
|
|
284
|
-
'3-security/security.md',
|
|
285
|
-
'4-devops/devops.md',
|
|
286
|
-
'5-quality/reviewer.md',
|
|
287
|
-
'5-quality/testing.md',
|
|
288
|
-
'5-quality/debugger.md',
|
|
289
|
-
'5-quality/documentation.md',
|
|
290
|
-
'6-specialist/performance.md',
|
|
291
|
-
'6-specialist/refactoring.md',
|
|
292
|
-
];
|
|
293
|
-
const DOC_FILES = [
|
|
294
|
-
'VERIFICATION.md',
|
|
295
|
-
'BUILD-AUTH-30M.md',
|
|
296
|
-
'QUICK-REFERENCE.md',
|
|
297
|
-
'TROUBLESHOOTING.md',
|
|
298
|
-
];
|
|
299
|
-
const GUIDE_FILES = [
|
|
300
|
-
'PROJECT-ORCHESTRATION.md',
|
|
301
|
-
'ADVANCED-WORKFLOWS.md',
|
|
302
|
-
'DATABASE-DECISION-FRAMEWORK.md',
|
|
303
|
-
'ARCHITECTURE-PATTERNS.md',
|
|
304
|
-
];
|
|
305
|
-
|
|
306
|
-
async function downloadFile(url, destPath) {
|
|
307
|
-
try {
|
|
308
|
-
const response = await fetch(url);
|
|
309
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
310
|
-
const content = await response.text();
|
|
311
|
-
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
312
|
-
await fs.writeFile(destPath, content);
|
|
313
|
-
return true;
|
|
314
|
-
} catch (err) {
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
async function readFileIfExists(filePath) {
|
|
320
|
-
try {
|
|
321
|
-
return await fs.readFile(filePath, 'utf-8');
|
|
322
|
-
} catch {
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function formatYamlExport(data) {
|
|
328
|
-
const lines = [];
|
|
329
|
-
lines.push(`generatedAt: ${JSON.stringify(data.generatedAt)}`);
|
|
330
|
-
lines.push(`root: ${JSON.stringify(data.root)}`);
|
|
331
|
-
lines.push('files:');
|
|
332
|
-
|
|
333
|
-
const fileEntries = Object.entries(data.files);
|
|
334
|
-
if (fileEntries.length === 0) {
|
|
335
|
-
lines.push(' {}');
|
|
336
|
-
} else {
|
|
337
|
-
fileEntries.forEach(([fileName, content]) => {
|
|
338
|
-
lines.push(` ${fileName}: |`);
|
|
339
|
-
const contentLines = content.length === 0 ? [''] : content.split('\n');
|
|
340
|
-
contentLines.forEach(line => {
|
|
341
|
-
lines.push(` ${line}`);
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (data.missing.length === 0) {
|
|
347
|
-
lines.push('missing: []');
|
|
348
|
-
} else {
|
|
349
|
-
lines.push('missing:');
|
|
350
|
-
data.missing.forEach(item => {
|
|
351
|
-
lines.push(` - ${item}`);
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
return lines.join('\n');
|
|
356
|
-
}
|
|
4
|
+
import { Command } from 'commander';
|
|
357
5
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
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';
|
|
371
19
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (fileList.some((file) => file.includes('next.config'))) return 'Next.js';
|
|
375
|
-
if (fileList.some((file) => file.includes('remix.config'))) return 'Remix';
|
|
376
|
-
if (fileList.some((file) => file.includes('svelte.config'))) return 'SvelteKit';
|
|
377
|
-
return 'Node.js';
|
|
378
|
-
}
|
|
379
|
-
if (fileList.some((file) => file.includes('pyproject.toml') || file.includes('requirements.txt'))) {
|
|
380
|
-
return 'Python';
|
|
381
|
-
}
|
|
382
|
-
return 'Unknown';
|
|
383
|
-
}
|
|
20
|
+
const program = new Command();
|
|
21
|
+
program.banner = banner;
|
|
384
22
|
|
|
385
23
|
program
|
|
386
24
|
.name('ultra-dex')
|
|
387
25
|
.description('CLI for Ultra-Dex SaaS Implementation Framework')
|
|
388
|
-
.version('
|
|
389
|
-
|
|
390
|
-
program
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
console.log(' CONTEXT.md');
|
|
404
|
-
console.log(' IMPLEMENTATION-PLAN.md');
|
|
405
|
-
console.log(' docs/CHECKLIST.md');
|
|
406
|
-
console.log(' docs/AI-PROMPTS.md');
|
|
407
|
-
console.log('\nRun without --preview to create files.');
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Gather project info
|
|
412
|
-
const answers = await inquirer.prompt([
|
|
413
|
-
{
|
|
414
|
-
type: 'input',
|
|
415
|
-
name: 'projectName',
|
|
416
|
-
message: 'What\'s your project name?',
|
|
417
|
-
default: options.name || 'my-saas',
|
|
418
|
-
validate: (input) => input.length > 0 || 'Project name is required',
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
type: 'input',
|
|
422
|
-
name: 'ideaWhat',
|
|
423
|
-
message: 'What are you building? (1 sentence)',
|
|
424
|
-
validate: (input) => input.length > 0 || 'Please describe your idea',
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
type: 'input',
|
|
428
|
-
name: 'ideaFor',
|
|
429
|
-
message: 'Who is it for?',
|
|
430
|
-
validate: (input) => input.length > 0 || 'Please specify your target users',
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
type: 'input',
|
|
434
|
-
name: 'problem1',
|
|
435
|
-
message: 'Problem #1 you\'re solving:',
|
|
436
|
-
default: '',
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
type: 'input',
|
|
440
|
-
name: 'problem2',
|
|
441
|
-
message: 'Problem #2 you\'re solving:',
|
|
442
|
-
default: '',
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
type: 'input',
|
|
446
|
-
name: 'problem3',
|
|
447
|
-
message: 'Problem #3 you\'re solving:',
|
|
448
|
-
default: '',
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
type: 'input',
|
|
452
|
-
name: 'feature1',
|
|
453
|
-
message: 'Critical production feature:',
|
|
454
|
-
default: '',
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
type: 'list',
|
|
458
|
-
name: 'frontend',
|
|
459
|
-
message: 'Frontend framework:',
|
|
460
|
-
choices: ['Next.js', 'Remix', 'SvelteKit', 'Nuxt', 'Other'],
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
type: 'list',
|
|
464
|
-
name: 'database',
|
|
465
|
-
message: 'Database:',
|
|
466
|
-
choices: ['PostgreSQL', 'Supabase', 'MongoDB', 'PlanetScale', 'Other'],
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
type: 'list',
|
|
470
|
-
name: 'auth',
|
|
471
|
-
message: 'Authentication:',
|
|
472
|
-
choices: ['NextAuth', 'Clerk', 'Auth0', 'Supabase Auth', 'Other'],
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
type: 'list',
|
|
476
|
-
name: 'payments',
|
|
477
|
-
message: 'Payments:',
|
|
478
|
-
choices: ['Stripe', 'Lemonsqueezy', 'Paddle', 'None (free)', 'Other'],
|
|
479
|
-
},
|
|
480
|
-
{
|
|
481
|
-
type: 'list',
|
|
482
|
-
name: 'hosting',
|
|
483
|
-
message: 'Hosting:',
|
|
484
|
-
choices: ['Vercel', 'Railway', 'Fly.io', 'AWS', 'Other'],
|
|
485
|
-
},
|
|
486
|
-
{
|
|
487
|
-
type: 'confirm',
|
|
488
|
-
name: 'includeCursorRules',
|
|
489
|
-
message: 'Include cursor-rules for AI assistants? (Cursor, Copilot)',
|
|
490
|
-
default: true,
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
type: 'confirm',
|
|
494
|
-
name: 'includeFullTemplate',
|
|
495
|
-
message: 'Copy full 34-section template locally?',
|
|
496
|
-
default: false,
|
|
497
|
-
},
|
|
498
|
-
{
|
|
499
|
-
type: 'confirm',
|
|
500
|
-
name: 'includeDocs',
|
|
501
|
-
message: 'Copy VERIFICATION.md & AGENT-INSTRUCTIONS.md to docs/?',
|
|
502
|
-
default: true,
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
type: 'confirm',
|
|
506
|
-
name: 'includeAgents',
|
|
507
|
-
message: 'Include AI agent prompts? (.agents/ folder)',
|
|
508
|
-
default: true,
|
|
509
|
-
},
|
|
510
|
-
]);
|
|
511
|
-
|
|
512
|
-
const spinner = ora('Creating project files...').start();
|
|
513
|
-
|
|
514
|
-
try {
|
|
515
|
-
const outputDir = path.resolve(options.dir, answers.projectName);
|
|
516
|
-
|
|
517
|
-
// Create directories
|
|
518
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
519
|
-
await fs.mkdir(path.join(outputDir, 'docs'), { recursive: true });
|
|
520
|
-
|
|
521
|
-
// Replace placeholders
|
|
522
|
-
const replacements = {
|
|
523
|
-
'{{PROJECT_NAME}}': answers.projectName,
|
|
524
|
-
'{{DATE}}': new Date().toISOString().split('T')[0],
|
|
525
|
-
'{{IDEA_WHAT}}': answers.ideaWhat,
|
|
526
|
-
'{{IDEA_FOR}}': answers.ideaFor,
|
|
527
|
-
'{{PROBLEM_1}}': answers.problem1 || 'Problem 1',
|
|
528
|
-
'{{PROBLEM_2}}': answers.problem2 || 'Problem 2',
|
|
529
|
-
'{{PROBLEM_3}}': answers.problem3 || 'Problem 3',
|
|
530
|
-
'{{FEATURE_1}}': answers.feature1 || 'Core feature',
|
|
531
|
-
'{{FRONTEND}}': answers.frontend,
|
|
532
|
-
'{{DATABASE}}': answers.database,
|
|
533
|
-
'{{AUTH}}': answers.auth,
|
|
534
|
-
'{{PAYMENTS}}': answers.payments,
|
|
535
|
-
'{{HOSTING}}': answers.hosting,
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
let quickStart = QUICK_START_TEMPLATE;
|
|
539
|
-
let context = CONTEXT_TEMPLATE;
|
|
540
|
-
|
|
541
|
-
for (const [key, value] of Object.entries(replacements)) {
|
|
542
|
-
quickStart = quickStart.replace(new RegExp(key, 'g'), value);
|
|
543
|
-
context = context.replace(new RegExp(key, 'g'), value);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// Write files
|
|
547
|
-
await fs.writeFile(path.join(outputDir, 'QUICK-START.md'), quickStart);
|
|
548
|
-
await fs.writeFile(path.join(outputDir, 'CONTEXT.md'), context);
|
|
549
|
-
|
|
550
|
-
// Create empty implementation plan
|
|
551
|
-
const planContent = `# ${answers.projectName} - Implementation Plan
|
|
552
|
-
|
|
553
|
-
> Generated with Ultra-Dex CLI
|
|
554
|
-
|
|
555
|
-
## Overview
|
|
556
|
-
|
|
557
|
-
${answers.ideaWhat} for ${answers.ideaFor}.
|
|
558
|
-
|
|
559
|
-
---
|
|
560
|
-
|
|
561
|
-
## Next Steps
|
|
562
|
-
|
|
563
|
-
1. Open QUICK-START.md and complete the remaining sections
|
|
564
|
-
2. Copy sections from the full Ultra-Dex template as needed
|
|
565
|
-
3. Use the TaskFlow example as reference
|
|
566
|
-
4. Start building!
|
|
567
|
-
|
|
568
|
-
## Resources
|
|
569
|
-
|
|
570
|
-
- [Full Template](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/04-Imp-Template.md)
|
|
571
|
-
- [TaskFlow Example](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/Examples/TaskFlow-Complete.md)
|
|
572
|
-
- [Methodology](https://github.com/Srujan0798/Ultra-Dex/blob/main/@%20Ultra%20DeX/Saas%20plan/03-METHODOLOGY.md)
|
|
573
|
-
`;
|
|
574
|
-
|
|
575
|
-
await fs.writeFile(path.join(outputDir, 'IMPLEMENTATION-PLAN.md'), planContent);
|
|
576
|
-
|
|
577
|
-
// Copy cursor-rules if requested
|
|
578
|
-
if (answers.includeCursorRules) {
|
|
579
|
-
const rulesDir = path.join(outputDir, '.cursor', 'rules');
|
|
580
|
-
await fs.mkdir(rulesDir, { recursive: true });
|
|
581
|
-
|
|
582
|
-
const cursorRulesPath = path.join(ASSETS_ROOT, 'cursor-rules');
|
|
583
|
-
try {
|
|
584
|
-
const ruleFiles = await fs.readdir(cursorRulesPath);
|
|
585
|
-
for (const file of ruleFiles.filter(f => f.endsWith('.mdc'))) {
|
|
586
|
-
await fs.copyFile(
|
|
587
|
-
path.join(cursorRulesPath, file),
|
|
588
|
-
path.join(rulesDir, file)
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
// Also generate .github/copilot-instructions.md for Copilot users
|
|
592
|
-
const coreRulePath = path.join(cursorRulesPath, '00-ultra-dex-core.mdc');
|
|
593
|
-
try {
|
|
594
|
-
const coreContent = await fs.readFile(coreRulePath, 'utf-8');
|
|
595
|
-
const dotGithub = path.join(outputDir, '.github');
|
|
596
|
-
await fs.mkdir(dotGithub, { recursive: true });
|
|
597
|
-
await fs.writeFile(path.join(dotGithub, 'copilot-instructions.md'), coreContent);
|
|
598
|
-
} catch (e) {
|
|
599
|
-
// Core rule not available - skip Copilot setup
|
|
600
|
-
}
|
|
601
|
-
} catch (err) {
|
|
602
|
-
// Fallback to repo root if assets are not packaged
|
|
603
|
-
const fallbackRulesPath = path.join(ROOT_FALLBACK, 'cursor-rules');
|
|
604
|
-
try {
|
|
605
|
-
const ruleFiles = await fs.readdir(fallbackRulesPath);
|
|
606
|
-
for (const file of ruleFiles.filter(f => f.endsWith('.mdc'))) {
|
|
607
|
-
await fs.copyFile(
|
|
608
|
-
path.join(fallbackRulesPath, file),
|
|
609
|
-
path.join(rulesDir, file)
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
} catch (fallbackErr) {
|
|
613
|
-
console.log(chalk.red('\n ❌ Cursor rules not found in assets or repo.'));
|
|
614
|
-
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --rules'));
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Copy full template if requested
|
|
620
|
-
if (answers.includeFullTemplate) {
|
|
621
|
-
const templatePath = path.join(ASSETS_ROOT, 'saas-plan', '04-Imp-Template.md');
|
|
622
|
-
try {
|
|
623
|
-
await fs.copyFile(templatePath, path.join(outputDir, 'docs', 'MASTER-PLAN.md'));
|
|
624
|
-
} catch (err) {
|
|
625
|
-
const fallbackTemplatePath = path.join(ROOT_FALLBACK, '@ Ultra DeX', 'Saas plan', '04-Imp-Template.md');
|
|
626
|
-
try {
|
|
627
|
-
await fs.copyFile(fallbackTemplatePath, path.join(outputDir, 'docs', 'MASTER-PLAN.md'));
|
|
628
|
-
} catch (fallbackErr) {
|
|
629
|
-
console.log(chalk.red('\n ❌ Full template not found in assets or repo.'));
|
|
630
|
-
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --docs'));
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// Copy docs if requested
|
|
636
|
-
if (answers.includeDocs) {
|
|
637
|
-
const verificationPath = path.join(ASSETS_ROOT, 'docs', 'VERIFICATION.md');
|
|
638
|
-
const agentPath = path.join(ASSETS_ROOT, 'agents', 'AGENT-INSTRUCTIONS.md');
|
|
639
|
-
try {
|
|
640
|
-
await fs.copyFile(verificationPath, path.join(outputDir, 'docs', 'CHECKLIST.md'));
|
|
641
|
-
await fs.copyFile(agentPath, path.join(outputDir, 'docs', 'AI-PROMPTS.md'));
|
|
642
|
-
} catch (err) {
|
|
643
|
-
const fallbackVerificationPath = path.join(ROOT_FALLBACK, 'docs', 'VERIFICATION.md');
|
|
644
|
-
const fallbackAgentPath = path.join(ROOT_FALLBACK, 'agents', 'AGENT-INSTRUCTIONS.md');
|
|
645
|
-
try {
|
|
646
|
-
await fs.copyFile(fallbackVerificationPath, path.join(outputDir, 'docs', 'CHECKLIST.md'));
|
|
647
|
-
await fs.copyFile(fallbackAgentPath, path.join(outputDir, 'docs', 'AI-PROMPTS.md'));
|
|
648
|
-
} catch (fallbackErr) {
|
|
649
|
-
console.log(chalk.red('\n ❌ Docs not found in assets or repo.'));
|
|
650
|
-
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --docs'));
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// Copy agents if requested
|
|
656
|
-
if (answers.includeAgents) {
|
|
657
|
-
const agentsDir = path.join(outputDir, '.agents');
|
|
658
|
-
await fs.mkdir(agentsDir, { recursive: true });
|
|
659
|
-
|
|
660
|
-
const agentsSourcePath = path.join(ASSETS_ROOT, 'agents');
|
|
661
|
-
try {
|
|
662
|
-
// Copy tier directories and agent files
|
|
663
|
-
const tiers = ['1-leadership', '2-development', '3-security', '4-devops', '5-quality', '6-specialist'];
|
|
664
|
-
for (const tier of tiers) {
|
|
665
|
-
const tierDir = path.join(agentsDir, tier);
|
|
666
|
-
await fs.mkdir(tierDir, { recursive: true });
|
|
667
|
-
|
|
668
|
-
const tierPath = path.join(agentsSourcePath, tier);
|
|
669
|
-
const tierFiles = await fs.readdir(tierPath);
|
|
670
|
-
for (const file of tierFiles.filter(f => f.endsWith('.md'))) {
|
|
671
|
-
await fs.copyFile(
|
|
672
|
-
path.join(tierPath, file),
|
|
673
|
-
path.join(tierDir, file)
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// Copy agent index and README
|
|
679
|
-
await fs.copyFile(
|
|
680
|
-
path.join(agentsSourcePath, '00-AGENT_INDEX.md'),
|
|
681
|
-
path.join(agentsDir, '00-AGENT_INDEX.md')
|
|
682
|
-
);
|
|
683
|
-
await fs.copyFile(
|
|
684
|
-
path.join(agentsSourcePath, 'README.md'),
|
|
685
|
-
path.join(agentsDir, 'README.md')
|
|
686
|
-
);
|
|
687
|
-
} catch (err) {
|
|
688
|
-
const fallbackAgentsPath = path.join(ROOT_FALLBACK, 'agents');
|
|
689
|
-
try {
|
|
690
|
-
const tiers = ['1-leadership', '2-development', '3-security', '4-devops', '5-quality', '6-specialist'];
|
|
691
|
-
for (const tier of tiers) {
|
|
692
|
-
const tierDir = path.join(agentsDir, tier);
|
|
693
|
-
await fs.mkdir(tierDir, { recursive: true });
|
|
694
|
-
|
|
695
|
-
const tierPath = path.join(fallbackAgentsPath, tier);
|
|
696
|
-
const tierFiles = await fs.readdir(tierPath);
|
|
697
|
-
for (const file of tierFiles.filter(f => f.endsWith('.md'))) {
|
|
698
|
-
await fs.copyFile(
|
|
699
|
-
path.join(tierPath, file),
|
|
700
|
-
path.join(tierDir, file)
|
|
701
|
-
);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
await fs.copyFile(
|
|
706
|
-
path.join(fallbackAgentsPath, '00-AGENT_INDEX.md'),
|
|
707
|
-
path.join(agentsDir, '00-AGENT_INDEX.md')
|
|
708
|
-
);
|
|
709
|
-
await fs.copyFile(
|
|
710
|
-
path.join(fallbackAgentsPath, 'README.md'),
|
|
711
|
-
path.join(agentsDir, 'README.md')
|
|
712
|
-
);
|
|
713
|
-
} catch (fallbackErr) {
|
|
714
|
-
console.log(chalk.red('\n ❌ Agent prompts not found in assets or repo.'));
|
|
715
|
-
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --agents'));
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
spinner.succeed(chalk.green('Project created successfully!'));
|
|
721
|
-
|
|
722
|
-
console.log('\n' + chalk.bold('Files created:'));
|
|
723
|
-
console.log(chalk.gray(` ${outputDir}/`));
|
|
724
|
-
console.log(chalk.gray(' ├── QUICK-START.md'));
|
|
725
|
-
console.log(chalk.gray(' ├── CONTEXT.md'));
|
|
726
|
-
console.log(chalk.gray(' ├── IMPLEMENTATION-PLAN.md'));
|
|
727
|
-
if (answers.includeFullTemplate) {
|
|
728
|
-
console.log(chalk.gray(' ├── docs/MASTER-PLAN.md (34 sections)'));
|
|
729
|
-
}
|
|
730
|
-
if (answers.includeDocs) {
|
|
731
|
-
console.log(chalk.gray(' ├── docs/CHECKLIST.md'));
|
|
732
|
-
console.log(chalk.gray(' ├── docs/AI-PROMPTS.md'));
|
|
733
|
-
}
|
|
734
|
-
if (answers.includeCursorRules) {
|
|
735
|
-
console.log(chalk.gray(' ├── .cursor/rules/ (11 AI rule files)'));
|
|
736
|
-
}
|
|
737
|
-
if (answers.includeAgents) {
|
|
738
|
-
console.log(chalk.gray(' └── .agents/ (15 AI agent prompts in 6 tiers)'));
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
console.log('\n' + chalk.bold('Next steps:'));
|
|
742
|
-
console.log(chalk.cyan(` 1. cd ${answers.projectName}`));
|
|
743
|
-
console.log(chalk.cyan(' 2. Open QUICK-START.md and complete it'));
|
|
744
|
-
console.log(chalk.cyan(' 3. Start building! 🚀'));
|
|
745
|
-
|
|
746
|
-
console.log('\n' + chalk.gray('Full Ultra-Dex repo:'));
|
|
747
|
-
console.log(chalk.blue(' https://github.com/Srujan0798/Ultra-Dex'));
|
|
748
|
-
|
|
749
|
-
} catch (error) {
|
|
750
|
-
spinner.fail(chalk.red('Failed to create project'));
|
|
751
|
-
console.error(error);
|
|
752
|
-
process.exit(1);
|
|
753
|
-
}
|
|
754
|
-
});
|
|
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);
|
|
755
41
|
|
|
756
42
|
program
|
|
757
43
|
.command('audit')
|
|
@@ -966,42 +252,492 @@ program
|
|
|
966
252
|
});
|
|
967
253
|
|
|
968
254
|
// ========================================
|
|
969
|
-
//
|
|
255
|
+
// GENERATE COMMAND - AI-Powered Plan Generation (v2.0)
|
|
970
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
|
+
|
|
971
361
|
program
|
|
972
362
|
.command('generate')
|
|
973
|
-
.description('Generate a full implementation plan from an idea
|
|
974
|
-
.
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
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
|
+
}
|
|
978
490
|
});
|
|
979
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
|
+
|
|
980
508
|
program
|
|
981
509
|
.command('build')
|
|
982
|
-
.description('Start AI-assisted
|
|
983
|
-
.
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
+
}
|
|
987
641
|
});
|
|
988
642
|
|
|
643
|
+
// ========================================
|
|
644
|
+
// REVIEW COMMAND - Code vs Plan Alignment (v2.2)
|
|
645
|
+
// ========================================
|
|
989
646
|
program
|
|
990
647
|
.command('review')
|
|
991
|
-
.description('Review code against implementation plan
|
|
992
|
-
.
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
console.log(chalk.cyan('
|
|
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'));
|
|
996
731
|
});
|
|
997
732
|
|
|
998
733
|
// ========================================
|
|
999
|
-
// MCP SERVER (Context over HTTP)
|
|
734
|
+
// MCP SERVER (Enhanced Context over HTTP)
|
|
1000
735
|
// ========================================
|
|
1001
736
|
program
|
|
1002
737
|
.command('serve')
|
|
1003
738
|
.description('Serve Ultra-Dex context over HTTP (MCP-compatible)')
|
|
1004
739
|
.option('-p, --port <port>', 'Port to listen on', '3001')
|
|
740
|
+
.option('-w, --watch', 'Watch for file changes and notify clients')
|
|
1005
741
|
.action(async (options) => {
|
|
1006
742
|
const port = Number.parseInt(options.port, 10);
|
|
1007
743
|
if (Number.isNaN(port)) {
|
|
@@ -1012,28 +748,144 @@ program
|
|
|
1012
748
|
async function readFileSafe(filePath, label) {
|
|
1013
749
|
try {
|
|
1014
750
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
1015
|
-
return { label, content };
|
|
751
|
+
return { label, content, exists: true };
|
|
1016
752
|
} catch {
|
|
1017
|
-
return { label, content: '' };
|
|
753
|
+
return { label, content: '', exists: false };
|
|
1018
754
|
}
|
|
1019
755
|
}
|
|
1020
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
|
+
|
|
1021
795
|
const server = http.createServer(async (req, res) => {
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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 }));
|
|
1025
834
|
return;
|
|
1026
835
|
}
|
|
1027
836
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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
|
+
`;
|
|
1034
881
|
|
|
1035
882
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1036
|
-
res.end(JSON.stringify({
|
|
883
|
+
res.end(JSON.stringify({
|
|
884
|
+
agent: agentConfig,
|
|
885
|
+
prompt,
|
|
886
|
+
contextLength: context.content.length,
|
|
887
|
+
planLength: plan.content.length,
|
|
888
|
+
}));
|
|
1037
889
|
return;
|
|
1038
890
|
}
|
|
1039
891
|
|
|
@@ -1042,9 +894,16 @@ program
|
|
|
1042
894
|
});
|
|
1043
895
|
|
|
1044
896
|
server.listen(port, () => {
|
|
1045
|
-
console.log(chalk.green(`\n✅ Ultra-Dex MCP
|
|
1046
|
-
console.log(chalk.
|
|
1047
|
-
console.log(chalk.
|
|
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'));
|
|
1048
907
|
});
|
|
1049
908
|
});
|
|
1050
909
|
|
|
@@ -1902,4 +1761,373 @@ program
|
|
|
1902
1761
|
console.log(chalk.gray(' No GitHub access needed after fetch.\n'));
|
|
1903
1762
|
});
|
|
1904
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
|
+
|
|
1905
2133
|
program.parse();
|