arela
Version:
AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.
181 lines (175 loc) ⢠5.5 kB
JavaScript
/**
* Search Middleware - ENFORCES arela_search before grep
*
* This wraps agent execution and intercepts search calls.
* If agent tries to use grep without arela_search first,
* we BLOCK it and force them to use arela_search.
*/
import { execa } from 'execa';
import fs from 'fs-extra';
import path from 'path';
export class SearchMiddleware {
searchHistory = [];
cwd;
constructor(cwd) {
this.cwd = cwd;
}
/**
* Check if RAG index exists and is recent
*/
async hasValidRagIndex() {
const ragIndexPath = path.join(this.cwd, '.arela', '.rag-index.json');
if (!await fs.pathExists(ragIndexPath)) {
return false;
}
const stats = await fs.stat(ragIndexPath);
const ageInHours = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60);
// Consider stale if older than 24 hours
return ageInHours < 24;
}
/**
* Check if MCP server is running
*/
async isMcpServerRunning() {
try {
// Check if arela mcp process is running
const result = await execa('pgrep', ['-f', 'arela mcp']);
return result.exitCode === 0;
}
catch {
return false;
}
}
/**
* Check if agent tried arela_search in last 2 minutes
*/
hasTriedArelaSearch() {
const twoMinutesAgo = Date.now() - (2 * 60 * 1000);
return this.searchHistory.some(s => s.type === 'arela_search' &&
s.timestamp > twoMinutesAgo &&
s.allowed);
}
/**
* Intercept and validate a search attempt
*/
async validateSearch(type, query) {
// Always allow arela_search
if (type === 'arela_search') {
this.recordSearch(type, query, true);
return { allowed: true };
}
// For grep/find, check if they tried arela_search first
if (!this.hasTriedArelaSearch()) {
const hasRagIndex = await this.hasValidRagIndex();
const hasMcpServer = await this.isMcpServerRunning();
let message = `
đ¨ BLOCKED: You must try arela_search before using ${type}!
Why this matters:
- arela_search: ~1k tokens ($0.01)
- ${type}: ~85k tokens ($0.85)
- You're wasting 85x more tokens!
`;
if (!hasRagIndex) {
message += `
â ď¸ RAG index is missing or stale!
Run: arela index
`;
}
if (!hasMcpServer) {
message += `
â ď¸ MCP server is not running!
Run: arela mcp (in background)
`;
}
message += `
Try this instead:
arela_search "${query}"
If arela_search doesn't find what you need, THEN you can use ${type}.
`;
this.recordSearch(type, query, false, message);
return {
allowed: false,
message,
suggestion: `arela_search "${query}"`
};
}
// They tried arela_search, allow grep/find
this.recordSearch(type, query, true);
return { allowed: true };
}
/**
* Record a search attempt
*/
recordSearch(type, query, allowed, blockedReason) {
this.searchHistory.push({
type,
query,
timestamp: Date.now(),
allowed,
blockedReason
});
// Keep only last 20 searches
if (this.searchHistory.length > 20) {
this.searchHistory.shift();
}
}
/**
* Get search statistics
*/
getStats() {
const total = this.searchHistory.length;
const arelaSearches = this.searchHistory.filter(s => s.type === 'arela_search').length;
const grepSearches = this.searchHistory.filter(s => s.type === 'grep' || s.type === 'find').length;
const blocked = this.searchHistory.filter(s => !s.allowed).length;
const tokensSaved = blocked * 84000; // 85k - 1k = 84k saved per blocked grep
const moneySaved = (tokensSaved / 1000) * 0.01; // Rough estimate
return {
total,
arelaSearches,
grepSearches,
blocked,
tokensSaved,
moneySaved: moneySaved.toFixed(2),
arelaSearchPercentage: total > 0 ? ((arelaSearches / total) * 100).toFixed(1) : '0'
};
}
/**
* Print search statistics
*/
printStats() {
const stats = this.getStats();
console.log(`
đ Search Statistics:
Total searches: ${stats.total}
arela_search: ${stats.arelaSearches} (${stats.arelaSearchPercentage}%)
grep/find: ${stats.grepSearches}
Blocked: ${stats.blocked}
đ° Savings:
Tokens saved: ${stats.tokensSaved.toLocaleString()}
Money saved: $${stats.moneySaved}
`);
}
}
/**
* Wrap agent execution with search enforcement
*/
export async function executeWithSearchEnforcement(cwd, agentCommand, prompt) {
const middleware = new SearchMiddleware(cwd);
// TODO: Actually intercept tool calls here
// This would require hooking into the agent's tool execution
// For now, this is a framework for future implementation
// Execute agent
const result = await execa('sh', ['-c', agentCommand], {
cwd,
input: prompt,
timeout: 30 * 60 * 1000
});
const stats = middleware.getStats();
middleware.printStats();
return {
success: result.exitCode === 0,
output: result.stdout,
stats
};
}
//# sourceMappingURL=search-middleware.js.map