Sign In

@eldrex/core

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@eldrex/core - npm Package Compare versions

Comparing version
1.8.0
to
1.8.1
+61
dist/chunk-VTMFJSCA.js
import {a}from'./chunk-W3CMXIN5.js';import*as h from'fs/promises';import*as A from'path';import {execFile}from'child_process';import*as I from'crypto';var F=class{static getLogPath(){return process.env.DEVDIFF_AUDIT_PATH||A.resolve(process.cwd(),".devdiff/security-audit.enc")}static getLegacyPath(){return process.env.DEVDIFF_LEGACY_AUDIT_PATH||A.resolve(process.cwd(),".devdiff/security-audit.json")}static getKey(){let e=process.env.DEVDIFF_AUDIT_KEY;return e?I.createHash("sha256").update(e).digest():null}static async readLegacyLogs(){let e=this.getLegacyPath();try{let t=await h.readFile(e,"utf-8");return t.trim()?JSON.parse(t):[]}catch{return []}}static async readEncryptedLogs(e){try{let s=(await h.readFile(this.getLogPath(),"utf-8")).split(`
`).filter(Boolean),i=[];for(let o of s)try{let r=this.decrypt(o.trim(),e);i.push(JSON.parse(r));}catch{}return i}catch{return []}}static encrypt(e,t){let s=I.randomBytes(12),i=I.createCipheriv("aes-256-gcm",t,s),o=Buffer.concat([i.update(e,"utf8"),i.final()]),r=i.getAuthTag();return Buffer.concat([s,r,o]).toString("base64")}static decrypt(e,t){let s=Buffer.from(e,"base64"),i=s.slice(0,12),o=s.slice(12,28),r=s.slice(28),n=I.createDecipheriv("aes-256-gcm",t,i);return n.setAuthTag(o),n.update(r)+n.final("utf8")}static async log(e){try{let t=this.getLogPath();await h.mkdir(A.dirname(t),{recursive:!0});let s=this.getKey(),i={...e,timestamp:Date.now()},o=JSON.stringify(i);if(s)try{let c=this.encrypt(o,s)+`
`;await h.appendFile(t,c,"utf-8");return}catch(c){console.warn("Encrypted audit log failed, falling back to plaintext:",c);}let r=this.getLegacyPath(),n=await this.readLegacyLogs();n.push(i),await h.writeFile(r,JSON.stringify(n,null,2),"utf-8");}catch(t){console.error("Failed to write to security audit trail:",t);}}static async getLogs(){let e=this.getKey();if(e){let t=await this.readEncryptedLogs(e);if(t.length>0)return t}return this.readLegacyLogs()}static async verifyIntegrity(){let e=this.getKey();if(!e)return {valid:true,total:0,corrupted:0};try{let s=(await h.readFile(this.getLogPath(),"utf-8")).split(`
`).filter(Boolean),i=0;for(let o of s)try{this.decrypt(o.trim(),e);}catch{i++;}return {valid:i===0,total:s.length,corrupted:i}}catch{return {valid:true,total:0,corrupted:0}}}};var L=class extends Error{constructor(e){super(e),this.name="ShellAccessDeniedError";}};function ne(a,e,t){return new Promise((s,i)=>{execFile(a,e,{timeout:t.timeout||3e4,cwd:t.cwd},(o,r,n)=>{o?i(o):s({stdout:r,stderr:n});});})}var K=class{static ALLOWED_COMMANDS=["git","ollama","which","node"];static BLOCKED_PATTERNS=[/rm\s+-rf/,/sudo/,/curl.*\|.*sh/,/eval/,/\$\(/,/`/,/&&/,/\|\|/,/;/];static async exec(e,t,s){let i=e.split(" ")[0];if(!this.ALLOWED_COMMANDS.includes(i))throw new L(`Command "${i}" is not in the allowed list. Allowed: ${this.ALLOWED_COMMANDS.join(", ")}.`);let o=`${e} ${t.join(" ")}`.trim();for(let n of this.BLOCKED_PATTERNS)if(n.test(o))throw new L(`Command contains blocked pattern: ${n}. This is a security precaution.`);return await F.log({type:"shell-access",command:i,args:t,timestamp:Date.now(),caller:new Error().stack?.split(`
`)[2]?.trim()}),(await ne(e,t,{timeout:s?.timeout||3e4,cwd:s?.cwd})).stdout}static disable(){console.log("\u26A0\uFE0F Shell access disabled. Git analysis will use isomorphic-git (pure JS).");}},de={disableShellAccess:false,allowedShellCommands:["git","ollama","which"],shellTimeout:3e4,auditShellAccess:true};var v=`You are DevDiff, an AI that analyzes git diffs and writes clear, human-readable changelogs.
Your task is to review the provided git diff, explain what changed and why (inferring the developer's intent), and assess the impact.
CRITICAL ACCURACY RULES:
1. If a PROJECT KNOWLEDGE BASE section is provided, use it to understand the project's purpose, architecture, and naming \u2014 but base your explanation ONLY on what is actually present in the diff.
2. Only mention file paths, function names, class names, or identifiers that explicitly appear in the diff. Do NOT invent or guess identifiers.
3. If you cannot determine the intent of a change, say "intent unclear from diff" rather than fabricating a reason.
4. Do NOT reference modules, files, or concepts from the project context unless they are directly touched by the diff.
5. File paths in the "files" array must exactly match paths shown in the diff headers (e.g., "diff --git a/src/foo.ts").
You must respond ONLY with a valid JSON object matching this schema:
{
"summary": "A concise, high-level explanation of the changes and why they were made in plain English.",
"impact": "none" | "minor" | "major" | "breaking",
"breaking": false,
"files": [
{
"path": "path/to/file.ts",
"explanation": "Specific description of changes in this file."
}
],
"relatedIssues": []
}
Do not include any markdown framing (like \`\`\`json ... \`\`\`) in your direct response, only return raw JSON text. If you must use markdown backticks, ensure the JSON is still valid and parseable.`;function P(a){let e=a.trim();e.startsWith("```")&&(e=e.replace(/^```json\s*/i,"").replace(/```$/,"").trim());try{let t=JSON.parse(e);return {summary:t.summary||"No summary generated.",impact:["none","minor","major","breaking"].includes(t.impact)?t.impact:"minor",breaking:!!t.breaking,files:Array.isArray(t.files)?t.files:[],relatedIssues:Array.isArray(t.relatedIssues)?t.relatedIssues:[]}}catch{return console.error("Failed to parse AI JSON response:",e),{summary:e.substring(0,500),impact:"minor",breaking:false,files:[],relatedIssues:[]}}}var j=class extends Error{platform;originalError;constructor(e,t){super(e),this.name="OllamaNotAvailableError",this.platform=t.platform,this.originalError=t.error;}},Y=class{static BASE_TIMEOUT_MS=15e3;static PER_FILE_MS=2e3;static PER_1K_TOKENS_MS=5e3;static MAX_TIMEOUT_MS=3e5;static MIN_TIMEOUT_MS=1e4;static calculate(e){let t=this.BASE_TIMEOUT_MS;t+=e.fileCount*this.PER_FILE_MS;let s=e.estimatedTokens/1e3;t+=s*this.PER_1K_TOKENS_MS;let i=parseFloat(e.modelSize)||3;return i<=3?t*=1.5:i<=7&&(t*=1.2),e.historicalAvgMs&&(t=Math.max(t,e.historicalAvgMs*1.5)),t=Math.max(this.MIN_TIMEOUT_MS,Math.min(this.MAX_TIMEOUT_MS,t)),Math.round(t)}static calculateForFallback(e){let t=this.calculate(e),s=1+e.attemptNumber*.5;return Math.round(t*s)}},$=class{name="ollama";host;timeoutMs;performanceHistory=new Map;constructor(e="http://localhost:11434",t){this.host=e,this.timeoutMs=t;}async generateExplanation(e,t,s,i){let o=`${this.host}/api/generate`,r=`System Instructions:
${s||v}
Git Diff:
${e}`,n=Date.now(),c=(e.match(/^diff --git /gm)||[]).length||1,d=this.estimateTokens(e),f=this.detectModelSize(t),N=this.getHistoricalAverage(t),y=this.timeoutMs;y===void 0&&(y=i&&i>1?Y.calculateForFallback({fileCount:c,estimatedTokens:d,modelSize:f,attemptNumber:i-1}):Y.calculate({fileCount:c,estimatedTokens:d,modelSize:f,historicalAvgMs:N})),console.log(`\u23F1\uFE0F Dynamic timeout: ${(y/1e3).toFixed(0)}s for ${c} files (~${d} tokens)`);let C=new AbortController,E=setTimeout(()=>C.abort(),y);try{let u=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:t,prompt:r,stream:!1,options:{temperature:.2}}),signal:C.signal});if(clearTimeout(E),!u.ok)throw new Error(`Ollama returned status ${u.status}: ${await u.text()}`);let l=await u.json(),S=Date.now()-n;return this.recordPerformance(t,S),console.log(`\u2705 Ollama response: ${S}ms (timeout was ${y}ms)`),P(l.response)}catch(u){clearTimeout(E);let l=Date.now()-n;throw u.name==="AbortError"||u.name==="TimeoutError"||u.message.includes("timed out")?(console.log(`\u23F1\uFE0F Ollama timed out after ${l}ms (timeout was ${y}ms)`),console.log(` Tip: ${c} files may be too many for model ${t}`),console.log(" Consider: devdiff generate --depth minimal (for faster results)"),console.log(" Or split into smaller commits"),new j(`Ollama request timed out after ${y}ms`,{platform:process.platform,error:u})):(console.log(""),console.log("\u274C Cannot connect to Ollama"),console.log(""),console.log(" DevDiff uses Ollama for local AI. It needs to be running."),console.log(""),process.platform==="win32"?(console.log(" Windows:"),console.log(" 1. Download Ollama from: https://ollama.com/download/windows"),console.log(" 2. Install and run the Ollama app"),console.log(" 3. Open PowerShell and run: ollama pull llama3.2:3b"),console.log(" 4. Verify it works: ollama list"),console.log(" 5. Try again: devdiff generate")):process.platform==="darwin"?(console.log(" macOS:"),console.log(" 1. brew install ollama"),console.log(" 2. ollama serve"),console.log(" 3. ollama pull llama3.2:3b")):(console.log(" Linux:"),console.log(" 1. curl -fsSL https://ollama.com/install.sh | sh"),console.log(" 2. ollama serve"),console.log(" 3. ollama pull llama3.2:3b")),console.log(""),console.log(" \u{1F4A1} No Ollama? You can use:"),console.log(" \u2022 Dry run mode: devdiff generate --dry-run"),console.log(" \u2022 Cloud AI: Set OPENAI_API_KEY in .env"),console.log(" \u2022 WebGPU: Open dashboard at http://localhost:3737"),console.log(""),console.log(" Error details:",u.message),console.log(""),new j("Ollama is not running. Install from https://ollama.com",{platform:process.platform,error:u}))}}estimateTokens(e){return Math.ceil(e.length/3.5)}detectModelSize(e){let t=e.match(/(\d+\.?\d*)b/i);return t?t[1]+"b":"3b"}getHistoricalAverage(e){let t=this.performanceHistory.get(e);if(!(!t||t.length===0))return t.reduce((s,i)=>s+i,0)/t.length}recordPerformance(e,t){let s=this.performanceHistory.get(e)||[];s.push(t),s.length>20&&s.shift(),this.performanceHistory.set(e,s);}};var M=class{name="openai";apiKey;constructor(e){this.apiKey=e||process.env.OPENAI_API_KEY;}async generateExplanation(e,t,s){let i=this.apiKey;if(!i)throw new Error("OpenAI API Key is not configured.");let o="https://api.openai.com/v1/chat/completions";try{let r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({model:t,messages:[{role:"system",content:s||v},{role:"user",content:`Analyze this diff:
${e}`}],temperature:.2,response_format:{type:"json_object"}})});if(!r.ok)throw new Error(`OpenAI API returned status ${r.status}: ${await r.text()}`);let c=(await r.json()).choices[0]?.message?.content||"";return P(c)}catch(r){throw console.error("OpenAI execution error:",r),r}}};var D=class{name="gemini";apiKey;constructor(e){this.apiKey=e||process.env.GEMINI_API_KEY;}async generateExplanation(e,t,s){let i=this.apiKey;if(!i)throw new Error("Gemini API Key is not configured.");let r=`https://generativelanguage.googleapis.com/v1beta/${t.includes("/")?t:`models/${t}`}:generateContent?key=${i}`;try{let n=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contents:[{parts:[{text:`Analyze this diff:
${e}`}]}],systemInstruction:{parts:[{text:s||v}]},generationConfig:{responseMimeType:"application/json",temperature:.2}})});if(!n.ok)throw new Error(`Gemini API returned status ${n.status}: ${await n.text()}`);let d=(await n.json()).candidates?.[0]?.content?.parts?.[0]?.text||"";return P(d)}catch(n){throw console.error("Gemini execution error:",n),n}}};var T=class{name="anthropic";apiKey;constructor(e){this.apiKey=e||process.env.ANTHROPIC_API_KEY;}async generateExplanation(e,t,s){let i=this.apiKey;if(!i)throw new Error("Anthropic API Key is not configured.");let o="https://api.anthropic.com/v1/messages";try{let r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":i,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:t,max_tokens:4e3,system:s||v,messages:[{role:"user",content:`Analyze this diff:
${e}`}],temperature:.2})});if(!r.ok)throw new Error(`Anthropic API returned status ${r.status}: ${await r.text()}`);let c=(await r.json()).content?.[0]?.text||"";return P(c)}catch(r){throw console.error("Anthropic execution error:",r),r}}};var U=class{cachePath;enabled;memoryCache={};constructor(e=true,t=".devdiff/cache.json"){this.enabled=e,this.cachePath=t;}hashDiff(e){return I.createHash("sha256").update(e).digest("hex")}async load(){if(this.enabled)try{let e=A.dirname(this.cachePath);await h.mkdir(e,{recursive:!0});let t=await h.readFile(this.cachePath,"utf-8");this.memoryCache=JSON.parse(t);}catch{this.memoryCache={};}}async get(e){if(!this.enabled)return null;let t=this.hashDiff(e);return Object.keys(this.memoryCache).length===0&&await this.load(),this.memoryCache[t]||null}async set(e,t){if(!this.enabled)return;let s=this.hashDiff(e);this.memoryCache[s]={...t,timestamp:new Date().toISOString()};try{let i=A.dirname(this.cachePath);await h.mkdir(i,{recursive:!0}),await h.writeFile(this.cachePath,JSON.stringify(this.memoryCache,null,2),"utf-8");}catch(i){console.warn("Failed to save explanation cache to disk:",i);}}};var B=class{session;checkpointDir;sessionPath;constructor(){this.session={startedAt:Date.now(),changes:[],checkpoints:[],failures:[],aiCalls:[]},this.checkpointDir=A.resolve(process.cwd(),".devdiff/checkpoints"),this.sessionPath=A.resolve(process.cwd(),".devdiff/vibe-session.json");}async loadSession(){try{let e=await h.readFile(this.sessionPath,"utf-8");this.session=JSON.parse(e);}catch{}}async saveSession(){await h.mkdir(A.dirname(this.sessionPath),{recursive:true}),await h.writeFile(this.sessionPath,JSON.stringify(this.session,null,2),"utf-8");}async deleteSession(){try{await h.rm(this.sessionPath,{force:!0});}catch{}}async preAICheckpoint(e){let t=await this.captureSnapshot(e.files),s={id:`ckpt-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,timestamp:Date.now(),type:"pre-ai-call",snapshot:t,metadata:{trigger:"ai-call",aiModel:e.model,prompt:e.prompt.slice(0,200)}};return await this.saveCheckpoint(s),this.session.checkpoints.push(s),console.log([`\u{1F4BE} Checkpoint: ${s.id}`,`\u{1F4C1} Files: ${e.files.length}`,`\u{1F916} Model: ${e.model}`,`\u{1F4DD} Prompt: ${e.prompt.slice(0,100)}...`,`\u23F1\uFE0F ${new Date(s.timestamp).toLocaleTimeString()}`].join(`
`)),s}async captureSnapshot(e){let t=[];for(let s of e)try{let i=A.resolve(process.cwd(),s),o=await h.readFile(i,"utf-8");t.push([s,o]);}catch{}return {files:t}}async saveCheckpoint(e){await h.mkdir(this.checkpointDir,{recursive:true});let t=A.join(this.checkpointDir,`${e.id}.json`);await h.writeFile(t,JSON.stringify(e,null,2),"utf-8");}async handleFailure(e){console.log(`
\u26A0\uFE0F AI CALL FAILED \u2014 Auto-recovery initiated`),console.log(`\u274C Error: ${e.error.message}`),console.log(`\u{1F916} Model: ${e.model}`),console.log(`\u{1F504} Attempt: ${e.attempt}/3`),this.session.failures.push({timestamp:Date.now(),error:e.error.message,model:e.model,attempt:e.attempt,checkpointId:e.checkpointId});let t={whatHappened:`AI call to ${e.model} failed on attempt ${e.attempt}`,why:e.error.message,whatWasSaved:`Checkpoint ${e.checkpointId} \u2014 ALL changes are safe`,whatHappensNext:e.attempt<3?"Retrying with fallback model...":"Restoring checkpoint. All changes preserved.",howToManualRecover:`npx devdiff recover --checkpoint ${e.checkpointId}`};if(console.log(`
\u{1F4CA} TRANSPARENCY REPORT:`),console.log(JSON.stringify(t,null,2)),e.attempt<3){let s=await this.getFallbackModel(e.model);return {status:"retrying",message:`Switching to ${s}`,nextModel:s,checkpointId:e.checkpointId,transparency:t}}return console.log(`
\u{1F6D1} Max retries exhausted. Restoring checkpoint...`),await this.restoreCheckpoint(e.checkpointId),{status:"failed-recovered",message:"All AI attempts failed. Changes restored from checkpoint. No work lost.",checkpointId:e.checkpointId,restoredFrom:e.checkpointId,transparency:t,manualActions:["Check internet connection","Verify Ollama is running: ollama ps","Try different model: npx devdiff config set ai.model ollama://llama3.1:8b",`Manual restore: npx devdiff recover --checkpoint ${e.checkpointId}`]}}async getFallbackModel(e){try{let{OllamaModelDiscovery:t}=await import('./ollama-discovery-QMQD7TQA.js'),s=await t.discoverModels();if(s.length>0){let i=e.replace("ollama://",""),o=s.filter(r=>r.name!==i);return o.length>0?`ollama://${o[0].name}`:e}}catch{}return e.includes("llama3.2:3b")?"ollama://llama3.1:8b":"ollama://llama3.2:3b"}async restoreCheckpoint(e){let t=this.session.checkpoints.find(s=>s.id===e);if(!t)try{let s=await h.readFile(A.join(this.checkpointDir,`${e}.json`),"utf-8");t=JSON.parse(s);}catch{}if(!t)throw new Error(`Checkpoint ${e} not found`);console.log(`
\u{1F504} Restoring checkpoint ${e}...`);for(let[s,i]of t.snapshot.files){let o=A.resolve(process.cwd(),s);await h.mkdir(A.dirname(o),{recursive:true}),await h.writeFile(o,i,"utf-8"),console.log(` \u2705 Restored: ${s}`);}console.log(`
\u2705 Restore complete. All files back to pre-AI state.`);}generateReport(){return {duration:Date.now()-this.session.startedAt,totalChanges:this.session.changes.length,checkpointsCreated:this.session.checkpoints.length,aiCallsSucceeded:this.session.aiCalls.filter(e=>e.success).length,aiCallsFailed:this.session.failures.length,dataLossEvents:0,guarantee:"\u2705 ZERO data loss. All changes preserved in local checkpoints.",recommendations:this.generateRecommendations()}}generateRecommendations(){return ["Keep llama3.1:8b as fallback"]}recordChange(e){this.session.changes.push({filename:e,timestamp:Date.now()});}recordAICall(e,t){this.session.aiCalls.push({model:e,success:t,timestamp:Date.now()});}};var x=class extends Error{code;exitCode;httpStatus;fix;docsUrl;context;constructor(e){super(e.message),this.name="DevDiffError",this.code=e.code,this.exitCode=e.exitCode||1,this.httpStatus=e.httpStatus||500,this.fix=e.fix||"Check the documentation for more information.",this.docsUrl=e.docsUrl||"https://devdiff.vercel.app/troubleshooting/common-fixes",this.context=e.context||{};}toCLIOutput(){let e=[];if(e.push(""),e.push(`\u274C ${this.message}`),e.push(""),e.push(` Error Code: ${this.code}`),e.push(` Fix: ${this.fix}`),e.push(` Docs: ${this.docsUrl}`),Object.keys(this.context).length>0){e.push(""),e.push(" Context:");for(let[t,s]of Object.entries(this.context))e.push(` \u2022 ${t}: ${s}`);}return e.push(""),e.join(`
`)}},W=class extends x{constructor(e,t){super({code:"GIT_001",message:e,exitCode:3,fix:`Ensure you are in a git repository with at least one commit.
Run: git init && git add . && git commit -m "initial commit"`,docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:t}),this.name="GitError";}},J=class extends x{constructor(e,t){let s={ollama:`Install Ollama: https://ollama.com/download
Then: ollama pull llama3.2:3b`,openai:`Set your API key: devdiff auth add openai
Or: export OPENAI_API_KEY=your-key`,anthropic:`Set your API key: devdiff auth add anthropic
Or: export ANTHROPIC_API_KEY=your-key`};super({code:"AI_001",message:`${e} is not available`,exitCode:4,fix:s[e]||`Check your ${e} configuration.`,docsUrl:`https://devdiff.vercel.app/ai-providers/${e}-setup`,context:t}),this.name="AINotAvailableError";}},V=class extends x{constructor(e,t){super({code:"CFG_001",message:e,exitCode:2,fix:`Run: devdiff config --validate (to check your configuration)
Run: devdiff config --reset (to reset to defaults)`,docsUrl:"https://devdiff.vercel.app/guide/configuration",context:t}),this.name="ConfigError";}},Q=class extends x{constructor(e,t){super({code:"NET_001",message:e,exitCode:5,fix:`Check your internet connection.
If using local AI, no internet is needed \u2014 check your AI provider.
Run: devdiff doctor (for full diagnostics)`,docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:t}),this.name="NetworkError";}},X=class extends x{constructor(e,t){super({code:"PERM_001",message:e,exitCode:6,fix:`Check file permissions for .devdiff/ directory.
On Linux/macOS: chmod -R 755 .devdiff/
On Windows: Run terminal as Administrator`,docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:t}),this.name="PermissionError";}},Z=class extends x{constructor(e,t,s){super({code:"RES_001",message:`${e} limit reached (${s} / ${t})`,exitCode:7,fix:e==="memory"?`Try using a smaller AI model: ollama pull llama3.2:1b
Or use a cloud provider: devdiff auth add openai`:"Free up disk space or change output directory.",docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:{resource:e,limit:t,current:s}}),this.name="ResourceLimitError";}},ee=class extends x{constructor(e){super({code:"TTY_001",message:e,exitCode:1,fix:`This command requires an interactive terminal.
For scripts, use environment variables or config files.`,docsUrl:"https://devdiff.vercel.app/guide/configuration"}),this.name="NonInteractiveError";}},te=class extends x{constructor(e,t){super({code:"MVP_001",message:e,exitCode:1,fix:`Run: devdiff mvp status (to check queue)
Run: devdiff mvp process (to process queued items)`,docsUrl:"https://devdiff.vercel.app/features/mvp-mode",context:t}),this.name="MVPStorageError";}};var se=class{config;cache;providers={};models={"ollama://llama3.2:3b":{name:"Llama 3.2 3B",provider:"ollama",maxContextTokens:128e3,maxOutputTokens:8192,costPer1kInput:0,costPer1kOutput:0,latencyMs:500,capabilities:{codeAnalysis:.6,securityAudit:.4,refactoringDetection:.5,multiFileAnalysis:.3,structuredOutput:.7}},"ollama://llama3.1:8b":{name:"Llama 3.1 8B",provider:"ollama",maxContextTokens:128e3,maxOutputTokens:8192,costPer1kInput:0,costPer1kOutput:0,latencyMs:1200,capabilities:{codeAnalysis:.8,securityAudit:.7,refactoringDetection:.75,multiFileAnalysis:.7,structuredOutput:.8}},"ollama://codellama:13b":{name:"CodeLlama 13B",provider:"ollama",maxContextTokens:16384,maxOutputTokens:4096,costPer1kInput:0,costPer1kOutput:0,latencyMs:3e3,capabilities:{codeAnalysis:.9,securityAudit:.85,refactoringDetection:.9,multiFileAnalysis:.6,structuredOutput:.75}},"openai://gpt-4o-mini":{name:"GPT-4o Mini",provider:"openai",maxContextTokens:128e3,maxOutputTokens:16384,costPer1kInput:15e-5,costPer1kOutput:6e-4,latencyMs:800,capabilities:{codeAnalysis:.85,securityAudit:.8,refactoringDetection:.85,multiFileAnalysis:.9,structuredOutput:.95}},"openai://gpt-4o":{name:"GPT-4o",provider:"openai",maxContextTokens:128e3,maxOutputTokens:16384,costPer1kInput:.0025,costPer1kOutput:.01,latencyMs:1500,capabilities:{codeAnalysis:.95,securityAudit:.95,refactoringDetection:.95,multiFileAnalysis:.95,structuredOutput:.98}}};discoveredModels=[];initialized=false;constructor(e){this.config=e,this.cache=new U(e.cache.enabled,e.cache.path),this.providers.ollama=new $,this.providers.openai=new M,this.providers.gemini=new D,this.providers.anthropic=new T;}async initialize(){if(!this.initialized){this.discoveredModels=await a.discoverModels();for(let e of this.discoveredModels){let t=`ollama://${e.name}`,s=["codellama","qwen","deepseek"].includes(e.family),i=parseInt(e.parameterSize)||3;this.models[t]={name:e.name,provider:"ollama",maxContextTokens:128e3,maxOutputTokens:8192,costPer1kInput:0,costPer1kOutput:0,latencyMs:i>13?3e3:i>7?1500:800,capabilities:{codeAnalysis:s?.9:.7,securityAudit:s?.8:.6,refactoringDetection:s?.95:.7,multiFileAnalysis:i>7?.8:.5,structuredOutput:.8}};}if(this.discoveredModels.length>0){console.log(`\u{1F4E6} Found ${this.discoveredModels.length} Ollama model(s):`);for(let e of this.discoveredModels)console.log(` \u2022 ${e.name} (${e.parameterSize}, ${e.family})`);}else console.log("\u{1F4E6} No Ollama models found."),console.log(" Install one: ollama pull llama3.2:3b"),console.log(" Or pull a code specialist: ollama pull qwen2.5-coder:7b");this.initialized=true;}}async getBestProvider(){if(await this.initialize(),this.discoveredModels.length>0){let e=a.selectBestModel(this.discoveredModels,this.config.preferredModel||this.config.ai?.preferredModel);if(e)return console.log(`\u{1F916} Using: ${e.name} (auto-detected, local, free)`),new $}if(process.env.OPENAI_API_KEY)return console.log("\u{1F916} Using: OpenAI (cloud, your API key)"),new M(process.env.OPENAI_API_KEY);if(process.env.ANTHROPIC_API_KEY)return console.log("\u{1F916} Using: Anthropic (cloud, your API key)"),new T(process.env.ANTHROPIC_API_KEY);throw new J("local",{discoveredModels:this.discoveredModels.map(e=>e.name),hasCloudKeys:{openai:!!process.env.OPENAI_API_KEY,anthropic:!!process.env.ANTHROPIC_API_KEY,groq:!!process.env.GROQ_API_KEY,gemini:!!process.env.GEMINI_API_KEY}})}getActualFallbackChain(){let e=[];for(let t of this.discoveredModels)e.push(`ollama://${t.name}`);return process.env.OPENAI_API_KEY&&e.push("openai://gpt-4o-mini"),process.env.ANTHROPIC_API_KEY&&e.push("anthropic://claude-3-haiku"),process.env.GROQ_API_KEY&&e.push("groq://llama3-70b"),process.env.GEMINI_API_KEY&&e.push("gemini://gemini-1.5-flash"),e}parseUrl(e){let t=e.match(/^([^:]+):\/\/(.+)$/);return t?{providerType:t[1].toLowerCase(),modelName:t[2]}:{providerType:"ollama",modelName:e}}route(e,t){let s=this.assessRequirements(e,t),i=Object.entries(this.models).filter(([r,n])=>this.meetsMinimumRequirements(n,s)).filter(([r,n])=>this.initialized?n.provider==="openai"&&!process.env.OPENAI_API_KEY||n.provider==="anthropic"&&!process.env.ANTHROPIC_API_KEY||n.provider==="gemini"&&!process.env.GEMINI_API_KEY?false:n.provider==="ollama"?this.discoveredModels.some(c=>`ollama://${c.name}`===r):true:true).sort((r,n)=>this.scoreModel(n[1],s)-this.scoreModel(r[1],s));if(i.length===0){let r=Object.entries(this.models).sort((n,c)=>c[1].maxContextTokens-n[1].maxContextTokens)[0];return {model:r[0],reason:"No model meets requirements. Using largest context window with truncation.",estimatedTokens:t.estimatedTokens,estimatedCost:this.estimateCost(r[0],t.estimatedTokens),estimatedLatency:r[1].latencyMs,willTruncate:true,fallbackChain:[]}}let o=i[0];return {model:o[0],reason:this.explainRouting(o[1],s),estimatedTokens:t.estimatedTokens,estimatedCost:this.estimateCost(o[0],t.estimatedTokens),estimatedLatency:o[1].latencyMs,willTruncate:t.estimatedTokens>o[1].maxContextTokens*.8,fallbackChain:i.slice(1,4).map(r=>r[0])}}meetsMinimumRequirements(e,t){return !(t.requiresSecurityAudit&&e.capabilities.securityAudit<t.minimumCapabilityScore||t.requiresMultiFile&&e.capabilities.multiFileAnalysis<t.minimumCapabilityScore)}assessRequirements(e,t){let i={minimal:.3,standard:.5,deep:.8,exhaustive:1}[e]||.5;return {estimatedTokens:t.estimatedTokens,requiresCodeAnalysis:true,requiresSecurityAudit:t.hasBreakingChanges,requiresMultiFile:t.fileCount>5,requiresStructuredOutput:true,minimumCapabilityScore:.3+i*.5,complexityScore:this.calculateComplexity(t)}}calculateComplexity(e){let t=0;return t+=Math.min(e.fileCount/20,1)*.3,t+=Math.min(e.totalChanges/500,1)*.3,t+=Math.min(e.maxASTDepth/10,1)*.2,e.hasBreakingChanges&&(t+=.2),Math.min(t,1)}scoreModel(e,t){let s=0;return t.estimatedTokens<=e.maxContextTokens*.8?s+=.3:t.estimatedTokens<=e.maxContextTokens&&(s+=.1),s+=e.capabilities.codeAnalysis*.25,t.requiresSecurityAudit&&(s+=e.capabilities.securityAudit*.2),t.requiresMultiFile&&(s+=e.capabilities.multiFileAnalysis*.15),t.requiresStructuredOutput&&(s+=e.capabilities.structuredOutput*.1),e.costPer1kInput>0&&(s-=.15),t.complexityScore>.7&&e.latencyMs>2e3&&(s-=.1),s}explainRouting(e,t){let s=[];return e.costPer1kInput===0&&s.push("local/free"),e.capabilities.codeAnalysis>=.9&&s.push("high code analysis capability"),t.requiresSecurityAudit&&e.capabilities.securityAudit>=.8&&s.push("security audit capable"),t.estimatedTokens>8e3&&e.maxContextTokens>=128e3&&s.push("large context window"),`Selected ${e.name}: ${s.join(", ")}`}estimateCost(e,t){let s=this.models[e];return s?t/1e3*s.costPer1kInput+t*.3/1e3*s.costPer1kOutput:0}async getExplanation(e,t){if(t?.dryRun)return {summary:"[DRY RUN] Would call AI to generate explanation for this diff.",impact:"none",breaking:false,files:[],relatedIssues:[]};let s=await this.cache.get(e);if(s)return s.result;let i=e.length,o=Math.ceil(i/4),n=e.split(`
`).filter(p=>p.startsWith("+")||p.startsWith("-")).length,c={fileCount:(e.match(/^diff --git /gm)||[]).length||1,totalChanges:n,maxASTDepth:5,hasBreakingChanges:e.includes("breaking")||e.includes("BREAKING CHANGE"),estimatedTokens:o};await this.initialize();let d=this.route(t?.depth||"standard",c);console.log(`[Intelligent Router] Decision: ${d.model} - ${d.reason}`);let{providerType:f,modelName:N}=this.parseUrl(d.model),y=this.providers[f];if(!y)throw new Error(`Unsupported routed provider type: ${f}`);f==="ollama"&&t?.timeoutMs!==void 0&&(y.timeoutMs=t.timeoutMs);let C=this.config.ai.providers.find(p=>p.url.startsWith(f));C?.apiKey&&(f==="openai"?this.providers.openai=new M(C.apiKey):f==="gemini"?this.providers.gemini=new D(C.apiKey):f==="anthropic"&&(this.providers.anthropic=new T(C.apiKey)));let E=v;if(t?.personaId)try{let{PersonaRegistry:p,PersonaEngine:m}=await import('@eldrex/personas'),g=p.get(t.personaId);g&&(E=`${v}
${m.generateSystemPrompt(g)}`);}catch{}let u=false,l=null,S=null;try{let p=A.resolve(process.cwd(),".devdiff/vibe-session.json");await h.access(p),u=!0;}catch{}if(u)try{l=new B,await l.loadSession();let p=[];try{p=(await K.exec("git",["status","--porcelain"])).split(`
`).map(g=>g.slice(3).trim()).filter(Boolean);}catch{}S=await l.preAICheckpoint({files:p,model:d.model,prompt:e}),await l.saveSession();}catch(p){console.warn("VibeCoderGuardian failed to create checkpoint:",p);}try{let p=t?.projectContext?`${t.projectContext}
${e}`:e,m=await y.generateExplanation(p,N,E);return l&&(l.recordAICall(d.model,!0),await l.saveSession()),await this.cache.set(e,{result:m,provider:f,model:N}),m}catch(p){if(l&&S){l.recordAICall(d.model,false);let m=await l.handleFailure({error:p,model:d.model,checkpointId:S.id,attempt:1});if(await l.saveSession(),m.status==="retrying"&&m.nextModel)try{let g=this.parseUrl(m.nextModel),H=await(this.providers[g.providerType]||this.providers.ollama).generateExplanation(e,g.modelName,E);return l.recordAICall(m.nextModel,!0),await l.saveSession(),H}catch(g){let R=await l.handleFailure({error:g,model:m.nextModel,checkpointId:S.id,attempt:3});throw await l.saveSession(),new Error(R.message)}else throw new Error(m.message)}console.warn(`Routed AI provider ${d.model} failed. Falling back to chain: ${d.fallbackChain.join(", ")}`);for(let m of d.fallbackChain)try{let g=this.parseUrl(m),R=this.providers[g.providerType];if(R)return await R.generateExplanation(e,g.modelName,E)}catch{}throw p}}};export{F as a,L as b,K as c,de as d,v as e,P as f,Y as g,U as h,B as i,x as j,W as k,J as l,V as m,Q as n,X as o,Z as p,ee as q,te as r,se as s};
export{s as AIRouter}from'./chunk-VTMFJSCA.js';import'./chunk-W3CMXIN5.js';
+3
-3
{
"name": "@eldrex/core",
"version": "1.8.0",
"version": "1.8.1",
"publishConfig": {

@@ -29,4 +29,4 @@ "access": "public",

"chalk": "^5.3.0",
"@eldrex/personas": "1.8.0",
"@eldrex/plugin-sdk": "1.8.0"
"@eldrex/personas": "1.8.1",
"@eldrex/plugin-sdk": "1.8.1"
},

@@ -33,0 +33,0 @@ "devDependencies": {

import {a}from'./chunk-W3CMXIN5.js';import*as h from'fs/promises';import*as A from'path';import {execFile}from'child_process';import*as I from'crypto';var F=class{static getLogPath(){return process.env.DEVDIFF_AUDIT_PATH||A.resolve(process.cwd(),".devdiff/security-audit.enc")}static getLegacyPath(){return process.env.DEVDIFF_LEGACY_AUDIT_PATH||A.resolve(process.cwd(),".devdiff/security-audit.json")}static getKey(){let e=process.env.DEVDIFF_AUDIT_KEY;return e?I.createHash("sha256").update(e).digest():null}static async readLegacyLogs(){let e=this.getLegacyPath();try{let t=await h.readFile(e,"utf-8");return t.trim()?JSON.parse(t):[]}catch{return []}}static async readEncryptedLogs(e){try{let s=(await h.readFile(this.getLogPath(),"utf-8")).split(`
`).filter(Boolean),i=[];for(let o of s)try{let r=this.decrypt(o.trim(),e);i.push(JSON.parse(r));}catch{}return i}catch{return []}}static encrypt(e,t){let s=I.randomBytes(12),i=I.createCipheriv("aes-256-gcm",t,s),o=Buffer.concat([i.update(e,"utf8"),i.final()]),r=i.getAuthTag();return Buffer.concat([s,r,o]).toString("base64")}static decrypt(e,t){let s=Buffer.from(e,"base64"),i=s.slice(0,12),o=s.slice(12,28),r=s.slice(28),n=I.createDecipheriv("aes-256-gcm",t,i);return n.setAuthTag(o),n.update(r)+n.final("utf8")}static async log(e){try{let t=this.getLogPath();await h.mkdir(A.dirname(t),{recursive:!0});let s=this.getKey(),i={...e,timestamp:Date.now()},o=JSON.stringify(i);if(s)try{let c=this.encrypt(o,s)+`
`;await h.appendFile(t,c,"utf-8");return}catch(c){console.warn("Encrypted audit log failed, falling back to plaintext:",c);}let r=this.getLegacyPath(),n=await this.readLegacyLogs();n.push(i),await h.writeFile(r,JSON.stringify(n,null,2),"utf-8");}catch(t){console.error("Failed to write to security audit trail:",t);}}static async getLogs(){let e=this.getKey();if(e){let t=await this.readEncryptedLogs(e);if(t.length>0)return t}return this.readLegacyLogs()}static async verifyIntegrity(){let e=this.getKey();if(!e)return {valid:true,total:0,corrupted:0};try{let s=(await h.readFile(this.getLogPath(),"utf-8")).split(`
`).filter(Boolean),i=0;for(let o of s)try{this.decrypt(o.trim(),e);}catch{i++;}return {valid:i===0,total:s.length,corrupted:i}}catch{return {valid:true,total:0,corrupted:0}}}};var L=class extends Error{constructor(e){super(e),this.name="ShellAccessDeniedError";}};function ne(a,e,t){return new Promise((s,i)=>{execFile(a,e,t,(o,r,n)=>{o?i(o):s({stdout:r,stderr:n});});})}var K=class{static ALLOWED_COMMANDS=["git","ollama","which","node"];static BLOCKED_PATTERNS=[/rm\s+-rf/,/sudo/,/curl.*\|.*sh/,/eval/,/\$\(/,/`/,/&&/,/\|\|/,/;/];static async exec(e,t){let s=e.split(" ")[0];if(!this.ALLOWED_COMMANDS.includes(s))throw new L(`Command "${s}" is not in the allowed list. Allowed: ${this.ALLOWED_COMMANDS.join(", ")}.`);let i=`${e} ${t.join(" ")}`.trim();for(let r of this.BLOCKED_PATTERNS)if(r.test(i))throw new L(`Command contains blocked pattern: ${r}. This is a security precaution.`);return await F.log({type:"shell-access",command:s,args:t,timestamp:Date.now(),caller:new Error().stack?.split(`
`)[2]?.trim()}),(await ne(e,t,{timeout:3e4})).stdout}static disable(){console.log("\u26A0\uFE0F Shell access disabled. Git analysis will use isomorphic-git (pure JS).");}},de={disableShellAccess:false,allowedShellCommands:["git","ollama","which"],shellTimeout:3e4,auditShellAccess:true};var v=`You are DevDiff, an AI that analyzes git diffs and writes clear, human-readable changelogs.
Your task is to review the provided git diff, explain what changed and why (inferring the developer's intent), and assess the impact.
CRITICAL ACCURACY RULES:
1. If a PROJECT KNOWLEDGE BASE section is provided, use it to understand the project's purpose, architecture, and naming \u2014 but base your explanation ONLY on what is actually present in the diff.
2. Only mention file paths, function names, class names, or identifiers that explicitly appear in the diff. Do NOT invent or guess identifiers.
3. If you cannot determine the intent of a change, say "intent unclear from diff" rather than fabricating a reason.
4. Do NOT reference modules, files, or concepts from the project context unless they are directly touched by the diff.
5. File paths in the "files" array must exactly match paths shown in the diff headers (e.g., "diff --git a/src/foo.ts").
You must respond ONLY with a valid JSON object matching this schema:
{
"summary": "A concise, high-level explanation of the changes and why they were made in plain English.",
"impact": "none" | "minor" | "major" | "breaking",
"breaking": false,
"files": [
{
"path": "path/to/file.ts",
"explanation": "Specific description of changes in this file."
}
],
"relatedIssues": []
}
Do not include any markdown framing (like \`\`\`json ... \`\`\`) in your direct response, only return raw JSON text. If you must use markdown backticks, ensure the JSON is still valid and parseable.`;function P(a){let e=a.trim();e.startsWith("```")&&(e=e.replace(/^```json\s*/i,"").replace(/```$/,"").trim());try{let t=JSON.parse(e);return {summary:t.summary||"No summary generated.",impact:["none","minor","major","breaking"].includes(t.impact)?t.impact:"minor",breaking:!!t.breaking,files:Array.isArray(t.files)?t.files:[],relatedIssues:Array.isArray(t.relatedIssues)?t.relatedIssues:[]}}catch{return console.error("Failed to parse AI JSON response:",e),{summary:e.substring(0,500),impact:"minor",breaking:false,files:[],relatedIssues:[]}}}var j=class extends Error{platform;originalError;constructor(e,t){super(e),this.name="OllamaNotAvailableError",this.platform=t.platform,this.originalError=t.error;}},Y=class{static BASE_TIMEOUT_MS=15e3;static PER_FILE_MS=2e3;static PER_1K_TOKENS_MS=5e3;static MAX_TIMEOUT_MS=3e5;static MIN_TIMEOUT_MS=1e4;static calculate(e){let t=this.BASE_TIMEOUT_MS;t+=e.fileCount*this.PER_FILE_MS;let s=e.estimatedTokens/1e3;t+=s*this.PER_1K_TOKENS_MS;let i=parseFloat(e.modelSize)||3;return i<=3?t*=1.5:i<=7&&(t*=1.2),e.historicalAvgMs&&(t=Math.max(t,e.historicalAvgMs*1.5)),t=Math.max(this.MIN_TIMEOUT_MS,Math.min(this.MAX_TIMEOUT_MS,t)),Math.round(t)}static calculateForFallback(e){let t=this.calculate(e),s=1+e.attemptNumber*.5;return Math.round(t*s)}},$=class{name="ollama";host;timeoutMs;performanceHistory=new Map;constructor(e="http://localhost:11434",t){this.host=e,this.timeoutMs=t;}async generateExplanation(e,t,s,i){let o=`${this.host}/api/generate`,r=`System Instructions:
${s||v}
Git Diff:
${e}`,n=Date.now(),c=(e.match(/^diff --git /gm)||[]).length||1,d=this.estimateTokens(e),f=this.detectModelSize(t),N=this.getHistoricalAverage(t),y=this.timeoutMs;y===void 0&&(y=i&&i>1?Y.calculateForFallback({fileCount:c,estimatedTokens:d,modelSize:f,attemptNumber:i-1}):Y.calculate({fileCount:c,estimatedTokens:d,modelSize:f,historicalAvgMs:N})),console.log(`\u23F1\uFE0F Dynamic timeout: ${(y/1e3).toFixed(0)}s for ${c} files (~${d} tokens)`);let C=new AbortController,E=setTimeout(()=>C.abort(),y);try{let u=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:t,prompt:r,stream:!1,options:{temperature:.2}}),signal:C.signal});if(clearTimeout(E),!u.ok)throw new Error(`Ollama returned status ${u.status}: ${await u.text()}`);let l=await u.json(),S=Date.now()-n;return this.recordPerformance(t,S),console.log(`\u2705 Ollama response: ${S}ms (timeout was ${y}ms)`),P(l.response)}catch(u){clearTimeout(E);let l=Date.now()-n;throw u.name==="AbortError"||u.name==="TimeoutError"||u.message.includes("timed out")?(console.log(`\u23F1\uFE0F Ollama timed out after ${l}ms (timeout was ${y}ms)`),console.log(` Tip: ${c} files may be too many for model ${t}`),console.log(" Consider: devdiff generate --depth minimal (for faster results)"),console.log(" Or split into smaller commits"),new j(`Ollama request timed out after ${y}ms`,{platform:process.platform,error:u})):(console.log(""),console.log("\u274C Cannot connect to Ollama"),console.log(""),console.log(" DevDiff uses Ollama for local AI. It needs to be running."),console.log(""),process.platform==="win32"?(console.log(" Windows:"),console.log(" 1. Download Ollama from: https://ollama.com/download/windows"),console.log(" 2. Install and run the Ollama app"),console.log(" 3. Open PowerShell and run: ollama pull llama3.2:3b"),console.log(" 4. Verify it works: ollama list"),console.log(" 5. Try again: devdiff generate")):process.platform==="darwin"?(console.log(" macOS:"),console.log(" 1. brew install ollama"),console.log(" 2. ollama serve"),console.log(" 3. ollama pull llama3.2:3b")):(console.log(" Linux:"),console.log(" 1. curl -fsSL https://ollama.com/install.sh | sh"),console.log(" 2. ollama serve"),console.log(" 3. ollama pull llama3.2:3b")),console.log(""),console.log(" \u{1F4A1} No Ollama? You can use:"),console.log(" \u2022 Dry run mode: devdiff generate --dry-run"),console.log(" \u2022 Cloud AI: Set OPENAI_API_KEY in .env"),console.log(" \u2022 WebGPU: Open dashboard at http://localhost:3737"),console.log(""),console.log(" Error details:",u.message),console.log(""),new j("Ollama is not running. Install from https://ollama.com",{platform:process.platform,error:u}))}}estimateTokens(e){return Math.ceil(e.length/3.5)}detectModelSize(e){let t=e.match(/(\d+\.?\d*)b/i);return t?t[1]+"b":"3b"}getHistoricalAverage(e){let t=this.performanceHistory.get(e);if(!(!t||t.length===0))return t.reduce((s,i)=>s+i,0)/t.length}recordPerformance(e,t){let s=this.performanceHistory.get(e)||[];s.push(t),s.length>20&&s.shift(),this.performanceHistory.set(e,s);}};var M=class{name="openai";apiKey;constructor(e){this.apiKey=e||process.env.OPENAI_API_KEY;}async generateExplanation(e,t,s){let i=this.apiKey;if(!i)throw new Error("OpenAI API Key is not configured.");let o="https://api.openai.com/v1/chat/completions";try{let r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({model:t,messages:[{role:"system",content:s||v},{role:"user",content:`Analyze this diff:
${e}`}],temperature:.2,response_format:{type:"json_object"}})});if(!r.ok)throw new Error(`OpenAI API returned status ${r.status}: ${await r.text()}`);let c=(await r.json()).choices[0]?.message?.content||"";return P(c)}catch(r){throw console.error("OpenAI execution error:",r),r}}};var D=class{name="gemini";apiKey;constructor(e){this.apiKey=e||process.env.GEMINI_API_KEY;}async generateExplanation(e,t,s){let i=this.apiKey;if(!i)throw new Error("Gemini API Key is not configured.");let r=`https://generativelanguage.googleapis.com/v1beta/${t.includes("/")?t:`models/${t}`}:generateContent?key=${i}`;try{let n=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contents:[{parts:[{text:`Analyze this diff:
${e}`}]}],systemInstruction:{parts:[{text:s||v}]},generationConfig:{responseMimeType:"application/json",temperature:.2}})});if(!n.ok)throw new Error(`Gemini API returned status ${n.status}: ${await n.text()}`);let d=(await n.json()).candidates?.[0]?.content?.parts?.[0]?.text||"";return P(d)}catch(n){throw console.error("Gemini execution error:",n),n}}};var T=class{name="anthropic";apiKey;constructor(e){this.apiKey=e||process.env.ANTHROPIC_API_KEY;}async generateExplanation(e,t,s){let i=this.apiKey;if(!i)throw new Error("Anthropic API Key is not configured.");let o="https://api.anthropic.com/v1/messages";try{let r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":i,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:t,max_tokens:4e3,system:s||v,messages:[{role:"user",content:`Analyze this diff:
${e}`}],temperature:.2})});if(!r.ok)throw new Error(`Anthropic API returned status ${r.status}: ${await r.text()}`);let c=(await r.json()).content?.[0]?.text||"";return P(c)}catch(r){throw console.error("Anthropic execution error:",r),r}}};var U=class{cachePath;enabled;memoryCache={};constructor(e=true,t=".devdiff/cache.json"){this.enabled=e,this.cachePath=t;}hashDiff(e){return I.createHash("sha256").update(e).digest("hex")}async load(){if(this.enabled)try{let e=A.dirname(this.cachePath);await h.mkdir(e,{recursive:!0});let t=await h.readFile(this.cachePath,"utf-8");this.memoryCache=JSON.parse(t);}catch{this.memoryCache={};}}async get(e){if(!this.enabled)return null;let t=this.hashDiff(e);return Object.keys(this.memoryCache).length===0&&await this.load(),this.memoryCache[t]||null}async set(e,t){if(!this.enabled)return;let s=this.hashDiff(e);this.memoryCache[s]={...t,timestamp:new Date().toISOString()};try{let i=A.dirname(this.cachePath);await h.mkdir(i,{recursive:!0}),await h.writeFile(this.cachePath,JSON.stringify(this.memoryCache,null,2),"utf-8");}catch(i){console.warn("Failed to save explanation cache to disk:",i);}}};var B=class{session;checkpointDir;sessionPath;constructor(){this.session={startedAt:Date.now(),changes:[],checkpoints:[],failures:[],aiCalls:[]},this.checkpointDir=A.resolve(process.cwd(),".devdiff/checkpoints"),this.sessionPath=A.resolve(process.cwd(),".devdiff/vibe-session.json");}async loadSession(){try{let e=await h.readFile(this.sessionPath,"utf-8");this.session=JSON.parse(e);}catch{}}async saveSession(){await h.mkdir(A.dirname(this.sessionPath),{recursive:true}),await h.writeFile(this.sessionPath,JSON.stringify(this.session,null,2),"utf-8");}async deleteSession(){try{await h.rm(this.sessionPath,{force:!0});}catch{}}async preAICheckpoint(e){let t=await this.captureSnapshot(e.files),s={id:`ckpt-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,timestamp:Date.now(),type:"pre-ai-call",snapshot:t,metadata:{trigger:"ai-call",aiModel:e.model,prompt:e.prompt.slice(0,200)}};return await this.saveCheckpoint(s),this.session.checkpoints.push(s),console.log([`\u{1F4BE} Checkpoint: ${s.id}`,`\u{1F4C1} Files: ${e.files.length}`,`\u{1F916} Model: ${e.model}`,`\u{1F4DD} Prompt: ${e.prompt.slice(0,100)}...`,`\u23F1\uFE0F ${new Date(s.timestamp).toLocaleTimeString()}`].join(`
`)),s}async captureSnapshot(e){let t=[];for(let s of e)try{let i=A.resolve(process.cwd(),s),o=await h.readFile(i,"utf-8");t.push([s,o]);}catch{}return {files:t}}async saveCheckpoint(e){await h.mkdir(this.checkpointDir,{recursive:true});let t=A.join(this.checkpointDir,`${e.id}.json`);await h.writeFile(t,JSON.stringify(e,null,2),"utf-8");}async handleFailure(e){console.log(`
\u26A0\uFE0F AI CALL FAILED \u2014 Auto-recovery initiated`),console.log(`\u274C Error: ${e.error.message}`),console.log(`\u{1F916} Model: ${e.model}`),console.log(`\u{1F504} Attempt: ${e.attempt}/3`),this.session.failures.push({timestamp:Date.now(),error:e.error.message,model:e.model,attempt:e.attempt,checkpointId:e.checkpointId});let t={whatHappened:`AI call to ${e.model} failed on attempt ${e.attempt}`,why:e.error.message,whatWasSaved:`Checkpoint ${e.checkpointId} \u2014 ALL changes are safe`,whatHappensNext:e.attempt<3?"Retrying with fallback model...":"Restoring checkpoint. All changes preserved.",howToManualRecover:`npx devdiff recover --checkpoint ${e.checkpointId}`};if(console.log(`
\u{1F4CA} TRANSPARENCY REPORT:`),console.log(JSON.stringify(t,null,2)),e.attempt<3){let s=await this.getFallbackModel(e.model);return {status:"retrying",message:`Switching to ${s}`,nextModel:s,checkpointId:e.checkpointId,transparency:t}}return console.log(`
\u{1F6D1} Max retries exhausted. Restoring checkpoint...`),await this.restoreCheckpoint(e.checkpointId),{status:"failed-recovered",message:"All AI attempts failed. Changes restored from checkpoint. No work lost.",checkpointId:e.checkpointId,restoredFrom:e.checkpointId,transparency:t,manualActions:["Check internet connection","Verify Ollama is running: ollama ps","Try different model: npx devdiff config set ai.model ollama://llama3.1:8b",`Manual restore: npx devdiff recover --checkpoint ${e.checkpointId}`]}}async getFallbackModel(e){try{let{OllamaModelDiscovery:t}=await import('./ollama-discovery-QMQD7TQA.js'),s=await t.discoverModels();if(s.length>0){let i=e.replace("ollama://",""),o=s.filter(r=>r.name!==i);return o.length>0?`ollama://${o[0].name}`:e}}catch{}return e.includes("llama3.2:3b")?"ollama://llama3.1:8b":"ollama://llama3.2:3b"}async restoreCheckpoint(e){let t=this.session.checkpoints.find(s=>s.id===e);if(!t)try{let s=await h.readFile(A.join(this.checkpointDir,`${e}.json`),"utf-8");t=JSON.parse(s);}catch{}if(!t)throw new Error(`Checkpoint ${e} not found`);console.log(`
\u{1F504} Restoring checkpoint ${e}...`);for(let[s,i]of t.snapshot.files){let o=A.resolve(process.cwd(),s);await h.mkdir(A.dirname(o),{recursive:true}),await h.writeFile(o,i,"utf-8"),console.log(` \u2705 Restored: ${s}`);}console.log(`
\u2705 Restore complete. All files back to pre-AI state.`);}generateReport(){return {duration:Date.now()-this.session.startedAt,totalChanges:this.session.changes.length,checkpointsCreated:this.session.checkpoints.length,aiCallsSucceeded:this.session.aiCalls.filter(e=>e.success).length,aiCallsFailed:this.session.failures.length,dataLossEvents:0,guarantee:"\u2705 ZERO data loss. All changes preserved in local checkpoints.",recommendations:this.generateRecommendations()}}generateRecommendations(){return ["Keep llama3.1:8b as fallback"]}recordChange(e){this.session.changes.push({filename:e,timestamp:Date.now()});}recordAICall(e,t){this.session.aiCalls.push({model:e,success:t,timestamp:Date.now()});}};var x=class extends Error{code;exitCode;httpStatus;fix;docsUrl;context;constructor(e){super(e.message),this.name="DevDiffError",this.code=e.code,this.exitCode=e.exitCode||1,this.httpStatus=e.httpStatus||500,this.fix=e.fix||"Check the documentation for more information.",this.docsUrl=e.docsUrl||"https://devdiff.vercel.app/troubleshooting/common-fixes",this.context=e.context||{};}toCLIOutput(){let e=[];if(e.push(""),e.push(`\u274C ${this.message}`),e.push(""),e.push(` Error Code: ${this.code}`),e.push(` Fix: ${this.fix}`),e.push(` Docs: ${this.docsUrl}`),Object.keys(this.context).length>0){e.push(""),e.push(" Context:");for(let[t,s]of Object.entries(this.context))e.push(` \u2022 ${t}: ${s}`);}return e.push(""),e.join(`
`)}},W=class extends x{constructor(e,t){super({code:"GIT_001",message:e,exitCode:3,fix:`Ensure you are in a git repository with at least one commit.
Run: git init && git add . && git commit -m "initial commit"`,docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:t}),this.name="GitError";}},J=class extends x{constructor(e,t){let s={ollama:`Install Ollama: https://ollama.com/download
Then: ollama pull llama3.2:3b`,openai:`Set your API key: devdiff auth add openai
Or: export OPENAI_API_KEY=your-key`,anthropic:`Set your API key: devdiff auth add anthropic
Or: export ANTHROPIC_API_KEY=your-key`};super({code:"AI_001",message:`${e} is not available`,exitCode:4,fix:s[e]||`Check your ${e} configuration.`,docsUrl:`https://devdiff.vercel.app/ai-providers/${e}-setup`,context:t}),this.name="AINotAvailableError";}},V=class extends x{constructor(e,t){super({code:"CFG_001",message:e,exitCode:2,fix:`Run: devdiff config --validate (to check your configuration)
Run: devdiff config --reset (to reset to defaults)`,docsUrl:"https://devdiff.vercel.app/guide/configuration",context:t}),this.name="ConfigError";}},Q=class extends x{constructor(e,t){super({code:"NET_001",message:e,exitCode:5,fix:`Check your internet connection.
If using local AI, no internet is needed \u2014 check your AI provider.
Run: devdiff doctor (for full diagnostics)`,docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:t}),this.name="NetworkError";}},X=class extends x{constructor(e,t){super({code:"PERM_001",message:e,exitCode:6,fix:`Check file permissions for .devdiff/ directory.
On Linux/macOS: chmod -R 755 .devdiff/
On Windows: Run terminal as Administrator`,docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:t}),this.name="PermissionError";}},Z=class extends x{constructor(e,t,s){super({code:"RES_001",message:`${e} limit reached (${s} / ${t})`,exitCode:7,fix:e==="memory"?`Try using a smaller AI model: ollama pull llama3.2:1b
Or use a cloud provider: devdiff auth add openai`:"Free up disk space or change output directory.",docsUrl:"https://devdiff.vercel.app/troubleshooting/common-fixes",context:{resource:e,limit:t,current:s}}),this.name="ResourceLimitError";}},ee=class extends x{constructor(e){super({code:"TTY_001",message:e,exitCode:1,fix:`This command requires an interactive terminal.
For scripts, use environment variables or config files.`,docsUrl:"https://devdiff.vercel.app/guide/configuration"}),this.name="NonInteractiveError";}},te=class extends x{constructor(e,t){super({code:"MVP_001",message:e,exitCode:1,fix:`Run: devdiff mvp status (to check queue)
Run: devdiff mvp process (to process queued items)`,docsUrl:"https://devdiff.vercel.app/features/mvp-mode",context:t}),this.name="MVPStorageError";}};var se=class{config;cache;providers={};models={"ollama://llama3.2:3b":{name:"Llama 3.2 3B",provider:"ollama",maxContextTokens:128e3,maxOutputTokens:8192,costPer1kInput:0,costPer1kOutput:0,latencyMs:500,capabilities:{codeAnalysis:.6,securityAudit:.4,refactoringDetection:.5,multiFileAnalysis:.3,structuredOutput:.7}},"ollama://llama3.1:8b":{name:"Llama 3.1 8B",provider:"ollama",maxContextTokens:128e3,maxOutputTokens:8192,costPer1kInput:0,costPer1kOutput:0,latencyMs:1200,capabilities:{codeAnalysis:.8,securityAudit:.7,refactoringDetection:.75,multiFileAnalysis:.7,structuredOutput:.8}},"ollama://codellama:13b":{name:"CodeLlama 13B",provider:"ollama",maxContextTokens:16384,maxOutputTokens:4096,costPer1kInput:0,costPer1kOutput:0,latencyMs:3e3,capabilities:{codeAnalysis:.9,securityAudit:.85,refactoringDetection:.9,multiFileAnalysis:.6,structuredOutput:.75}},"openai://gpt-4o-mini":{name:"GPT-4o Mini",provider:"openai",maxContextTokens:128e3,maxOutputTokens:16384,costPer1kInput:15e-5,costPer1kOutput:6e-4,latencyMs:800,capabilities:{codeAnalysis:.85,securityAudit:.8,refactoringDetection:.85,multiFileAnalysis:.9,structuredOutput:.95}},"openai://gpt-4o":{name:"GPT-4o",provider:"openai",maxContextTokens:128e3,maxOutputTokens:16384,costPer1kInput:.0025,costPer1kOutput:.01,latencyMs:1500,capabilities:{codeAnalysis:.95,securityAudit:.95,refactoringDetection:.95,multiFileAnalysis:.95,structuredOutput:.98}}};discoveredModels=[];initialized=false;constructor(e){this.config=e,this.cache=new U(e.cache.enabled,e.cache.path),this.providers.ollama=new $,this.providers.openai=new M,this.providers.gemini=new D,this.providers.anthropic=new T;}async initialize(){if(!this.initialized){this.discoveredModels=await a.discoverModels();for(let e of this.discoveredModels){let t=`ollama://${e.name}`,s=["codellama","qwen","deepseek"].includes(e.family),i=parseInt(e.parameterSize)||3;this.models[t]={name:e.name,provider:"ollama",maxContextTokens:128e3,maxOutputTokens:8192,costPer1kInput:0,costPer1kOutput:0,latencyMs:i>13?3e3:i>7?1500:800,capabilities:{codeAnalysis:s?.9:.7,securityAudit:s?.8:.6,refactoringDetection:s?.95:.7,multiFileAnalysis:i>7?.8:.5,structuredOutput:.8}};}if(this.discoveredModels.length>0){console.log(`\u{1F4E6} Found ${this.discoveredModels.length} Ollama model(s):`);for(let e of this.discoveredModels)console.log(` \u2022 ${e.name} (${e.parameterSize}, ${e.family})`);}else console.log("\u{1F4E6} No Ollama models found."),console.log(" Install one: ollama pull llama3.2:3b"),console.log(" Or pull a code specialist: ollama pull qwen2.5-coder:7b");this.initialized=true;}}async getBestProvider(){if(await this.initialize(),this.discoveredModels.length>0){let e=a.selectBestModel(this.discoveredModels,this.config.preferredModel||this.config.ai?.preferredModel);if(e)return console.log(`\u{1F916} Using: ${e.name} (auto-detected, local, free)`),new $}if(process.env.OPENAI_API_KEY)return console.log("\u{1F916} Using: OpenAI (cloud, your API key)"),new M(process.env.OPENAI_API_KEY);if(process.env.ANTHROPIC_API_KEY)return console.log("\u{1F916} Using: Anthropic (cloud, your API key)"),new T(process.env.ANTHROPIC_API_KEY);throw new J("local",{discoveredModels:this.discoveredModels.map(e=>e.name),hasCloudKeys:{openai:!!process.env.OPENAI_API_KEY,anthropic:!!process.env.ANTHROPIC_API_KEY,groq:!!process.env.GROQ_API_KEY,gemini:!!process.env.GEMINI_API_KEY}})}getActualFallbackChain(){let e=[];for(let t of this.discoveredModels)e.push(`ollama://${t.name}`);return process.env.OPENAI_API_KEY&&e.push("openai://gpt-4o-mini"),process.env.ANTHROPIC_API_KEY&&e.push("anthropic://claude-3-haiku"),process.env.GROQ_API_KEY&&e.push("groq://llama3-70b"),process.env.GEMINI_API_KEY&&e.push("gemini://gemini-1.5-flash"),e}parseUrl(e){let t=e.match(/^([^:]+):\/\/(.+)$/);return t?{providerType:t[1].toLowerCase(),modelName:t[2]}:{providerType:"ollama",modelName:e}}route(e,t){let s=this.assessRequirements(e,t),i=Object.entries(this.models).filter(([r,n])=>this.meetsMinimumRequirements(n,s)).filter(([r,n])=>this.initialized?n.provider==="openai"&&!process.env.OPENAI_API_KEY||n.provider==="anthropic"&&!process.env.ANTHROPIC_API_KEY||n.provider==="gemini"&&!process.env.GEMINI_API_KEY?false:n.provider==="ollama"?this.discoveredModels.some(c=>`ollama://${c.name}`===r):true:true).sort((r,n)=>this.scoreModel(n[1],s)-this.scoreModel(r[1],s));if(i.length===0){let r=Object.entries(this.models).sort((n,c)=>c[1].maxContextTokens-n[1].maxContextTokens)[0];return {model:r[0],reason:"No model meets requirements. Using largest context window with truncation.",estimatedTokens:t.estimatedTokens,estimatedCost:this.estimateCost(r[0],t.estimatedTokens),estimatedLatency:r[1].latencyMs,willTruncate:true,fallbackChain:[]}}let o=i[0];return {model:o[0],reason:this.explainRouting(o[1],s),estimatedTokens:t.estimatedTokens,estimatedCost:this.estimateCost(o[0],t.estimatedTokens),estimatedLatency:o[1].latencyMs,willTruncate:t.estimatedTokens>o[1].maxContextTokens*.8,fallbackChain:i.slice(1,4).map(r=>r[0])}}meetsMinimumRequirements(e,t){return !(t.requiresSecurityAudit&&e.capabilities.securityAudit<t.minimumCapabilityScore||t.requiresMultiFile&&e.capabilities.multiFileAnalysis<t.minimumCapabilityScore)}assessRequirements(e,t){let i={minimal:.3,standard:.5,deep:.8,exhaustive:1}[e]||.5;return {estimatedTokens:t.estimatedTokens,requiresCodeAnalysis:true,requiresSecurityAudit:t.hasBreakingChanges,requiresMultiFile:t.fileCount>5,requiresStructuredOutput:true,minimumCapabilityScore:.3+i*.5,complexityScore:this.calculateComplexity(t)}}calculateComplexity(e){let t=0;return t+=Math.min(e.fileCount/20,1)*.3,t+=Math.min(e.totalChanges/500,1)*.3,t+=Math.min(e.maxASTDepth/10,1)*.2,e.hasBreakingChanges&&(t+=.2),Math.min(t,1)}scoreModel(e,t){let s=0;return t.estimatedTokens<=e.maxContextTokens*.8?s+=.3:t.estimatedTokens<=e.maxContextTokens&&(s+=.1),s+=e.capabilities.codeAnalysis*.25,t.requiresSecurityAudit&&(s+=e.capabilities.securityAudit*.2),t.requiresMultiFile&&(s+=e.capabilities.multiFileAnalysis*.15),t.requiresStructuredOutput&&(s+=e.capabilities.structuredOutput*.1),e.costPer1kInput>0&&(s-=.15),t.complexityScore>.7&&e.latencyMs>2e3&&(s-=.1),s}explainRouting(e,t){let s=[];return e.costPer1kInput===0&&s.push("local/free"),e.capabilities.codeAnalysis>=.9&&s.push("high code analysis capability"),t.requiresSecurityAudit&&e.capabilities.securityAudit>=.8&&s.push("security audit capable"),t.estimatedTokens>8e3&&e.maxContextTokens>=128e3&&s.push("large context window"),`Selected ${e.name}: ${s.join(", ")}`}estimateCost(e,t){let s=this.models[e];return s?t/1e3*s.costPer1kInput+t*.3/1e3*s.costPer1kOutput:0}async getExplanation(e,t){if(t?.dryRun)return {summary:"[DRY RUN] Would call AI to generate explanation for this diff.",impact:"none",breaking:false,files:[],relatedIssues:[]};let s=await this.cache.get(e);if(s)return s.result;let i=e.length,o=Math.ceil(i/4),n=e.split(`
`).filter(p=>p.startsWith("+")||p.startsWith("-")).length,c={fileCount:(e.match(/^diff --git /gm)||[]).length||1,totalChanges:n,maxASTDepth:5,hasBreakingChanges:e.includes("breaking")||e.includes("BREAKING CHANGE"),estimatedTokens:o};await this.initialize();let d=this.route(t?.depth||"standard",c);console.log(`[Intelligent Router] Decision: ${d.model} - ${d.reason}`);let{providerType:f,modelName:N}=this.parseUrl(d.model),y=this.providers[f];if(!y)throw new Error(`Unsupported routed provider type: ${f}`);f==="ollama"&&t?.timeoutMs!==void 0&&(y.timeoutMs=t.timeoutMs);let C=this.config.ai.providers.find(p=>p.url.startsWith(f));C?.apiKey&&(f==="openai"?this.providers.openai=new M(C.apiKey):f==="gemini"?this.providers.gemini=new D(C.apiKey):f==="anthropic"&&(this.providers.anthropic=new T(C.apiKey)));let E=v;if(t?.personaId)try{let{PersonaRegistry:p,PersonaEngine:m}=await import('@eldrex/personas'),g=p.get(t.personaId);g&&(E=`${v}
${m.generateSystemPrompt(g)}`);}catch{}let u=false,l=null,S=null;try{let p=A.resolve(process.cwd(),".devdiff/vibe-session.json");await h.access(p),u=!0;}catch{}if(u)try{l=new B,await l.loadSession();let p=[];try{p=(await K.exec("git",["status","--porcelain"])).split(`
`).map(g=>g.slice(3).trim()).filter(Boolean);}catch{}S=await l.preAICheckpoint({files:p,model:d.model,prompt:e}),await l.saveSession();}catch(p){console.warn("VibeCoderGuardian failed to create checkpoint:",p);}try{let p=t?.projectContext?`${t.projectContext}
${e}`:e,m=await y.generateExplanation(p,N,E);return l&&(l.recordAICall(d.model,!0),await l.saveSession()),await this.cache.set(e,{result:m,provider:f,model:N}),m}catch(p){if(l&&S){l.recordAICall(d.model,false);let m=await l.handleFailure({error:p,model:d.model,checkpointId:S.id,attempt:1});if(await l.saveSession(),m.status==="retrying"&&m.nextModel)try{let g=this.parseUrl(m.nextModel),H=await(this.providers[g.providerType]||this.providers.ollama).generateExplanation(e,g.modelName,E);return l.recordAICall(m.nextModel,!0),await l.saveSession(),H}catch(g){let R=await l.handleFailure({error:g,model:m.nextModel,checkpointId:S.id,attempt:3});throw await l.saveSession(),new Error(R.message)}else throw new Error(m.message)}console.warn(`Routed AI provider ${d.model} failed. Falling back to chain: ${d.fallbackChain.join(", ")}`);for(let m of d.fallbackChain)try{let g=this.parseUrl(m),R=this.providers[g.providerType];if(R)return await R.generateExplanation(e,g.modelName,E)}catch{}throw p}}};export{F as a,L as b,K as c,de as d,v as e,P as f,Y as g,U as h,B as i,x as j,W as k,J as l,V as m,Q as n,X as o,Z as p,ee as q,te as r,se as s};
export{s as AIRouter}from'./chunk-BQWW3NTR.js';import'./chunk-W3CMXIN5.js';

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display