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,190 @@
1
+ // cli/lib/commands/upgrade.js
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { execSync } from 'child_process';
5
+ import { readFileSync, existsSync } from 'fs';
6
+ import { join, dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ export async function upgradeCommand(options) {
12
+ console.log(chalk.cyan.bold('\n⬆️ Ultra-Dex Upgrade Check\n'));
13
+
14
+ // Get local version from package.json
15
+ const localVersion = getLocalVersion();
16
+
17
+ const spinner = ora('Checking npm registry...').start();
18
+
19
+ try {
20
+ // Query npm registry for latest version
21
+ const latestVersion = await getLatestVersion();
22
+ spinner.succeed('Registry check complete');
23
+
24
+ console.log(chalk.gray('─'.repeat(50)));
25
+ console.log(` ${chalk.gray('Installed:')} ${chalk.white(localVersion)}`);
26
+ console.log(` ${chalk.gray('Latest:')} ${chalk.cyan(latestVersion)}`);
27
+ console.log(chalk.gray('─'.repeat(50)));
28
+
29
+ const comparison = compareVersions(localVersion, latestVersion);
30
+
31
+ if (comparison < 0) {
32
+ // Update available
33
+ console.log(chalk.yellow.bold('\n 📦 Update available!\n'));
34
+
35
+ // Show version diff summary
36
+ const [localMajor, localMinor] = localVersion.split('.').map(Number);
37
+ const [latestMajor, latestMinor] = latestVersion.split('.').map(Number);
38
+
39
+ if (latestMajor > localMajor) {
40
+ console.log(chalk.red(' ⚠️ Major version update - may contain breaking changes'));
41
+ } else if (latestMinor > localMinor) {
42
+ console.log(chalk.yellow(' ✨ Minor version update - new features available'));
43
+ } else {
44
+ console.log(chalk.green(' 🔧 Patch update - bug fixes and improvements'));
45
+ }
46
+
47
+ // Try to fetch changelog
48
+ if (!options.check) {
49
+ const changelogSpinner = ora('Fetching changelog...').start();
50
+ try {
51
+ const changelog = await fetchChangelog(localVersion, latestVersion);
52
+ if (changelog) {
53
+ changelogSpinner.succeed('Changelog retrieved');
54
+ console.log(chalk.bold('\n 📋 What\'s New:\n'));
55
+ console.log(chalk.gray(indent(changelog, 4)));
56
+ } else {
57
+ changelogSpinner.info('No changelog available');
58
+ }
59
+ } catch {
60
+ changelogSpinner.info('Could not fetch changelog');
61
+ }
62
+ }
63
+
64
+ // Show install instructions or run update
65
+ console.log('');
66
+ if (options.install) {
67
+ const installSpinner = ora('Installing update...').start();
68
+ try {
69
+ execSync('npm update -g ultra-dex', { encoding: 'utf-8', stdio: 'pipe' });
70
+ installSpinner.succeed(chalk.green(`Updated to v${latestVersion}`));
71
+ console.log(chalk.gray('\n Run `ultra-dex --version` to verify.\n'));
72
+ } catch (e) {
73
+ installSpinner.fail('Installation failed');
74
+ console.log(chalk.red(` ${e.message}`));
75
+ console.log(chalk.gray('\n Try running manually:'));
76
+ console.log(chalk.white(' npm install -g ultra-dex@latest\n'));
77
+ }
78
+ } else if (!options.check) {
79
+ console.log(chalk.gray(' To upgrade, run:'));
80
+ console.log(chalk.white(' npm install -g ultra-dex@latest'));
81
+ console.log(chalk.gray(' Or use:'));
82
+ console.log(chalk.white(' ultra-dex upgrade --install\n'));
83
+ } else {
84
+ console.log(chalk.gray(' Check complete. Use --install to update.\n'));
85
+ }
86
+
87
+ } else if (comparison === 0) {
88
+ // Up to date
89
+ console.log(chalk.green.bold('\n ✅ You are running the latest version!\n'));
90
+ } else {
91
+ // Local is newer (dev/beta)
92
+ console.log(chalk.blue.bold('\n 🔬 You are running a development/pre-release version\n'));
93
+ }
94
+
95
+ } catch (e) {
96
+ spinner.warn('Could not reach npm registry');
97
+ console.log(chalk.gray(` Current version: ${localVersion}`));
98
+ console.log(chalk.yellow(`\n ${e.message}`));
99
+ console.log(chalk.gray('\n Check your network connection and try again.\n'));
100
+ }
101
+ }
102
+
103
+ function getLocalVersion() {
104
+ try {
105
+ // Try to get from the CLI's package.json
106
+ const pkgPath = join(__dirname, '..', '..', 'package.json');
107
+ if (existsSync(pkgPath)) {
108
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
109
+ return pkg.version;
110
+ }
111
+ } catch { /* fall through */ }
112
+
113
+ // Fallback to hardcoded version
114
+ return '3.0.0';
115
+ }
116
+
117
+ async function getLatestVersion() {
118
+ return new Promise((resolve, reject) => {
119
+ try {
120
+ const output = execSync('npm view ultra-dex version 2>/dev/null', {
121
+ encoding: 'utf-8',
122
+ timeout: 10000
123
+ }).trim();
124
+
125
+ if (output && /^\d+\.\d+\.\d+/.test(output)) {
126
+ resolve(output);
127
+ } else {
128
+ reject(new Error('Invalid version format from registry'));
129
+ }
130
+ } catch (e) {
131
+ // Package might not be published yet
132
+ reject(new Error('Package not found in npm registry (may not be published yet)'));
133
+ }
134
+ });
135
+ }
136
+
137
+ function compareVersions(v1, v2) {
138
+ const parts1 = v1.split('.').map(Number);
139
+ const parts2 = v2.split('.').map(Number);
140
+
141
+ for (let i = 0; i < 3; i++) {
142
+ const a = parts1[i] || 0;
143
+ const b = parts2[i] || 0;
144
+ if (a < b) return -1;
145
+ if (a > b) return 1;
146
+ }
147
+ return 0;
148
+ }
149
+
150
+ async function fetchChangelog(fromVersion, toVersion) {
151
+ try {
152
+ // Try to fetch from GitHub releases API
153
+ const output = execSync(
154
+ `curl -s "https://api.github.com/repos/Srujan0798/Ultra-Dex/releases" | head -c 5000`,
155
+ { encoding: 'utf-8', timeout: 10000 }
156
+ );
157
+
158
+ const releases = JSON.parse(output);
159
+ if (!Array.isArray(releases) || releases.length === 0) {
160
+ return null;
161
+ }
162
+
163
+ // Get release notes between versions
164
+ const changelog = [];
165
+ for (const release of releases.slice(0, 5)) {
166
+ const releaseVersion = release.tag_name?.replace(/^v/, '') || '';
167
+ if (compareVersions(releaseVersion, fromVersion) > 0 &&
168
+ compareVersions(releaseVersion, toVersion) <= 0) {
169
+ changelog.push(`v${releaseVersion}:`);
170
+ // Extract first few bullet points from body
171
+ const body = release.body || '';
172
+ const lines = body.split('\n')
173
+ .filter(line => line.trim().startsWith('-') || line.trim().startsWith('*'))
174
+ .slice(0, 5)
175
+ .map(line => ' ' + line.trim());
176
+ changelog.push(...lines);
177
+ changelog.push('');
178
+ }
179
+ }
180
+
181
+ return changelog.length > 0 ? changelog.join('\n') : null;
182
+ } catch {
183
+ return null;
184
+ }
185
+ }
186
+
187
+ function indent(text, spaces) {
188
+ const prefix = ' '.repeat(spaces);
189
+ return text.split('\n').map(line => prefix + line).join('\n');
190
+ }
@@ -2,12 +2,14 @@ import chalk from 'chalk';
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import { validateSafePath } from '../utils/validation.js';
5
+ import { runQualityScan } from '../quality/scanner.js';
5
6
 
6
7
  export function registerValidateCommand(program) {
7
8
  program
8
9
  .command('validate')
9
10
  .description('Validate project structure against Ultra-Dex standards')
10
11
  .option('-d, --dir <directory>', 'Project directory to validate', '.')
12
+ .option('--scan', 'Run deep code quality scan')
11
13
  .action(async (options) => {
12
14
  console.log(chalk.cyan('\n✅ Ultra-Dex Structure Validator\n'));
13
15
 
@@ -114,6 +116,34 @@ export function registerValidateCommand(program) {
114
116
  console.log(chalk.gray(' ⊘ Could not validate IMPLEMENTATION-PLAN.md content'));
115
117
  }
116
118
 
119
+ // Deep Code Scan
120
+ if (options.scan) {
121
+ console.log(chalk.bold('\nRunning Deep Code Scan (Active State Tracking)...\n'));
122
+ const scanResults = await runQualityScan(projectDir);
123
+
124
+ if (scanResults.failed > 0) {
125
+ failed += scanResults.failed;
126
+ console.log(chalk.red(` ❌ Code Scan Failed: ${scanResults.failed} critical issues found.`));
127
+ } else {
128
+ passed++;
129
+ console.log(chalk.green(` ✅ Code Scan Passed (${scanResults.filesScanned} files scanned).`));
130
+ }
131
+
132
+ if (scanResults.warnings > 0) {
133
+ console.log(chalk.yellow(` ⚠️ ${scanResults.warnings} code warnings found.`));
134
+ }
135
+
136
+ if (scanResults.details.length > 0) {
137
+ console.log(chalk.gray('\n Scan Details:'));
138
+ scanResults.details.forEach(issue => {
139
+ const icon = issue.severity === 'error' || issue.severity === 'critical' ? '❌' : '⚠️';
140
+ console.log(` ${icon} [${issue.ruleName}] ${issue.file}: ${issue.message}`);
141
+ });
142
+ }
143
+ } else {
144
+ console.log(chalk.gray('\nℹ️ Run with --scan to enable Deep Code Quality Scan.'));
145
+ }
146
+
117
147
  console.log('\n' + chalk.bold('─'.repeat(50)));
118
148
  console.log(chalk.bold('\nValidation Summary:\n'));
119
149
  console.log(chalk.green(` ✅ Passed: ${passed}`));
@@ -126,6 +156,10 @@ export function registerValidateCommand(program) {
126
156
  } else {
127
157
  console.log(chalk.bold.yellow('\n⚠️ VALIDATION INCOMPLETE\n'));
128
158
  console.log(chalk.gray('Fix required files to meet Ultra-Dex standards.'));
159
+ if (options.scan && failed > 0) {
160
+ console.log(chalk.red('Code quality gates failed. Commit rejected (if in pre-commit).'));
161
+ }
162
+ process.exit(1);
129
163
  }
130
164
 
131
165
  if (warnings.length > 0) {
@@ -0,0 +1,81 @@
1
+ /**
2
+ * ultra-dex verify command
3
+ * Executable 21-step verification framework
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import { createProvider, getDefaultProvider } from '../providers/index.js';
9
+ import { runAgentLoop } from './run.js';
10
+ import { loadState } from './plan.js';
11
+ import { projectGraph } from '../mcp/graph.js';
12
+
13
+ const CHECKLIST = [
14
+ "Atomic Scope Defined", "Context Loaded", "Architecture Alignment",
15
+ "Security Patterns Applied", "Type Safety Check", "Error Handling Strategy",
16
+ "API Documentation Updated", "Database Schema Verified", "Environment Variables Set",
17
+ "Implementation Complete", "Console Logs Removed", "Edge Cases Handled",
18
+ "Performance Check", "Accessibility (A11y) Check", "Cross-browser Check",
19
+ "Unit Tests Passed", "Integration Tests Passed", "Linting & Formatting",
20
+ "Code Review Approved", "Migration Scripts Ready", "Deployment Readiness"
21
+ ];
22
+
23
+ export async function verifyCommand(taskName, options) {
24
+ console.log(chalk.cyan.bold('\n⚖️ Ultra-Dex 21-Step Verification\n'));
25
+
26
+ const providerId = options.provider || getDefaultProvider();
27
+ const provider = createProvider(providerId);
28
+ const state = await loadState();
29
+
30
+ const spinner = ora(`@Reviewer is verifying: "${taskName || 'Project'}"...`).start();
31
+
32
+ try {
33
+ // 1. Structural Scan
34
+ await projectGraph.scan();
35
+ const graphSummary = projectGraph.getSummary();
36
+
37
+ // 2. AI Review
38
+ const projectContext = {
39
+ state,
40
+ graph: graphSummary,
41
+ context: `Task to verify: ${taskName || 'All completed tasks'}`
42
+ };
43
+
44
+ const prompt = `
45
+ Verify the following task against the 21-Step Verification Framework:
46
+ "${taskName || 'Full Project Readiness'}"
47
+
48
+ The framework consists of:
49
+ ${CHECKLIST.map((s, i) => `${i+1}. ${s}`).join('\n')}
50
+
51
+ Based on the codebase graph and current state, provide a report in this format:
52
+ [ ] Step Name: [PASS/FAIL/SKIP] - Reasoning
53
+
54
+ Final Verdict: [APPROVED/REJECTED]
55
+ `;
56
+
57
+ const report = await runAgentLoop('reviewer', prompt, provider, projectContext);
58
+ spinner.succeed('Verification complete.');
59
+
60
+ console.log(chalk.bold('\n📋 Verification Report:'));
61
+ console.log(chalk.white(report));
62
+
63
+ if (report.includes('REJECTED')) {
64
+ console.log(chalk.red.bold('\n❌ Task failed verification. Please address the issues above.'));
65
+ process.exit(1);
66
+ } else {
67
+ console.log(chalk.green.bold('\n✅ Task passed verification!'));
68
+ }
69
+
70
+ } catch (e) {
71
+ spinner.fail(chalk.red(`Verification failed: ${e.message}`));
72
+ }
73
+ }
74
+
75
+ export function registerVerifyCommand(program) {
76
+ program
77
+ .command('verify [task]')
78
+ .description('Run executable 21-step verification on a task or project')
79
+ .option('-p, --provider <provider>', 'AI provider')
80
+ .action(verifyCommand);
81
+ }
@@ -0,0 +1,79 @@
1
+ // cli/lib/commands/watch.js
2
+ import chalk from 'chalk';
3
+ import { watch } from 'fs';
4
+ import { join } from 'path';
5
+ import { existsSync } from 'fs';
6
+ import { updateState, computeState } from './state.js';
7
+
8
+ async function calculateAlignmentScore() {
9
+ const state = await computeState();
10
+ return state.score || 0;
11
+ }
12
+
13
+ export function watchCommand(options) {
14
+ console.log(chalk.cyan.bold('\n👁️ Ultra-Dex Watch Mode v3.0\n'));
15
+ console.log(chalk.gray('Watching for file changes...\n'));
16
+
17
+ const interval = options.interval ? parseInt(options.interval, 10) : 500;
18
+ console.log(chalk.gray(`Debounce interval: ${interval}ms`));
19
+
20
+ const watchPaths = [
21
+ 'CONTEXT.md',
22
+ 'IMPLEMENTATION-PLAN.md',
23
+ 'src',
24
+ 'app',
25
+ 'lib'
26
+ ];
27
+
28
+ const validPaths = watchPaths.filter(p => {
29
+ const fullPath = join(process.cwd(), p);
30
+ return existsSync(fullPath);
31
+ });
32
+
33
+ console.log(chalk.gray(`Watching: ${validPaths.join(', ')}\n`));
34
+
35
+ let debounceTimer = null;
36
+ let lastScore = null;
37
+
38
+ // Initial score display
39
+ calculateAlignmentScore().then(score => {
40
+ lastScore = score;
41
+ console.log(chalk.blue(`📊 Initial alignment score: ${score}%\n`));
42
+ });
43
+
44
+ validPaths.forEach(path => {
45
+ const fullPath = join(process.cwd(), path);
46
+ try {
47
+ watch(fullPath, { recursive: true }, (eventType, filename) => {
48
+ if (debounceTimer) clearTimeout(debounceTimer);
49
+ debounceTimer = setTimeout(async () => {
50
+ const timestamp = new Date().toLocaleTimeString();
51
+ console.log(chalk.yellow(`\n[${timestamp}] 📝 ${filename || path} changed`));
52
+
53
+ await updateState();
54
+
55
+ const newScore = await calculateAlignmentScore();
56
+ const scoreDiff = lastScore !== null ? newScore - lastScore : 0;
57
+ const diffIndicator = scoreDiff > 0
58
+ ? chalk.green(`↑ +${scoreDiff}`)
59
+ : scoreDiff < 0
60
+ ? chalk.red(`↓ ${scoreDiff}`)
61
+ : chalk.gray('→ 0');
62
+
63
+ lastScore = newScore;
64
+
65
+ const scoreColor = newScore >= 80 ? 'green' : newScore >= 50 ? 'yellow' : 'red';
66
+ console.log(chalk[scoreColor](`✅ State updated | Alignment: ${newScore}% ${diffIndicator}`));
67
+
68
+ }, interval);
69
+ });
70
+ } catch (e) {
71
+ console.log(chalk.gray(` ⚠️ Cannot watch ${path}: ${e.message}`));
72
+ }
73
+ });
74
+
75
+ console.log(chalk.gray('\nPress Ctrl+C to stop'));
76
+
77
+ // Keep process running
78
+ process.stdin.resume();
79
+ }
@@ -0,0 +1,92 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { glob } from 'glob';
4
+
5
+ export class CodeGraph {
6
+ constructor() {
7
+ this.nodes = new Map(); // file path -> node info
8
+ this.edges = []; // { from, to, type }
9
+ }
10
+
11
+ async scan() {
12
+ this.nodes.clear();
13
+ this.edges = [];
14
+
15
+ // Find all js/ts/jsx/tsx files
16
+ // Ignoring node_modules, .git, dist, build
17
+ const files = await glob('**/*.{js,ts,jsx,tsx}', {
18
+ ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**', '.next/**'],
19
+ absolute: false,
20
+ cwd: process.cwd()
21
+ });
22
+
23
+ for (const file of files) {
24
+ await this.analyzeFile(file);
25
+ }
26
+
27
+ return this.getSummary();
28
+ }
29
+
30
+ async analyzeFile(filePath) {
31
+ try {
32
+ const content = await fs.readFile(path.resolve(process.cwd(), filePath), 'utf8');
33
+
34
+ // Basic Node Info
35
+ this.nodes.set(filePath, {
36
+ id: filePath,
37
+ size: content.length,
38
+ type: path.extname(filePath).substring(1),
39
+ // Simple heuristic for "component" vs "utility"
40
+ isComponent: /^[A-Z]/.test(path.basename(filePath)) || content.includes('React') || content.includes('Component'),
41
+ });
42
+
43
+ // Extract Imports (Regex based for speed/simplicity without AST parsing overhead)
44
+ const importRegex = /import\s+(?:[\w\s{},*]+)\s+from\s+['"]([^'"]+)['"]/g;
45
+ let match;
46
+
47
+ while ((match = importRegex.exec(content)) !== null) {
48
+ const importPath = match[1];
49
+
50
+ // Resolve relative imports
51
+ if (importPath.startsWith('.')) {
52
+ const absoluteDir = path.dirname(path.resolve(process.cwd(), filePath));
53
+ const resolvedAbs = path.resolve(absoluteDir, importPath);
54
+ const relativeResolved = path.relative(process.cwd(), resolvedAbs);
55
+
56
+ // Add edge
57
+ this.edges.push({
58
+ from: filePath,
59
+ to: relativeResolved, // Note: This might not match exactly if extensions are missing, but good enough for rough graph
60
+ type: 'depends_on'
61
+ });
62
+ } else {
63
+ // Package import
64
+ this.edges.push({
65
+ from: filePath,
66
+ to: importPath,
67
+ type: 'package_dependency'
68
+ });
69
+ }
70
+ }
71
+
72
+ } catch (e) {
73
+ console.error(`Failed to analyze ${filePath}:`, e);
74
+ }
75
+ }
76
+
77
+ getSummary() {
78
+ return {
79
+ nodeCount: this.nodes.size,
80
+ edgeCount: this.edges.length,
81
+ files: Array.from(this.nodes.keys()),
82
+ dependencies: this.edges
83
+ };
84
+ }
85
+
86
+ findRefereces(fileName) {
87
+ return this.edges.filter(e => e.to.includes(fileName));
88
+ }
89
+ }
90
+
91
+ // Singleton instance
92
+ export const projectGraph = new CodeGraph();
@@ -0,0 +1,95 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { existsSync } from 'fs';
4
+
5
+ const MEMORY_DIR = '.ultra';
6
+ const MEMORY_FILE = 'memory.json';
7
+ const MEMORY_PATH = path.resolve(process.cwd(), MEMORY_DIR, MEMORY_FILE);
8
+
9
+ /**
10
+ * Ultra-Dex Persistent Memory System
11
+ * Stores facts, snippets, and context across sessions.
12
+ */
13
+ export class UltraMemory {
14
+ constructor() {
15
+ this.memory = [];
16
+ this.initialized = false;
17
+ }
18
+
19
+ async init() {
20
+ if (this.initialized) return;
21
+
22
+ try {
23
+ if (!existsSync(path.dirname(MEMORY_PATH))) {
24
+ await fs.mkdir(path.dirname(MEMORY_PATH), { recursive: true });
25
+ }
26
+
27
+ if (existsSync(MEMORY_PATH)) {
28
+ const data = await fs.readFile(MEMORY_PATH, 'utf-8');
29
+ this.memory = JSON.parse(data);
30
+ } else {
31
+ this.memory = [];
32
+ await this.saveToFile();
33
+ }
34
+ this.initialized = true;
35
+ } catch (error) {
36
+ console.error('Failed to initialize memory:', error);
37
+ this.memory = [];
38
+ }
39
+ }
40
+
41
+ async saveToFile() {
42
+ try {
43
+ await fs.writeFile(MEMORY_PATH, JSON.stringify(this.memory, null, 2));
44
+ } catch (error) {
45
+ console.error('Failed to save memory to file:', error);
46
+ }
47
+ }
48
+
49
+ async remember(text, tags = [], source = 'manual') {
50
+ await this.init();
51
+ const entry = {
52
+ id: crypto.randomUUID(),
53
+ text,
54
+ tags,
55
+ source,
56
+ timestamp: new Date().toISOString()
57
+ };
58
+ this.memory.push(entry);
59
+ await this.saveToFile();
60
+ return entry;
61
+ }
62
+
63
+ async search(query, limit = 5) {
64
+ await this.init();
65
+ const lowerQuery = query.toLowerCase();
66
+
67
+ // Simple keyword search for now
68
+ // Future: Vector search / Semantic search
69
+ return this.memory
70
+ .filter(entry =>
71
+ entry.text.toLowerCase().includes(lowerQuery) ||
72
+ entry.tags.some(t => t.toLowerCase().includes(lowerQuery))
73
+ )
74
+ .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
75
+ .slice(0, limit);
76
+ }
77
+
78
+ async clear(beforeDate = null) {
79
+ await this.init();
80
+ if (beforeDate) {
81
+ const date = new Date(beforeDate);
82
+ this.memory = this.memory.filter(entry => new Date(entry.timestamp) >= date);
83
+ } else {
84
+ this.memory = [];
85
+ }
86
+ await this.saveToFile();
87
+ }
88
+
89
+ async getAll() {
90
+ await this.init();
91
+ return this.memory;
92
+ }
93
+ }
94
+
95
+ export const ultraMemory = new UltraMemory();