ultra-dex 2.2.0 → 3.1.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 (61) hide show
  1. package/README.md +84 -122
  2. package/assets/agents/0-orchestration/orchestrator.md +2 -2
  3. package/assets/agents/00-AGENT_INDEX.md +1 -1
  4. package/assets/docs/LAUNCH-POSTS.md +1 -1
  5. package/assets/docs/QUICK-REFERENCE.md +12 -7
  6. package/assets/docs/ROADMAP.md +5 -5
  7. package/assets/docs/VISION-V2.md +1 -1
  8. package/assets/docs/WORKFLOW-DIAGRAMS.md +1 -1
  9. package/assets/hooks/pre-commit +98 -0
  10. package/assets/saas-plan/04-Imp-Template.md +1 -1
  11. package/assets/templates/README.md +1 -1
  12. package/bin/ultra-dex.js +93 -2096
  13. package/lib/commands/advanced.js +471 -0
  14. package/lib/commands/agent-builder.js +226 -0
  15. package/lib/commands/agents.js +101 -47
  16. package/lib/commands/auto-implement.js +68 -0
  17. package/lib/commands/build.js +73 -187
  18. package/lib/commands/ci-monitor.js +84 -0
  19. package/lib/commands/config.js +207 -0
  20. package/lib/commands/dashboard.js +770 -0
  21. package/lib/commands/diff.js +233 -0
  22. package/lib/commands/doctor.js +397 -0
  23. package/lib/commands/export.js +408 -0
  24. package/lib/commands/fix.js +96 -0
  25. package/lib/commands/generate.js +96 -72
  26. package/lib/commands/hooks.js +251 -76
  27. package/lib/commands/init.js +56 -6
  28. package/lib/commands/memory.js +80 -0
  29. package/lib/commands/plan.js +82 -0
  30. package/lib/commands/review.js +34 -5
  31. package/lib/commands/run.js +233 -0
  32. package/lib/commands/serve.js +188 -40
  33. package/lib/commands/state.js +354 -0
  34. package/lib/commands/swarm.js +284 -0
  35. package/lib/commands/sync.js +94 -0
  36. package/lib/commands/team.js +275 -0
  37. package/lib/commands/upgrade.js +190 -0
  38. package/lib/commands/validate.js +34 -0
  39. package/lib/commands/verify.js +81 -0
  40. package/lib/commands/watch.js +79 -0
  41. package/lib/mcp/graph.js +92 -0
  42. package/lib/mcp/memory.js +95 -0
  43. package/lib/mcp/resources.js +152 -0
  44. package/lib/mcp/server.js +34 -0
  45. package/lib/mcp/tools.js +481 -0
  46. package/lib/mcp/websocket.js +117 -0
  47. package/lib/providers/index.js +49 -4
  48. package/lib/providers/ollama.js +136 -0
  49. package/lib/providers/router.js +63 -0
  50. package/lib/quality/scanner.js +128 -0
  51. package/lib/swarm/coordinator.js +97 -0
  52. package/lib/swarm/index.js +598 -0
  53. package/lib/swarm/protocol.js +677 -0
  54. package/lib/swarm/tiers.js +485 -0
  55. package/lib/templates/context.js +2 -2
  56. package/lib/templates/custom-agent.md +10 -0
  57. package/lib/utils/fallback.js +4 -2
  58. package/lib/utils/files.js +7 -34
  59. package/lib/utils/graph.js +108 -0
  60. package/lib/utils/sync.js +216 -0
  61. package/package.json +22 -13
