ultra-dex 3.1.0 → 3.3.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 +79 -74
- package/assets/code-patterns/clerk-middleware.ts +138 -0
- package/assets/code-patterns/prisma-schema.prisma +224 -0
- package/assets/code-patterns/rls-policies.sql +246 -0
- package/assets/code-patterns/server-actions.ts +191 -0
- package/assets/code-patterns/trpc-router.ts +258 -0
- package/assets/cursor-rules/13-ai-integration.mdc +155 -0
- package/assets/cursor-rules/14-server-components.mdc +81 -0
- package/assets/cursor-rules/15-server-actions.mdc +102 -0
- package/assets/cursor-rules/16-edge-middleware.mdc +105 -0
- package/assets/cursor-rules/17-streaming-ssr.mdc +138 -0
- package/bin/ultra-dex.js +50 -1
- package/lib/commands/agents.js +16 -13
- package/lib/commands/banner.js +43 -21
- package/lib/commands/build.js +26 -17
- package/lib/commands/cloud.js +780 -0
- package/lib/commands/doctor.js +98 -79
- package/lib/commands/exec.js +434 -0
- package/lib/commands/generate.js +19 -16
- package/lib/commands/github.js +475 -0
- package/lib/commands/init.js +52 -56
- package/lib/commands/scaffold.js +151 -0
- package/lib/commands/search.js +477 -0
- package/lib/commands/serve.js +15 -13
- package/lib/commands/state.js +43 -70
- package/lib/commands/swarm.js +31 -9
- package/lib/config/theme.js +47 -0
- package/lib/mcp/client.js +502 -0
- package/lib/providers/agent-sdk.js +630 -0
- package/lib/providers/anthropic-agents.js +580 -0
- package/lib/templates/code/clerk-middleware.ts +138 -0
- package/lib/templates/code/prisma-schema.prisma +224 -0
- package/lib/templates/code/rls-policies.sql +246 -0
- package/lib/templates/code/server-actions.ts +191 -0
- package/lib/templates/code/trpc-router.ts +258 -0
- package/lib/themes/doomsday.js +229 -0
- package/lib/ui/index.js +5 -0
- package/lib/ui/interface.js +241 -0
- package/lib/ui/spinners.js +116 -0
- package/lib/ui/theme.js +183 -0
- package/lib/utils/agents.js +32 -0
- package/lib/utils/browser.js +373 -0
- package/lib/utils/help.js +64 -0
- package/lib/utils/messages.js +35 -0
- package/lib/utils/progress.js +24 -0
- package/lib/utils/prompts.js +47 -0
- package/lib/utils/spinners.js +46 -0
- package/lib/utils/status.js +31 -0
- package/lib/utils/tables.js +41 -0
- package/lib/utils/theme-state.js +9 -0
- package/lib/utils/version-display.js +32 -0
- package/package.json +19 -4
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Agent SDK Integration for Ultra-Dex
|
|
3
|
+
* Enables TRUE autonomous agents using Claude's Agent SDK
|
|
4
|
+
* This is the future of AI-powered development
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// AGENT SDK CONFIGURATION
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
const AGENT_SDK_CONFIG = {
|
|
16
|
+
// Default model for agents
|
|
17
|
+
defaultModel: 'claude-sonnet-4-20250514',
|
|
18
|
+
|
|
19
|
+
// Tool definitions for agents
|
|
20
|
+
tools: {
|
|
21
|
+
read_file: {
|
|
22
|
+
name: 'read_file',
|
|
23
|
+
description: 'Read the contents of a file from the codebase',
|
|
24
|
+
input_schema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
path: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'The file path to read',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ['path'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
write_file: {
|
|
37
|
+
name: 'write_file',
|
|
38
|
+
description: 'Write content to a file, creating directories if needed',
|
|
39
|
+
input_schema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
path: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'The file path to write to',
|
|
45
|
+
},
|
|
46
|
+
content: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'The content to write',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ['path', 'content'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
search_code: {
|
|
56
|
+
name: 'search_code',
|
|
57
|
+
description: 'Search for patterns in the codebase using grep',
|
|
58
|
+
input_schema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
pattern: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: 'The pattern to search for',
|
|
64
|
+
},
|
|
65
|
+
file_pattern: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'Optional file glob pattern (e.g., "*.ts")',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ['pattern'],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
run_command: {
|
|
75
|
+
name: 'run_command',
|
|
76
|
+
description: 'Execute a shell command in the project directory',
|
|
77
|
+
input_schema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
command: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'The command to execute',
|
|
83
|
+
},
|
|
84
|
+
timeout: {
|
|
85
|
+
type: 'number',
|
|
86
|
+
description: 'Timeout in milliseconds (default: 30000)',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ['command'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
delegate_to_agent: {
|
|
94
|
+
name: 'delegate_to_agent',
|
|
95
|
+
description: 'Delegate a task to another specialized agent',
|
|
96
|
+
input_schema: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
agent: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
enum: ['backend', 'frontend', 'database', 'testing', 'security', 'devops'],
|
|
102
|
+
description: 'The agent to delegate to',
|
|
103
|
+
},
|
|
104
|
+
task: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'The task to delegate',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ['agent', 'task'],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
create_checkpoint: {
|
|
114
|
+
name: 'create_checkpoint',
|
|
115
|
+
description: 'Create a git checkpoint before making changes',
|
|
116
|
+
input_schema: {
|
|
117
|
+
type: 'object',
|
|
118
|
+
properties: {
|
|
119
|
+
message: {
|
|
120
|
+
type: 'string',
|
|
121
|
+
description: 'Checkpoint description',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ['message'],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Agent system prompts
|
|
130
|
+
agentPrompts: {
|
|
131
|
+
orchestrator: `You are the Ultra-Dex Orchestrator Agent - the "Hive Mind" that coordinates complex software development tasks.
|
|
132
|
+
|
|
133
|
+
Your role:
|
|
134
|
+
1. Break down complex tasks into atomic subtasks
|
|
135
|
+
2. Delegate to specialized agents (@Backend, @Frontend, @Database, etc.)
|
|
136
|
+
3. Track progress and handle failures
|
|
137
|
+
4. Ensure code quality and consistency
|
|
138
|
+
|
|
139
|
+
You have access to tools to read/write files, search code, run commands, and delegate to other agents.
|
|
140
|
+
|
|
141
|
+
IMPORTANT: Always create a checkpoint before making significant changes. Always verify changes work before completing.`,
|
|
142
|
+
|
|
143
|
+
backend: `You are the @Backend Agent - an expert in API development, server-side logic, and system architecture.
|
|
144
|
+
|
|
145
|
+
Expertise:
|
|
146
|
+
- Node.js, Python, Go, Rust backends
|
|
147
|
+
- REST APIs, GraphQL, tRPC
|
|
148
|
+
- Database interactions (Prisma, Drizzle, raw SQL)
|
|
149
|
+
- Authentication and authorization
|
|
150
|
+
- Server Actions (Next.js 15)
|
|
151
|
+
|
|
152
|
+
When implementing:
|
|
153
|
+
1. Follow existing code patterns in the project
|
|
154
|
+
2. Add proper error handling
|
|
155
|
+
3. Include TypeScript types
|
|
156
|
+
4. Write tests for critical paths`,
|
|
157
|
+
|
|
158
|
+
frontend: `You are the @Frontend Agent - an expert in modern UI development and user experience.
|
|
159
|
+
|
|
160
|
+
Expertise:
|
|
161
|
+
- React, Next.js, Vue, Svelte
|
|
162
|
+
- Server Components vs Client Components
|
|
163
|
+
- State management (Zustand, Jotai, Redux)
|
|
164
|
+
- CSS-in-JS, Tailwind CSS
|
|
165
|
+
- Accessibility (WCAG 2.1)
|
|
166
|
+
|
|
167
|
+
When implementing:
|
|
168
|
+
1. Prefer Server Components when possible
|
|
169
|
+
2. Use proper semantic HTML
|
|
170
|
+
3. Ensure responsive design
|
|
171
|
+
4. Follow the project's component patterns`,
|
|
172
|
+
|
|
173
|
+
database: `You are the @Database Agent - an expert in data modeling, queries, and database optimization.
|
|
174
|
+
|
|
175
|
+
Expertise:
|
|
176
|
+
- PostgreSQL, MySQL, MongoDB, SQLite
|
|
177
|
+
- Prisma, Drizzle, TypeORM
|
|
178
|
+
- Schema design and migrations
|
|
179
|
+
- Query optimization
|
|
180
|
+
- Row-Level Security (RLS)
|
|
181
|
+
|
|
182
|
+
When implementing:
|
|
183
|
+
1. Design normalized schemas
|
|
184
|
+
2. Add proper indexes
|
|
185
|
+
3. Consider query performance
|
|
186
|
+
4. Implement soft deletes when appropriate`,
|
|
187
|
+
|
|
188
|
+
testing: `You are the @Testing Agent - an expert in test automation and quality assurance.
|
|
189
|
+
|
|
190
|
+
Expertise:
|
|
191
|
+
- Jest, Vitest, Mocha
|
|
192
|
+
- Playwright, Cypress for E2E
|
|
193
|
+
- Testing Library for component tests
|
|
194
|
+
- Mock Service Worker (MSW)
|
|
195
|
+
- Coverage analysis
|
|
196
|
+
|
|
197
|
+
When implementing:
|
|
198
|
+
1. Write unit tests for business logic
|
|
199
|
+
2. Write integration tests for APIs
|
|
200
|
+
3. Write E2E tests for critical flows
|
|
201
|
+
4. Aim for 80%+ coverage on critical code`,
|
|
202
|
+
|
|
203
|
+
security: `You are the @Security Agent - an expert in application security and vulnerability prevention.
|
|
204
|
+
|
|
205
|
+
Expertise:
|
|
206
|
+
- OWASP Top 10
|
|
207
|
+
- Authentication/Authorization
|
|
208
|
+
- Input validation and sanitization
|
|
209
|
+
- SQL injection, XSS, CSRF prevention
|
|
210
|
+
- Secure coding practices
|
|
211
|
+
|
|
212
|
+
When reviewing:
|
|
213
|
+
1. Check for injection vulnerabilities
|
|
214
|
+
2. Verify authentication flows
|
|
215
|
+
3. Ensure proper authorization checks
|
|
216
|
+
4. Review data exposure risks`,
|
|
217
|
+
|
|
218
|
+
devops: `You are the @DevOps Agent - an expert in CI/CD, deployment, and infrastructure.
|
|
219
|
+
|
|
220
|
+
Expertise:
|
|
221
|
+
- GitHub Actions, GitLab CI
|
|
222
|
+
- Docker, Kubernetes
|
|
223
|
+
- Vercel, Railway, AWS
|
|
224
|
+
- Monitoring (Sentry, DataDog)
|
|
225
|
+
- Infrastructure as Code
|
|
226
|
+
|
|
227
|
+
When implementing:
|
|
228
|
+
1. Create reliable CI/CD pipelines
|
|
229
|
+
2. Configure proper environments
|
|
230
|
+
3. Set up monitoring and alerts
|
|
231
|
+
4. Document deployment procedures`,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// TOOL EXECUTORS
|
|
237
|
+
// ============================================================================
|
|
238
|
+
|
|
239
|
+
import { exec as execCallback } from 'child_process';
|
|
240
|
+
import { promisify } from 'util';
|
|
241
|
+
import { glob } from 'glob';
|
|
242
|
+
|
|
243
|
+
const execAsync = promisify(execCallback);
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Execute a tool call
|
|
247
|
+
*/
|
|
248
|
+
async function executeTool(toolName, input, context = {}) {
|
|
249
|
+
const { workdir = process.cwd() } = context;
|
|
250
|
+
|
|
251
|
+
switch (toolName) {
|
|
252
|
+
case 'read_file': {
|
|
253
|
+
const filepath = path.resolve(workdir, input.path);
|
|
254
|
+
try {
|
|
255
|
+
const content = await fs.readFile(filepath, 'utf8');
|
|
256
|
+
return { success: true, content };
|
|
257
|
+
} catch (err) {
|
|
258
|
+
return { success: false, error: `Failed to read file: ${err.message}` };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
case 'write_file': {
|
|
263
|
+
const filepath = path.resolve(workdir, input.path);
|
|
264
|
+
try {
|
|
265
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
266
|
+
await fs.writeFile(filepath, input.content, 'utf8');
|
|
267
|
+
return { success: true, message: `Wrote ${input.path}` };
|
|
268
|
+
} catch (err) {
|
|
269
|
+
return { success: false, error: `Failed to write file: ${err.message}` };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
case 'search_code': {
|
|
274
|
+
try {
|
|
275
|
+
const pattern = input.pattern;
|
|
276
|
+
const filePattern = input.file_pattern || '**/*';
|
|
277
|
+
|
|
278
|
+
const files = await glob(filePattern, {
|
|
279
|
+
cwd: workdir,
|
|
280
|
+
ignore: ['node_modules/**', '.git/**', 'dist/**'],
|
|
281
|
+
nodir: true,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const results = [];
|
|
285
|
+
for (const file of files.slice(0, 50)) { // Limit search
|
|
286
|
+
try {
|
|
287
|
+
const content = await fs.readFile(path.join(workdir, file), 'utf8');
|
|
288
|
+
const lines = content.split('\n');
|
|
289
|
+
|
|
290
|
+
lines.forEach((line, idx) => {
|
|
291
|
+
if (line.includes(pattern)) {
|
|
292
|
+
results.push({
|
|
293
|
+
file,
|
|
294
|
+
line: idx + 1,
|
|
295
|
+
content: line.trim().slice(0, 200),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
} catch {
|
|
300
|
+
// Skip unreadable files
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return { success: true, results: results.slice(0, 20) };
|
|
305
|
+
} catch (err) {
|
|
306
|
+
return { success: false, error: `Search failed: ${err.message}` };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
case 'run_command': {
|
|
311
|
+
try {
|
|
312
|
+
const timeout = input.timeout || 30000;
|
|
313
|
+
const { stdout, stderr } = await execAsync(input.command, {
|
|
314
|
+
cwd: workdir,
|
|
315
|
+
timeout,
|
|
316
|
+
});
|
|
317
|
+
return { success: true, stdout, stderr };
|
|
318
|
+
} catch (err) {
|
|
319
|
+
return { success: false, error: err.message, stderr: err.stderr };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
case 'create_checkpoint': {
|
|
324
|
+
try {
|
|
325
|
+
await execAsync('git add -A', { cwd: workdir });
|
|
326
|
+
await execAsync(`git commit -m "checkpoint: ${input.message}" --allow-empty`, { cwd: workdir });
|
|
327
|
+
return { success: true, message: `Checkpoint created: ${input.message}` };
|
|
328
|
+
} catch (err) {
|
|
329
|
+
return { success: false, error: `Checkpoint failed: ${err.message}` };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
case 'delegate_to_agent': {
|
|
334
|
+
// This will be handled by the agent loop
|
|
335
|
+
return {
|
|
336
|
+
success: true,
|
|
337
|
+
delegation: {
|
|
338
|
+
agent: input.agent,
|
|
339
|
+
task: input.task,
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
default:
|
|
345
|
+
return { success: false, error: `Unknown tool: ${toolName}` };
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ============================================================================
|
|
350
|
+
// AUTONOMOUS AGENT CLASS
|
|
351
|
+
// ============================================================================
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Autonomous Agent powered by Anthropic Agent SDK
|
|
355
|
+
*/
|
|
356
|
+
export class AutonomousAgent {
|
|
357
|
+
constructor(agentType, options = {}) {
|
|
358
|
+
this.type = agentType;
|
|
359
|
+
this.options = options;
|
|
360
|
+
this.history = [];
|
|
361
|
+
this.toolResults = [];
|
|
362
|
+
this.maxTurns = options.maxTurns || 10;
|
|
363
|
+
this.verbose = options.verbose || false;
|
|
364
|
+
|
|
365
|
+
// System prompt for this agent type
|
|
366
|
+
this.systemPrompt = AGENT_SDK_CONFIG.agentPrompts[agentType] ||
|
|
367
|
+
AGENT_SDK_CONFIG.agentPrompts.orchestrator;
|
|
368
|
+
|
|
369
|
+
// Available tools
|
|
370
|
+
this.tools = Object.values(AGENT_SDK_CONFIG.tools);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Run the agent on a task
|
|
375
|
+
*/
|
|
376
|
+
async run(task, context = {}) {
|
|
377
|
+
const { provider, workdir = process.cwd() } = context;
|
|
378
|
+
|
|
379
|
+
if (!provider) {
|
|
380
|
+
throw new Error('Provider required for autonomous agent');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (this.verbose) {
|
|
384
|
+
console.log(chalk.cyan(`\n🤖 ${this.type.toUpperCase()} Agent starting...`));
|
|
385
|
+
console.log(chalk.gray(`Task: ${task}\n`));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const messages = [
|
|
389
|
+
{ role: 'user', content: task },
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
let turn = 0;
|
|
393
|
+
let finalResult = null;
|
|
394
|
+
|
|
395
|
+
while (turn < this.maxTurns) {
|
|
396
|
+
turn++;
|
|
397
|
+
|
|
398
|
+
if (this.verbose) {
|
|
399
|
+
console.log(chalk.gray(`--- Turn ${turn}/${this.maxTurns} ---`));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Call the model
|
|
403
|
+
const response = await provider.generateWithTools(
|
|
404
|
+
this.systemPrompt,
|
|
405
|
+
messages,
|
|
406
|
+
this.tools
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// Check for tool calls
|
|
410
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
411
|
+
// Execute tools
|
|
412
|
+
const toolMessages = [];
|
|
413
|
+
|
|
414
|
+
for (const toolCall of response.toolCalls) {
|
|
415
|
+
if (this.verbose) {
|
|
416
|
+
console.log(chalk.yellow(` 🔧 ${toolCall.name}(${JSON.stringify(toolCall.input).slice(0, 50)}...)`));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const result = await executeTool(toolCall.name, toolCall.input, { workdir });
|
|
420
|
+
this.toolResults.push({ tool: toolCall.name, input: toolCall.input, result });
|
|
421
|
+
|
|
422
|
+
// Handle delegation
|
|
423
|
+
if (result.delegation) {
|
|
424
|
+
const subAgent = new AutonomousAgent(result.delegation.agent, this.options);
|
|
425
|
+
const subResult = await subAgent.run(result.delegation.task, context);
|
|
426
|
+
toolMessages.push({
|
|
427
|
+
role: 'tool_result',
|
|
428
|
+
tool_use_id: toolCall.id,
|
|
429
|
+
content: JSON.stringify({
|
|
430
|
+
delegated: true,
|
|
431
|
+
agent: result.delegation.agent,
|
|
432
|
+
result: subResult,
|
|
433
|
+
}),
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
toolMessages.push({
|
|
437
|
+
role: 'tool_result',
|
|
438
|
+
tool_use_id: toolCall.id,
|
|
439
|
+
content: JSON.stringify(result),
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Add assistant message and tool results to history
|
|
445
|
+
messages.push({ role: 'assistant', content: response.content, tool_calls: response.toolCalls });
|
|
446
|
+
messages.push(...toolMessages);
|
|
447
|
+
|
|
448
|
+
} else {
|
|
449
|
+
// No tool calls - agent is done
|
|
450
|
+
finalResult = response.content;
|
|
451
|
+
|
|
452
|
+
if (this.verbose) {
|
|
453
|
+
console.log(chalk.green(`\n✅ Agent completed`));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (!finalResult) {
|
|
461
|
+
finalResult = 'Max turns reached without completion';
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
this.history = messages;
|
|
465
|
+
|
|
466
|
+
return {
|
|
467
|
+
success: true,
|
|
468
|
+
result: finalResult,
|
|
469
|
+
turns: turn,
|
|
470
|
+
toolsUsed: this.toolResults.length,
|
|
471
|
+
history: this.history,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Get execution summary
|
|
477
|
+
*/
|
|
478
|
+
getSummary() {
|
|
479
|
+
return {
|
|
480
|
+
agent: this.type,
|
|
481
|
+
toolsUsed: this.toolResults.map(t => t.tool),
|
|
482
|
+
historyLength: this.history.length,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ============================================================================
|
|
488
|
+
// MULTI-AGENT SWARM
|
|
489
|
+
// ============================================================================
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Run a multi-agent swarm for complex tasks
|
|
493
|
+
*/
|
|
494
|
+
export async function runAutonomousSwarm(task, options = {}) {
|
|
495
|
+
const { provider, workdir = process.cwd(), verbose = false } = options;
|
|
496
|
+
|
|
497
|
+
console.log(chalk.cyan('\n🐝 Ultra-Dex Autonomous Swarm\n'));
|
|
498
|
+
console.log(chalk.bold(`Task: ${task}\n`));
|
|
499
|
+
|
|
500
|
+
// Start with orchestrator
|
|
501
|
+
const orchestrator = new AutonomousAgent('orchestrator', {
|
|
502
|
+
maxTurns: 15,
|
|
503
|
+
verbose,
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const result = await orchestrator.run(task, { provider, workdir });
|
|
507
|
+
|
|
508
|
+
console.log(chalk.cyan('\n📊 Swarm Summary:'));
|
|
509
|
+
console.log(` Turns: ${result.turns}`);
|
|
510
|
+
console.log(` Tools used: ${result.toolsUsed}`);
|
|
511
|
+
console.log(chalk.green(`\n✅ Swarm completed`));
|
|
512
|
+
|
|
513
|
+
return result;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ============================================================================
|
|
517
|
+
// PROVIDER EXTENSION
|
|
518
|
+
// ============================================================================
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Extend a provider with tool-use capability
|
|
522
|
+
*/
|
|
523
|
+
export function extendProviderWithTools(provider) {
|
|
524
|
+
if (provider.generateWithTools) {
|
|
525
|
+
return provider; // Already extended
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
provider.generateWithTools = async function (systemPrompt, messages, tools) {
|
|
529
|
+
// For providers that don't natively support tools,
|
|
530
|
+
// we simulate tool use through the prompt
|
|
531
|
+
const toolDescriptions = tools.map(t =>
|
|
532
|
+
`- ${t.name}: ${t.description}`
|
|
533
|
+
).join('\n');
|
|
534
|
+
|
|
535
|
+
const enhancedSystem = `${systemPrompt}
|
|
536
|
+
|
|
537
|
+
Available tools:
|
|
538
|
+
${toolDescriptions}
|
|
539
|
+
|
|
540
|
+
To use a tool, output JSON in this format:
|
|
541
|
+
{"tool": "tool_name", "input": {...}}
|
|
542
|
+
|
|
543
|
+
After receiving tool results, continue your work or provide your final answer.`;
|
|
544
|
+
|
|
545
|
+
const result = await this.generate(enhancedSystem, messages[messages.length - 1].content);
|
|
546
|
+
|
|
547
|
+
// Parse tool calls from response
|
|
548
|
+
const toolCallMatch = result.content.match(/\{"tool":\s*"(\w+)",\s*"input":\s*(\{[^}]+\})\}/);
|
|
549
|
+
|
|
550
|
+
if (toolCallMatch) {
|
|
551
|
+
return {
|
|
552
|
+
content: result.content,
|
|
553
|
+
toolCalls: [{
|
|
554
|
+
id: `tool_${Date.now()}`,
|
|
555
|
+
name: toolCallMatch[1],
|
|
556
|
+
input: JSON.parse(toolCallMatch[2]),
|
|
557
|
+
}],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
content: result.content,
|
|
563
|
+
toolCalls: null,
|
|
564
|
+
};
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
return provider;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ============================================================================
|
|
571
|
+
// EXPORTS
|
|
572
|
+
// ============================================================================
|
|
573
|
+
|
|
574
|
+
export default {
|
|
575
|
+
AutonomousAgent,
|
|
576
|
+
runAutonomousSwarm,
|
|
577
|
+
extendProviderWithTools,
|
|
578
|
+
executeTool,
|
|
579
|
+
AGENT_SDK_CONFIG,
|
|
580
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Ultra-Dex Production Pattern: Clerk Middleware
|
|
2
|
+
// Copy to middleware.ts in your Next.js root
|
|
3
|
+
|
|
4
|
+
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
5
|
+
import { NextResponse } from 'next/server';
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// ROUTE MATCHERS
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
// Public routes - accessible without authentication
|
|
12
|
+
const isPublicRoute = createRouteMatcher([
|
|
13
|
+
'/',
|
|
14
|
+
'/sign-in(.*)',
|
|
15
|
+
'/sign-up(.*)',
|
|
16
|
+
'/api/webhooks(.*)',
|
|
17
|
+
'/pricing',
|
|
18
|
+
'/about',
|
|
19
|
+
'/blog(.*)',
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
// Admin routes - require admin role
|
|
23
|
+
const isAdminRoute = createRouteMatcher([
|
|
24
|
+
'/admin(.*)',
|
|
25
|
+
'/api/admin(.*)',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
// API routes that need special handling
|
|
29
|
+
const isApiRoute = createRouteMatcher(['/api(.*)']);
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// MIDDLEWARE
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
export default clerkMiddleware(async (auth, req) => {
|
|
36
|
+
const { userId, sessionClaims } = await auth();
|
|
37
|
+
|
|
38
|
+
// Allow public routes
|
|
39
|
+
if (isPublicRoute(req)) {
|
|
40
|
+
return NextResponse.next();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Protect all non-public routes
|
|
44
|
+
if (!userId) {
|
|
45
|
+
const signInUrl = new URL('/sign-in', req.url);
|
|
46
|
+
signInUrl.searchParams.set('redirect_url', req.url);
|
|
47
|
+
return NextResponse.redirect(signInUrl);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check admin routes
|
|
51
|
+
if (isAdminRoute(req)) {
|
|
52
|
+
const role = sessionClaims?.metadata?.role as string | undefined;
|
|
53
|
+
|
|
54
|
+
if (role !== 'admin' && role !== 'super_admin') {
|
|
55
|
+
return NextResponse.redirect(new URL('/unauthorized', req.url));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add user info to headers for API routes
|
|
60
|
+
if (isApiRoute(req)) {
|
|
61
|
+
const requestHeaders = new Headers(req.headers);
|
|
62
|
+
requestHeaders.set('x-user-id', userId);
|
|
63
|
+
|
|
64
|
+
return NextResponse.next({
|
|
65
|
+
request: {
|
|
66
|
+
headers: requestHeaders,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return NextResponse.next();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export const config = {
|
|
75
|
+
matcher: [
|
|
76
|
+
// Skip Next.js internals and static files
|
|
77
|
+
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
|
|
78
|
+
// Always run for API routes
|
|
79
|
+
'/(api|trpc)(.*)',
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// USAGE EXAMPLES
|
|
85
|
+
// =============================================================================
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
// In a Server Component - get current user:
|
|
89
|
+
|
|
90
|
+
import { auth, currentUser } from '@clerk/nextjs/server';
|
|
91
|
+
|
|
92
|
+
export default async function DashboardPage() {
|
|
93
|
+
const { userId } = await auth();
|
|
94
|
+
const user = await currentUser();
|
|
95
|
+
|
|
96
|
+
if (!userId) {
|
|
97
|
+
redirect('/sign-in');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return <div>Welcome, {user?.firstName}!</div>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// In a Client Component - use hooks:
|
|
104
|
+
|
|
105
|
+
'use client';
|
|
106
|
+
|
|
107
|
+
import { useUser, useAuth } from '@clerk/nextjs';
|
|
108
|
+
|
|
109
|
+
export function UserProfile() {
|
|
110
|
+
const { user, isLoaded } = useUser();
|
|
111
|
+
const { signOut } = useAuth();
|
|
112
|
+
|
|
113
|
+
if (!isLoaded) return <div>Loading...</div>;
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div>
|
|
117
|
+
<p>Hello, {user?.firstName}</p>
|
|
118
|
+
<button onClick={() => signOut()}>Sign Out</button>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// In an API Route:
|
|
124
|
+
|
|
125
|
+
import { auth } from '@clerk/nextjs/server';
|
|
126
|
+
import { NextResponse } from 'next/server';
|
|
127
|
+
|
|
128
|
+
export async function GET() {
|
|
129
|
+
const { userId } = await auth();
|
|
130
|
+
|
|
131
|
+
if (!userId) {
|
|
132
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Your logic here
|
|
136
|
+
return NextResponse.json({ userId });
|
|
137
|
+
}
|
|
138
|
+
*/
|