@vortex-ai/cli 0.1.50 → 0.1.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -211002,19 +211002,16 @@ var require_dist3 = __commonJS({
211002
211002
  module2.exports = __toCommonJS(index_exports);
211003
211003
  var Prompts = {
211004
211004
  // ── Engine / Intelligence Agent Prompts ──
211005
- extractSearchQueries: (diff) => `
211006
- You are an expert code analyzer.
211007
- Analyze the following git diff and extract exactly 3 short search queries.
211008
- These queries should be the names of the most important functions, classes, or architectural concepts that are modified or referenced in this PR.
211009
- Your goal is to extract queries that can be used in a vector search engine to find the relevant codebase context.
211005
+ extractSearchQueries: (diff) => `You must return ONLY a valid JSON array of exactly 3 strings. Example: ["auth flow", "DatabaseService", "user login"]
211010
211006
 
211011
- Return ONLY a valid JSON array of 3 strings. No markdown formatting, no explanation.
211007
+ You are an expert code analyzer.
211008
+ Analyze the following git diff and extract 3 short search queries.
211009
+ These queries should be the names of the most important functions, classes, or architectural concepts modified or referenced in this PR.
211012
211010
 
211013
211011
  Diff:
211014
211012
  \`\`\`diff
211015
211013
  ${diff}
211016
- \`\`\`
211017
- `,
211014
+ \`\`\``,
211018
211015
  ragReview: (diff, contextStr) => `
211019
211016
  You are an expert Principal Software Engineer reviewing a pull request.
211020
211017
  You have been provided with the git diff of the pull request, AND some relevant code chunks from the existing codebase for context.
@@ -211075,34 +211072,33 @@ Your answer must follow these strict guidelines:
211075
211072
  4. **Professional Tone**: Sound like a highly experienced senior engineer mentoring a junior. Use emojis sparingly but effectively (e.g., \u{1F4A1} for tips, \u26A0\uFE0F for warnings).
211076
211073
  5. If the provided chunks do not contain enough information to answer fully, explicitly state what is missing.
211077
211074
  `,
211078
- executionPlan: (task, contextStr) => `You are an autonomous coding agent.
211079
-
211080
- # Task
211081
- ${task}
211082
-
211083
- # Relevant Codebase Context
211084
- ${contextStr}
211085
-
211086
- Analyze the task and available codebase context.
211075
+ executionPlan: (task, contextStr) => `You must return ONLY a structured JSON object matching this schema:
211076
+ {
211077
+ "summary": "A deep, 3-4 sentence explanation of the architectural approach.",
211078
+ "filesToRead": ["src/index.ts", "package.json"],
211079
+ "steps": [
211080
+ {
211081
+ "action": "create" | "modify" | "delete" | "test",
211082
+ "file": "src/App.js",
211083
+ "description": "A very detailed, multi-sentence explanation of exactly what code needs to be written or changed, including specific variables, functions, or UI elements to touch."
211084
+ }
211085
+ ]
211086
+ }
211087
211087
 
211088
- Output ONLY a structured JSON execution plan containing the steps and the files that need to be read before the agent starts.
211088
+ You are a Principal AI Software Architect. Analyze the task and the extensive codebase context provided below to create a highly detailed execution plan.
211089
211089
 
211090
211090
  Rules:
211091
- * Maximum 7-10 steps.
211092
- * Each step must be one sentence.
211093
- * Focus only on actions required to complete the task.
211094
- * Do not explain reasoning.
211095
- * Do not describe implementation details.
211096
- * Do not list speculative edge cases.
211097
- * Do not create documentation-style plans and DO NOT commit anything.
211098
- * Prefer concrete actions such as inspect, modify, implement, test, validate.
211091
+ - Make the plan EXTREMELY detailed. Do not skip steps.
211092
+ - Explain the 'why' and the 'how' for each step.
211093
+ - Include precise implementation details, such as variable names, function signatures, and logic flows.
211094
+ - Make sure to identify all related files that need to be read or modified.
211095
+ - Output ONLY valid JSON.
211099
211096
 
211100
- Schema:
211101
- {
211102
- "steps": ["Step 1", "Step 2"],
211103
- "filesToRead": ["src/index.ts", "package.json"]
211104
- }
211105
- `,
211097
+ # Task
211098
+ ${task}
211099
+
211100
+ # Extensive Codebase Context
211101
+ ${contextStr}`,
211106
211102
  // ── Multi-Agent PR Review Prompts ──
211107
211103
  securitySystemPrompt: `You are a world-class Application Security Engineer conducting a security-focused code review.
211108
211104
  Your ONLY job is to find GENUINE, HIGH-IMPACT security vulnerabilities in the PR diff. You do NOT care about code style, architecture, performance, or nit-picks.
@@ -211164,6 +211160,53 @@ Severity Guide:
211164
211160
 
211165
211161
  If the PR does not contain any breaking or major structural issues, return an empty findings array with consistencyScore "excellent".
211166
211162
  Return ONLY the JSON object. No markdown. No explanation outside the JSON.`,
211163
+ combinedReviewSystemPrompt: `You must return your findings as a valid JSON object matching this EXACT schema:
211164
+ {
211165
+ "securityFindings": [
211166
+ {
211167
+ "title": "Short title",
211168
+ "severity": "critical" | "high" | "medium" | "low" | "info",
211169
+ "description": "Detailed explanation",
211170
+ "file": "filename or N/A",
211171
+ "lineHint": "approximate line",
211172
+ "recommendation": "How to fix"
211173
+ }
211174
+ ],
211175
+ "securitySummary": "1-2 sentence overall security assessment",
211176
+ "securityRiskLevel": "safe" | "low_risk" | "medium_risk" | "high_risk" | "critical_risk",
211177
+ "architectureFindings": [
211178
+ {
211179
+ "title": "Short title",
211180
+ "severity": "breaking" | "major" | "minor" | "suggestion",
211181
+ "description": "Detailed explanation",
211182
+ "affectedPattern": "Which existing pattern is affected",
211183
+ "recommendation": "How to align with existing architecture"
211184
+ }
211185
+ ],
211186
+ "architectureSummary": "1-2 sentence overall architecture assessment",
211187
+ "architectureConsistencyScore": "excellent" | "good" | "fair" | "poor"
211188
+ }
211189
+
211190
+ You are a Principal Software Architect and Security Engineer conducting a unified code review.
211191
+ Your ONLY job is to analyze the PR diff for BOTH Security Vulnerabilities and Architectural Consistency.
211192
+
211193
+ RULES:
211194
+ - ONLY use the provided PR Diff and Codebase Context. Do not invent code.
211195
+ - Ignore code style, nit-picks, and low severity issues.
211196
+ - Do NOT flag "Prompt Injection" or "Data Exfiltration" in local CLI code.
211197
+ - Do NOT flag imports from \`@vortex/shared\`.
211198
+
211199
+ Example valid output:
211200
+ {
211201
+ "securityFindings": [],
211202
+ "securitySummary": "No high-risk vulnerabilities found.",
211203
+ "securityRiskLevel": "safe",
211204
+ "architectureFindings": [],
211205
+ "architectureSummary": "Architecture remains highly cohesive.",
211206
+ "architectureConsistencyScore": "excellent"
211207
+ }
211208
+
211209
+ Return ONLY valid JSON. No markdown fences.`,
211167
211210
  synthesizerSystemPrompt: `You are a Staff Engineer writing the FINAL code review report for a pull request.
211168
211211
  You have received analysis from two specialist agents:
211169
211212
  1. **SecurityAgent** \u2014 found security vulnerabilities
@@ -211504,17 +211547,20 @@ var require_dist4 = __commonJS({
211504
211547
  parent
211505
211548
  } = params;
211506
211549
  const actualContentNode = contentNode ?? node;
211507
- const content = actualContentNode.getText(
211508
- sourceFile
211509
- );
211510
- const startLine = getLine(
211511
- actualContentNode.getStart(
211512
- sourceFile
211513
- )
211514
- );
211515
- const endLine = getLine(
211516
- actualContentNode.getEnd()
211517
- );
211550
+ let content = actualContentNode.getText(sourceFile);
211551
+ const startLine = getLine(actualContentNode.getStart(sourceFile));
211552
+ const endLine = getLine(actualContentNode.getEnd());
211553
+ if (startLine > 1) {
211554
+ const allLines = source.split("\n");
211555
+ const startIdx = Math.max(0, startLine - 1 - 30);
211556
+ const prepended = allLines.slice(startIdx, startLine - 1).join("\n");
211557
+ if (prepended.trim().length > 0) {
211558
+ content = `// Context Overlap:
211559
+ ${prepended}
211560
+ // End Overlap
211561
+ ${content}`;
211562
+ }
211563
+ }
211518
211564
  const kind = getChunkKind(node);
211519
211565
  const symbolPath = parent ? `${parent}.${name}` : name;
211520
211566
  const hash = getHash(content);
@@ -224913,6 +224959,7 @@ var require_dist5 = __commonJS({
224913
224959
  ArchitectureSeverity: () => ArchitectureSeverity,
224914
224960
  AutonomousAgent: () => AutonomousAgent2,
224915
224961
  BaseAgent: () => BaseAgent,
224962
+ CombinedReviewOutputSchema: () => CombinedReviewOutputSchema,
224916
224963
  FileEditTool: () => FileEditTool2,
224917
224964
  FileReadTool: () => FileReadTool4,
224918
224965
  FileWriteTool: () => FileWriteTool2,
@@ -224939,11 +224986,11 @@ var require_dist5 = __commonJS({
224939
224986
  module2.exports = __toCommonJS(index_exports);
224940
224987
  var import_git4 = require_dist();
224941
224988
  var import_retrieval5 = require_dist4();
224942
- var import_shared8 = require_dist3();
224989
+ var import_shared9 = require_dist3();
224943
224990
  var path42 = __toESM2(require("path"));
224944
224991
  var fs4 = __toESM2(require("fs"));
224945
224992
  var import_db3 = require_dist2();
224946
- var import_genai3 = require("@google/genai");
224993
+ var import_genai4 = require("@google/genai");
224947
224994
  var import_retrieval3 = require_dist4();
224948
224995
  var import_genai2 = require("@google/genai");
224949
224996
  var import_genai = require("@google/genai");
@@ -224952,6 +224999,7 @@ var require_dist5 = __commonJS({
224952
224999
  var import_db4 = require_dist2();
224953
225000
  var crypto2 = __toESM2(require("crypto"));
224954
225001
  var LLMCacheManager2 = class {
225002
+ static sessionCache = /* @__PURE__ */ new Map();
224955
225003
  /**
224956
225004
  * Generates a deterministic SHA-256 cache key.
224957
225005
  */
@@ -224964,7 +225012,6 @@ var require_dist5 = __commonJS({
224964
225012
  model: params.model,
224965
225013
  promptHash,
224966
225014
  contextHash,
224967
- commitHash: params.commitHash || null,
224968
225015
  temperature: params.temperature || 0
224969
225016
  })
224970
225017
  ).digest("hex");
@@ -224975,11 +225022,15 @@ var require_dist5 = __commonJS({
224975
225022
  * Updates lastAccessedAt and hitCount on cache hit.
224976
225023
  */
224977
225024
  static async getCache(key) {
225025
+ if (this.sessionCache.has(key)) {
225026
+ return this.sessionCache.get(key) || null;
225027
+ }
224978
225028
  try {
224979
225029
  const entry = await import_db4.prisma.lLMCache.findUnique({
224980
225030
  where: { key }
224981
225031
  });
224982
225032
  if (!entry) return null;
225033
+ this.sessionCache.set(key, Promise.resolve(entry.response));
224983
225034
  import_db4.prisma.lLMCache.update({
224984
225035
  where: { key },
224985
225036
  data: {
@@ -225013,6 +225064,7 @@ var require_dist5 = __commonJS({
225013
225064
  lastAccessedAt: /* @__PURE__ */ new Date()
225014
225065
  }
225015
225066
  });
225067
+ this.sessionCache.set(data.key, Promise.resolve(data.response));
225016
225068
  this.cleanupOldCache().catch(() => {
225017
225069
  });
225018
225070
  } catch (err) {
@@ -225023,6 +225075,7 @@ var require_dist5 = __commonJS({
225023
225075
  * Clears all entries from the cache.
225024
225076
  */
225025
225077
  static async clearCache() {
225078
+ this.sessionCache.clear();
225026
225079
  await import_db4.prisma.lLMCache.deleteMany();
225027
225080
  }
225028
225081
  /**
@@ -225105,21 +225158,24 @@ var require_dist5 = __commonJS({
225105
225158
  }
225106
225159
  };
225107
225160
  var DEFAULT_MODEL_PRIORITY = [
225161
+ // Gemini
225162
+ "gemini-2.5-flash",
225163
+ "gemini-3.1-flash-lite",
225164
+ "gemini-2.5-flash-lite",
225165
+ // Groq
225166
+ "llama-3.3-70b-versatile",
225167
+ "llama-3.1-8b-instant",
225168
+ "allam-2-7b",
225169
+ // OpenRouter
225108
225170
  "nvidia/nemotron-3-ultra-550b-a55b:free",
225109
225171
  "nex-agi/nex-n2-pro:free",
225110
225172
  "openrouter/owl-alpha",
225111
225173
  "nvidia/nemotron-3-super-120b-a12b:free",
225112
225174
  "openai/gpt-oss-20b:free",
225113
225175
  "openai/gpt-oss-120b:free",
225114
- "gemini-2.5-flash",
225115
225176
  "openai/gpt-oss-120b",
225116
- "llama-3.3-70b-versatile",
225117
225177
  "qwen/qwen3.6-27b",
225118
- "qwen/qwen3-32b",
225119
- "gemini-3.1-flash-lite",
225120
- "gemini-2.5-flash-lite",
225121
- "llama-3.1-8b-instant",
225122
- "allam-2-7b"
225178
+ "qwen/qwen3-32b"
225123
225179
  ];
225124
225180
  var getApiKeys = (prefix) => {
225125
225181
  const keys = [];
@@ -225483,20 +225539,42 @@ OBSERVATIONS (${toolCalls.length} results):${toolResultsPrompts}`;
225483
225539
  extractToolCalls(response) {
225484
225540
  const cleanResponse = response.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/<state_update>[\s\S]*?<\/state_update>/ig, "").trim();
225485
225541
  const calls = [];
225486
- const xmlRegex = /<([a-zA-Z0-9_]+_)?tool_call>\s*([a-zA-Z0-9_]+)[\s\S]*?<\/\1tool_call>/g;
225542
+ const xmlRegex = /<([a-zA-Z0-9_]+_)?tool_call>([\s\S]*?)<\/\1tool_call>/g;
225487
225543
  let xmlMatch;
225488
225544
  while ((xmlMatch = xmlRegex.exec(cleanResponse)) !== null) {
225489
225545
  const prefix = xmlMatch[1] || "";
225490
- const name = (xmlMatch[2] || "").trim();
225546
+ const blockContent = xmlMatch[2] || "";
225547
+ let name = "";
225548
+ const nameMatch = blockContent.match(/^\s*([a-zA-Z0-9_]+)/);
225549
+ const tagMatch = blockContent.match(/<([a-zA-Z0-9_]*_)?(tool_)?name>\s*([a-zA-Z0-9_]+)\s*<\/\1\2name>/);
225550
+ if (tagMatch && tagMatch[3]) {
225551
+ name = tagMatch[3].trim();
225552
+ } else if (nameMatch && nameMatch[1]) {
225553
+ name = nameMatch[1].trim();
225554
+ }
225491
225555
  const args = {};
225492
225556
  const argRegex = new RegExp(`<(${prefix})?arg_key>\\s*([^<]+)\\s*<\\/\\1?arg_key>\\s*<(${prefix})?arg_value>\\s*([\\s\\S]*?)\\s*<\\/\\3?arg_value>`, "g");
225493
225557
  let match;
225494
- while ((match = argRegex.exec(xmlMatch[0])) !== null) {
225558
+ while ((match = argRegex.exec(blockContent)) !== null) {
225495
225559
  if (match[2] && match[4]) {
225496
225560
  args[match[2].trim()] = match[4].trim();
225497
225561
  }
225498
225562
  }
225499
- calls.push({ name, args, isXml: true, prefix });
225563
+ if (Object.keys(args).length === 0 || name === "write_file" && (!args.path || !args.content) || name === "replace_in_file" && (!args.path || !args.target && !args.replacement)) {
225564
+ const directTagRegex = /<([a-zA-Z0-9_]+)>\s*([\s\S]*?)\s*<\/\1>/g;
225565
+ let directMatch;
225566
+ while ((directMatch = directTagRegex.exec(blockContent)) !== null) {
225567
+ if (directMatch[1] && directMatch[2]) {
225568
+ const key = directMatch[1].trim();
225569
+ if (key !== "tool_call" && key !== "tool_name" && key !== "name" && key !== "arg_key" && key !== "arg_value") {
225570
+ args[key] = directMatch[2].trim();
225571
+ }
225572
+ }
225573
+ }
225574
+ }
225575
+ if (name) {
225576
+ calls.push({ name, args, isXml: true, prefix });
225577
+ }
225500
225578
  }
225501
225579
  if (calls.length > 0) return calls;
225502
225580
  const jsonBlockMatch = cleanResponse.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
@@ -225653,6 +225731,14 @@ OBSERVATIONS (${toolCalls.length} results):${toolResultsPrompts}`;
225653
225731
  summary: import_zod.z.string().describe("1-2 sentence overall architecture assessment"),
225654
225732
  consistencyScore: import_zod.z.enum(["excellent", "good", "fair", "poor"])
225655
225733
  });
225734
+ var CombinedReviewOutputSchema = import_zod.z.object({
225735
+ securityFindings: import_zod.z.array(SecurityFindingSchema),
225736
+ securitySummary: import_zod.z.string().describe("1-2 sentence overall security assessment"),
225737
+ securityRiskLevel: import_zod.z.enum(["safe", "low_risk", "medium_risk", "high_risk", "critical_risk"]),
225738
+ architectureFindings: import_zod.z.array(ArchitectureFindingSchema),
225739
+ architectureSummary: import_zod.z.string().describe("1-2 sentence overall architecture assessment"),
225740
+ architectureConsistencyScore: import_zod.z.enum(["excellent", "good", "fair", "poor"])
225741
+ });
225656
225742
  var ReviewVerdictSchema = import_zod.z.enum(["SAFE_TO_MERGE", "REQUIRES_CHANGES", "NEEDS_DISCUSSION"]);
225657
225743
  var SynthesisOutputSchema = import_zod.z.object({
225658
225744
  verdict: ReviewVerdictSchema,
@@ -225662,128 +225748,9 @@ OBSERVATIONS (${toolCalls.length} results):${toolResultsPrompts}`;
225662
225748
  markdownReport: import_zod.z.string().describe("Full beautifully formatted markdown review report")
225663
225749
  });
225664
225750
  var import_shared2 = require_dist3();
225665
- var SecurityAgent = class extends BaseAgent {
225666
- name = "SecurityAgent";
225667
- systemPrompt = import_shared2.Prompts.securitySystemPrompt;
225668
- buildPrompt(input) {
225669
- let prompt = `## PR Diff to Review
225670
- \`\`\`diff
225671
- ${input.diff}
225672
- \`\`\`
225673
- `;
225674
- if (input.contextChunks.length > 0) {
225675
- prompt += `
225676
- ## Existing Codebase Context
225677
- Use this to understand what security patterns the codebase already uses:
225678
- `;
225679
- input.contextChunks.forEach((chunk, i) => {
225680
- prompt += `
225681
- ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath}
225682
- \`\`\`${chunk.kind}
225683
- ${chunk.content}
225684
- \`\`\`
225685
- `;
225686
- });
225687
- }
225688
- if (input.memories && input.memories.length > 0) {
225689
- prompt += `
225690
- ## Historical Security Context
225691
- These are relevant findings from past reviews:
225692
- `;
225693
- input.memories.forEach((mem, i) => {
225694
- prompt += `- Memory ${i + 1}: ${mem}
225695
- `;
225696
- });
225697
- }
225698
- return prompt;
225699
- }
225700
- parseOutput(rawResponse) {
225701
- const parsed = this.extractJSON(rawResponse);
225702
- if (parsed) {
225703
- const validated = SecurityOutputSchema.safeParse(parsed);
225704
- if (validated.success) {
225705
- return {
225706
- agentName: this.name,
225707
- findings: validated.data.findings,
225708
- summary: validated.data.summary,
225709
- riskLevel: validated.data.riskLevel
225710
- };
225711
- }
225712
- }
225713
- return {
225714
- agentName: this.name,
225715
- findings: [],
225716
- summary: rawResponse.slice(0, 500),
225717
- riskLevel: "low_risk"
225718
- };
225719
- }
225720
- };
225721
- var import_shared3 = require_dist3();
225722
- var ArchitectureAgent = class extends BaseAgent {
225723
- name = "ArchitectureAgent";
225724
- systemPrompt = import_shared3.Prompts.architectureSystemPrompt;
225725
- buildPrompt(input) {
225726
- let prompt = `## PR Diff to Review
225727
- \`\`\`diff
225728
- ${input.diff}
225729
- \`\`\`
225730
- `;
225731
- if (input.contextChunks.length > 0) {
225732
- prompt += `
225733
- ## Existing Codebase Architecture (Retrieved Context)
225734
- These are the most relevant code chunks from the existing codebase. Compare the PR diff against these patterns:
225735
- `;
225736
- input.contextChunks.forEach((chunk, i) => {
225737
- prompt += `
225738
- ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath} (${chunk.kind})
225739
- \`\`\`
225740
- ${chunk.content}
225741
- \`\`\`
225742
- `;
225743
- });
225744
- } else {
225745
- prompt += `
225746
- ## Note
225747
- No existing codebase context was retrieved. Evaluate the PR diff on its own merits, focusing on internal consistency and general best practices.
225748
- `;
225749
- }
225750
- if (input.memories && input.memories.length > 0) {
225751
- prompt += `
225752
- ## Historical Architecture Context
225753
- These are relevant findings from past reviews:
225754
- `;
225755
- input.memories.forEach((mem, i) => {
225756
- prompt += `- Memory ${i + 1}: ${mem}
225757
- `;
225758
- });
225759
- }
225760
- return prompt;
225761
- }
225762
- parseOutput(rawResponse) {
225763
- const parsed = this.extractJSON(rawResponse);
225764
- if (parsed) {
225765
- const validated = ArchitectureOutputSchema.safeParse(parsed);
225766
- if (validated.success) {
225767
- return {
225768
- agentName: this.name,
225769
- findings: validated.data.findings,
225770
- summary: validated.data.summary,
225771
- consistencyScore: validated.data.consistencyScore
225772
- };
225773
- }
225774
- }
225775
- return {
225776
- agentName: this.name,
225777
- findings: [],
225778
- summary: rawResponse.slice(0, 500),
225779
- consistencyScore: "good"
225780
- };
225781
- }
225782
- };
225783
- var import_shared4 = require_dist3();
225784
225751
  var SynthesizerAgent = class extends BaseAgent {
225785
225752
  name = "SynthesizerAgent";
225786
- systemPrompt = import_shared4.Prompts.synthesizerSystemPrompt;
225753
+ systemPrompt = import_shared2.Prompts.synthesizerSystemPrompt;
225787
225754
  buildPrompt(input) {
225788
225755
  const securityOutput = input.previousOutputs?.security;
225789
225756
  const architectureOutput = input.previousOutputs?.architecture;
@@ -225888,57 +225855,127 @@ ${JSON.stringify(parsed, null, 2)}
225888
225855
  };
225889
225856
  }
225890
225857
  };
225858
+ async function generateStructured(client, prompt, schema, options) {
225859
+ const maxRetries = options?.maxValidationRetries ?? 3;
225860
+ let currentPrompt = prompt;
225861
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
225862
+ const responseText = await generateWithRetry(client, currentPrompt, options);
225863
+ try {
225864
+ let cleanText = responseText.trim();
225865
+ if (cleanText.startsWith("```json")) {
225866
+ cleanText = cleanText.replace(/^```json/, "").replace(/```$/, "").trim();
225867
+ } else if (cleanText.startsWith("```")) {
225868
+ cleanText = cleanText.replace(/^```/, "").replace(/```$/, "").trim();
225869
+ }
225870
+ const parsedJson = JSON.parse(cleanText);
225871
+ const result = schema.safeParse(parsedJson);
225872
+ if (result.success) {
225873
+ return result.data;
225874
+ }
225875
+ const errorMsg = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("\n");
225876
+ if (process.env.DEBUG) {
225877
+ console.warn(`[Structured LLM] Validation failed (Attempt ${attempt}/${maxRetries}):
225878
+ ${errorMsg}`);
225879
+ }
225880
+ if (attempt === maxRetries) {
225881
+ throw new Error(`Failed to generate valid structured output after ${maxRetries} attempts. Last error: ${errorMsg}`);
225882
+ }
225883
+ currentPrompt += `
225884
+
225885
+ Your previous JSON response was invalid. Please fix the following errors and try again:
225886
+ ${errorMsg}
225887
+ Make sure to output ONLY valid JSON matching the exact schema requested.`;
225888
+ } catch (e) {
225889
+ if (process.env.DEBUG) {
225890
+ console.warn(`[Structured LLM] JSON Parsing failed (Attempt ${attempt}/${maxRetries}):
225891
+ ${e.message}`);
225892
+ }
225893
+ if (attempt === maxRetries) {
225894
+ throw new Error(`Failed to parse JSON after ${maxRetries} attempts. Last response: ${responseText.slice(0, 200)}...`);
225895
+ }
225896
+ currentPrompt += `
225897
+
225898
+ Your previous response was not valid JSON. Error: ${e.message}
225899
+ Please output ONLY valid JSON.`;
225900
+ }
225901
+ }
225902
+ throw new Error("Unreachable");
225903
+ }
225904
+ var import_genai3 = require("@google/genai");
225905
+ var import_shared3 = require_dist3();
225891
225906
  var ReviewOrchestrator = class {
225892
- securityAgent;
225893
- architectureAgent;
225894
225907
  synthesizerAgent;
225908
+ client;
225895
225909
  constructor(apiKey) {
225896
- this.securityAgent = new SecurityAgent(apiKey);
225897
- this.architectureAgent = new ArchitectureAgent(apiKey);
225910
+ const key = apiKey || process.env.GEMINI_API_KEY;
225911
+ if (!key) {
225912
+ throw new Error("GEMINI_API_KEY is not set.");
225913
+ }
225914
+ this.client = new import_genai3.GoogleGenAI({ apiKey: key });
225898
225915
  this.synthesizerAgent = new SynthesizerAgent(apiKey);
225899
225916
  }
225900
225917
  /**
225901
225918
  * Register tools across all agents that support self-verification.
225902
225919
  */
225903
225920
  registerTools(tools) {
225904
- this.securityAgent.registerTools(tools);
225905
- this.architectureAgent.registerTools(tools);
225921
+ this.synthesizerAgent.registerTools(tools);
225906
225922
  }
225907
225923
  /**
225908
225924
  * Runs the full multi-agent review pipeline.
225909
- *
225910
- * @param diff - The raw PR diff text
225911
- * @param contextChunks - Relevant codebase chunks from hybrid retrieval
225912
- * @param memories - Optional relevant memories from past reviews
225913
- * @returns Comprehensive OrchestratedReview with all agent outputs
225914
225925
  */
225915
225926
  async runReview(diff, contextChunks, memories) {
225916
225927
  const startTime = Date.now();
225917
- const baseInput = {
225918
- diff,
225919
- contextChunks,
225920
- memories
225921
- };
225922
- if (process.env.DEBUG) console.log("\nRunning Security and Architecture agents in parallel...");
225923
- const [securityResult, architectureResult] = await Promise.all([
225924
- this.runAgentSafe(
225925
- "SecurityAgent",
225926
- () => this.securityAgent.run(baseInput)
225927
- ),
225928
- this.runAgentSafe(
225929
- "ArchitectureAgent",
225930
- () => this.architectureAgent.run(baseInput)
225931
- )
225932
- ]);
225928
+ if (process.env.DEBUG) console.log("\nRunning Batched Security + Architecture Review...");
225929
+ let combinedPrompt = `## PR Diff to Review
225930
+ \`\`\`diff
225931
+ ${diff}
225932
+ \`\`\`
225933
+ `;
225934
+ if (contextChunks.length > 0) {
225935
+ combinedPrompt += `
225936
+ ## Existing Codebase Context
225937
+ Use this to understand what patterns the codebase already uses:
225938
+ `;
225939
+ contextChunks.forEach((chunk, i) => {
225940
+ combinedPrompt += `
225941
+ ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath}
225942
+ \`\`\`${chunk.kind}
225943
+ ${chunk.content}
225944
+ \`\`\`
225945
+ `;
225946
+ });
225947
+ }
225948
+ if (memories && memories.length > 0) {
225949
+ combinedPrompt += `
225950
+ ## Historical Context
225951
+ These are relevant findings from past reviews:
225952
+ `;
225953
+ memories.forEach((mem, i) => {
225954
+ combinedPrompt += `- Memory ${i + 1}: ${mem}
225955
+ `;
225956
+ });
225957
+ }
225958
+ combinedPrompt = import_shared3.Prompts.combinedReviewSystemPrompt + "\n\n" + combinedPrompt;
225959
+ let combinedResult = null;
225960
+ try {
225961
+ combinedResult = await generateStructured(
225962
+ this.client,
225963
+ combinedPrompt,
225964
+ CombinedReviewOutputSchema,
225965
+ { label: "ReviewOrchestrator (Batched)", maxValidationRetries: 3 }
225966
+ );
225967
+ } catch (err) {
225968
+ console.error("\nBatched Review failed:", err);
225969
+ }
225933
225970
  const securityOutput = {
225934
- findings: securityResult.findings ?? [],
225935
- summary: securityResult.summary,
225936
- riskLevel: securityResult.riskLevel ?? "low_risk"
225971
+ findings: combinedResult?.securityFindings ?? [],
225972
+ summary: combinedResult?.securitySummary ?? "Security analysis encountered an error.",
225973
+ riskLevel: combinedResult?.securityRiskLevel ?? "low_risk"
225937
225974
  };
225938
225975
  const architectureOutput = {
225939
- findings: architectureResult.findings ?? [],
225940
- summary: architectureResult.summary,
225941
- consistencyScore: architectureResult.consistencyScore ?? "good"
225976
+ findings: combinedResult?.architectureFindings ?? [],
225977
+ summary: combinedResult?.architectureSummary ?? "Architecture analysis encountered an error.",
225978
+ consistencyScore: combinedResult?.architectureConsistencyScore ?? "good"
225942
225979
  };
225943
225980
  if (process.env.DEBUG) {
225944
225981
  console.log(` Security: ${securityOutput.riskLevel} (${securityOutput.findings.length} findings)`);
@@ -226002,7 +226039,7 @@ ${agentName} failed:`, err);
226002
226039
  };
226003
226040
  var import_child_process2 = require("child_process");
226004
226041
  var crypto22 = __toESM2(require("crypto"));
226005
- var import_shared5 = require_dist3();
226042
+ var import_shared4 = require_dist3();
226006
226043
  var IntelligenceAgent5 = class {
226007
226044
  client;
226008
226045
  embedder;
@@ -226013,7 +226050,7 @@ ${agentName} failed:`, err);
226013
226050
  if (!key) {
226014
226051
  throw new Error("GEMINI_API_KEY is not set.");
226015
226052
  }
226016
- this.client = new import_genai3.GoogleGenAI({ apiKey: key });
226053
+ this.client = new import_genai4.GoogleGenAI({ apiKey: key });
226017
226054
  this.embedder = new import_retrieval3.LocalEmbedder();
226018
226055
  this.store = new import_retrieval3.VectorStore();
226019
226056
  this.orchestrator = new ReviewOrchestrator(key);
@@ -226031,8 +226068,8 @@ ${agentName} failed:`, err);
226031
226068
  }
226032
226069
  getCurrentCommitHash() {
226033
226070
  try {
226034
- const hash = (0, import_child_process2.execSync)("git rev-parse HEAD").toString().trim();
226035
- const isDirty = (0, import_child_process2.execSync)("git status --porcelain").toString().trim().length > 0;
226071
+ const hash = (0, import_child_process2.execSync)("git rev-parse HEAD", { stdio: ["pipe", "pipe", "ignore"] }).toString().trim();
226072
+ const isDirty = (0, import_child_process2.execSync)("git status --porcelain", { stdio: ["pipe", "pipe", "ignore"] }).toString().trim().length > 0;
226036
226073
  return isDirty ? `${hash}-dirty` : hash;
226037
226074
  } catch {
226038
226075
  return "unknown";
@@ -226046,7 +226083,7 @@ ${agentName} failed:`, err);
226046
226083
  * Extracts search queries (keywords, function names, classes) from a PR diff.
226047
226084
  */
226048
226085
  async extractSearchQueriesFromDiff(diff) {
226049
- const prompt = import_shared5.Prompts.extractSearchQueries(diff);
226086
+ const prompt = import_shared4.Prompts.extractSearchQueries(diff);
226050
226087
  try {
226051
226088
  let result = await this.callLLM(prompt, { bypassCache: true });
226052
226089
  if (result.startsWith("```")) {
@@ -226062,6 +226099,29 @@ ${agentName} failed:`, err);
226062
226099
  return [];
226063
226100
  }
226064
226101
  }
226102
+ /**
226103
+ * Expands a single query into 3-5 variants for better retrieval recall.
226104
+ */
226105
+ async expandQuery(query) {
226106
+ const prompt = `You are a search query expansion assistant.
226107
+ Given the user query, generate 3-5 distinct, highly relevant search queries that capture different keywords or ways to express the intent.
226108
+ Return ONLY a valid JSON array of strings.
226109
+ Query: "${query}"`;
226110
+ try {
226111
+ let result = await this.callLLM(prompt, { bypassCache: false });
226112
+ if (result.startsWith("```")) {
226113
+ result = result.replace(/^\`\`\`[a-z]*\n/, "").replace(/\n\`\`\`$/, "");
226114
+ }
226115
+ const queries = JSON.parse(result);
226116
+ if (Array.isArray(queries)) {
226117
+ return Array.from(/* @__PURE__ */ new Set([query, ...queries])).slice(0, 5);
226118
+ }
226119
+ return [query];
226120
+ } catch (err) {
226121
+ console.warn("Failed to expand query:", err);
226122
+ return [query];
226123
+ }
226124
+ }
226065
226125
  /**
226066
226126
  * Generates a context-aware RAG code review for a given PR diff.
226067
226127
  */
@@ -226071,7 +226131,7 @@ ${agentName} failed:`, err);
226071
226131
  Code:
226072
226132
  ${c.content}`;
226073
226133
  }).join("\n\n---\n\n");
226074
- const prompt = import_shared5.Prompts.ragReview(diff, contextStr);
226134
+ const prompt = import_shared4.Prompts.ragReview(diff, contextStr);
226075
226135
  const result = await this.callLLM(prompt, {
226076
226136
  commitHash: this.getCurrentCommitHash(),
226077
226137
  retrievalContextHash: this.computeChunksHash(chunks)
@@ -226082,7 +226142,7 @@ ${c.content}`;
226082
226142
  const contextStr = relevantContext.map((c, i) => `--- Chunk ${i + 1} (${c.file} - ${c.symbolPath || "anonymous"}) ---
226083
226143
  ${c.content}`).join("\n\n");
226084
226144
  const commentsStr = comments.map((c, i) => `Comment ${i + 1} (@${c.user?.login}): ${c.body}`).join("\n");
226085
- const prompt = import_shared5.Prompts.issueAnalysis(issueTitle, issueBody, commentsStr, contextStr);
226145
+ const prompt = import_shared4.Prompts.issueAnalysis(issueTitle, issueBody, commentsStr, contextStr);
226086
226146
  return this.callLLM(prompt, {
226087
226147
  commitHash: this.getCurrentCommitHash(),
226088
226148
  retrievalContextHash: this.computeChunksHash(relevantContext)
@@ -226094,7 +226154,7 @@ ${c.content}`).join("\n\n");
226094
226154
  Code:
226095
226155
  ${c.content}`;
226096
226156
  }).join("\n\n---\n\n");
226097
- const prompt = import_shared5.Prompts.answerQuery(query, contextStr);
226157
+ const prompt = import_shared4.Prompts.answerQuery(query, contextStr);
226098
226158
  return await this.callLLM(prompt, {
226099
226159
  commitHash: this.getCurrentCommitHash(),
226100
226160
  retrievalContextHash: this.computeChunksHash(chunks)
@@ -226110,7 +226170,7 @@ ${c.content}`;
226110
226170
  Code:
226111
226171
  ${c.content}`;
226112
226172
  }).join("\n\n---\n\n");
226113
- const prompt = import_shared5.Prompts.executionPlan(task, contextStr);
226173
+ const prompt = import_shared4.Prompts.executionPlan(task, contextStr);
226114
226174
  let result = await this.callLLM(prompt, {
226115
226175
  commitHash: this.getCurrentCommitHash(),
226116
226176
  retrievalContextHash: this.computeChunksHash(contextChunks)
@@ -226143,12 +226203,131 @@ ${c.content}`;
226143
226203
  return this.orchestrator.runReview(diff, contextChunks, memories);
226144
226204
  }
226145
226205
  };
226206
+ var import_shared5 = require_dist3();
226207
+ var SecurityAgent = class extends BaseAgent {
226208
+ name = "SecurityAgent";
226209
+ systemPrompt = import_shared5.Prompts.securitySystemPrompt;
226210
+ buildPrompt(input) {
226211
+ let prompt = `## PR Diff to Review
226212
+ \`\`\`diff
226213
+ ${input.diff}
226214
+ \`\`\`
226215
+ `;
226216
+ if (input.contextChunks.length > 0) {
226217
+ prompt += `
226218
+ ## Existing Codebase Context
226219
+ Use this to understand what security patterns the codebase already uses:
226220
+ `;
226221
+ input.contextChunks.forEach((chunk, i) => {
226222
+ prompt += `
226223
+ ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath}
226224
+ \`\`\`${chunk.kind}
226225
+ ${chunk.content}
226226
+ \`\`\`
226227
+ `;
226228
+ });
226229
+ }
226230
+ if (input.memories && input.memories.length > 0) {
226231
+ prompt += `
226232
+ ## Historical Security Context
226233
+ These are relevant findings from past reviews:
226234
+ `;
226235
+ input.memories.forEach((mem, i) => {
226236
+ prompt += `- Memory ${i + 1}: ${mem}
226237
+ `;
226238
+ });
226239
+ }
226240
+ return prompt;
226241
+ }
226242
+ parseOutput(rawResponse) {
226243
+ const parsed = this.extractJSON(rawResponse);
226244
+ if (parsed) {
226245
+ const validated = SecurityOutputSchema.safeParse(parsed);
226246
+ if (validated.success) {
226247
+ return {
226248
+ agentName: this.name,
226249
+ findings: validated.data.findings,
226250
+ summary: validated.data.summary,
226251
+ riskLevel: validated.data.riskLevel
226252
+ };
226253
+ }
226254
+ }
226255
+ return {
226256
+ agentName: this.name,
226257
+ findings: [],
226258
+ summary: rawResponse.slice(0, 500),
226259
+ riskLevel: "low_risk"
226260
+ };
226261
+ }
226262
+ };
226146
226263
  var import_shared6 = require_dist3();
226264
+ var ArchitectureAgent = class extends BaseAgent {
226265
+ name = "ArchitectureAgent";
226266
+ systemPrompt = import_shared6.Prompts.architectureSystemPrompt;
226267
+ buildPrompt(input) {
226268
+ let prompt = `## PR Diff to Review
226269
+ \`\`\`diff
226270
+ ${input.diff}
226271
+ \`\`\`
226272
+ `;
226273
+ if (input.contextChunks.length > 0) {
226274
+ prompt += `
226275
+ ## Existing Codebase Architecture (Retrieved Context)
226276
+ These are the most relevant code chunks from the existing codebase. Compare the PR diff against these patterns:
226277
+ `;
226278
+ input.contextChunks.forEach((chunk, i) => {
226279
+ prompt += `
226280
+ ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath} (${chunk.kind})
226281
+ \`\`\`
226282
+ ${chunk.content}
226283
+ \`\`\`
226284
+ `;
226285
+ });
226286
+ } else {
226287
+ prompt += `
226288
+ ## Note
226289
+ No existing codebase context was retrieved. Evaluate the PR diff on its own merits, focusing on internal consistency and general best practices.
226290
+ `;
226291
+ }
226292
+ if (input.memories && input.memories.length > 0) {
226293
+ prompt += `
226294
+ ## Historical Architecture Context
226295
+ These are relevant findings from past reviews:
226296
+ `;
226297
+ input.memories.forEach((mem, i) => {
226298
+ prompt += `- Memory ${i + 1}: ${mem}
226299
+ `;
226300
+ });
226301
+ }
226302
+ return prompt;
226303
+ }
226304
+ parseOutput(rawResponse) {
226305
+ const parsed = this.extractJSON(rawResponse);
226306
+ if (parsed) {
226307
+ const validated = ArchitectureOutputSchema.safeParse(parsed);
226308
+ if (validated.success) {
226309
+ return {
226310
+ agentName: this.name,
226311
+ findings: validated.data.findings,
226312
+ summary: validated.data.summary,
226313
+ consistencyScore: validated.data.consistencyScore
226314
+ };
226315
+ }
226316
+ }
226317
+ return {
226318
+ agentName: this.name,
226319
+ findings: [],
226320
+ summary: rawResponse.slice(0, 500),
226321
+ consistencyScore: "good"
226322
+ };
226323
+ }
226324
+ };
226325
+ var import_shared7 = require_dist3();
226147
226326
  var AutonomousAgent2 = class extends BaseAgent {
226148
226327
  name = "Vortex";
226149
226328
  maxToolIterations = 30;
226150
226329
  // overridden by maxSteps if provided
226151
- systemPrompt = import_shared6.Prompts.autonomousSystemPrompt;
226330
+ systemPrompt = import_shared7.Prompts.autonomousSystemPrompt;
226152
226331
  buildPrompt(input) {
226153
226332
  const { diff, contextChunks } = input;
226154
226333
  let prompt = `USER TASK:
@@ -226177,7 +226356,7 @@ ${chunk.content}
226177
226356
  };
226178
226357
  var import_db22 = require_dist2();
226179
226358
  var import_retrieval22 = require_dist4();
226180
- var import_shared7 = require_dist3();
226359
+ var import_shared8 = require_dist3();
226181
226360
  var MemoryService3 = class {
226182
226361
  embedder;
226183
226362
  constructor() {
@@ -226206,7 +226385,7 @@ ${chunk.content}
226206
226385
  let embedding;
226207
226386
  try {
226208
226387
  const embeddings = await this.embedder.embedChunks([
226209
- (0, import_shared7.createQueryChunk)(memoryContent, "memory")
226388
+ (0, import_shared8.createQueryChunk)(memoryContent, "memory")
226210
226389
  ]);
226211
226390
  if (embeddings[0]) {
226212
226391
  embedding = JSON.stringify(embeddings[0]);
@@ -226231,7 +226410,7 @@ ${chunk.content}
226231
226410
  let embedding;
226232
226411
  try {
226233
226412
  const embeddings = await this.embedder.embedChunks([
226234
- (0, import_shared7.createQueryChunk)(content, "memory")
226413
+ (0, import_shared8.createQueryChunk)(content, "memory")
226235
226414
  ]);
226236
226415
  if (embeddings[0]) {
226237
226416
  embedding = JSON.stringify(embeddings[0]);
@@ -226253,7 +226432,7 @@ ${chunk.content}
226253
226432
  let queryEmbedding = null;
226254
226433
  try {
226255
226434
  const embeddings = await this.embedder.embedChunks([
226256
- (0, import_shared7.createQueryChunk)(query)
226435
+ (0, import_shared8.createQueryChunk)(query)
226257
226436
  ]);
226258
226437
  queryEmbedding = embeddings[0] ?? null;
226259
226438
  } catch {
@@ -226273,7 +226452,7 @@ ${chunk.content}
226273
226452
  if (mem.embedding) {
226274
226453
  try {
226275
226454
  const memEmbedding = JSON.parse(mem.embedding);
226276
- similarity = (0, import_shared7.cosineSimilarity)(queryEmbedding, memEmbedding);
226455
+ similarity = (0, import_shared8.cosineSimilarity)(queryEmbedding, memEmbedding);
226277
226456
  } catch {
226278
226457
  }
226279
226458
  }
@@ -226793,33 +226972,50 @@ URL: ${r.url}
226793
226972
  * Builds both the vector store (for semantic search) and the BM25 index (for keyword search).
226794
226973
  */
226795
226974
  async indexRepository(cwd) {
226796
- if (!(0, import_git4.isGitRepo)(cwd)) {
226797
- throw new Error(`Directory ${cwd} is not a git repository.`);
226975
+ let root = cwd;
226976
+ if ((0, import_git4.isGitRepo)(cwd)) {
226977
+ try {
226978
+ root = (0, import_git4.getGitRoot)(cwd);
226979
+ } catch {
226980
+ }
226798
226981
  }
226799
- const root = (0, import_git4.getGitRoot)(cwd);
226800
226982
  await (0, import_db3.initDatabase)();
226801
- const files = (0, import_git4.listTrackedFiles)(root).filter((file) => {
226802
- const ext = path42.extname(file);
226803
- const supportedExts = [
226804
- ".ts",
226805
- ".tsx",
226806
- ".js",
226807
- ".jsx",
226808
- ".py",
226809
- ".go",
226810
- ".rs",
226811
- ".java",
226812
- ".cpp",
226813
- ".hpp",
226814
- ".c",
226815
- ".h",
226816
- ".rb",
226817
- ".php",
226818
- ".html",
226819
- ".css"
226820
- ];
226821
- return supportedExts.includes(ext) && !file.includes("node_modules");
226822
- });
226983
+ let files = [];
226984
+ if ((0, import_git4.isGitRepo)(cwd)) {
226985
+ try {
226986
+ const tracked = (0, import_git4.listTrackedFiles)(root).filter((file) => {
226987
+ const ext = path42.extname(file);
226988
+ const supportedExts = [
226989
+ ".ts",
226990
+ ".tsx",
226991
+ ".js",
226992
+ ".jsx",
226993
+ ".py",
226994
+ ".go",
226995
+ ".rs",
226996
+ ".java",
226997
+ ".cpp",
226998
+ ".hpp",
226999
+ ".c",
227000
+ ".h",
227001
+ ".rb",
227002
+ ".php",
227003
+ ".html",
227004
+ ".css"
227005
+ ];
227006
+ return supportedExts.includes(ext) && !file.includes("node_modules");
227007
+ });
227008
+ if (tracked.length > 0) {
227009
+ files = tracked;
227010
+ }
227011
+ } catch (e) {
227012
+ }
227013
+ }
227014
+ if (files.length === 0) {
227015
+ for await (const file of (0, import_retrieval5.scanFiles)(root)) {
227016
+ files.push(file);
227017
+ }
227018
+ }
226823
227019
  let totalChunks = 0;
226824
227020
  for (const file of files) {
226825
227021
  try {
@@ -226855,7 +227051,7 @@ URL: ${r.url}
226855
227051
  console.log(`Generating embedding for query: "${query}"...`);
226856
227052
  await (0, import_db3.initDatabase)();
226857
227053
  const queryEmbedding = await this.embedder.embedChunks([
226858
- (0, import_shared8.createQueryChunk)(query)
227054
+ (0, import_shared9.createQueryChunk)(query)
226859
227055
  ]);
226860
227056
  if (queryEmbedding.length === 0) {
226861
227057
  return [];
@@ -227138,7 +227334,7 @@ var require_package = __commonJS({
227138
227334
  "package.json"(exports2, module2) {
227139
227335
  module2.exports = {
227140
227336
  name: "@vortex-ai/cli",
227141
- version: "0.1.45",
227337
+ version: "0.1.50",
227142
227338
  description: "Vortex CLI - The main entry point",
227143
227339
  main: "./dist/index.js",
227144
227340
  bin: {
@@ -227255,17 +227451,25 @@ async function searchCommand(options) {
227255
227451
  const spinner = ora2(`Searching codebase for: "${options.query}"...`).start();
227256
227452
  const indexer = new import_engine2.Indexer();
227257
227453
  try {
227258
- spinner.text = "Running hybrid search (vector + BM25 + cross-encoder)...";
227259
- const results = await indexer.hybridSearch(options.query, parseInt(options.limit, 10));
227260
- if (results.length === 0) {
227454
+ spinner.text = options.expandQuery ? "Expanding query and running parallel hybrid search..." : "Running hybrid search (vector + BM25 + cross-encoder)...";
227455
+ let queriesToSearch = [options.query];
227456
+ const agent = new import_engine2.IntelligenceAgent();
227457
+ if (options.expandQuery) {
227458
+ queriesToSearch = await agent.expandQuery(options.query);
227459
+ }
227460
+ const allResults = await Promise.all(
227461
+ queriesToSearch.map((q) => indexer.hybridSearch(q, parseInt(options.limit, 10)))
227462
+ );
227463
+ const flatResults = allResults.flat();
227464
+ const uniqueResults = Array.from(new Map(flatResults.map((c) => [c.id, c])).values()).sort((a, b) => b.score - a.score).slice(0, parseInt(options.limit, 10));
227465
+ if (uniqueResults.length === 0) {
227261
227466
  spinner.fail("No relevant code found.");
227262
227467
  return;
227263
227468
  }
227264
- spinner.text = `Found ${results.length} relevant code chunks. Analyzing with AI engine...`;
227469
+ spinner.text = `Found ${uniqueResults.length} relevant code chunks. Analyzing with AI engine...`;
227265
227470
  const memoryService = new import_engine2.MemoryService();
227266
227471
  const memories = await memoryService.recallRelevantMemories(options.query, 3);
227267
- const agent = new import_engine2.IntelligenceAgent();
227268
- const answer = await agent.answerQueryWithContext(options.query, results);
227472
+ const answer = await agent.answerQueryWithContext(options.query, uniqueResults);
227269
227473
  spinner.succeed("Analysis complete!\n");
227270
227474
  const parsedAnswer = await marked.parse(answer);
227271
227475
  const formatted = boxen(parsedAnswer.trim(), {
@@ -227278,7 +227482,7 @@ async function searchCommand(options) {
227278
227482
  });
227279
227483
  console.log(formatted);
227280
227484
  console.log(chalk2.cyan.dim(" Reference Material (Hybrid Retrieval)"));
227281
- results.forEach((res, i) => {
227485
+ uniqueResults.forEach((res, i) => {
227282
227486
  const sources = res.sources ? res.sources.join("+") : "vector";
227283
227487
  const scoreStr = res.score ? (res.score * 100).toFixed(1) + "%" : "N/A";
227284
227488
  console.log(chalk2.gray(` \u2502 [${i + 1}] ${res.file.replace(process.cwd(), "")} \u2794 ${res.symbolPath || "(anonymous)"} (${scoreStr}) [${sources}]`));
@@ -227357,11 +227561,11 @@ async function reviewCommand(options) {
227357
227561
  const queries = await agent.extractSearchQueriesFromDiff(diff);
227358
227562
  const allChunks = [];
227359
227563
  if (queries.length > 0) {
227360
- spinner.text = `Hybrid searching for context...`;
227361
- for (const query of queries) {
227362
- const results = await indexer.hybridSearch(query, 3);
227363
- allChunks.push(...results);
227364
- }
227564
+ spinner.text = `Hybrid searching for context (parallel)...`;
227565
+ const allResults = await Promise.all(
227566
+ queries.map((query) => indexer.hybridSearch(query, 3))
227567
+ );
227568
+ allChunks.push(...allResults.flat());
227365
227569
  }
227366
227570
  const uniqueChunks = Array.from(
227367
227571
  new Map(allChunks.map((c) => [c.id, c])).values()
@@ -227614,7 +227818,7 @@ Created new project folder: ${projectPath}`));
227614
227818
  if (!options.contextChunks || options.contextChunks.length === 0) {
227615
227819
  try {
227616
227820
  const indexer = new import_engine5.Indexer();
227617
- const relevantContext = await indexer.hybridSearch(prompt, 5);
227821
+ const relevantContext = await indexer.hybridSearch(prompt, 15);
227618
227822
  options.contextChunks = relevantContext.map((c) => ({
227619
227823
  file: c.file,
227620
227824
  symbolPath: c.symbolPath || "anonymous",
@@ -227637,7 +227841,11 @@ Created new project folder: ${projectPath}`));
227637
227841
  const parsedPlan = JSON.parse(executionPlanStr);
227638
227842
  planSummary = parsedPlan.summary || planSummary;
227639
227843
  stepCount = parsedPlan.steps ? parsedPlan.steps.length : 0;
227640
- executionPlan = parsedPlan.steps ? parsedPlan.steps.map((s, i) => `${i + 1}. ${s}`).join("\n") : executionPlanStr;
227844
+ executionPlan = parsedPlan.steps ? parsedPlan.steps.map((s, i) => {
227845
+ if (typeof s === "string") return `${i + 1}. ${s}`;
227846
+ return `${i + 1}. [${(s.action || "MODIFY").toUpperCase()}] ${s.file || "General"}
227847
+ ${s.description}`;
227848
+ }).join("\n\n") : executionPlanStr;
227641
227849
  filesToRead = parsedPlan.filesToRead || [];
227642
227850
  } catch {
227643
227851
  executionPlan = executionPlanStr;
@@ -227734,6 +227942,7 @@ Allow? (y/N) `;
227734
227942
  },
227735
227943
  {
227736
227944
  initialState,
227945
+ verifyCommand: options.verify,
227737
227946
  onToolCall: (toolName, args) => {
227738
227947
  if (toolName === "write_file") {
227739
227948
  spinner.text = `Writing ${args.path}...`;
@@ -227869,7 +228078,8 @@ Please fix this issue in the codebase.`;
227869
228078
  await solveCommand(prompt, {
227870
228079
  autoApprove: options.autoApprove,
227871
228080
  maxSteps: options.maxSteps,
227872
- contextChunks
228081
+ contextChunks,
228082
+ verify: options.verify
227873
228083
  });
227874
228084
  } catch (err) {
227875
228085
  spinner.fail("Failed to setup solve-issue");
@@ -227886,9 +228096,9 @@ cacheCommand.command("stats").description("View LLM cache statistics").action(as
227886
228096
  const stats = await import_engine7.LLMCacheManager.getStats();
227887
228097
  console.log("\nLLM Cache Statistics\n");
227888
228098
  console.log(`Entries: ${stats.entries.toLocaleString()}`);
227889
- console.log(`Hits: ${stats.hits.toLocaleString()}`);
228099
+ console.log(`Saved API Calls: ${stats.hits.toLocaleString()}`);
227890
228100
  const storageMB = (stats.storage / 1024 / 1024).toFixed(2);
227891
- console.log(`Storage: ${storageMB} MB
228101
+ console.log(`Storage Used: ${storageMB} MB
227892
228102
  `);
227893
228103
  } catch (err) {
227894
228104
  console.error("Failed to retrieve cache stats:", err);
@@ -227953,12 +228163,12 @@ program.hook("preAction", async (thisCommand, actionCommand) => {
227953
228163
  }
227954
228164
  });
227955
228165
  program.command("init").description("Initialize repository intelligence, embeddings, and PR history").option("--reindex", "Rebuild repository embeddings while preserving historical PR intelligence").action(initCommand);
227956
- program.command("search").description("Search the indexed codebase semantically and get an AI explanation").requiredOption("-q, --query <text>", "Search query").option("-l, --limit <number>", "Number of results to consider", "5").option("--no-cache", "Disable LLM response caching").action(searchCommand);
228166
+ program.command("search").description("Search the indexed codebase semantically and get an AI explanation").requiredOption("-q, --query <text>", "Search query").option("-l, --limit <number>", "Number of results to consider", "5").option("--expand-query", "Expand the search query using the LLM for better recall").option("--no-cache", "Disable LLM response caching").action(searchCommand);
227957
228167
  program.command("review").description("Review your changes using repository intelligence and historical PR patterns").option("--pr <number>", "Pull request number", Number).option("--deep", "Enable deep review analysis").option("--no-cache", "Disable LLM response caching").action(reviewCommand);
227958
228168
  program.command("issue").description("Analyze a GitHub issue, locate relevant codebase files, and propose a fix").requiredOption("--id <number>", "Issue number", Number).option("--no-cache", "Disable LLM response caching").action(issueCommand);
227959
228169
  program.command("graph").description("Generate a Mermaid JS dependency graph of the project or a specific file").option("--file <path>", "Filter graph to only include dependencies for a specific file").option("--detailed", "Include individual functions and classes in the graph instead of just files").action(graphCommand);
227960
- program.command("solve").description("Autonomously solve a task by writing code and executing commands").argument("<prompt>", "The task you want the autonomous agent to solve").option("--auto-approve", "Skip interactive prompts for file writes and shell commands").option("--max-steps <number>", "Maximum number of agent loop iterations", Number, 30).option("--new-project <folder>", "Create a new project folder and initialize git before solving").action((prompt, options) => solveCommand(prompt, options));
227961
- program.command("solve-issue").description("Autonomously solve a GitHub issue using local RAG context").requiredOption("--id <number>", "Issue number", Number).option("--auto-approve", "Skip interactive prompts for file writes and shell commands").option("--max-steps <number>", "Maximum number of agent loop iterations", Number, 30).action(solveIssueCommand);
228170
+ program.command("solve").description("Autonomously solve a task by writing code and executing commands").argument("<prompt>", "The task you want the autonomous agent to solve").option("--auto-approve", "Skip interactive prompts for file writes and shell commands").option("--max-steps <number>", "Maximum number of agent loop iterations", Number, 30).option("--new-project <folder>", "Create a new project folder and initialize git before solving").option("--verify [command]", "Run a verification command after completion (e.g., 'npm run check-types'). Agent will self-correct on failure.").action((prompt, options) => solveCommand(prompt, options));
228171
+ program.command("solve-issue").description("Autonomously solve a GitHub issue using local RAG context").requiredOption("--id <number>", "Issue number", Number).option("--auto-approve", "Skip interactive prompts for file writes and shell commands").option("--max-steps <number>", "Maximum number of agent loop iterations", Number, 30).option("--verify [command]", "Run a verification command after completion. Agent will self-correct on failure.").action(solveIssueCommand);
227962
228172
  program.addCommand(cacheCommand);
227963
228173
  program.parse();
227964
228174
  /*! Bundled license information: