@cpretzinger/boss-claude 1.0.0 → 1.0.2
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 +304 -1
- package/bin/boss-claude.js +1138 -0
- package/bin/commands/mode.js +250 -0
- package/bin/onyx-guard.js +259 -0
- package/bin/onyx-guard.sh +251 -0
- package/bin/prompts.js +284 -0
- package/bin/rollback.js +85 -0
- package/bin/setup-wizard.js +492 -0
- package/config/.env.example +17 -0
- package/lib/README.md +83 -0
- package/lib/agent-logger.js +61 -0
- package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
- package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
- package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
- package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
- package/lib/agents/memory-supervisor.js +526 -0
- package/lib/agents/registry.js +135 -0
- package/lib/auto-monitor.js +131 -0
- package/lib/checkpoint-hook.js +112 -0
- package/lib/checkpoint.js +319 -0
- package/lib/commentator.js +213 -0
- package/lib/context-scribe.js +120 -0
- package/lib/delegation-strategies.js +326 -0
- package/lib/hierarchy-validator.js +643 -0
- package/lib/index.js +15 -0
- package/lib/init-with-mode.js +261 -0
- package/lib/init.js +44 -6
- package/lib/memory-result-aggregator.js +252 -0
- package/lib/memory.js +35 -7
- package/lib/mode-enforcer.js +473 -0
- package/lib/onyx-banner.js +169 -0
- package/lib/onyx-identity.js +214 -0
- package/lib/onyx-monitor.js +381 -0
- package/lib/onyx-reminder.js +188 -0
- package/lib/onyx-tool-interceptor.js +341 -0
- package/lib/onyx-wrapper.js +315 -0
- package/lib/orchestrator-gate.js +334 -0
- package/lib/output-formatter.js +296 -0
- package/lib/postgres.js +1 -1
- package/lib/prompt-injector.js +220 -0
- package/lib/prompts.js +532 -0
- package/lib/session.js +153 -6
- package/lib/setup/README.md +187 -0
- package/lib/setup/env-manager.js +785 -0
- package/lib/setup/error-recovery.js +630 -0
- package/lib/setup/explain-scopes.js +385 -0
- package/lib/setup/github-instructions.js +333 -0
- package/lib/setup/github-repo.js +254 -0
- package/lib/setup/import-credentials.js +498 -0
- package/lib/setup/index.js +62 -0
- package/lib/setup/init-postgres.js +785 -0
- package/lib/setup/init-redis.js +456 -0
- package/lib/setup/integration-test.js +652 -0
- package/lib/setup/progress.js +357 -0
- package/lib/setup/rollback.js +670 -0
- package/lib/setup/rollback.test.js +452 -0
- package/lib/setup/setup-with-rollback.example.js +351 -0
- package/lib/setup/summary.js +400 -0
- package/lib/setup/test-github-setup.js +10 -0
- package/lib/setup/test-postgres-init.js +98 -0
- package/lib/setup/verify-setup.js +102 -0
- package/lib/task-agent-worker.js +235 -0
- package/lib/token-monitor.js +466 -0
- package/lib/tool-wrapper-integration.js +369 -0
- package/lib/tool-wrapper.js +387 -0
- package/lib/validators/README.md +497 -0
- package/lib/validators/config.js +583 -0
- package/lib/validators/config.test.js +175 -0
- package/lib/validators/github.js +310 -0
- package/lib/validators/github.test.js +61 -0
- package/lib/validators/index.js +15 -0
- package/lib/validators/postgres.js +525 -0
- package/package.json +98 -13
- package/scripts/benchmark-memory.js +433 -0
- package/scripts/check-secrets.sh +12 -0
- package/scripts/fetch-todos.mjs +148 -0
- package/scripts/graceful-shutdown.sh +156 -0
- package/scripts/install-onyx-hooks.js +373 -0
- package/scripts/install.js +119 -18
- package/scripts/redis-monitor.js +284 -0
- package/scripts/redis-setup.js +412 -0
- package/scripts/test-memory-retrieval.js +201 -0
- package/scripts/validate-exports.js +68 -0
- package/scripts/validate-package.js +120 -0
- package/scripts/verify-onyx-deployment.js +309 -0
- package/scripts/verify-redis-deployment.js +354 -0
- package/scripts/verify-redis-init.js +219 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MEMORY SYSTEM BENCHMARK
|
|
5
|
+
*
|
|
6
|
+
* Compares old GitHub-based memory system vs new MemorySupervisor system
|
|
7
|
+
*
|
|
8
|
+
* Metrics:
|
|
9
|
+
* - Response time (target: <5s vs 120s)
|
|
10
|
+
* - Cache hit rate (Redis vs none)
|
|
11
|
+
* - Memory usage (Node.js heap)
|
|
12
|
+
* - Token savings at startup
|
|
13
|
+
* - Engineer response times (parallel vs sequential)
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* node scripts/benchmark-memory.js
|
|
17
|
+
* node scripts/benchmark-memory.js --verbose
|
|
18
|
+
* node scripts/benchmark-memory.js --runs 10
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { searchMemory } from '../lib/memory.js';
|
|
22
|
+
import { queryMemorySupervisor, invalidateMemoryCache, getMemoryCacheStats, closeConnections } from '../lib/agents/memory-supervisor.js';
|
|
23
|
+
import { performance } from 'perf_hooks';
|
|
24
|
+
import { fileURLToPath } from 'url';
|
|
25
|
+
import { dirname } from 'path';
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = dirname(__filename);
|
|
29
|
+
|
|
30
|
+
// Parse CLI args
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
33
|
+
const runsArg = args.find(arg => arg.startsWith('--runs='));
|
|
34
|
+
const numberOfRuns = runsArg ? parseInt(runsArg.split('=')[1]) : 3;
|
|
35
|
+
|
|
36
|
+
// Test queries (varied complexity)
|
|
37
|
+
const TEST_QUERIES = [
|
|
38
|
+
'postgres database schema',
|
|
39
|
+
'redis cache optimization',
|
|
40
|
+
'n8n workflow automation',
|
|
41
|
+
'github repository setup',
|
|
42
|
+
'session management',
|
|
43
|
+
'memory system architecture',
|
|
44
|
+
'authentication implementation',
|
|
45
|
+
'api endpoint design'
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get memory usage in MB
|
|
50
|
+
*/
|
|
51
|
+
function getMemoryUsage() {
|
|
52
|
+
const usage = process.memoryUsage();
|
|
53
|
+
return {
|
|
54
|
+
heapUsed: Math.round(usage.heapUsed / 1024 / 1024),
|
|
55
|
+
heapTotal: Math.round(usage.heapTotal / 1024 / 1024),
|
|
56
|
+
rss: Math.round(usage.rss / 1024 / 1024),
|
|
57
|
+
external: Math.round(usage.external / 1024 / 1024)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Benchmark OLD system (direct GitHub API)
|
|
63
|
+
*/
|
|
64
|
+
async function benchmarkOldSystem(query, limit = 5) {
|
|
65
|
+
const startTime = performance.now();
|
|
66
|
+
const startMemory = getMemoryUsage();
|
|
67
|
+
|
|
68
|
+
let result = null;
|
|
69
|
+
let error = null;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
result = await searchMemory(query, limit);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
error = err.message;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const endTime = performance.now();
|
|
78
|
+
const endMemory = getMemoryUsage();
|
|
79
|
+
const duration = endTime - startTime;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
system: 'old',
|
|
83
|
+
query,
|
|
84
|
+
duration_ms: Math.round(duration),
|
|
85
|
+
duration_s: (duration / 1000).toFixed(2),
|
|
86
|
+
result_count: result ? result.length : 0,
|
|
87
|
+
error,
|
|
88
|
+
cache_hit: false,
|
|
89
|
+
memory_delta_mb: endMemory.heapUsed - startMemory.heapUsed,
|
|
90
|
+
memory_used_mb: endMemory.heapUsed,
|
|
91
|
+
engineers_queried: 0,
|
|
92
|
+
source: 'github-api-direct'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Benchmark NEW system (MemorySupervisor with 4 engineers)
|
|
98
|
+
*/
|
|
99
|
+
async function benchmarkNewSystem(query, limit = 5, useCache = true) {
|
|
100
|
+
const startTime = performance.now();
|
|
101
|
+
const startMemory = getMemoryUsage();
|
|
102
|
+
|
|
103
|
+
let result = null;
|
|
104
|
+
let error = null;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
result = await queryMemorySupervisor(query, {
|
|
108
|
+
useCache,
|
|
109
|
+
timeout: 5000,
|
|
110
|
+
cacheTtl: 300,
|
|
111
|
+
limit
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
error = err.message;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const endTime = performance.now();
|
|
118
|
+
const endMemory = getMemoryUsage();
|
|
119
|
+
const duration = endTime - startTime;
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
system: 'new',
|
|
123
|
+
query,
|
|
124
|
+
duration_ms: Math.round(duration),
|
|
125
|
+
duration_s: (duration / 1000).toFixed(2),
|
|
126
|
+
result_count: result ? result.total_results : 0,
|
|
127
|
+
error,
|
|
128
|
+
cache_hit: result ? result.cache_hit : false,
|
|
129
|
+
memory_delta_mb: endMemory.heapUsed - startMemory.heapUsed,
|
|
130
|
+
memory_used_mb: endMemory.heapUsed,
|
|
131
|
+
engineers_queried: result ? result.engineers_queried.length : 0,
|
|
132
|
+
engineer_details: result ? result.engineers_queried : [],
|
|
133
|
+
source: result && result.cache_hit ? 'redis-cache' : 'parallel-engineers',
|
|
134
|
+
query_time_from_response: result ? result.query_time_ms : null
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Run benchmark comparison
|
|
140
|
+
*/
|
|
141
|
+
async function runBenchmark(query, runNumber, totalRuns) {
|
|
142
|
+
if (verbose) {
|
|
143
|
+
console.log(`\n[${'='.repeat(60)}]`);
|
|
144
|
+
console.log(` Run ${runNumber}/${totalRuns}: "${query}"`);
|
|
145
|
+
console.log(`[${'='.repeat(60)}]`);
|
|
146
|
+
} else {
|
|
147
|
+
console.log(`\n🔄 Run ${runNumber}/${totalRuns}: "${query}"`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Benchmark OLD system
|
|
151
|
+
if (verbose) console.log('\n[OLD SYSTEM] Running...');
|
|
152
|
+
const oldResult = await benchmarkOldSystem(query);
|
|
153
|
+
if (verbose) {
|
|
154
|
+
console.log(` Duration: ${oldResult.duration_ms}ms`);
|
|
155
|
+
console.log(` Results: ${oldResult.result_count}`);
|
|
156
|
+
console.log(` Memory: ${oldResult.memory_used_mb}MB`);
|
|
157
|
+
console.log(` Source: ${oldResult.source}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Wait 500ms between tests
|
|
161
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
162
|
+
|
|
163
|
+
// Benchmark NEW system (cache miss first)
|
|
164
|
+
if (verbose) console.log('\n[NEW SYSTEM] Running (cache MISS)...');
|
|
165
|
+
const newResultNoCache = await benchmarkNewSystem(query, 5, false);
|
|
166
|
+
if (verbose) {
|
|
167
|
+
console.log(` Duration: ${newResultNoCache.duration_ms}ms`);
|
|
168
|
+
console.log(` Results: ${newResultNoCache.result_count}`);
|
|
169
|
+
console.log(` Memory: ${newResultNoCache.memory_used_mb}MB`);
|
|
170
|
+
console.log(` Engineers: ${newResultNoCache.engineers_queried}`);
|
|
171
|
+
console.log(` Source: ${newResultNoCache.source}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Wait 500ms
|
|
175
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
176
|
+
|
|
177
|
+
// Benchmark NEW system (cache HIT)
|
|
178
|
+
if (verbose) console.log('\n[NEW SYSTEM] Running (cache HIT)...');
|
|
179
|
+
const newResultWithCache = await benchmarkNewSystem(query, 5, true);
|
|
180
|
+
if (verbose) {
|
|
181
|
+
console.log(` Duration: ${newResultWithCache.duration_ms}ms`);
|
|
182
|
+
console.log(` Results: ${newResultWithCache.result_count}`);
|
|
183
|
+
console.log(` Memory: ${newResultWithCache.memory_used_mb}MB`);
|
|
184
|
+
console.log(` Cache Hit: ${newResultWithCache.cache_hit ? 'YES ✅' : 'NO ❌'}`);
|
|
185
|
+
console.log(` Source: ${newResultWithCache.source}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
query,
|
|
190
|
+
old: oldResult,
|
|
191
|
+
new_no_cache: newResultNoCache,
|
|
192
|
+
new_with_cache: newResultWithCache
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Calculate aggregate statistics
|
|
198
|
+
*/
|
|
199
|
+
function calculateStats(results) {
|
|
200
|
+
const oldTimes = results.map(r => r.old.duration_ms);
|
|
201
|
+
const newNoCache = results.map(r => r.new_no_cache.duration_ms);
|
|
202
|
+
const newWithCache = results.map(r => r.new_with_cache.duration_ms);
|
|
203
|
+
|
|
204
|
+
const avg = arr => arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
205
|
+
const min = arr => Math.min(...arr);
|
|
206
|
+
const max = arr => Math.max(...arr);
|
|
207
|
+
const median = arr => {
|
|
208
|
+
const sorted = [...arr].sort((a, b) => a - b);
|
|
209
|
+
const mid = Math.floor(sorted.length / 2);
|
|
210
|
+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
old_system: {
|
|
215
|
+
avg_ms: Math.round(avg(oldTimes)),
|
|
216
|
+
min_ms: min(oldTimes),
|
|
217
|
+
max_ms: max(oldTimes),
|
|
218
|
+
median_ms: Math.round(median(oldTimes)),
|
|
219
|
+
avg_s: (avg(oldTimes) / 1000).toFixed(2)
|
|
220
|
+
},
|
|
221
|
+
new_system_no_cache: {
|
|
222
|
+
avg_ms: Math.round(avg(newNoCache)),
|
|
223
|
+
min_ms: min(newNoCache),
|
|
224
|
+
max_ms: max(newNoCache),
|
|
225
|
+
median_ms: Math.round(median(newNoCache)),
|
|
226
|
+
avg_s: (avg(newNoCache) / 1000).toFixed(2),
|
|
227
|
+
improvement_vs_old_pct: Math.round(((avg(oldTimes) - avg(newNoCache)) / avg(oldTimes)) * 100)
|
|
228
|
+
},
|
|
229
|
+
new_system_with_cache: {
|
|
230
|
+
avg_ms: Math.round(avg(newWithCache)),
|
|
231
|
+
min_ms: min(newWithCache),
|
|
232
|
+
max_ms: max(newWithCache),
|
|
233
|
+
median_ms: Math.round(median(newWithCache)),
|
|
234
|
+
avg_s: (avg(newWithCache) / 1000).toFixed(2),
|
|
235
|
+
improvement_vs_old_pct: Math.round(((avg(oldTimes) - avg(newWithCache)) / avg(oldTimes)) * 100),
|
|
236
|
+
improvement_vs_no_cache_pct: Math.round(((avg(newNoCache) - avg(newWithCache)) / avg(newNoCache)) * 100)
|
|
237
|
+
},
|
|
238
|
+
cache_hit_speedup: (avg(newNoCache) / avg(newWithCache)).toFixed(2) + 'x'
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Estimate token savings
|
|
244
|
+
*/
|
|
245
|
+
function estimateTokenSavings(stats, assumedStartupCalls = 3) {
|
|
246
|
+
// Old system: every call hits GitHub API (~120s per call)
|
|
247
|
+
const oldStartupTime = stats.old_system.avg_ms * assumedStartupCalls;
|
|
248
|
+
|
|
249
|
+
// New system: first call misses cache, subsequent calls hit cache
|
|
250
|
+
const newStartupTime = stats.new_system_no_cache.avg_ms +
|
|
251
|
+
(stats.new_system_with_cache.avg_ms * (assumedStartupCalls - 1));
|
|
252
|
+
|
|
253
|
+
const timeSaved = oldStartupTime - newStartupTime;
|
|
254
|
+
const pctFaster = Math.round((timeSaved / oldStartupTime) * 100);
|
|
255
|
+
|
|
256
|
+
// Estimate token savings (rough heuristic: 1 second = ~50 tokens at startup)
|
|
257
|
+
const tokensSavedEstimate = Math.round((timeSaved / 1000) * 50);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
old_startup_time_ms: Math.round(oldStartupTime),
|
|
261
|
+
new_startup_time_ms: Math.round(newStartupTime),
|
|
262
|
+
time_saved_ms: Math.round(timeSaved),
|
|
263
|
+
percent_faster: pctFaster,
|
|
264
|
+
estimated_tokens_saved: tokensSavedEstimate,
|
|
265
|
+
assumed_startup_calls: assumedStartupCalls
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Main execution
|
|
271
|
+
*/
|
|
272
|
+
async function main() {
|
|
273
|
+
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
|
274
|
+
console.log('║ BOSS CLAUDE - MEMORY SYSTEM BENCHMARK ║');
|
|
275
|
+
console.log('╚════════════════════════════════════════════════════════════════╝');
|
|
276
|
+
console.log('\nComparing:');
|
|
277
|
+
console.log(' OLD: Direct GitHub API calls (lib/memory.js)');
|
|
278
|
+
console.log(' NEW: MemorySupervisor with 4 parallel engineers + Redis cache');
|
|
279
|
+
console.log(`\nTest Configuration:`);
|
|
280
|
+
console.log(` Queries: ${TEST_QUERIES.length}`);
|
|
281
|
+
console.log(` Runs per query: ${numberOfRuns}`);
|
|
282
|
+
console.log(` Total tests: ${TEST_QUERIES.length * numberOfRuns * 3} (${TEST_QUERIES.length * numberOfRuns} per system)`);
|
|
283
|
+
console.log(` Verbose: ${verbose ? 'ON' : 'OFF'}`);
|
|
284
|
+
|
|
285
|
+
// Clear cache before starting
|
|
286
|
+
console.log('\n🔄 Clearing Redis cache...');
|
|
287
|
+
await invalidateMemoryCache();
|
|
288
|
+
|
|
289
|
+
const allResults = [];
|
|
290
|
+
let testNumber = 0;
|
|
291
|
+
const totalTests = TEST_QUERIES.length * numberOfRuns;
|
|
292
|
+
|
|
293
|
+
for (let run = 1; run <= numberOfRuns; run++) {
|
|
294
|
+
for (const query of TEST_QUERIES) {
|
|
295
|
+
testNumber++;
|
|
296
|
+
const result = await runBenchmark(query, testNumber, totalTests);
|
|
297
|
+
allResults.push(result);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Calculate statistics
|
|
302
|
+
const stats = calculateStats(allResults);
|
|
303
|
+
const tokenSavings = estimateTokenSavings(stats);
|
|
304
|
+
|
|
305
|
+
// Get cache statistics
|
|
306
|
+
const cacheStats = await getMemoryCacheStats();
|
|
307
|
+
|
|
308
|
+
// Calculate cache hit rate
|
|
309
|
+
const totalNewSystemCalls = allResults.length * 2; // no-cache + with-cache
|
|
310
|
+
const cacheHits = allResults.filter(r => r.new_with_cache.cache_hit).length;
|
|
311
|
+
const cacheHitRate = Math.round((cacheHits / totalNewSystemCalls) * 100);
|
|
312
|
+
|
|
313
|
+
// Print results
|
|
314
|
+
console.log('\n\n╔════════════════════════════════════════════════════════════════╗');
|
|
315
|
+
console.log('║ BENCHMARK RESULTS ║');
|
|
316
|
+
console.log('╚════════════════════════════════════════════════════════════════╝');
|
|
317
|
+
|
|
318
|
+
console.log('\n📊 RESPONSE TIME COMPARISON');
|
|
319
|
+
console.log('─'.repeat(64));
|
|
320
|
+
console.log(`\nOLD SYSTEM (Direct GitHub API):`);
|
|
321
|
+
console.log(` Average: ${stats.old_system.avg_ms}ms (${stats.old_system.avg_s}s)`);
|
|
322
|
+
console.log(` Median: ${stats.old_system.median_ms}ms`);
|
|
323
|
+
console.log(` Range: ${stats.old_system.min_ms}ms - ${stats.old_system.max_ms}ms`);
|
|
324
|
+
|
|
325
|
+
console.log(`\nNEW SYSTEM (MemorySupervisor - Cache MISS):`);
|
|
326
|
+
console.log(` Average: ${stats.new_system_no_cache.avg_ms}ms (${stats.new_system_no_cache.avg_s}s)`);
|
|
327
|
+
console.log(` Median: ${stats.new_system_no_cache.median_ms}ms`);
|
|
328
|
+
console.log(` Range: ${stats.new_system_no_cache.min_ms}ms - ${stats.new_system_no_cache.max_ms}ms`);
|
|
329
|
+
console.log(` Improvement: ${stats.new_system_no_cache.improvement_vs_old_pct}% faster than old`);
|
|
330
|
+
|
|
331
|
+
console.log(`\nNEW SYSTEM (MemorySupervisor - Cache HIT):`);
|
|
332
|
+
console.log(` Average: ${stats.new_system_with_cache.avg_ms}ms (${stats.new_system_with_cache.avg_s}s)`);
|
|
333
|
+
console.log(` Median: ${stats.new_system_with_cache.median_ms}ms`);
|
|
334
|
+
console.log(` Range: ${stats.new_system_with_cache.min_ms}ms - ${stats.new_system_with_cache.max_ms}ms`);
|
|
335
|
+
console.log(` Improvement: ${stats.new_system_with_cache.improvement_vs_old_pct}% faster than old`);
|
|
336
|
+
console.log(` Improvement: ${stats.new_system_with_cache.improvement_vs_no_cache_pct}% faster than cache miss`);
|
|
337
|
+
console.log(` Cache Speedup: ${stats.cache_hit_speedup}`);
|
|
338
|
+
|
|
339
|
+
console.log('\n\n💾 CACHE PERFORMANCE');
|
|
340
|
+
console.log('─'.repeat(64));
|
|
341
|
+
console.log(` Cache Hit Rate: ${cacheHitRate}%`);
|
|
342
|
+
console.log(` Cache Hits: ${cacheHits}/${totalNewSystemCalls} calls`);
|
|
343
|
+
console.log(` Cached Queries: ${cacheStats.total_cached_queries}`);
|
|
344
|
+
console.log(` Cache Strategy: Redis with 5-minute TTL`);
|
|
345
|
+
|
|
346
|
+
console.log('\n\n🚀 STARTUP TIME IMPACT');
|
|
347
|
+
console.log('─'.repeat(64));
|
|
348
|
+
console.log(` Assumed startup calls: ${tokenSavings.assumed_startup_calls}`);
|
|
349
|
+
console.log(` OLD system startup: ${tokenSavings.old_startup_time_ms}ms`);
|
|
350
|
+
console.log(` NEW system startup: ${tokenSavings.new_startup_time_ms}ms`);
|
|
351
|
+
console.log(` Time saved: ${tokenSavings.time_saved_ms}ms (${tokenSavings.percent_faster}% faster)`);
|
|
352
|
+
console.log(` Estimated token savings: ~${tokenSavings.estimated_tokens_saved} tokens`);
|
|
353
|
+
|
|
354
|
+
console.log('\n\n🔧 ARCHITECTURE IMPROVEMENTS');
|
|
355
|
+
console.log('─'.repeat(64));
|
|
356
|
+
console.log(` OLD: Sequential GitHub API calls`);
|
|
357
|
+
console.log(` NEW: 4 parallel engineer agents + Redis cache`);
|
|
358
|
+
console.log(` Engineers: postgres-n8n-specialist, redis-architect,`);
|
|
359
|
+
console.log(` n8n-workflow-architect, github-expert`);
|
|
360
|
+
console.log(` Timeout: 5 seconds per engineer (parallel execution)`);
|
|
361
|
+
console.log(` Cache Layer: Redis with automatic invalidation`);
|
|
362
|
+
|
|
363
|
+
console.log('\n\n📈 TARGET METRICS');
|
|
364
|
+
console.log('─'.repeat(64));
|
|
365
|
+
console.log(` Target: <5s response (cache miss)`);
|
|
366
|
+
console.log(` Actual: ${stats.new_system_no_cache.avg_s}s`);
|
|
367
|
+
console.log(` Status: ${stats.new_system_no_cache.avg_ms < 5000 ? '✅ PASS' : '❌ FAIL'}`);
|
|
368
|
+
console.log(`\n Target: <1s response (cache hit)`);
|
|
369
|
+
console.log(` Actual: ${stats.new_system_with_cache.avg_s}s`);
|
|
370
|
+
console.log(` Status: ${stats.new_system_with_cache.avg_ms < 1000 ? '✅ PASS' : '⚠️ NEEDS OPTIMIZATION'}`);
|
|
371
|
+
|
|
372
|
+
// JSON output for programmatic consumption
|
|
373
|
+
const jsonOutput = {
|
|
374
|
+
benchmark: 'memory-system-comparison',
|
|
375
|
+
timestamp: new Date().toISOString(),
|
|
376
|
+
config: {
|
|
377
|
+
queries: TEST_QUERIES.length,
|
|
378
|
+
runs_per_query: numberOfRuns,
|
|
379
|
+
total_tests: totalTests
|
|
380
|
+
},
|
|
381
|
+
results: {
|
|
382
|
+
old_system: stats.old_system,
|
|
383
|
+
new_system_no_cache: stats.new_system_no_cache,
|
|
384
|
+
new_system_with_cache: stats.new_system_with_cache,
|
|
385
|
+
cache_performance: {
|
|
386
|
+
hit_rate_pct: cacheHitRate,
|
|
387
|
+
total_queries_cached: cacheStats.total_cached_queries,
|
|
388
|
+
cache_speedup: stats.cache_hit_speedup
|
|
389
|
+
},
|
|
390
|
+
startup_impact: tokenSavings,
|
|
391
|
+
target_metrics: {
|
|
392
|
+
cache_miss_target_ms: 5000,
|
|
393
|
+
cache_miss_actual_ms: stats.new_system_no_cache.avg_ms,
|
|
394
|
+
cache_miss_pass: stats.new_system_no_cache.avg_ms < 5000,
|
|
395
|
+
cache_hit_target_ms: 1000,
|
|
396
|
+
cache_hit_actual_ms: stats.new_system_with_cache.avg_ms,
|
|
397
|
+
cache_hit_pass: stats.new_system_with_cache.avg_ms < 1000
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
raw_results: verbose ? allResults : null
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
console.log('\n\n📄 JSON OUTPUT');
|
|
404
|
+
console.log('─'.repeat(64));
|
|
405
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
406
|
+
|
|
407
|
+
console.log('\n\n✅ Benchmark complete!\n');
|
|
408
|
+
|
|
409
|
+
// Close connections gracefully
|
|
410
|
+
console.log('\n🔌 Closing connections...');
|
|
411
|
+
await closeConnections();
|
|
412
|
+
|
|
413
|
+
// Give a moment for cleanup to finish
|
|
414
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
415
|
+
|
|
416
|
+
console.log('✅ All connections closed\n');
|
|
417
|
+
process.exit(0);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Run benchmark
|
|
421
|
+
main().catch(async (err) => {
|
|
422
|
+
console.error('\n❌ Benchmark failed:', err);
|
|
423
|
+
console.error(err.stack);
|
|
424
|
+
|
|
425
|
+
// Attempt cleanup even on error
|
|
426
|
+
try {
|
|
427
|
+
await closeConnections();
|
|
428
|
+
} catch (cleanupErr) {
|
|
429
|
+
console.warn('Cleanup error:', cleanupErr.message);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
process.exit(1);
|
|
433
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Pre-commit hook: Block commits with real credentials
|
|
3
|
+
|
|
4
|
+
PATTERNS="trolley|caboose|turntable|yamanote|5Prl6LQo|guAPbZww|sk-proj-J7Szng|proxy\.rlwy\.net"
|
|
5
|
+
|
|
6
|
+
if grep -rn -E "$PATTERNS" lib/ config/ bin/ README.md --include="*.js" --include="*.md" --include="*.json" 2>/dev/null | grep -v "check-secrets.sh"; then
|
|
7
|
+
echo "❌ BLOCKED: Real credentials found! Remove before committing."
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
echo "✅ No credentials detected"
|
|
12
|
+
exit 0
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createClient } from 'redis';
|
|
4
|
+
|
|
5
|
+
// Try local Redis first
|
|
6
|
+
const REDIS_URL = 'redis://localhost:6379';
|
|
7
|
+
|
|
8
|
+
async function fetchTodos() {
|
|
9
|
+
const client = createClient({ url: REDIS_URL });
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
await client.connect();
|
|
13
|
+
console.log('✅ Connected to Redis (localhost:6379)');
|
|
14
|
+
|
|
15
|
+
// Check for Boss Claude todo keys
|
|
16
|
+
const patterns = [
|
|
17
|
+
'boss:todos:*',
|
|
18
|
+
'boss:tasks:*',
|
|
19
|
+
'session:current:*',
|
|
20
|
+
'boss:*'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const allKeys = new Set();
|
|
24
|
+
|
|
25
|
+
for (const pattern of patterns) {
|
|
26
|
+
console.log(`\n🔍 Scanning for: ${pattern}`);
|
|
27
|
+
const keys = await client.keys(pattern);
|
|
28
|
+
keys.forEach(k => allKeys.add(k));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (allKeys.size === 0) {
|
|
32
|
+
console.log('\n⚠️ No Boss Claude keys found in Redis');
|
|
33
|
+
console.log('\nSearching for ANY keys to verify connection...');
|
|
34
|
+
|
|
35
|
+
const allRedisKeys = await client.keys('*');
|
|
36
|
+
console.log(`Total keys in Redis: ${allRedisKeys.length}`);
|
|
37
|
+
allRedisKeys.slice(0, 20).forEach(k => console.log(` - ${k}`));
|
|
38
|
+
if (allRedisKeys.length > 20) {
|
|
39
|
+
console.log(' ... (showing first 20 keys)');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await client.disconnect();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`\n📋 Found ${allKeys.size} Boss Claude keys:\n`);
|
|
47
|
+
|
|
48
|
+
const todoData = {};
|
|
49
|
+
|
|
50
|
+
for (const key of Array.from(allKeys).sort()) {
|
|
51
|
+
const type = await client.type(key);
|
|
52
|
+
let value;
|
|
53
|
+
|
|
54
|
+
switch (type) {
|
|
55
|
+
case 'string':
|
|
56
|
+
value = await client.get(key);
|
|
57
|
+
break;
|
|
58
|
+
case 'list':
|
|
59
|
+
value = await client.lRange(key, 0, -1);
|
|
60
|
+
break;
|
|
61
|
+
case 'set':
|
|
62
|
+
value = await client.sMembers(key);
|
|
63
|
+
break;
|
|
64
|
+
case 'zset':
|
|
65
|
+
value = await client.zRangeWithScores(key, 0, -1);
|
|
66
|
+
break;
|
|
67
|
+
case 'hash':
|
|
68
|
+
value = await client.hGetAll(key);
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
value = `<${type}>`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
todoData[key] = { type, value };
|
|
75
|
+
|
|
76
|
+
console.log(`\n🔑 ${key}`);
|
|
77
|
+
console.log(` Type: ${type}`);
|
|
78
|
+
|
|
79
|
+
if (type === 'string') {
|
|
80
|
+
// Try to parse as JSON
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(value);
|
|
83
|
+
console.log(' Value:', JSON.stringify(parsed, null, 2).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n'));
|
|
84
|
+
} catch {
|
|
85
|
+
console.log(' Value:', value);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
console.log(' Value:', JSON.stringify(value, null, 2).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n'));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get TTL
|
|
92
|
+
const ttl = await client.ttl(key);
|
|
93
|
+
if (ttl > 0) {
|
|
94
|
+
console.log(` TTL: ${ttl}s (${Math.floor(ttl / 60)} minutes)`);
|
|
95
|
+
} else if (ttl === -1) {
|
|
96
|
+
console.log(' TTL: No expiration');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Summary
|
|
101
|
+
console.log('\n' + '='.repeat(80));
|
|
102
|
+
console.log('📊 BOSS CLAUDE TODO SUMMARY');
|
|
103
|
+
console.log('='.repeat(80));
|
|
104
|
+
|
|
105
|
+
// Look for main todo list
|
|
106
|
+
if (todoData['boss:todos:main']) {
|
|
107
|
+
console.log('\n✅ Main Todo List Found:');
|
|
108
|
+
const todos = todoData['boss:todos:main'].value;
|
|
109
|
+
if (typeof todos === 'string') {
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(todos);
|
|
112
|
+
console.log(JSON.stringify(parsed, null, 2));
|
|
113
|
+
} catch {
|
|
114
|
+
console.log(todos);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
console.log(JSON.stringify(todos, null, 2));
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
console.log('\n⚠️ boss:todos:main not found');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Look for pending tasks
|
|
124
|
+
if (todoData['boss:todos:pending']) {
|
|
125
|
+
console.log('\n📌 Pending Tasks:');
|
|
126
|
+
console.log(JSON.stringify(todoData['boss:todos:pending'].value, null, 2));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Look for session data
|
|
130
|
+
const sessionKeys = Array.from(allKeys).filter(k => k.startsWith('session:'));
|
|
131
|
+
if (sessionKeys.length > 0) {
|
|
132
|
+
console.log('\n🔄 Session Data:');
|
|
133
|
+
sessionKeys.forEach(key => {
|
|
134
|
+
console.log(` ${key}:`, JSON.stringify(todoData[key].value, null, 2));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await client.disconnect();
|
|
139
|
+
console.log('\n✅ Disconnected from Redis');
|
|
140
|
+
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error('❌ Error:', error.message);
|
|
143
|
+
console.error(error.stack);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fetchTodos();
|