@@ -0,0 +1,216 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ const IGNORED_DIRS = new Set([
5
+ '.git',
6
+ 'node_modules',
7
+ '.next',
8
+ 'dist',
9
+ 'build',
10
+ 'out',
11
+ '.turbo',
12
+ '.cache',
13
+ '.ultra-dex',
14
+ '.cursor',
15
+ '.agents',
16
+ 'coverage',
17
+ '.idea',
18
+ '.vscode',
19
+ ]);
20
+ const IGNORED_FILES = new Set(['CONTEXT.md', '.DS_Store']);
21
+ const SNAPSHOT_DIR = '.ultra-dex';
22
+ const SNAPSHOT_FILE = 'context-snapshot.json';
23
+ const AUTO_SYNC_HEADER = '## Auto-Sync Snapshot';
24
+ const SCHEMA_PATTERNS = [
25
+ /schema\.prisma$/i,
26
+ /drizzle\/schema/i,
27
+ /supabase\/migrations/i,
28
+ /migrations\/.*\.(sql|ts|js)$/i,
29
+ /db\/schema/i,
30
+ ];
31
+
32
+ async function listFilesRecursive(rootDir, baseDir = rootDir) {
33
+ let results = [];
34
+ const entries = await fs.readdir(rootDir, { withFileTypes: true });
35
+ for (const entry of entries) {
36
+ if (IGNORED_DIRS.has(entry.name)) continue;
37
+ const fullPath = path.join(rootDir, entry.name);
38
+ const relativePath = path.relative(baseDir, fullPath);
39
+ if (entry.isDirectory()) {
40
+ results = results.concat(await listFilesRecursive(fullPath, baseDir));
41
+ } else if (entry.isFile()) {
42
+ if (IGNORED_FILES.has(entry.name)) continue;
43
+ results.push(relativePath);
44
+ }
45
+ }
46
+ return results;
47
+ }
48
+
49
+ function inferStackFromFiles(fileList) {
50
+ if (fileList.some((file) => file.includes('package.json'))) {
51
+ if (fileList.some((file) => file.includes('next.config'))) return 'Next.js';
52
+ if (fileList.some((file) => file.includes('remix.config'))) return 'Remix';
53
+ if (fileList.some((file) => file.includes('svelte.config'))) return 'SvelteKit';
54
+ return 'Node.js';
55
+ }
56
+ if (fileList.some((file) => file.includes('pyproject.toml') || file.includes('requirements.txt'))) {
57
+ return 'Python';
58
+ }
59
+ return 'Unknown';
60
+ }
61
+
62
+ function classifyFilePaths(files) {
63
+ const appFiles = [];
64
+ const apiFiles = [];
65
+ const schemaFiles = [];
66
+ const configFiles = [];
67
+
68
+ for (const file of files) {
69
+ if (SCHEMA_PATTERNS.some((pattern) => pattern.test(file))) {
70
+ schemaFiles.push(file);
71
+ continue;
72
+ }
73
+ if (/^app\/api\//i.test(file) || /api\/.*\.(ts|js)$/i.test(file)) {
74
+ apiFiles.push(file);
75
+ continue;
76
+ }
77
+ if (/\.(tsx|jsx|svelte|vue)$/i.test(file) || /^app\//i.test(file)) {
78
+ appFiles.push(file);
79
+ continue;
80
+ }
81
+ if (/(config|\.config)\.(js|ts|json)$/i.test(file) || /\.(env|toml|yaml|yml)$/i.test(file)) {
82
+ configFiles.push(file);
83
+ }
84
+ }
85
+
86
+ return { appFiles, apiFiles, schemaFiles, configFiles };
87
+ }
88
+
89
+ function buildAutoSyncSection(summary) {
90
+ const lines = [];
91
+ lines.push(AUTO_SYNC_HEADER);
92
+ lines.push('');
93
+ lines.push(`- Last synced: ${summary.generatedAt}`);
94
+ lines.push(`- Project root: ${summary.root}`);
95
+ lines.push(`- Stack guess: ${summary.stack}`);
96
+ lines.push(`- Total files scanned: ${summary.fileCount}`);
97
+ lines.push(`- App/UI files: ${summary.appCount}`);
98
+ lines.push(`- API files: ${summary.apiCount}`);
99
+ lines.push(`- Schema files: ${summary.schemaCount}`);
100
+ lines.push(`- Config files: ${summary.configCount}`);
101
+ lines.push('');
102
+ if (summary.appFiles.length > 0) {
103
+ lines.push('### App/UI Files');
104
+ lines.push(...summary.appFiles.map((file) => `- ${file}`));
105
+ lines.push('');
106
+ }
107
+ if (summary.apiFiles.length > 0) {
108
+ lines.push('### API Files');
109
+ lines.push(...summary.apiFiles.map((file) => `- ${file}`));
110
+ lines.push('');
111
+ }
112
+ if (summary.schemaFiles.length > 0) {
113
+ lines.push('### Schema Files');
114
+ lines.push(...summary.schemaFiles.map((file) => `- ${file}`));
115
+ lines.push('');
116
+ }
117
+ if (summary.configFiles.length > 0) {
118
+ lines.push('### Config Files');
119
+ lines.push(...summary.configFiles.map((file) => `- ${file}`));
120
+ lines.push('');
121
+ }
122
+
123
+ return lines.join('\n').trim();
124
+ }
125
+
126
+ function summarizeDiff(previous, next) {
127
+ if (!previous) {
128
+ return {
129
+ added: next.fileCount,
130
+ removed: 0,
131
+ };
132
+ }
133
+ const previousSet = new Set(previous.fileList || []);
134
+ const nextSet = new Set(next.fileList || []);
135
+ let added = 0;
136
+ let removed = 0;
137
+ for (const file of nextSet) {
138
+ if (!previousSet.has(file)) added++;
139
+ }
140
+ for (const file of previousSet) {
141
+ if (!nextSet.has(file)) removed++;
142
+ }
143
+ return { added, removed };
144
+ }
145
+
146
+ export async function snapshotContext(rootDir) {
147
+ const files = await listFilesRecursive(rootDir);
148
+ const { appFiles, apiFiles, schemaFiles, configFiles } = classifyFilePaths(files);
149
+ const summary = {
150
+ generatedAt: new Date().toISOString(),
151
+ root: rootDir,
152
+ fileCount: files.length,
153
+ appCount: appFiles.length,
154
+ apiCount: apiFiles.length,
155
+ schemaCount: schemaFiles.length,
156
+ configCount: configFiles.length,
157
+ appFiles: appFiles.slice(0, 25),
158
+ apiFiles: apiFiles.slice(0, 25),
159
+ schemaFiles: schemaFiles.slice(0, 25),
160
+ configFiles: configFiles.slice(0, 25),
161
+ stack: inferStackFromFiles(files),
162
+ fileList: files,
163
+ };
164
+
165
+ const snapshotDir = path.join(rootDir, SNAPSHOT_DIR);
166
+ await fs.mkdir(snapshotDir, { recursive: true });
167
+ const snapshotPath = path.join(snapshotDir, SNAPSHOT_FILE);
168
+ let previous = null;
169
+ try {
170
+ const previousRaw = await fs.readFile(snapshotPath, 'utf-8');
171
+ previous = JSON.parse(previousRaw);
172
+ } catch {
173
+ previous = null;
174
+ }
175
+
176
+ await fs.writeFile(snapshotPath, JSON.stringify(summary, null, 2));
177
+
178
+ const contextPath = path.join(rootDir, 'CONTEXT.md');
179
+ let contextContent = null;
180
+ try {
181
+ contextContent = await fs.readFile(contextPath, 'utf-8');
182
+ } catch {
183
+ return {
184
+ summary,
185
+ updated: false,
186
+ missingContext: true,
187
+ diff: summarizeDiff(previous, summary),
188
+ };
189
+ }
190
+
191
+ const section = buildAutoSyncSection(summary);
192
+ let updatedContext = contextContent;
193
+ if (contextContent.includes(AUTO_SYNC_HEADER)) {
194
+ const pattern = new RegExp(`${AUTO_SYNC_HEADER}[\\s\\S]*?(?=^##\\s|\\n##\\s|$)`, 'm');
195
+ updatedContext = contextContent.replace(pattern, `${section}\n\n`);
196
+ } else {
197
+ updatedContext = `${contextContent.trim()}\n\n${section}\n`;
198
+ }
199
+
200
+ if (updatedContext !== contextContent) {
201
+ await fs.writeFile(contextPath, updatedContext);
202
+ return {
203
+ summary,
204
+ updated: true,
205
+ missingContext: false,
206
+ diff: summarizeDiff(previous, summary),
207
+ };
208
+ }
209
+
210
+ return {
211
+ summary,
212
+ updated: false,
213
+ missingContext: false,
214
+ diff: summarizeDiff(previous, summary),
215
+ };
216
+ }
package/package.json CHANGED
@@ -1,20 +1,18 @@
1
1
  {
2
2
  "name": "ultra-dex",
3
- "version": "2.2.0",
4
- "description": "CLI for Ultra-Dex SaaS Implementation Framework with AI-Powered Plan Generation, Build Mode, and Code Review",
3
+ "version": "3.1.0",
4
+ "description": "AI Orchestration Meta-Layer for SaaS Development",
5
5
  "keywords": [
6
- "saas",
7
- "template",
8
- "implementation",
9
- "planning",
10
- "startup",
11
- "framework",
12
6
  "ai",
7
+ "orchestration",
8
+ "saas",
9
+ "mcp",
10
+ "langchain",
13
11
  "claude",
14
12
  "openai",
15
13
  "gemini",
16
- "orchestration",
17
- "mcp"
14
+ "agents",
15
+ "swarm"
18
16
  ],
19
17
  "author": "Srujan Sai Karna",
20
18
  "license": "MIT",
@@ -36,15 +34,26 @@
36
34
  "node": ">=18"
37
35
  },
38
36
  "dependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.25.3",
39
38
  "chalk": "^5.3.0",
40
39
  "commander": "^11.1.0",
40
+ "glob": "^10.5.0",
41
41
  "inquirer": "^9.2.12",
42
- "ora": "^8.0.1"
42
+ "ora": "^8.0.1",
43
+ "ws": "^8.19.0",
44
+ "zod": "^3.25.76"
45
+ },
46
+ "scripts": {
47
+ "test": "node --test test/*.test.js",
48
+ "lint": "eslint lib bin --fix"
43
49
  },
44
50
  "optionalDependencies": {
45
51
  "@anthropic-ai/sdk": "^0.30.0",
46
- "openai": "^4.70.0",
47
52
  "@google/generative-ai": "^0.21.0",
48
- "dotenv": "^16.4.5"
53
+ "dotenv": "^16.4.5",
54
+ "openai": "^4.70.0"
55
+ },
56
+ "devDependencies": {
57
+ "eslint": "^8.57.0"
49
58
  }
50
59
  }