@robinson_ai_systems/free-agent-mcp
Version:
Free Agent MCP - Portable, workspace-agnostic code generation using FREE models (Ollama)
1,436 lines (1,388 loc) • 683 kB
JavaScript
#!/usr/bin/env node
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require2() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/shared/shared-llm/ollama-client.ts
async function pingOllama(timeoutMs = 1e3) {
const urls = [
`${BASE}/api/tags`,
`${BASE.replace("localhost", "127.0.0.1")}/api/tags`
];
for (const url of urls) {
try {
const r = await fetch(url, {
method: "GET",
signal: AbortSignal.timeout(timeoutMs),
headers: { "Accept": "application/json" }
});
if (r.ok) {
return true;
}
} catch (error) {
console.error(`[pingOllama] Failed to ping ${url}: ${error?.message || error}`);
}
}
return false;
}
async function ollamaGenerate(opts) {
const { model, prompt, format, timeoutMs = 12e4, retries = 2 } = opts;
console.error(`[sharedGenerate] Starting generation with model: ${model}, timeout: ${timeoutMs}ms`);
let lastErr;
for (let i = 0; i <= retries; i++) {
try {
console.error(`[sharedGenerate] Attempt ${i + 1}/${retries + 1}`);
const body = { model, prompt, stream: false };
if (format === "json") {
body.format = "json";
}
console.error(`[sharedGenerate] Sending fetch to ${BASE}/api/generate`);
const r = await fetch(`${BASE}/api/generate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
signal: AbortSignal.timeout(timeoutMs)
});
console.error(`[sharedGenerate] Fetch completed with status: ${r.status}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
console.error("[sharedGenerate] Parsing JSON response...");
const json = await r.json();
console.error("[sharedGenerate] JSON parsed successfully");
return json.response || "";
} catch (e) {
console.error(`[sharedGenerate] Error on attempt ${i + 1}:`, e);
lastErr = e;
if (i < retries) {
console.error(`[sharedGenerate] Retrying in ${500 * (i + 1)}ms...`);
await sleep(500 * (i + 1));
}
}
}
throw new Error(`Ollama generate failed after ${retries + 1} attempt(s): ${lastErr?.message || lastErr}`);
}
async function warmModels(models) {
for (const model of models) {
try {
await ollamaGenerate({ model, prompt: "test", timeoutMs: 1e4, retries: 0 });
} catch {
}
}
}
var BASE, sleep;
var init_ollama_client = __esm({
"src/shared/shared-llm/ollama-client.ts"() {
"use strict";
BASE = (process.env.OLLAMA_BASE_URL || "http://localhost:11434").replace(/\/+$/, "");
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
}
});
// src/utils/model-manager.ts
import { Ollama } from "ollama";
function getModelManager(baseUrl) {
if (!modelManager) {
modelManager = new ModelManager(baseUrl);
}
return modelManager;
}
var ModelManager, modelManager;
var init_model_manager = __esm({
"src/utils/model-manager.ts"() {
"use strict";
ModelManager = class {
ollama;
models = /* @__PURE__ */ new Map();
lastDiscovery = 0;
discoveryInterval = 6e4;
// Re-discover every 60 seconds
baseUrl;
constructor(baseUrl = "http://localhost:11434") {
this.baseUrl = baseUrl;
this.ollama = new Ollama({ host: baseUrl });
}
/**
* Discover all available models from Ollama
*/
async discoverModels(force = false) {
const now = Date.now();
if (!force && this.models.size > 0 && now - this.lastDiscovery < this.discoveryInterval) {
return Array.from(this.models.values());
}
try {
const response = await this.ollama.list();
this.models.clear();
for (const model of response.models) {
const info = this.parseModelInfo(model);
this.models.set(info.name, info);
}
this.lastDiscovery = now;
console.error(`[ModelManager] Discovered ${this.models.size} models`);
return Array.from(this.models.values());
} catch (error) {
console.error("[ModelManager] Failed to discover models:", error);
return [];
}
}
/**
* Parse model information from Ollama response
*/
parseModelInfo(model) {
const name = model.name;
const size = model.size || 0;
const sizeGB = size / (1024 * 1024 * 1024);
const parts = name.split(":");
const baseName = parts[0] || name;
const paramSize = parts[1] || "unknown";
let family = "unknown";
if (baseName.includes("qwen")) family = "qwen";
else if (baseName.includes("deepseek")) family = "deepseek";
else if (baseName.includes("llama")) family = "llama";
else if (baseName.includes("codellama")) family = "codellama";
else if (baseName.includes("mistral")) family = "mistral";
else if (baseName.includes("phi")) family = "phi";
else if (baseName.includes("gemma")) family = "gemma";
const capabilities = ["chat"];
const isCodeCapable = baseName.includes("coder") || baseName.includes("code") || family === "codellama" || family === "mistral" || // Mistral is excellent for code
family === "deepseek" || // DeepSeek is code-focused
family === "qwen";
if (isCodeCapable) {
capabilities.push("code");
}
if (baseName.includes("vision") || baseName.includes("llava")) {
capabilities.push("vision");
}
if (baseName.includes("embed")) {
capabilities.push("embedding");
}
let speed;
if (sizeGB < 2) speed = "fast";
else if (sizeGB < 5) speed = "medium";
else speed = "slow";
let quality;
const paramNum = parseInt(paramSize);
if (isNaN(paramNum)) quality = "good";
else if (paramNum < 7) quality = "good";
else if (paramNum < 20) quality = "better";
else quality = "best";
return {
name,
size,
sizeGB,
family,
parameter_size: paramSize,
capabilities,
speed,
quality,
modified_at: model.modified_at || (/* @__PURE__ */ new Date()).toISOString()
};
}
/**
* Select the best model for a task
*/
async selectModel(criteria) {
const models = await this.discoverModels();
if (models.length === 0) {
console.error("[ModelManager] No models available");
return null;
}
let candidates = models;
if (criteria.requiredCapabilities && criteria.requiredCapabilities.length > 0) {
candidates = models.filter(
(m) => criteria.requiredCapabilities.every((cap) => m.capabilities.includes(cap))
);
}
if (criteria.task === "code" || criteria.task === "refactor" || criteria.task === "test") {
const codeModels = candidates.filter((m) => m.capabilities.includes("code"));
if (codeModels.length > 0) {
candidates = codeModels;
}
}
candidates.sort((a, b) => {
if (criteria.preferSpeed) {
if (a.speed !== b.speed) {
const speedOrder = { fast: 0, medium: 1, slow: 2 };
return speedOrder[a.speed] - speedOrder[b.speed];
}
}
const targetQuality = this.getTargetQuality(criteria.complexity);
const aMatch = a.quality === targetQuality ? 0 : 1;
const bMatch = b.quality === targetQuality ? 0 : 1;
if (aMatch !== bMatch) return aMatch - bMatch;
if (criteria.complexity === "complex") {
return b.size - a.size;
}
if (criteria.complexity === "simple") {
return a.size - b.size;
}
return Math.abs(a.sizeGB - 5) - Math.abs(b.sizeGB - 5);
});
return candidates[0]?.name || models[0]?.name || null;
}
/**
* Get fallback chain for a model
*/
async getFallbackChain(primaryModel, criteria) {
const models = await this.discoverModels();
const chain = [primaryModel];
let candidates = models.filter(
(m) => m.name !== primaryModel && (!criteria.requiredCapabilities || criteria.requiredCapabilities.every((cap) => m.capabilities.includes(cap)))
);
candidates.sort((a, b) => a.size - b.size);
chain.push(...candidates.slice(0, 2).map((m) => m.name));
return chain;
}
/**
* Get adaptive timeout for a model
*/
async getAdaptiveTimeout(modelName, isColdStart = false) {
const model = this.models.get(modelName);
if (!model) {
return isColdStart ? 18e4 : 6e4;
}
let baseTimeout;
if (model.sizeGB < 2) {
baseTimeout = 3e4;
} else if (model.sizeGB < 5) {
baseTimeout = 6e4;
} else if (model.sizeGB < 10) {
baseTimeout = 12e4;
} else {
baseTimeout = 18e4;
}
if (isColdStart) {
baseTimeout *= 2;
}
return baseTimeout;
}
/**
* Get model info
*/
getModelInfo(modelName) {
return this.models.get(modelName);
}
/**
* List all models
*/
listModels() {
return Array.from(this.models.values());
}
/**
* List available model names (for quick lookup)
*/
async listAvailableModels() {
await this.discoverModels();
return Array.from(this.models.keys());
}
/**
* Get models by capability
*/
getModelsByCapability(capability) {
return Array.from(this.models.values()).filter(
(m) => m.capabilities.includes(capability)
);
}
/**
* Get target quality for complexity
*/
getTargetQuality(complexity) {
switch (complexity) {
case "simple":
return "good";
case "medium":
return "better";
case "complex":
return "best";
default:
return "good";
}
}
/**
* Check if Ollama is running
*/
async isOllamaRunning() {
try {
await this.ollama.list();
return true;
} catch {
return false;
}
}
};
modelManager = null;
}
});
// src/ollama-client.ts
import { Ollama as Ollama2 } from "ollama";
import { spawn } from "child_process";
var OllamaClient;
var init_ollama_client2 = __esm({
"src/ollama-client.ts"() {
"use strict";
init_ollama_client();
init_model_manager();
OllamaClient = class {
ollama;
models;
baseUrl;
autoStart;
ollamaProcess = null;
startedByUs = false;
constructor(autoStart = true) {
this.baseUrl = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
this.ollama = new Ollama2({ host: this.baseUrl });
this.models = this.initializeModels();
this.autoStart = autoStart;
}
/**
* Get the current Ollama base URL
*/
getBaseUrl() {
return this.baseUrl;
}
initializeModels() {
return /* @__PURE__ */ new Map([
// PRIMARY: Qwen 2.5 Coder 7B - BEST 7B code model available
[
"qwen2.5-coder:7b",
{
name: "qwen2.5-coder:7b",
speed: "medium",
quality: "best",
useCase: ["code", "medium", "refactoring", "tests", "features", "complex"]
}
],
// ANALYSIS: Mistral 7B - Excellent for API integration, analysis, planning
[
"mistral:7b",
{
name: "mistral:7b",
speed: "medium",
quality: "better",
useCase: ["analysis", "api-integration", "research", "planning", "tool-setup", "configuration"]
}
],
// FALLBACK: DeepSeek Coder 1.3B - Small but code-specific
[
"deepseek-coder:1.3b",
{
name: "deepseek-coder:1.3b",
speed: "fast",
quality: "good",
useCase: ["code", "simple", "quick-generation"]
}
],
// ROUTER: Qwen 3B - Fast for routing/intent detection only
[
"qwen2.5:3b",
{
name: "qwen2.5:3b",
speed: "fast",
quality: "good",
useCase: ["router", "intent", "scaffolding", "simple"]
}
],
// EMBEDDINGS: Nomic Embed Text - For semantic search
[
"nomic-embed-text",
{
name: "nomic-embed-text",
speed: "fast",
quality: "good",
useCase: ["embeddings", "semantic-search"]
}
]
]);
}
/**
* Select the best model for the task
*/
async selectModel(options) {
const modelManager2 = getModelManager(this.baseUrl);
if (options.model && options.model !== "auto") {
const modelMap = {
"primary": "qwen2.5-coder:7b",
"fallback": "deepseek-coder:1.3b",
"router": "qwen2.5:3b",
"qwen-coder": "qwen2.5-coder:7b",
"deepseek": "deepseek-coder:1.3b"
};
const requestedModel = modelMap[options.model] || options.model;
const modelInfo = modelManager2.getModelInfo(requestedModel);
if (modelInfo) {
return requestedModel;
}
console.warn(`[OllamaClient] Requested model ${requestedModel} not found, auto-selecting...`);
}
const modelManager_models = await modelManager2.listAvailableModels();
if (options.complexity === "simple" && (options.model?.includes("analysis") || options.model?.includes("research"))) {
if (modelManager_models.includes("mistral:7b")) {
return "mistral:7b";
}
}
if (modelManager_models.includes("qwen2.5-coder:7b")) {
return "qwen2.5-coder:7b";
}
if (modelManager_models.includes("mistral:7b")) {
return "mistral:7b";
}
if (modelManager_models.includes("deepseek-coder:1.3b")) {
return "deepseek-coder:1.3b";
}
const criteria = {
task: "code",
// Default to code task
complexity: options.complexity || "medium",
preferSpeed: false,
// Prefer quality over speed for code
requiredCapabilities: ["code"]
};
const selectedModel = await modelManager2.selectModel(criteria);
if (!selectedModel) {
throw new Error("No Ollama models available. Please pull mistral:7b or qwen2.5-coder:7b.");
}
return selectedModel;
}
/**
* Generate text using Ollama (with auto-start, dynamic model selection, and adaptive timeouts!)
*/
async generate(prompt, options = {}) {
await this.ensureRunning();
const modelManager2 = getModelManager(this.baseUrl);
await modelManager2.discoverModels();
const model = await this.selectModel(options);
const startTime = Date.now();
const isColdStart = false;
let timeout = await modelManager2.getAdaptiveTimeout(model, isColdStart);
if (process.env.OLLAMA_REQUEST_TIMEOUT) {
timeout = parseInt(process.env.OLLAMA_REQUEST_TIMEOUT, 10) * 1e3;
} else if (isColdStart && process.env.OLLAMA_STARTUP_TIMEOUT) {
timeout = parseInt(process.env.OLLAMA_STARTUP_TIMEOUT, 10) * 1e3;
} else if (!isColdStart && process.env.OLLAMA_WARMUP_TIMEOUT) {
timeout = parseInt(process.env.OLLAMA_WARMUP_TIMEOUT, 10) * 1e3;
}
console.error(`[OllamaClient] Using model: ${model} (timeout: ${timeout}ms, cold_start: ${isColdStart})`);
console.error(`[OllamaClient] Prompt length: ${prompt.length} chars`);
try {
console.error("[OllamaClient] Calling sharedGenerate...");
const text = await ollamaGenerate({
model,
prompt,
format: "text",
timeoutMs: timeout,
retries: 2
});
const timeMs = Date.now() - startTime;
console.error(`[OllamaClient] sharedGenerate completed in ${timeMs}ms`);
const tokensInput = Math.ceil(prompt.length / 4);
const tokensGenerated = Math.ceil(text.length / 4);
return {
text,
model,
tokensGenerated,
tokensInput,
tokensTotal: tokensInput + tokensGenerated,
timeMs
};
} catch (error) {
if (error.message?.includes("not found") || error.message?.includes("404")) {
console.warn(`[OllamaClient] Model ${model} failed, trying fallback...`);
const criteria = {
task: "code",
complexity: options.complexity || "simple"
};
const fallbackChain = await modelManager2.getFallbackChain(model, criteria);
if (fallbackChain.length > 1) {
const fallbackModel = fallbackChain[1];
console.error(`[OllamaClient] Retrying with fallback model: ${fallbackModel}`);
const fallbackTimeout = await modelManager2.getAdaptiveTimeout(fallbackModel, isColdStart);
const text = await ollamaGenerate({
model: fallbackModel,
prompt,
format: "text",
timeoutMs: fallbackTimeout,
retries: 1
});
const timeMs = Date.now() - startTime;
const tokensInput = Math.ceil(prompt.length / 4);
const tokensGenerated = Math.ceil(text.length / 4);
return {
text,
model: fallbackModel,
tokensGenerated,
tokensInput,
tokensTotal: tokensInput + tokensGenerated,
timeMs
};
}
throw new Error(
`No available models found. Please pull at least one model: ollama pull qwen2.5:3b`
);
}
throw error;
}
}
/**
* Check if Ollama is running and models are available
*/
async checkHealth() {
const errors = [];
let running = false;
let availableModels = [];
try {
const response = await this.ollama.list();
running = true;
availableModels = response.models.map((m) => m.name);
const recommendedModels = [
"qwen2.5-coder:7b",
// PRIMARY: Best 7B code model
"mistral:7b",
// ANALYSIS: API integration, research, planning
"deepseek-coder:1.3b",
// FALLBACK: Code-specific, small
"qwen2.5:3b",
// ROUTER: Fast for routing
"nomic-embed-text"
// EMBEDDINGS: For semantic search
];
for (const model of recommendedModels) {
if (!availableModels.includes(model)) {
errors.push(`Model ${model} not found. Run: ollama pull ${model}`);
}
}
} catch (error) {
errors.push(`Ollama not running. Please start Ollama.`);
}
return {
running,
models: availableModels,
errors
};
}
/**
* Get model info
*/
getModelInfo(modelName) {
return this.models.get(modelName);
}
/**
* List all configured models
*/
listModels() {
return Array.from(this.models.values());
}
/**
* Auto-start Ollama if not running (saves Augment credits!)
* Enhanced with better detection, configurable timeout, and exponential backoff
*/
async startOllama() {
console.error("\u{1F680} Auto-starting Ollama...");
const timeoutSeconds = parseInt(process.env.OLLAMA_STARTUP_TIMEOUT || "180", 10);
console.error(`\u23F1\uFE0F Using startup timeout: ${timeoutSeconds} seconds`);
const ollamaPath = process.env.OLLAMA_PATH || (process.platform === "win32" ? "C:\\Users\\chris\\AppData\\Local\\Programs\\Ollama\\ollama.exe" : "ollama");
try {
console.error("\u{1F50D} Checking if Ollama is already running...");
console.error(`\u{1F517} Base URL: ${this.baseUrl}`);
const isRunning = await pingOllama(5e3);
if (isRunning) {
console.error("\u2705 Ollama is already running!");
return;
}
console.error("\u274C Ollama not responding, attempting to start...");
console.error(`\u{1F680} Spawning Ollama process: ${ollamaPath}`);
this.ollamaProcess = spawn(ollamaPath, ["serve"], {
detached: true,
stdio: "ignore",
windowsHide: true
});
this.ollamaProcess.unref();
this.startedByUs = true;
console.error(`\u23F3 Waiting for Ollama to be ready (timeout: ${timeoutSeconds}s)...`);
const delays = [1e3, 2e3, 4e3, 8e3];
let totalWait = 0;
let attemptCount = 0;
while (totalWait < timeoutSeconds * 1e3) {
const delay = attemptCount < delays.length ? delays[attemptCount] : 1e3;
await new Promise((resolve3) => setTimeout(resolve3, delay));
totalWait += delay;
attemptCount++;
try {
const ready = await pingOllama(2e3);
if (ready) {
console.error(`\u2705 Ollama ready after ${totalWait}ms!`);
return;
}
} catch {
console.error(`\u23F3 Still waiting... (${Math.floor(totalWait / 1e3)}s / ${timeoutSeconds}s)`);
}
}
throw new Error(`Ollama started but not ready within ${timeoutSeconds} seconds. Try increasing OLLAMA_START_TIMEOUT.`);
} catch (error) {
if (error.code === "ENOENT") {
throw new Error(
`Ollama not found at: ${ollamaPath}
Please install Ollama from https://ollama.com or set OLLAMA_PATH environment variable.`
);
}
if (error.code === "EADDRINUSE" || error.message?.includes("address already in use")) {
throw new Error(
`Port 11434 is already in use. Another Ollama instance may be running.
Try: pkill ollama (Linux/Mac) or taskkill /F /IM ollama.exe (Windows)`
);
}
throw new Error(`Failed to auto-start Ollama: ${error.message}`);
}
}
/**
* Ensure Ollama is running (auto-start if needed)
* Enhanced with better health checking using pingOllama
*/
async ensureRunning() {
console.error(`[OllamaClient] Ensuring Ollama is running at ${this.baseUrl}...`);
try {
console.error("[OllamaClient] Checking Ollama health...");
const isRunning = await pingOllama(1e4);
if (isRunning) {
console.error("[OllamaClient] \u2705 Ollama is running and healthy!");
return;
}
console.error("[OllamaClient] \u274C Ollama not responding");
if (this.autoStart) {
console.error("[OllamaClient] Auto-start enabled, attempting to start Ollama...");
await this.startOllama();
const isNowRunning = await pingOllama(5e3);
if (!isNowRunning) {
throw new Error("Ollama started but still not responding to health checks");
}
console.error("[OllamaClient] \u2705 Ollama started successfully!");
} else {
throw new Error(
"Ollama is not running. Please start Ollama with: ollama serve\nOr enable auto-start by setting autoStart=true in constructor."
);
}
} catch (error) {
console.error(`[OllamaClient] Error in ensureRunning: ${error.message}`);
if (this.autoStart && !error.message?.includes("auto-start") && !error.message?.includes("started but still not responding")) {
console.error("[OllamaClient] Ping failed, trying auto-start as fallback...");
await this.startOllama();
} else {
throw error;
}
}
}
/**
* Cleanup spawned Ollama process on shutdown
*/
async cleanup() {
if (this.ollamaProcess && this.startedByUs) {
console.error("\u{1F9F9} Cleaning up spawned Ollama process...");
try {
this.ollamaProcess.kill();
console.error("\u2705 Ollama process terminated");
} catch (error) {
console.error(`\u26A0\uFE0F Failed to kill Ollama process: ${error.message}`);
}
}
}
};
}
});
// src/utils/prompt-builder.ts
var PromptBuilder;
var init_prompt_builder = __esm({
"src/utils/prompt-builder.ts"() {
"use strict";
PromptBuilder = class {
/**
* Build code generation prompt (alias for buildGenerationPrompt)
*/
buildCodePrompt(request) {
return this.buildGenerationPrompt(request);
}
/**
* Build code generation prompt
*/
buildGenerationPrompt(request) {
const { task, context, template } = request;
let prompt = `You are an expert software engineer. Generate COMPLETE, PRODUCTION-READY code.
Task: ${task}
Context: ${context}
CRITICAL: DO NOT INVENT OR HALLUCINATE APIs!
- If the task asks for simple logic (like adding numbers), write the logic directly
- DO NOT use AWS, external services, or imaginary APIs unless explicitly required
- DO NOT add comments like "for demonstration purposes" or "imaginary service"
- Write REAL, WORKING code that actually does what the task asks
STRICT REQUIREMENTS (MANDATORY):
1. NO PLACEHOLDERS - No TODO, FIXME, PLACEHOLDER, STUB, TBD, MOCK, or "implement this later"
2. SIMPLE LOGIC FIRST - For simple tasks, write simple code. Don't overcomplicate with external APIs
3. REAL APIs ONLY - If you must use external APIs, only use real, documented ones
4. COMPLETE IMPLEMENTATIONS - Every function must have full logic, no empty bodies
5. PROPER IMPORTS - Use ES6 imports (import X from 'Y'), never require()
6. SYNTACTICALLY CORRECT - Balanced braces, brackets, parentheses
7. ERROR HANDLING - Include try/catch for async operations and external calls
8. TYPE SAFETY - Add TypeScript types for all parameters, returns, and variables
9. PRODUCTION READY - Code must compile and run without modifications
FORBIDDEN PATTERNS (WILL FAIL VALIDATION):
- throw new Error('Not implemented')
- // TODO: implement this
- // FIXME: ...
- function foo() { } // empty body
- const x = PLACEHOLDER;
- // ... (ellipsis indicating incomplete code)
- new AWS.RestifyClient() // This doesn't exist!
- "for demonstration purposes" // This means fake code!
- "imaginary service" // This means fake code!
EXAMPLE - CORRECT (simple task, simple code):
Task: "Create a function that adds two numbers"
\`\`\`typescript
export function addNumbers(a: number, b: number): number {
return a + b;
}
\`\`\`
EXAMPLE - WRONG (overcomplicated with fake APIs):
\`\`\`typescript
import { sum } from '@aws-sdk/client-cognito-identity'; // \u274C WRONG! Not needed!
export async function addNumbers(a: number, b: number): Promise<number> {
const result = await sum(...); // \u274C WRONG! Fake API!
return result;
}
\`\`\`
CODE QUALITY STANDARDS:
- Write clean, maintainable code following DRY principles
- Follow best practices for the framework/language specified in context
- Add JSDoc comments for public functions/classes only
- Use meaningful variable and function names
- Keep functions focused and single-purpose
`;
if (template && template !== "none") {
prompt += `Template Guide: Use the ${template} template pattern as a structural reference.
`;
}
prompt += `Generate the COMPLETE code now. Wrap code in triple backticks with the file path as a comment:
\`\`\`typescript // path/to/file.ts
// Your complete, production-ready code here
\`\`\`
If multiple files are needed, provide each in separate code blocks with their paths.
REMEMBER: Code will be validated. Any placeholders, fake APIs, or incomplete implementations will be rejected.`;
return prompt;
}
/**
* Build code analysis prompt
*/
buildAnalysisPrompt(request) {
const { code, files, question } = request;
let prompt = `You are an expert code reviewer. Analyze this code for issues.
Question: ${question}
`;
if (code) {
prompt += `Code to analyze:
\`\`\`
${code}
\`\`\`
`;
}
if (files && files.length > 0) {
prompt += `Files to analyze:
${files.map((f, i) => `${i + 1}. ${f}`).join("\n")}
`;
}
prompt += `Analyze for:
- Performance issues
- Security vulnerabilities
- Code smells
- Best practice violations
- Potential bugs
- Maintainability concerns
Provide detailed analysis with:
1. Summary of findings
2. Specific issues found (use format: [TYPE] (SEVERITY) location: description)
3. Recommendations for improvement
Be specific and actionable.`;
return prompt;
}
/**
* Build refactoring prompt
*/
buildRefactorPrompt(request) {
const { code, instructions, style } = request;
let prompt = `You are an expert software engineer. Refactor this code to PRODUCTION QUALITY.
Code to refactor:
\`\`\`
${code}
\`\`\`
Refactoring Instructions: ${instructions}
`;
if (style) {
prompt += `Code Style: ${style}
`;
}
prompt += `STRICT REQUIREMENTS (MANDATORY):
1. MAINTAIN FUNCTIONALITY - Same behavior, improved structure
2. NO PLACEHOLDERS - No TODO, FIXME, STUB, or incomplete implementations
3. COMPLETE CODE - All functions must have full implementations
4. REAL APIs ONLY - Use only documented, real APIs
5. IMPROVE QUALITY - Better naming, structure, and maintainability
6. PRESERVE TESTS - Keep existing test compatibility
7. ${style || "modern"} CODING PRACTICES - Follow industry standards
FORBIDDEN IN REFACTORED CODE:
- Empty function bodies
- Placeholder comments (TODO, FIXME, etc.)
- Fake or non-existent APIs
- throw new Error('Not implemented')
- Incomplete logic or ellipsis comments
Provide:
1. COMPLETE refactored code (in triple backticks)
2. List of changes made (bullet points starting with -)
Format:
\`\`\`typescript
// Complete refactored code here
\`\`\`
Changes made:
- Extracted UserProfile component with full implementation
- Renamed handleClick to handleUserClick for clarity
- Applied dependency injection pattern with proper types
REMEMBER: Refactored code will be validated. Any placeholders or incomplete code will be rejected.`;
return prompt;
}
/**
* Build test generation prompt
*/
buildTestPrompt(request) {
const { code, framework, coverage } = request;
let prompt = `You are an expert test engineer. Generate COMPLETE, RUNNABLE tests.
Code to test:
\`\`\`
${code}
\`\`\`
Test Framework: ${framework}
Coverage Level: ${coverage || "comprehensive"}
STRICT REQUIREMENTS (MANDATORY):
1. COMPLETE TESTS - All test cases must be fully implemented
2. NO PLACEHOLDERS - No TODO, FIXME, or "add test here" comments
3. REAL ASSERTIONS - Use actual ${framework} assertion methods (no fake APIs)
4. RUNNABLE - Tests must execute without modifications
5. PROPER SETUP - Include all necessary imports, mocks, and setup/teardown
6. DESCRIPTIVE NAMES - Use clear, specific test names (not "test1", "test2")
7. FOLLOW ${framework} BEST PRACTICES - Use framework conventions
TEST COVERAGE REQUIREMENTS:
- Test ALL public methods/functions
- Include happy path scenarios
- Include error/exception handling
- Test edge cases (null, undefined, empty, boundary values)
- Test async operations with proper await/promises
- Include setup/teardown if needed
`;
if (coverage === "edge-cases") {
prompt += `EXTRA FOCUS ON EDGE CASES:
- Null and undefined inputs
- Empty arrays, objects, and strings
- Boundary values (min, max, zero, negative)
- Invalid input types
- Error conditions and exceptions
- Race conditions for async code
- Timeout scenarios
`;
}
prompt += `FORBIDDEN IN TESTS:
- Empty test bodies: it('should work', () => { })
- Placeholder comments: // TODO: add test
- Fake assertion methods
- Incomplete async handling
Generate the COMPLETE test file now. Wrap in triple backticks:
\`\`\`typescript
// Complete, runnable test file here
\`\`\`
REMEMBER: Tests will be validated and must be executable without modifications.`;
return prompt;
}
/**
* Build documentation prompt
*/
buildDocPrompt(request) {
const { code, style, detail } = request;
let prompt = `You are a technical writer. Generate clear documentation for this code.
Code to document:
\`\`\`
${code}
\`\`\`
Documentation style: ${style || "tsdoc"}
Detail level: ${detail || "detailed"}
`;
if (style === "jsdoc" || style === "tsdoc") {
prompt += `Requirements:
- Document all public functions/methods
- Include @param tags with types
- Include @returns tag
- Include @throws for errors
- Add @example for complex functions
- Keep descriptions clear and concise
`;
} else if (style === "markdown" || style === "readme") {
prompt += `Requirements:
- Create a README-style document
- Include overview/description
- List all functions/methods
- Provide usage examples
- Include installation/setup if applicable
- Add troubleshooting section
`;
}
prompt += `Generate the documentation now.`;
return prompt;
}
};
}
});
// src/pipeline/types.ts
function calculateWeightedScore(scores, weights = DEFAULT_PIPELINE_CONFIG.weights) {
const w = weights;
return scores.compilation * w.compilation + scores.tests_functional * w.tests_functional + scores.tests_edge * w.tests_edge + scores.types * w.types + scores.style * w.style + scores.security * w.security + (scores.conventions || 0) * (w.conventions || 0);
}
function meetsAcceptanceCriteria(verdict, config = DEFAULT_PIPELINE_CONFIG) {
const score = calculateWeightedScore(verdict.scores, config.weights);
if (score < (config.acceptThreshold ?? DEFAULT_PIPELINE_CONFIG.acceptThreshold)) {
return false;
}
if (verdict.scores.compilation === 0) return false;
if (verdict.scores.security === 0) return false;
return true;
}
var DEFAULT_PIPELINE_CONFIG;
var init_types = __esm({
"src/pipeline/types.ts"() {
"use strict";
DEFAULT_PIPELINE_CONFIG = {
maxAttempts: 5,
acceptThreshold: 0.9,
weights: {
compilation: 0.15,
tests_functional: 0.25,
tests_edge: 0.15,
types: 0.1,
security: 0.1,
style: 0.05,
conventions: 0.2
},
allowedLibraries: [
// Node.js built-ins
"fs",
"path",
"util",
"crypto",
"stream",
"events",
"buffer",
// Common safe libraries
"lodash",
"axios",
"express",
"react",
"vue",
"next",
// Testing
"jest",
"@jest/globals",
"vitest",
"mocha",
"chai",
// TypeScript
"typescript",
"@types/*"
],
minCoverage: 80,
testTimeout: 5e3,
globalTimeout: 3e4,
memoryLimit: 512,
spec: "",
provider: "ollama",
model: "qwen2.5-coder:7b"
};
}
});
// src/utils/repo-tools.ts
import * as fs from "fs";
import * as path from "path";
import { execSync } from "child_process";
async function runRepoTools(root, files) {
const result = {
passed: true,
lintErrors: [],
typeErrors: [],
boundaryErrors: [],
customRuleErrors: [],
allErrors: []
};
const lintResult = await runESLint(root, files);
result.lintErrors = lintResult.errors;
if (lintResult.errors.length > 0) result.passed = false;
const typeResult = await runTypeScript(root, files);
result.typeErrors = typeResult.errors;
if (typeResult.errors.length > 0) result.passed = false;
const boundaryResult = await checkBoundaries(root, files);
result.boundaryErrors = boundaryResult.errors;
if (boundaryResult.errors.length > 0) result.passed = false;
const customResult = await runCustomRules(root, files);
result.customRuleErrors = customResult.errors;
if (customResult.errors.length > 0) result.passed = false;
result.allErrors = [
...result.lintErrors,
...result.typeErrors,
...result.boundaryErrors,
...result.customRuleErrors
];
return result;
}
async function runESLint(root, files) {
const errors = [];
const eslintPath = path.join(root, "node_modules", ".bin", "eslint");
if (!fs.existsSync(eslintPath) && !fs.existsSync(eslintPath + ".cmd")) {
return { errors: [] };
}
try {
const fileArgs = files.map((f) => `"${f}"`).join(" ");
const cmd = process.platform === "win32" ? `"${eslintPath}.cmd" --format json ${fileArgs}` : `"${eslintPath}" --format json ${fileArgs}`;
const output = execSync(cmd, {
cwd: root,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
});
const results = JSON.parse(output);
for (const result of results) {
for (const message of result.messages) {
errors.push(`${result.filePath}:${message.line}:${message.column} - ${message.message} (${message.ruleId})`);
}
}
} catch (error) {
if (error.stdout) {
try {
const results = JSON.parse(error.stdout);
for (const result of results) {
for (const message of result.messages) {
errors.push(`${result.filePath}:${message.line}:${message.column} - ${message.message} (${message.ruleId})`);
}
}
} catch (parseError) {
errors.push(`ESLint error: ${error.message}`);
}
}
}
return { errors: errors.slice(0, 10) };
}
async function runTypeScript(root, files) {
const errors = [];
const tscPath = path.join(root, "node_modules", ".bin", "tsc");
if (!fs.existsSync(tscPath) && !fs.existsSync(tscPath + ".cmd")) {
return { errors: [] };
}
try {
const cmd = process.platform === "win32" ? `"${tscPath}.cmd" --noEmit --pretty false` : `"${tscPath}" --noEmit --pretty false`;
execSync(cmd, {
cwd: root,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
});
} catch (error) {
if (error.stderr || error.stdout) {
const output = error.stderr || error.stdout;
const lines = output.split("\n");
for (const line of lines) {
const match = line.match(/^(.+?)\((\d+),(\d+)\):\s*error\s+TS\d+:\s*(.+)$/);
if (match) {
const [, file, line2, col, message] = match;
errors.push(`${file}:${line2}:${col} - ${message}`);
}
}
}
}
return { errors: errors.slice(0, 10) };
}
async function checkBoundaries(root, files) {
const errors = [];
const eslintConfigPath = findESLintConfig(root);
if (!eslintConfigPath) {
return { errors: [] };
}
try {
const configContent = fs.readFileSync(eslintConfigPath, "utf-8");
if (!configContent.includes("boundaries")) {
return { errors: [] };
}
const boundaryRules = parseBoundaryRules(configContent);
for (const file of files) {
const violations = checkFileBoundaries(root, file, boundaryRules);
errors.push(...violations);
}
} catch (error) {
}
return { errors: errors.slice(0, 10) };
}
function findESLintConfig(root) {
const configFiles = [
".eslintrc.js",
".eslintrc.cjs",
".eslintrc.json",
".eslintrc",
"eslint.config.js"
];
for (const file of configFiles) {
const fullPath = path.join(root, file);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
return null;
}
function parseBoundaryRules(configContent) {
const rules = [];
const ruleMatches = configContent.matchAll(/"from":\s*\[([^\]]+)\][^}]*"allow":\s*\[([^\]]+)\]/g);
for (const match of ruleMatches) {
const from = match[1].split(",").map((s) => s.trim().replace(/['"]/g, ""));
const allow = match[2].split(",").map((s) => s.trim().replace(/['"]/g, ""));
rules.push({ from, allow });
}
return rules;
}
function checkFileBoundaries(root, file, rules) {
const errors = [];
try {
const content = fs.readFileSync(path.join(root, file), "utf-8");
const imports = extractImports(content);
const fileLayer = determineLayer(file);
if (!fileLayer) return errors;
const rule = rules.find((r) => r.from.includes(fileLayer));
if (!rule) return errors;
for (const imp of imports) {
const importLayer = determineLayer(imp);
if (!importLayer) continue;
if (!rule.allow.includes(importLayer)) {
errors.push(`${file}: Cannot import from ${importLayer} (${imp}). ${fileLayer} can only import from: ${rule.allow.join(", ")}`);
}
}
} catch (error) {
}
return errors;
}
function extractImports(content) {
const imports = [];
const importMatches = content.matchAll(/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g);
for (const match of importMatches) {
imports.push(match[1]);
}
const requireMatches = content.matchAll(/require\(['"]([^'"]+)['"]\)/g);
for (const match of requireMatches) {
imports.push(match[1]);
}
return imports;
}
function determineLayer(filePath) {
if (filePath.includes("/features/") || filePath.includes("\\features\\")) return "feature";
if (filePath.includes("/domain/") || filePath.includes("\\domain\\")) return "domain";
if (filePath.includes("/infra/") || filePath.includes("\\infra\\")) return "infra";
if (filePath.includes("/utils/") || filePath.includes("\\utils\\")) return "utils";
if (filePath.includes("/lib/") || filePath.includes("\\lib\\")) return "lib";
if (filePath.includes("/core/") || filePath.includes("\\core\\")) return "core";
return null;
}
async function runCustomRules(root, files) {
const errors = [];
for (const file of files) {
try {
const content = fs.readFileSync(path.join(root, file), "utf-8");
const snakeCaseMatches = content.matchAll(/\b(const|let|var)\s+([a-z]+_[a-z_]+)\s*=/g);
for (const match of snakeCaseMatches) {
const varName = match[2];
if (!/^[A-Z_]+$/.test(varName)) {
errors.push(`${file}: Use camelCase instead of snake_case for variable '${varName}'`);
}
}
const anyTypeMatches = content.matchAll(/export\s+(function|const|class|interface|type)\s+[^:]+:\s*any/g);
for (const match of anyTypeMatches) {
errors.push(`${file}: Do not use 'any' type in public exports`);
}
if (!file.includes(".test.") && !file.includes(".spec.")) {
const consoleMatches = content.matchAll(/console\.log\(/g);
for (const match of consoleMatches) {
errors.push(`${file}: Remove console.log() from production code`);
}
}
} catch (error) {
}
}
return { errors: errors.slice(0, 10) };
}
var init_repo_tools = __esm({
"src/utils/repo-tools.ts"() {
"use strict";
}
});
// src/utils/edit-constraints.ts
import * as path2 from "path";
async function checkEditConstraints(root, files, constraints) {
const violations = [];
for (const file of files) {
const isAllowed = constraints.allowedPaths.some(
(allowed) => file.path.startsWith(allowed) || matchesGlob(file.path, allowed)
);
if (!isAllowed) {
const isReadOnly = constraints.readOnlyPaths.some(
(readonly) => file.path.startsWith(readonly) || matchesGlob(file.path, readonly)
);
if (isReadOnly) {
violations.push({
file: file.path,
violation: "File is read-only and cannot be modified",
severity: "error"
});
continue;
}
}
if (!constraints.allowPublicRenames) {
const renameViolations = await checkPublicRenames(root, file);
violations.push(...renameViolations);
}
}
return violations;
}
function matchesGlob(filePath, pattern) {
const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\./g, "\\.");
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(filePath);
}
async function checkPublicRenames(root, file) {
const violations = [];
try {
const newSymbols = extractSymbolsFromContent(file.content, file.path);
const fs23 = await import("fs");
const fullPath = path2.join(root, file.path);
if (!fs23.existsSync(fullPath)) {
return violations;
}
const originalContent = fs23.readFileSync(fullPath, "utf-8");
const originalSymbols = extractSymbolsFromContent(originalContent, file.path);
const originalPublic = originalSymbols.filter((s) => s.isPublic);
const newPublic = newSymbols.filter((s) => s.isPublic);
const originalNames = new Set(originalPublic.map((s) => s.name));
const newNames = new Set(newPublic.map((s) => s.name));
for (const name of originalNames) {
if (!newNames.has(name)) {
violations.push({
file: file.path,
violation: `Public symbol '${name}' was removed or renamed. This is a breaking change.`,
severity: "error"
});
}
}
} catch (error) {
}
return violations;
}
function extractSymbolsFromContent(content, filePath) {
const symbols = [];
const lines = content.split("\n");
const patterns = [
// export function/const/class/interface/type
{ regex: /export\s+(async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, type: "function", isPublic: true },
{ regex: /export\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, type: "const", isPublic: true },
{ regex: /export\s+class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, type: "class", isPublic: true },
{ regex: /export\s+interface\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, type: "interface", isPublic: true },
{ regex: /export\s+type\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, type: "type", isPublic: true },
{ regex: /export\s+enum\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, type: "enum", isPublic: true }
];
for (const pattern of patterns) {
let match;
while ((match = pattern.regex.exec(content)) !== null) {
const name = match[2] || match[1];
if (!name) continue;
const lineNumber = content.substring(0, match.index).split("\n").length;
symbols.push({
name,
type: pattern.type,
file: filePath,
line: lineNumber,
isPublic: pattern.isPublic
});
}
}
return symbols;
}
function inferAllowedPaths(spec, root) {
const allowedPaths = [];
const pathMatches = spec.matchAll(/(?:^|\s)([a-zA-Z0-9_\-/]+\.[a-zA-Z]+)/g);
for (const match of pathMatches) {
allowedPaths.push(match[1]);
}
const dirMatches = spec.matchAll(/(?:^|\s)([a-zA-Z0-9_\-/]+\/)/g);
for (const match of dirMatches) {
allowedPaths.push(match[1] + "**/*");
}
if (allowedPaths.length === 0) {
allowedPaths.push("src/**/*");
}
return allowedPaths;
}
function getDefaultReadOnlyPaths() {
return [
"node_modules/**/*",
"dist/**/*",
"build/**/*",
".next/**/*",
"coverage/**/*",
"__generated__/**/*",
"src/gen/**/*",
"src/generated/**/*",
"prisma/client/**/*"
];
}
function calculateDiffSize(originalContent, newContent) {
const originalLines = originalContent.split("\n");
const newLines = newContent.split("\n");
let changes = 0;
const maxLines = Math.max(originalLines.length, newLines.length);
for (let i = 0; i < maxLines; i++) {
const originalLine = originalLines[i] || "";
const newLine = newLines[i] || "";
if (originalLine !== newLine) {
changes++;
}
}
return changes;
}
function isMinimalDiff(originalContent, newContent) {
const diffSize = calculateDiffSize(originalContent, newContent);
const totalLines = originalContent.split("\n").length;
if (totalLines === 0) return true;
const changePercentage = diffSize / totalLines * 100;
return changePercentage < 50;
}
async function enforceMinimalDiffs(root, files) {
const violations = [];
const fs23 = await import("fs");
for (const file of files) {
const fullPath = path2.join(root, file.path);
if (!fs23.existsSync(fullPath)) {
continue;
}
const originalContent = fs23.readFileSync(fullPath, "utf-8");
if (!isMinimalDiff(originalContent, file.content)) {
const diffSize = calculateDiffSize(originalContent, file.content);
const totalLines = originalContent.split("\n").length;
const changePercentage = (diffSize / totalLines * 100).toFixed(1);
violations.push({
file: file.path,
viol