ultra-dex 2.2.1 → 3.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 (87) hide show
  1. package/README.md +112 -151
  2. package/assets/agents/00-AGENT_INDEX.md +1 -1
  3. package/assets/code-patterns/clerk-middleware.ts +138 -0
  4. package/assets/code-patterns/prisma-schema.prisma +224 -0
  5. package/assets/code-patterns/rls-policies.sql +246 -0
  6. package/assets/code-patterns/server-actions.ts +191 -0
  7. package/assets/code-patterns/trpc-router.ts +258 -0
  8. package/assets/cursor-rules/13-ai-integration.mdc +155 -0
  9. package/assets/cursor-rules/14-server-components.mdc +81 -0
  10. package/assets/cursor-rules/15-server-actions.mdc +102 -0
  11. package/assets/cursor-rules/16-edge-middleware.mdc +105 -0
  12. package/assets/cursor-rules/17-streaming-ssr.mdc +138 -0
  13. package/assets/docs/LAUNCH-POSTS.md +1 -1
  14. package/assets/docs/QUICK-REFERENCE.md +9 -4
  15. package/assets/docs/VISION-V2.md +1 -1
  16. package/assets/hooks/pre-commit +98 -0
  17. package/assets/saas-plan/04-Imp-Template.md +1 -1
  18. package/bin/ultra-dex.js +132 -4
  19. package/lib/commands/advanced.js +471 -0
  20. package/lib/commands/agent-builder.js +226 -0
  21. package/lib/commands/agents.js +102 -42
  22. package/lib/commands/auto-implement.js +68 -0
  23. package/lib/commands/banner.js +43 -21
  24. package/lib/commands/build.js +78 -183
  25. package/lib/commands/ci-monitor.js +84 -0
  26. package/lib/commands/config.js +207 -0
  27. package/lib/commands/dashboard.js +770 -0
  28. package/lib/commands/diff.js +233 -0
  29. package/lib/commands/doctor.js +416 -0
  30. package/lib/commands/export.js +408 -0
  31. package/lib/commands/fix.js +96 -0
  32. package/lib/commands/generate.js +105 -78
  33. package/lib/commands/hooks.js +251 -76
  34. package/lib/commands/init.js +102 -54
  35. package/lib/commands/memory.js +80 -0
  36. package/lib/commands/plan.js +82 -0
  37. package/lib/commands/review.js +34 -5
  38. package/lib/commands/run.js +233 -0
  39. package/lib/commands/scaffold.js +151 -0
  40. package/lib/commands/serve.js +179 -146
  41. package/lib/commands/state.js +327 -0
  42. package/lib/commands/swarm.js +306 -0
  43. package/lib/commands/sync.js +82 -23
  44. package/lib/commands/team.js +275 -0
  45. package/lib/commands/upgrade.js +190 -0
  46. package/lib/commands/validate.js +34 -0
  47. package/lib/commands/verify.js +81 -0
  48. package/lib/commands/watch.js +79 -0
  49. package/lib/config/theme.js +47 -0
  50. package/lib/mcp/graph.js +92 -0
  51. package/lib/mcp/memory.js +95 -0
  52. package/lib/mcp/resources.js +152 -0
  53. package/lib/mcp/server.js +34 -0
  54. package/lib/mcp/tools.js +481 -0
  55. package/lib/mcp/websocket.js +117 -0
  56. package/lib/providers/index.js +49 -4
  57. package/lib/providers/ollama.js +136 -0
  58. package/lib/providers/router.js +63 -0
  59. package/lib/quality/scanner.js +128 -0
  60. package/lib/swarm/coordinator.js +97 -0
  61. package/lib/swarm/index.js +598 -0
  62. package/lib/swarm/protocol.js +677 -0
  63. package/lib/swarm/tiers.js +485 -0
  64. package/lib/templates/code/clerk-middleware.ts +138 -0
  65. package/lib/templates/code/prisma-schema.prisma +224 -0
  66. package/lib/templates/code/rls-policies.sql +246 -0
  67. package/lib/templates/code/server-actions.ts +191 -0
  68. package/lib/templates/code/trpc-router.ts +258 -0
  69. package/lib/templates/custom-agent.md +10 -0
  70. package/lib/themes/doomsday.js +229 -0
  71. package/lib/ui/index.js +5 -0
  72. package/lib/ui/interface.js +241 -0
  73. package/lib/ui/spinners.js +116 -0
  74. package/lib/ui/theme.js +183 -0
  75. package/lib/utils/agents.js +32 -0
  76. package/lib/utils/files.js +14 -0
  77. package/lib/utils/graph.js +108 -0
  78. package/lib/utils/help.js +64 -0
  79. package/lib/utils/messages.js +35 -0
  80. package/lib/utils/progress.js +24 -0
  81. package/lib/utils/prompts.js +47 -0
  82. package/lib/utils/spinners.js +46 -0
  83. package/lib/utils/status.js +31 -0
  84. package/lib/utils/tables.js +41 -0
  85. package/lib/utils/theme-state.js +9 -0
  86. package/lib/utils/version-display.js +32 -0
  87. package/package.json +31 -13
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Ollama AI Provider (Local)
3
+ * Provides local intelligence for Ultra-Dex
4
+ */
5
+
6
+ import { BaseProvider } from './base.js';
7
+
8
+ const MODELS = [
9
+ { id: 'llama3:8b', name: 'Llama 3 (8B)', maxTokens: 8192, default: true },
10
+ { id: 'mistral', name: 'Mistral', maxTokens: 8192 },
11
+ { id: 'phi3', name: 'Phi-3', maxTokens: 4096 },
12
+ { id: 'codellama', name: 'CodeLlama', maxTokens: 8192 },
13
+ ];
14
+
15
+ export class OllamaProvider extends BaseProvider {
16
+ constructor(apiKey, options = {}) {
17
+ // Ollama doesn't typically require an API key
18
+ super(apiKey || 'not-required', options);
19
+ this.baseUrl = options.baseUrl || 'http://localhost:11434/api';
20
+ }
21
+
22
+ getName() {
23
+ return 'Ollama (Local)';
24
+ }
25
+
26
+ getDefaultModel() {
27
+ return 'llama3:8b';
28
+ }
29
+
30
+ getAvailableModels() {
31
+ return MODELS;
32
+ }
33
+
34
+ estimateCost(inputTokens, outputTokens) {
35
+ // Local is free!
36
+ return {
37
+ input: 0,
38
+ output: 0,
39
+ total: 0,
40
+ };
41
+ }
42
+
43
+ async generate(systemPrompt, userPrompt, options = {}) {
44
+ const response = await fetch(`${this.baseUrl}/generate`, {
45
+ method: 'POST',
46
+ headers: {
47
+ 'Content-Type': 'application/json',
48
+ },
49
+ body: JSON.stringify({
50
+ model: this.model,
51
+ prompt: `${systemPrompt}\n\n${userPrompt}`,
52
+ stream: false,
53
+ options: {
54
+ num_predict: options.maxTokens || this.maxTokens,
55
+ },
56
+ }),
57
+ });
58
+
59
+ if (!response.ok) {
60
+ const error = await response.text().catch(() => response.statusText);
61
+ throw new Error(`Ollama API error: ${error}`);
62
+ }
63
+
64
+ const data = await response.json();
65
+
66
+ return {
67
+ content: data.response || '',
68
+ usage: {
69
+ inputTokens: data.prompt_eval_count || 0,
70
+ outputTokens: data.eval_count || 0,
71
+ },
72
+ };
73
+ }
74
+
75
+ async generateStream(systemPrompt, userPrompt, onChunk, options = {}) {
76
+ const response = await fetch(`${this.baseUrl}/generate`, {
77
+ method: 'POST',
78
+ headers: {
79
+ 'Content-Type': 'application/json',
80
+ },
81
+ body: JSON.stringify({
82
+ model: this.model,
83
+ prompt: `${systemPrompt}\n\n${userPrompt}`,
84
+ stream: true,
85
+ options: {
86
+ num_predict: options.maxTokens || this.maxTokens,
87
+ },
88
+ }),
89
+ });
90
+
91
+ if (!response.ok) {
92
+ throw new Error(`Ollama API error: ${response.statusText}`);
93
+ }
94
+
95
+ const reader = response.body.getReader();
96
+ const decoder = new TextDecoder();
97
+ let fullContent = '';
98
+
99
+ while (true) {
100
+ const { done, value } = await reader.read();
101
+ if (done) break;
102
+
103
+ const chunk = decoder.decode(value);
104
+ const lines = chunk.split('\n');
105
+
106
+ for (const line of lines) {
107
+ if (!line.trim()) continue;
108
+ try {
109
+ const parsed = JSON.parse(line);
110
+ if (parsed.response) {
111
+ fullContent += parsed.response;
112
+ onChunk(parsed.response);
113
+ }
114
+ } catch {
115
+ // Skip malformed JSON
116
+ }
117
+ }
118
+ }
119
+
120
+ return {
121
+ content: fullContent,
122
+ usage: { inputTokens: 0, outputTokens: 0 } // Ollama streaming usage is complex to track line by line
123
+ };
124
+ }
125
+
126
+ async validateApiKey() {
127
+ try {
128
+ const response = await fetch(`${this.baseUrl}/tags`);
129
+ return response.ok;
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+ }
135
+
136
+ export default OllamaProvider;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Semantic Router AI Provider
3
+ * Routes tasks between local and cloud intelligence
4
+ */
5
+
6
+ import { BaseProvider } from './base.js';
7
+
8
+ export class RouterProvider extends BaseProvider {
9
+ constructor(apiKey, options = {}) {
10
+ super(apiKey, options);
11
+ this.localProvider = options.localProvider;
12
+ this.cloudProvider = options.cloudProvider;
13
+ this.threshold = options.threshold || 'medium'; // complexity threshold
14
+ }
15
+
16
+ getName() {
17
+ return `Semantic Router (${this.localProvider?.getName() || 'Local'} + ${this.cloudProvider?.getName() || 'Cloud'})`;
18
+ }
19
+
20
+ getDefaultModel() {
21
+ return 'router-v1';
22
+ }
23
+
24
+ async generate(systemPrompt, userPrompt, options = {}) {
25
+ const isComplex = this.assessComplexity(systemPrompt, userPrompt);
26
+ const provider = (isComplex || !this.localProvider) ? this.cloudProvider : this.localProvider;
27
+
28
+ console.error(`[Router] Routing to ${provider.getName()} (Complexity: ${isComplex ? 'High' : 'Low'})`);
29
+
30
+ return provider.generate(systemPrompt, userPrompt, options);
31
+ }
32
+
33
+ async generateStream(systemPrompt, userPrompt, onChunk, options = {}) {
34
+ const isComplex = this.assessComplexity(systemPrompt, userPrompt);
35
+ const provider = (isComplex || !this.localProvider) ? this.cloudProvider : this.localProvider;
36
+
37
+ console.error(`[Router] Routing to ${provider.getName()} (Complexity: ${isComplex ? 'High' : 'Low'})`);
38
+
39
+ return provider.generateStream(systemPrompt, userPrompt, onChunk, options);
40
+ }
41
+
42
+ assessComplexity(systemPrompt, userPrompt) {
43
+ const combined = (systemPrompt + userPrompt).toLowerCase();
44
+
45
+ // Heuristics for "High Complexity"
46
+ const complexKeywords = [
47
+ 'refactor', 'architect', 'security audit', 'design pattern',
48
+ 'migration', 'performance optimization', 'complex', 'fix the bug'
49
+ ];
50
+
51
+ const isComplexKeyword = complexKeywords.some(k => combined.includes(k));
52
+ const isLongPrompt = combined.length > 2000;
53
+
54
+ return isComplexKeyword || isLongPrompt;
55
+ }
56
+
57
+ async validateApiKey() {
58
+ const cloudValid = await this.cloudProvider?.validateApiKey();
59
+ return !!cloudValid;
60
+ }
61
+ }
62
+
63
+ export default RouterProvider;
@@ -0,0 +1,128 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ // Define the Quality Rules
5
+ const RULES = [
6
+ {
7
+ id: 'api-zod-validation',
8
+ name: 'API Input Validation',
9
+ description: 'API endpoints must validate input using Zod',
10
+ severity: 'error',
11
+ // Regex based pattern matching for file path
12
+ pattern: /app\/api\/.*\.ts|src\/routes\/.*\.ts|pages\/api\/.*\.ts/,
13
+ check: (content) => {
14
+ const isApi = /NextRequest|NextResponse|express\.Router|fastify/.test(content);
15
+ if (!isApi) return true;
16
+ return /import.*zod|require\(['"]zod['"]\)/.test(content);
17
+ },
18
+ message: 'API files must import "zod" for validation.'
19
+ },
20
+ {
21
+ id: 'no-explicit-any',
22
+ name: 'No Explicit Any',
23
+ description: 'Avoid using "any" type in TypeScript',
24
+ severity: 'warning',
25
+ pattern: /.*\.tsx?$/,
26
+ check: (content) => {
27
+ return !/:\s*any\b|<\s*any\s*>/.test(content);
28
+ },
29
+ message: 'Found explicit "any" type. Use unknown or a specific type.'
30
+ },
31
+ {
32
+ id: 'console-log-in-api',
33
+ name: 'No Console Log in Prod',
34
+ description: 'Use a logger instead of console.log in API routes',
35
+ severity: 'warning',
36
+ pattern: /app\/api\/.*|src\/routes\/.*/,
37
+ check: (content) => {
38
+ return !/console\.log\(/.test(content);
39
+ },
40
+ message: 'Found console.log in API. Use a proper logger or console.error/warn.'
41
+ },
42
+ {
43
+ id: 'secret-leak',
44
+ name: 'Secret Key Leak',
45
+ description: 'Do not commit secrets starting with sk_ or similar',
46
+ severity: 'critical',
47
+ pattern: /.*/,
48
+ check: (content) => {
49
+ // Obfuscate regex even further to avoid self-triggering on the string literals
50
+ const p1 = 'sk' + '_live' + '_';
51
+ const p2 = 'sk' + '_test' + '_';
52
+ const p3 = 'gh' + 'p_';
53
+ const p4 = 'ey' + 'J';
54
+ const pattern = new RegExp(`${p1}|${p2}|${p3}|${p4}`);
55
+ return !pattern.test(content);
56
+ },
57
+ message: 'Potential secret key detected!'
58
+ }
59
+ ];
60
+
61
+ async function getFiles(dir) {
62
+ const dirents = await fs.readdir(dir, { withFileTypes: true });
63
+ const files = await Promise.all(dirents.map((dirent) => {
64
+ const res = path.resolve(dir, dirent.name);
65
+ if (dirent.isDirectory()) {
66
+ if (['node_modules', '.git', '.next', 'dist', 'build'].includes(dirent.name)) return [];
67
+ return getFiles(res);
68
+ }
69
+ return res;
70
+ }));
71
+ return files.flat();
72
+ }
73
+
74
+ export async function runQualityScan(dir) {
75
+ const results = {
76
+ passed: 0,
77
+ failed: 0,
78
+ warnings: 0,
79
+ filesScanned: 0,
80
+ details: []
81
+ };
82
+
83
+ const projectRoot = path.resolve(dir);
84
+ const allFiles = await getFiles(projectRoot);
85
+
86
+ for (const filePath of allFiles) {
87
+ // Relative path for pattern matching
88
+ const relativePath = path.relative(projectRoot, filePath);
89
+
90
+ // Skip non-code/text files roughly
91
+ if (/\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|pdf|lock)$/.test(filePath)) continue;
92
+
93
+ let content = '';
94
+ try {
95
+ content = await fs.readFile(filePath, 'utf8');
96
+ } catch { continue; }
97
+
98
+ results.filesScanned++;
99
+
100
+ for (const rule of RULES) {
101
+ if (rule.pattern.test(relativePath) || rule.pattern.test(filePath)) { // Match against both just in case
102
+ try {
103
+ const passed = rule.check(content);
104
+ if (!passed) {
105
+ const issue = {
106
+ ruleId: rule.id,
107
+ ruleName: rule.name,
108
+ file: relativePath,
109
+ severity: rule.severity,
110
+ message: rule.message
111
+ };
112
+ results.details.push(issue);
113
+
114
+ if (rule.severity === 'error' || rule.severity === 'critical') {
115
+ results.failed++;
116
+ } else {
117
+ results.warnings++;
118
+ }
119
+ }
120
+ } catch (err) {
121
+ // Ignore check errors
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ return results;
128
+ }
@@ -0,0 +1,97 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { runAgentLoop } from '../commands/run.js';
4
+ import { createProvider, getDefaultProvider } from '../providers/index.js';
5
+ import { loadState } from '../commands/plan.js';
6
+ import fs from 'fs/promises';
7
+
8
+ export class SwarmCoordinator {
9
+ constructor(provider, context) {
10
+ this.provider = provider;
11
+ this.context = context;
12
+ this.history = [];
13
+ }
14
+
15
+ async plan(feature) {
16
+ const spinner = ora('🧠 Hive Mind: Planning feature implementation...').start();
17
+
18
+ // System prompt to force JSON output for the plan
19
+ const plannerPrompt = `
20
+ You are the Hive Mind Planner.
21
+ Your goal: Break down the feature "${feature}" into sequential atomic tasks for other agents.
22
+
23
+ Available Agents:
24
+ - @Backend (API, logic)
25
+ - @Frontend (UI, React)
26
+ - @Database (Schema, migrations)
27
+ - @Auth (Authentication)
28
+ - @Testing (Tests)
29
+
30
+ Output STRICT JSON format only:
31
+ {
32
+ "tasks": [
33
+ {
34
+ "id": 1,
35
+ "agent": "backend",
36
+ "task": "Create API endpoint for...",
37
+ "context": "Needs to handle..."
38
+ },
39
+ ...
40
+ ]
41
+ }
42
+ `;
43
+
44
+ try {
45
+ const result = await this.provider.generate(plannerPrompt, `Feature: ${feature}`);
46
+
47
+ // Attempt to parse JSON (handling potential markdown code blocks)
48
+ let jsonStr = result.content.trim();
49
+ if (jsonStr.startsWith('```json')) {
50
+ jsonStr = jsonStr.replace(/^```json\n?/, '').replace(/\n?```$/, '');
51
+ } else if (jsonStr.startsWith('```')) {
52
+ jsonStr = jsonStr.replace(/^```\n?/, '').replace(/\n?```$/, '');
53
+ }
54
+
55
+ const plan = JSON.parse(jsonStr);
56
+ spinner.succeed(`Plan generated: ${plan.tasks.length} tasks identified.`);
57
+ return plan.tasks;
58
+ } catch (error) {
59
+ spinner.fail('Planning failed.');
60
+ console.error(chalk.red(error.message));
61
+ return null;
62
+ }
63
+ }
64
+
65
+ async execute(tasks) {
66
+ console.log(chalk.bold('\nšŸ Swarm Execution Started\n'));
67
+
68
+ for (const task of tasks) {
69
+ console.log(chalk.bold.cyan(`\nšŸ”¹ Step ${task.id}: [${task.agent.toUpperCase()}] ${task.task}`));
70
+
71
+ // Inject previous history into context
72
+ const currentContext = {
73
+ ...this.context,
74
+ history: this.history.join('\n\n---\n\n')
75
+ };
76
+
77
+ try {
78
+ const output = await runAgentLoop(task.agent, task.task, this.provider, currentContext);
79
+
80
+ // Save to history
81
+ this.history.push(`## Task ${task.id} (${task.agent})\n**Goal:** ${task.task}\n\n**Output:**\n${output}`);
82
+
83
+ // Save artifact
84
+ const filename = `swarm-task-${task.id}-${task.agent}.md`;
85
+ await fs.writeFile(filename, output);
86
+ console.log(chalk.green(` āœ“ Output saved to ${filename}`));
87
+
88
+ } catch (error) {
89
+ console.log(chalk.red(` āŒ Task failed: ${error.message}`));
90
+ // Decide whether to continue or stop
91
+ // For now, we continue
92
+ }
93
+ }
94
+
95
+ console.log(chalk.bold.green('\nāœ… Swarm Mission Complete'));
96
+ }
97
+ }