@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.mjs CHANGED
@@ -210765,19 +210765,16 @@ var require_dist3 = __commonJS({
210765
210765
  module.exports = __toCommonJS(index_exports);
210766
210766
  var Prompts = {
210767
210767
  // ── Engine / Intelligence Agent Prompts ──
210768
- extractSearchQueries: (diff) => `
210769
- You are an expert code analyzer.
210770
- Analyze the following git diff and extract exactly 3 short search queries.
210771
- These queries should be the names of the most important functions, classes, or architectural concepts that are modified or referenced in this PR.
210772
- Your goal is to extract queries that can be used in a vector search engine to find the relevant codebase context.
210768
+ extractSearchQueries: (diff) => `You must return ONLY a valid JSON array of exactly 3 strings. Example: ["auth flow", "DatabaseService", "user login"]
210773
210769
 
210774
- Return ONLY a valid JSON array of 3 strings. No markdown formatting, no explanation.
210770
+ You are an expert code analyzer.
210771
+ Analyze the following git diff and extract 3 short search queries.
210772
+ These queries should be the names of the most important functions, classes, or architectural concepts modified or referenced in this PR.
210775
210773
 
210776
210774
  Diff:
210777
210775
  \`\`\`diff
210778
210776
  ${diff}
210779
- \`\`\`
210780
- `,
210777
+ \`\`\``,
210781
210778
  ragReview: (diff, contextStr) => `
210782
210779
  You are an expert Principal Software Engineer reviewing a pull request.
210783
210780
  You have been provided with the git diff of the pull request, AND some relevant code chunks from the existing codebase for context.
@@ -210838,34 +210835,33 @@ Your answer must follow these strict guidelines:
210838
210835
  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).
210839
210836
  5. If the provided chunks do not contain enough information to answer fully, explicitly state what is missing.
210840
210837
  `,
210841
- executionPlan: (task, contextStr) => `You are an autonomous coding agent.
210842
-
210843
- # Task
210844
- ${task}
210845
-
210846
- # Relevant Codebase Context
210847
- ${contextStr}
210848
-
210849
- Analyze the task and available codebase context.
210838
+ executionPlan: (task, contextStr) => `You must return ONLY a structured JSON object matching this schema:
210839
+ {
210840
+ "summary": "A deep, 3-4 sentence explanation of the architectural approach.",
210841
+ "filesToRead": ["src/index.ts", "package.json"],
210842
+ "steps": [
210843
+ {
210844
+ "action": "create" | "modify" | "delete" | "test",
210845
+ "file": "src/App.js",
210846
+ "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."
210847
+ }
210848
+ ]
210849
+ }
210850
210850
 
210851
- Output ONLY a structured JSON execution plan containing the steps and the files that need to be read before the agent starts.
210851
+ You are a Principal AI Software Architect. Analyze the task and the extensive codebase context provided below to create a highly detailed execution plan.
210852
210852
 
210853
210853
  Rules:
210854
- * Maximum 7-10 steps.
210855
- * Each step must be one sentence.
210856
- * Focus only on actions required to complete the task.
210857
- * Do not explain reasoning.
210858
- * Do not describe implementation details.
210859
- * Do not list speculative edge cases.
210860
- * Do not create documentation-style plans and DO NOT commit anything.
210861
- * Prefer concrete actions such as inspect, modify, implement, test, validate.
210854
+ - Make the plan EXTREMELY detailed. Do not skip steps.
210855
+ - Explain the 'why' and the 'how' for each step.
210856
+ - Include precise implementation details, such as variable names, function signatures, and logic flows.
210857
+ - Make sure to identify all related files that need to be read or modified.
210858
+ - Output ONLY valid JSON.
210862
210859
 
210863
- Schema:
210864
- {
210865
- "steps": ["Step 1", "Step 2"],
210866
- "filesToRead": ["src/index.ts", "package.json"]
210867
- }
210868
- `,
210860
+ # Task
210861
+ ${task}
210862
+
210863
+ # Extensive Codebase Context
210864
+ ${contextStr}`,
210869
210865
  // ── Multi-Agent PR Review Prompts ──
210870
210866
  securitySystemPrompt: `You are a world-class Application Security Engineer conducting a security-focused code review.
210871
210867
  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.
@@ -210927,6 +210923,53 @@ Severity Guide:
210927
210923
 
210928
210924
  If the PR does not contain any breaking or major structural issues, return an empty findings array with consistencyScore "excellent".
210929
210925
  Return ONLY the JSON object. No markdown. No explanation outside the JSON.`,
210926
+ combinedReviewSystemPrompt: `You must return your findings as a valid JSON object matching this EXACT schema:
210927
+ {
210928
+ "securityFindings": [
210929
+ {
210930
+ "title": "Short title",
210931
+ "severity": "critical" | "high" | "medium" | "low" | "info",
210932
+ "description": "Detailed explanation",
210933
+ "file": "filename or N/A",
210934
+ "lineHint": "approximate line",
210935
+ "recommendation": "How to fix"
210936
+ }
210937
+ ],
210938
+ "securitySummary": "1-2 sentence overall security assessment",
210939
+ "securityRiskLevel": "safe" | "low_risk" | "medium_risk" | "high_risk" | "critical_risk",
210940
+ "architectureFindings": [
210941
+ {
210942
+ "title": "Short title",
210943
+ "severity": "breaking" | "major" | "minor" | "suggestion",
210944
+ "description": "Detailed explanation",
210945
+ "affectedPattern": "Which existing pattern is affected",
210946
+ "recommendation": "How to align with existing architecture"
210947
+ }
210948
+ ],
210949
+ "architectureSummary": "1-2 sentence overall architecture assessment",
210950
+ "architectureConsistencyScore": "excellent" | "good" | "fair" | "poor"
210951
+ }
210952
+
210953
+ You are a Principal Software Architect and Security Engineer conducting a unified code review.
210954
+ Your ONLY job is to analyze the PR diff for BOTH Security Vulnerabilities and Architectural Consistency.
210955
+
210956
+ RULES:
210957
+ - ONLY use the provided PR Diff and Codebase Context. Do not invent code.
210958
+ - Ignore code style, nit-picks, and low severity issues.
210959
+ - Do NOT flag "Prompt Injection" or "Data Exfiltration" in local CLI code.
210960
+ - Do NOT flag imports from \`@vortex/shared\`.
210961
+
210962
+ Example valid output:
210963
+ {
210964
+ "securityFindings": [],
210965
+ "securitySummary": "No high-risk vulnerabilities found.",
210966
+ "securityRiskLevel": "safe",
210967
+ "architectureFindings": [],
210968
+ "architectureSummary": "Architecture remains highly cohesive.",
210969
+ "architectureConsistencyScore": "excellent"
210970
+ }
210971
+
210972
+ Return ONLY valid JSON. No markdown fences.`,
210930
210973
  synthesizerSystemPrompt: `You are a Staff Engineer writing the FINAL code review report for a pull request.
210931
210974
  You have received analysis from two specialist agents:
210932
210975
  1. **SecurityAgent** \u2014 found security vulnerabilities
@@ -211267,17 +211310,20 @@ var require_dist4 = __commonJS({
211267
211310
  parent
211268
211311
  } = params;
211269
211312
  const actualContentNode = contentNode ?? node;
211270
- const content = actualContentNode.getText(
211271
- sourceFile
211272
- );
211273
- const startLine = getLine(
211274
- actualContentNode.getStart(
211275
- sourceFile
211276
- )
211277
- );
211278
- const endLine = getLine(
211279
- actualContentNode.getEnd()
211280
- );
211313
+ let content = actualContentNode.getText(sourceFile);
211314
+ const startLine = getLine(actualContentNode.getStart(sourceFile));
211315
+ const endLine = getLine(actualContentNode.getEnd());
211316
+ if (startLine > 1) {
211317
+ const allLines = source.split("\n");
211318
+ const startIdx = Math.max(0, startLine - 1 - 30);
211319
+ const prepended = allLines.slice(startIdx, startLine - 1).join("\n");
211320
+ if (prepended.trim().length > 0) {
211321
+ content = `// Context Overlap:
211322
+ ${prepended}
211323
+ // End Overlap
211324
+ ${content}`;
211325
+ }
211326
+ }
211281
211327
  const kind = getChunkKind(node);
211282
211328
  const symbolPath = parent ? `${parent}.${name}` : name;
211283
211329
  const hash = getHash(content);
@@ -224676,6 +224722,7 @@ var require_dist5 = __commonJS({
224676
224722
  ArchitectureSeverity: () => ArchitectureSeverity,
224677
224723
  AutonomousAgent: () => AutonomousAgent2,
224678
224724
  BaseAgent: () => BaseAgent,
224725
+ CombinedReviewOutputSchema: () => CombinedReviewOutputSchema,
224679
224726
  FileEditTool: () => FileEditTool2,
224680
224727
  FileReadTool: () => FileReadTool4,
224681
224728
  FileWriteTool: () => FileWriteTool2,
@@ -224702,11 +224749,11 @@ var require_dist5 = __commonJS({
224702
224749
  module.exports = __toCommonJS(index_exports);
224703
224750
  var import_git4 = require_dist();
224704
224751
  var import_retrieval5 = require_dist4();
224705
- var import_shared8 = require_dist3();
224752
+ var import_shared9 = require_dist3();
224706
224753
  var path42 = __toESM2(__require("path"));
224707
224754
  var fs4 = __toESM2(__require("fs"));
224708
224755
  var import_db3 = require_dist2();
224709
- var import_genai3 = __require("@google/genai");
224756
+ var import_genai4 = __require("@google/genai");
224710
224757
  var import_retrieval3 = require_dist4();
224711
224758
  var import_genai2 = __require("@google/genai");
224712
224759
  var import_genai = __require("@google/genai");
@@ -224715,6 +224762,7 @@ var require_dist5 = __commonJS({
224715
224762
  var import_db4 = require_dist2();
224716
224763
  var crypto2 = __toESM2(__require("crypto"));
224717
224764
  var LLMCacheManager2 = class {
224765
+ static sessionCache = /* @__PURE__ */ new Map();
224718
224766
  /**
224719
224767
  * Generates a deterministic SHA-256 cache key.
224720
224768
  */
@@ -224727,7 +224775,6 @@ var require_dist5 = __commonJS({
224727
224775
  model: params.model,
224728
224776
  promptHash,
224729
224777
  contextHash,
224730
- commitHash: params.commitHash || null,
224731
224778
  temperature: params.temperature || 0
224732
224779
  })
224733
224780
  ).digest("hex");
@@ -224738,11 +224785,15 @@ var require_dist5 = __commonJS({
224738
224785
  * Updates lastAccessedAt and hitCount on cache hit.
224739
224786
  */
224740
224787
  static async getCache(key) {
224788
+ if (this.sessionCache.has(key)) {
224789
+ return this.sessionCache.get(key) || null;
224790
+ }
224741
224791
  try {
224742
224792
  const entry = await import_db4.prisma.lLMCache.findUnique({
224743
224793
  where: { key }
224744
224794
  });
224745
224795
  if (!entry) return null;
224796
+ this.sessionCache.set(key, Promise.resolve(entry.response));
224746
224797
  import_db4.prisma.lLMCache.update({
224747
224798
  where: { key },
224748
224799
  data: {
@@ -224776,6 +224827,7 @@ var require_dist5 = __commonJS({
224776
224827
  lastAccessedAt: /* @__PURE__ */ new Date()
224777
224828
  }
224778
224829
  });
224830
+ this.sessionCache.set(data.key, Promise.resolve(data.response));
224779
224831
  this.cleanupOldCache().catch(() => {
224780
224832
  });
224781
224833
  } catch (err) {
@@ -224786,6 +224838,7 @@ var require_dist5 = __commonJS({
224786
224838
  * Clears all entries from the cache.
224787
224839
  */
224788
224840
  static async clearCache() {
224841
+ this.sessionCache.clear();
224789
224842
  await import_db4.prisma.lLMCache.deleteMany();
224790
224843
  }
224791
224844
  /**
@@ -224868,21 +224921,24 @@ var require_dist5 = __commonJS({
224868
224921
  }
224869
224922
  };
224870
224923
  var DEFAULT_MODEL_PRIORITY = [
224924
+ // Gemini
224925
+ "gemini-2.5-flash",
224926
+ "gemini-3.1-flash-lite",
224927
+ "gemini-2.5-flash-lite",
224928
+ // Groq
224929
+ "llama-3.3-70b-versatile",
224930
+ "llama-3.1-8b-instant",
224931
+ "allam-2-7b",
224932
+ // OpenRouter
224871
224933
  "nvidia/nemotron-3-ultra-550b-a55b:free",
224872
224934
  "nex-agi/nex-n2-pro:free",
224873
224935
  "openrouter/owl-alpha",
224874
224936
  "nvidia/nemotron-3-super-120b-a12b:free",
224875
224937
  "openai/gpt-oss-20b:free",
224876
224938
  "openai/gpt-oss-120b:free",
224877
- "gemini-2.5-flash",
224878
224939
  "openai/gpt-oss-120b",
224879
- "llama-3.3-70b-versatile",
224880
224940
  "qwen/qwen3.6-27b",
224881
- "qwen/qwen3-32b",
224882
- "gemini-3.1-flash-lite",
224883
- "gemini-2.5-flash-lite",
224884
- "llama-3.1-8b-instant",
224885
- "allam-2-7b"
224941
+ "qwen/qwen3-32b"
224886
224942
  ];
224887
224943
  var getApiKeys = (prefix) => {
224888
224944
  const keys = [];
@@ -225246,20 +225302,42 @@ OBSERVATIONS (${toolCalls.length} results):${toolResultsPrompts}`;
225246
225302
  extractToolCalls(response) {
225247
225303
  const cleanResponse = response.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/<state_update>[\s\S]*?<\/state_update>/ig, "").trim();
225248
225304
  const calls = [];
225249
- const xmlRegex = /<([a-zA-Z0-9_]+_)?tool_call>\s*([a-zA-Z0-9_]+)[\s\S]*?<\/\1tool_call>/g;
225305
+ const xmlRegex = /<([a-zA-Z0-9_]+_)?tool_call>([\s\S]*?)<\/\1tool_call>/g;
225250
225306
  let xmlMatch;
225251
225307
  while ((xmlMatch = xmlRegex.exec(cleanResponse)) !== null) {
225252
225308
  const prefix = xmlMatch[1] || "";
225253
- const name = (xmlMatch[2] || "").trim();
225309
+ const blockContent = xmlMatch[2] || "";
225310
+ let name = "";
225311
+ const nameMatch = blockContent.match(/^\s*([a-zA-Z0-9_]+)/);
225312
+ const tagMatch = blockContent.match(/<([a-zA-Z0-9_]*_)?(tool_)?name>\s*([a-zA-Z0-9_]+)\s*<\/\1\2name>/);
225313
+ if (tagMatch && tagMatch[3]) {
225314
+ name = tagMatch[3].trim();
225315
+ } else if (nameMatch && nameMatch[1]) {
225316
+ name = nameMatch[1].trim();
225317
+ }
225254
225318
  const args = {};
225255
225319
  const argRegex = new RegExp(`<(${prefix})?arg_key>\\s*([^<]+)\\s*<\\/\\1?arg_key>\\s*<(${prefix})?arg_value>\\s*([\\s\\S]*?)\\s*<\\/\\3?arg_value>`, "g");
225256
225320
  let match;
225257
- while ((match = argRegex.exec(xmlMatch[0])) !== null) {
225321
+ while ((match = argRegex.exec(blockContent)) !== null) {
225258
225322
  if (match[2] && match[4]) {
225259
225323
  args[match[2].trim()] = match[4].trim();
225260
225324
  }
225261
225325
  }
225262
- calls.push({ name, args, isXml: true, prefix });
225326
+ if (Object.keys(args).length === 0 || name === "write_file" && (!args.path || !args.content) || name === "replace_in_file" && (!args.path || !args.target && !args.replacement)) {
225327
+ const directTagRegex = /<([a-zA-Z0-9_]+)>\s*([\s\S]*?)\s*<\/\1>/g;
225328
+ let directMatch;
225329
+ while ((directMatch = directTagRegex.exec(blockContent)) !== null) {
225330
+ if (directMatch[1] && directMatch[2]) {
225331
+ const key = directMatch[1].trim();
225332
+ if (key !== "tool_call" && key !== "tool_name" && key !== "name" && key !== "arg_key" && key !== "arg_value") {
225333
+ args[key] = directMatch[2].trim();
225334
+ }
225335
+ }
225336
+ }
225337
+ }
225338
+ if (name) {
225339
+ calls.push({ name, args, isXml: true, prefix });
225340
+ }
225263
225341
  }
225264
225342
  if (calls.length > 0) return calls;
225265
225343
  const jsonBlockMatch = cleanResponse.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
@@ -225416,6 +225494,14 @@ OBSERVATIONS (${toolCalls.length} results):${toolResultsPrompts}`;
225416
225494
  summary: import_zod.z.string().describe("1-2 sentence overall architecture assessment"),
225417
225495
  consistencyScore: import_zod.z.enum(["excellent", "good", "fair", "poor"])
225418
225496
  });
225497
+ var CombinedReviewOutputSchema = import_zod.z.object({
225498
+ securityFindings: import_zod.z.array(SecurityFindingSchema),
225499
+ securitySummary: import_zod.z.string().describe("1-2 sentence overall security assessment"),
225500
+ securityRiskLevel: import_zod.z.enum(["safe", "low_risk", "medium_risk", "high_risk", "critical_risk"]),
225501
+ architectureFindings: import_zod.z.array(ArchitectureFindingSchema),
225502
+ architectureSummary: import_zod.z.string().describe("1-2 sentence overall architecture assessment"),
225503
+ architectureConsistencyScore: import_zod.z.enum(["excellent", "good", "fair", "poor"])
225504
+ });
225419
225505
  var ReviewVerdictSchema = import_zod.z.enum(["SAFE_TO_MERGE", "REQUIRES_CHANGES", "NEEDS_DISCUSSION"]);
225420
225506
  var SynthesisOutputSchema = import_zod.z.object({
225421
225507
  verdict: ReviewVerdictSchema,
@@ -225425,128 +225511,9 @@ OBSERVATIONS (${toolCalls.length} results):${toolResultsPrompts}`;
225425
225511
  markdownReport: import_zod.z.string().describe("Full beautifully formatted markdown review report")
225426
225512
  });
225427
225513
  var import_shared2 = require_dist3();
225428
- var SecurityAgent = class extends BaseAgent {
225429
- name = "SecurityAgent";
225430
- systemPrompt = import_shared2.Prompts.securitySystemPrompt;
225431
- buildPrompt(input) {
225432
- let prompt = `## PR Diff to Review
225433
- \`\`\`diff
225434
- ${input.diff}
225435
- \`\`\`
225436
- `;
225437
- if (input.contextChunks.length > 0) {
225438
- prompt += `
225439
- ## Existing Codebase Context
225440
- Use this to understand what security patterns the codebase already uses:
225441
- `;
225442
- input.contextChunks.forEach((chunk, i) => {
225443
- prompt += `
225444
- ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath}
225445
- \`\`\`${chunk.kind}
225446
- ${chunk.content}
225447
- \`\`\`
225448
- `;
225449
- });
225450
- }
225451
- if (input.memories && input.memories.length > 0) {
225452
- prompt += `
225453
- ## Historical Security Context
225454
- These are relevant findings from past reviews:
225455
- `;
225456
- input.memories.forEach((mem, i) => {
225457
- prompt += `- Memory ${i + 1}: ${mem}
225458
- `;
225459
- });
225460
- }
225461
- return prompt;
225462
- }
225463
- parseOutput(rawResponse) {
225464
- const parsed = this.extractJSON(rawResponse);
225465
- if (parsed) {
225466
- const validated = SecurityOutputSchema.safeParse(parsed);
225467
- if (validated.success) {
225468
- return {
225469
- agentName: this.name,
225470
- findings: validated.data.findings,
225471
- summary: validated.data.summary,
225472
- riskLevel: validated.data.riskLevel
225473
- };
225474
- }
225475
- }
225476
- return {
225477
- agentName: this.name,
225478
- findings: [],
225479
- summary: rawResponse.slice(0, 500),
225480
- riskLevel: "low_risk"
225481
- };
225482
- }
225483
- };
225484
- var import_shared3 = require_dist3();
225485
- var ArchitectureAgent = class extends BaseAgent {
225486
- name = "ArchitectureAgent";
225487
- systemPrompt = import_shared3.Prompts.architectureSystemPrompt;
225488
- buildPrompt(input) {
225489
- let prompt = `## PR Diff to Review
225490
- \`\`\`diff
225491
- ${input.diff}
225492
- \`\`\`
225493
- `;
225494
- if (input.contextChunks.length > 0) {
225495
- prompt += `
225496
- ## Existing Codebase Architecture (Retrieved Context)
225497
- These are the most relevant code chunks from the existing codebase. Compare the PR diff against these patterns:
225498
- `;
225499
- input.contextChunks.forEach((chunk, i) => {
225500
- prompt += `
225501
- ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath} (${chunk.kind})
225502
- \`\`\`
225503
- ${chunk.content}
225504
- \`\`\`
225505
- `;
225506
- });
225507
- } else {
225508
- prompt += `
225509
- ## Note
225510
- No existing codebase context was retrieved. Evaluate the PR diff on its own merits, focusing on internal consistency and general best practices.
225511
- `;
225512
- }
225513
- if (input.memories && input.memories.length > 0) {
225514
- prompt += `
225515
- ## Historical Architecture Context
225516
- These are relevant findings from past reviews:
225517
- `;
225518
- input.memories.forEach((mem, i) => {
225519
- prompt += `- Memory ${i + 1}: ${mem}
225520
- `;
225521
- });
225522
- }
225523
- return prompt;
225524
- }
225525
- parseOutput(rawResponse) {
225526
- const parsed = this.extractJSON(rawResponse);
225527
- if (parsed) {
225528
- const validated = ArchitectureOutputSchema.safeParse(parsed);
225529
- if (validated.success) {
225530
- return {
225531
- agentName: this.name,
225532
- findings: validated.data.findings,
225533
- summary: validated.data.summary,
225534
- consistencyScore: validated.data.consistencyScore
225535
- };
225536
- }
225537
- }
225538
- return {
225539
- agentName: this.name,
225540
- findings: [],
225541
- summary: rawResponse.slice(0, 500),
225542
- consistencyScore: "good"
225543
- };
225544
- }
225545
- };
225546
- var import_shared4 = require_dist3();
225547
225514
  var SynthesizerAgent = class extends BaseAgent {
225548
225515
  name = "SynthesizerAgent";
225549
- systemPrompt = import_shared4.Prompts.synthesizerSystemPrompt;
225516
+ systemPrompt = import_shared2.Prompts.synthesizerSystemPrompt;
225550
225517
  buildPrompt(input) {
225551
225518
  const securityOutput = input.previousOutputs?.security;
225552
225519
  const architectureOutput = input.previousOutputs?.architecture;
@@ -225651,57 +225618,127 @@ ${JSON.stringify(parsed, null, 2)}
225651
225618
  };
225652
225619
  }
225653
225620
  };
225621
+ async function generateStructured(client, prompt, schema, options) {
225622
+ const maxRetries = options?.maxValidationRetries ?? 3;
225623
+ let currentPrompt = prompt;
225624
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
225625
+ const responseText = await generateWithRetry(client, currentPrompt, options);
225626
+ try {
225627
+ let cleanText = responseText.trim();
225628
+ if (cleanText.startsWith("```json")) {
225629
+ cleanText = cleanText.replace(/^```json/, "").replace(/```$/, "").trim();
225630
+ } else if (cleanText.startsWith("```")) {
225631
+ cleanText = cleanText.replace(/^```/, "").replace(/```$/, "").trim();
225632
+ }
225633
+ const parsedJson = JSON.parse(cleanText);
225634
+ const result = schema.safeParse(parsedJson);
225635
+ if (result.success) {
225636
+ return result.data;
225637
+ }
225638
+ const errorMsg = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("\n");
225639
+ if (process.env.DEBUG) {
225640
+ console.warn(`[Structured LLM] Validation failed (Attempt ${attempt}/${maxRetries}):
225641
+ ${errorMsg}`);
225642
+ }
225643
+ if (attempt === maxRetries) {
225644
+ throw new Error(`Failed to generate valid structured output after ${maxRetries} attempts. Last error: ${errorMsg}`);
225645
+ }
225646
+ currentPrompt += `
225647
+
225648
+ Your previous JSON response was invalid. Please fix the following errors and try again:
225649
+ ${errorMsg}
225650
+ Make sure to output ONLY valid JSON matching the exact schema requested.`;
225651
+ } catch (e) {
225652
+ if (process.env.DEBUG) {
225653
+ console.warn(`[Structured LLM] JSON Parsing failed (Attempt ${attempt}/${maxRetries}):
225654
+ ${e.message}`);
225655
+ }
225656
+ if (attempt === maxRetries) {
225657
+ throw new Error(`Failed to parse JSON after ${maxRetries} attempts. Last response: ${responseText.slice(0, 200)}...`);
225658
+ }
225659
+ currentPrompt += `
225660
+
225661
+ Your previous response was not valid JSON. Error: ${e.message}
225662
+ Please output ONLY valid JSON.`;
225663
+ }
225664
+ }
225665
+ throw new Error("Unreachable");
225666
+ }
225667
+ var import_genai3 = __require("@google/genai");
225668
+ var import_shared3 = require_dist3();
225654
225669
  var ReviewOrchestrator = class {
225655
- securityAgent;
225656
- architectureAgent;
225657
225670
  synthesizerAgent;
225671
+ client;
225658
225672
  constructor(apiKey) {
225659
- this.securityAgent = new SecurityAgent(apiKey);
225660
- this.architectureAgent = new ArchitectureAgent(apiKey);
225673
+ const key = apiKey || process.env.GEMINI_API_KEY;
225674
+ if (!key) {
225675
+ throw new Error("GEMINI_API_KEY is not set.");
225676
+ }
225677
+ this.client = new import_genai3.GoogleGenAI({ apiKey: key });
225661
225678
  this.synthesizerAgent = new SynthesizerAgent(apiKey);
225662
225679
  }
225663
225680
  /**
225664
225681
  * Register tools across all agents that support self-verification.
225665
225682
  */
225666
225683
  registerTools(tools) {
225667
- this.securityAgent.registerTools(tools);
225668
- this.architectureAgent.registerTools(tools);
225684
+ this.synthesizerAgent.registerTools(tools);
225669
225685
  }
225670
225686
  /**
225671
225687
  * Runs the full multi-agent review pipeline.
225672
- *
225673
- * @param diff - The raw PR diff text
225674
- * @param contextChunks - Relevant codebase chunks from hybrid retrieval
225675
- * @param memories - Optional relevant memories from past reviews
225676
- * @returns Comprehensive OrchestratedReview with all agent outputs
225677
225688
  */
225678
225689
  async runReview(diff, contextChunks, memories) {
225679
225690
  const startTime = Date.now();
225680
- const baseInput = {
225681
- diff,
225682
- contextChunks,
225683
- memories
225684
- };
225685
- if (process.env.DEBUG) console.log("\nRunning Security and Architecture agents in parallel...");
225686
- const [securityResult, architectureResult] = await Promise.all([
225687
- this.runAgentSafe(
225688
- "SecurityAgent",
225689
- () => this.securityAgent.run(baseInput)
225690
- ),
225691
- this.runAgentSafe(
225692
- "ArchitectureAgent",
225693
- () => this.architectureAgent.run(baseInput)
225694
- )
225695
- ]);
225691
+ if (process.env.DEBUG) console.log("\nRunning Batched Security + Architecture Review...");
225692
+ let combinedPrompt = `## PR Diff to Review
225693
+ \`\`\`diff
225694
+ ${diff}
225695
+ \`\`\`
225696
+ `;
225697
+ if (contextChunks.length > 0) {
225698
+ combinedPrompt += `
225699
+ ## Existing Codebase Context
225700
+ Use this to understand what patterns the codebase already uses:
225701
+ `;
225702
+ contextChunks.forEach((chunk, i) => {
225703
+ combinedPrompt += `
225704
+ ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath}
225705
+ \`\`\`${chunk.kind}
225706
+ ${chunk.content}
225707
+ \`\`\`
225708
+ `;
225709
+ });
225710
+ }
225711
+ if (memories && memories.length > 0) {
225712
+ combinedPrompt += `
225713
+ ## Historical Context
225714
+ These are relevant findings from past reviews:
225715
+ `;
225716
+ memories.forEach((mem, i) => {
225717
+ combinedPrompt += `- Memory ${i + 1}: ${mem}
225718
+ `;
225719
+ });
225720
+ }
225721
+ combinedPrompt = import_shared3.Prompts.combinedReviewSystemPrompt + "\n\n" + combinedPrompt;
225722
+ let combinedResult = null;
225723
+ try {
225724
+ combinedResult = await generateStructured(
225725
+ this.client,
225726
+ combinedPrompt,
225727
+ CombinedReviewOutputSchema,
225728
+ { label: "ReviewOrchestrator (Batched)", maxValidationRetries: 3 }
225729
+ );
225730
+ } catch (err) {
225731
+ console.error("\nBatched Review failed:", err);
225732
+ }
225696
225733
  const securityOutput = {
225697
- findings: securityResult.findings ?? [],
225698
- summary: securityResult.summary,
225699
- riskLevel: securityResult.riskLevel ?? "low_risk"
225734
+ findings: combinedResult?.securityFindings ?? [],
225735
+ summary: combinedResult?.securitySummary ?? "Security analysis encountered an error.",
225736
+ riskLevel: combinedResult?.securityRiskLevel ?? "low_risk"
225700
225737
  };
225701
225738
  const architectureOutput = {
225702
- findings: architectureResult.findings ?? [],
225703
- summary: architectureResult.summary,
225704
- consistencyScore: architectureResult.consistencyScore ?? "good"
225739
+ findings: combinedResult?.architectureFindings ?? [],
225740
+ summary: combinedResult?.architectureSummary ?? "Architecture analysis encountered an error.",
225741
+ consistencyScore: combinedResult?.architectureConsistencyScore ?? "good"
225705
225742
  };
225706
225743
  if (process.env.DEBUG) {
225707
225744
  console.log(` Security: ${securityOutput.riskLevel} (${securityOutput.findings.length} findings)`);
@@ -225765,7 +225802,7 @@ ${agentName} failed:`, err);
225765
225802
  };
225766
225803
  var import_child_process2 = __require("child_process");
225767
225804
  var crypto22 = __toESM2(__require("crypto"));
225768
- var import_shared5 = require_dist3();
225805
+ var import_shared4 = require_dist3();
225769
225806
  var IntelligenceAgent5 = class {
225770
225807
  client;
225771
225808
  embedder;
@@ -225776,7 +225813,7 @@ ${agentName} failed:`, err);
225776
225813
  if (!key) {
225777
225814
  throw new Error("GEMINI_API_KEY is not set.");
225778
225815
  }
225779
- this.client = new import_genai3.GoogleGenAI({ apiKey: key });
225816
+ this.client = new import_genai4.GoogleGenAI({ apiKey: key });
225780
225817
  this.embedder = new import_retrieval3.LocalEmbedder();
225781
225818
  this.store = new import_retrieval3.VectorStore();
225782
225819
  this.orchestrator = new ReviewOrchestrator(key);
@@ -225794,8 +225831,8 @@ ${agentName} failed:`, err);
225794
225831
  }
225795
225832
  getCurrentCommitHash() {
225796
225833
  try {
225797
- const hash = (0, import_child_process2.execSync)("git rev-parse HEAD").toString().trim();
225798
- const isDirty = (0, import_child_process2.execSync)("git status --porcelain").toString().trim().length > 0;
225834
+ const hash = (0, import_child_process2.execSync)("git rev-parse HEAD", { stdio: ["pipe", "pipe", "ignore"] }).toString().trim();
225835
+ const isDirty = (0, import_child_process2.execSync)("git status --porcelain", { stdio: ["pipe", "pipe", "ignore"] }).toString().trim().length > 0;
225799
225836
  return isDirty ? `${hash}-dirty` : hash;
225800
225837
  } catch {
225801
225838
  return "unknown";
@@ -225809,7 +225846,7 @@ ${agentName} failed:`, err);
225809
225846
  * Extracts search queries (keywords, function names, classes) from a PR diff.
225810
225847
  */
225811
225848
  async extractSearchQueriesFromDiff(diff) {
225812
- const prompt = import_shared5.Prompts.extractSearchQueries(diff);
225849
+ const prompt = import_shared4.Prompts.extractSearchQueries(diff);
225813
225850
  try {
225814
225851
  let result = await this.callLLM(prompt, { bypassCache: true });
225815
225852
  if (result.startsWith("```")) {
@@ -225825,6 +225862,29 @@ ${agentName} failed:`, err);
225825
225862
  return [];
225826
225863
  }
225827
225864
  }
225865
+ /**
225866
+ * Expands a single query into 3-5 variants for better retrieval recall.
225867
+ */
225868
+ async expandQuery(query) {
225869
+ const prompt = `You are a search query expansion assistant.
225870
+ Given the user query, generate 3-5 distinct, highly relevant search queries that capture different keywords or ways to express the intent.
225871
+ Return ONLY a valid JSON array of strings.
225872
+ Query: "${query}"`;
225873
+ try {
225874
+ let result = await this.callLLM(prompt, { bypassCache: false });
225875
+ if (result.startsWith("```")) {
225876
+ result = result.replace(/^\`\`\`[a-z]*\n/, "").replace(/\n\`\`\`$/, "");
225877
+ }
225878
+ const queries = JSON.parse(result);
225879
+ if (Array.isArray(queries)) {
225880
+ return Array.from(/* @__PURE__ */ new Set([query, ...queries])).slice(0, 5);
225881
+ }
225882
+ return [query];
225883
+ } catch (err) {
225884
+ console.warn("Failed to expand query:", err);
225885
+ return [query];
225886
+ }
225887
+ }
225828
225888
  /**
225829
225889
  * Generates a context-aware RAG code review for a given PR diff.
225830
225890
  */
@@ -225834,7 +225894,7 @@ ${agentName} failed:`, err);
225834
225894
  Code:
225835
225895
  ${c.content}`;
225836
225896
  }).join("\n\n---\n\n");
225837
- const prompt = import_shared5.Prompts.ragReview(diff, contextStr);
225897
+ const prompt = import_shared4.Prompts.ragReview(diff, contextStr);
225838
225898
  const result = await this.callLLM(prompt, {
225839
225899
  commitHash: this.getCurrentCommitHash(),
225840
225900
  retrievalContextHash: this.computeChunksHash(chunks)
@@ -225845,7 +225905,7 @@ ${c.content}`;
225845
225905
  const contextStr = relevantContext.map((c, i) => `--- Chunk ${i + 1} (${c.file} - ${c.symbolPath || "anonymous"}) ---
225846
225906
  ${c.content}`).join("\n\n");
225847
225907
  const commentsStr = comments.map((c, i) => `Comment ${i + 1} (@${c.user?.login}): ${c.body}`).join("\n");
225848
- const prompt = import_shared5.Prompts.issueAnalysis(issueTitle, issueBody, commentsStr, contextStr);
225908
+ const prompt = import_shared4.Prompts.issueAnalysis(issueTitle, issueBody, commentsStr, contextStr);
225849
225909
  return this.callLLM(prompt, {
225850
225910
  commitHash: this.getCurrentCommitHash(),
225851
225911
  retrievalContextHash: this.computeChunksHash(relevantContext)
@@ -225857,7 +225917,7 @@ ${c.content}`).join("\n\n");
225857
225917
  Code:
225858
225918
  ${c.content}`;
225859
225919
  }).join("\n\n---\n\n");
225860
- const prompt = import_shared5.Prompts.answerQuery(query, contextStr);
225920
+ const prompt = import_shared4.Prompts.answerQuery(query, contextStr);
225861
225921
  return await this.callLLM(prompt, {
225862
225922
  commitHash: this.getCurrentCommitHash(),
225863
225923
  retrievalContextHash: this.computeChunksHash(chunks)
@@ -225873,7 +225933,7 @@ ${c.content}`;
225873
225933
  Code:
225874
225934
  ${c.content}`;
225875
225935
  }).join("\n\n---\n\n");
225876
- const prompt = import_shared5.Prompts.executionPlan(task, contextStr);
225936
+ const prompt = import_shared4.Prompts.executionPlan(task, contextStr);
225877
225937
  let result = await this.callLLM(prompt, {
225878
225938
  commitHash: this.getCurrentCommitHash(),
225879
225939
  retrievalContextHash: this.computeChunksHash(contextChunks)
@@ -225906,12 +225966,131 @@ ${c.content}`;
225906
225966
  return this.orchestrator.runReview(diff, contextChunks, memories);
225907
225967
  }
225908
225968
  };
225969
+ var import_shared5 = require_dist3();
225970
+ var SecurityAgent = class extends BaseAgent {
225971
+ name = "SecurityAgent";
225972
+ systemPrompt = import_shared5.Prompts.securitySystemPrompt;
225973
+ buildPrompt(input) {
225974
+ let prompt = `## PR Diff to Review
225975
+ \`\`\`diff
225976
+ ${input.diff}
225977
+ \`\`\`
225978
+ `;
225979
+ if (input.contextChunks.length > 0) {
225980
+ prompt += `
225981
+ ## Existing Codebase Context
225982
+ Use this to understand what security patterns the codebase already uses:
225983
+ `;
225984
+ input.contextChunks.forEach((chunk, i) => {
225985
+ prompt += `
225986
+ ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath}
225987
+ \`\`\`${chunk.kind}
225988
+ ${chunk.content}
225989
+ \`\`\`
225990
+ `;
225991
+ });
225992
+ }
225993
+ if (input.memories && input.memories.length > 0) {
225994
+ prompt += `
225995
+ ## Historical Security Context
225996
+ These are relevant findings from past reviews:
225997
+ `;
225998
+ input.memories.forEach((mem, i) => {
225999
+ prompt += `- Memory ${i + 1}: ${mem}
226000
+ `;
226001
+ });
226002
+ }
226003
+ return prompt;
226004
+ }
226005
+ parseOutput(rawResponse) {
226006
+ const parsed = this.extractJSON(rawResponse);
226007
+ if (parsed) {
226008
+ const validated = SecurityOutputSchema.safeParse(parsed);
226009
+ if (validated.success) {
226010
+ return {
226011
+ agentName: this.name,
226012
+ findings: validated.data.findings,
226013
+ summary: validated.data.summary,
226014
+ riskLevel: validated.data.riskLevel
226015
+ };
226016
+ }
226017
+ }
226018
+ return {
226019
+ agentName: this.name,
226020
+ findings: [],
226021
+ summary: rawResponse.slice(0, 500),
226022
+ riskLevel: "low_risk"
226023
+ };
226024
+ }
226025
+ };
225909
226026
  var import_shared6 = require_dist3();
226027
+ var ArchitectureAgent = class extends BaseAgent {
226028
+ name = "ArchitectureAgent";
226029
+ systemPrompt = import_shared6.Prompts.architectureSystemPrompt;
226030
+ buildPrompt(input) {
226031
+ let prompt = `## PR Diff to Review
226032
+ \`\`\`diff
226033
+ ${input.diff}
226034
+ \`\`\`
226035
+ `;
226036
+ if (input.contextChunks.length > 0) {
226037
+ prompt += `
226038
+ ## Existing Codebase Architecture (Retrieved Context)
226039
+ These are the most relevant code chunks from the existing codebase. Compare the PR diff against these patterns:
226040
+ `;
226041
+ input.contextChunks.forEach((chunk, i) => {
226042
+ prompt += `
226043
+ ### Context ${i + 1}: ${chunk.file} \u2192 ${chunk.symbolPath} (${chunk.kind})
226044
+ \`\`\`
226045
+ ${chunk.content}
226046
+ \`\`\`
226047
+ `;
226048
+ });
226049
+ } else {
226050
+ prompt += `
226051
+ ## Note
226052
+ No existing codebase context was retrieved. Evaluate the PR diff on its own merits, focusing on internal consistency and general best practices.
226053
+ `;
226054
+ }
226055
+ if (input.memories && input.memories.length > 0) {
226056
+ prompt += `
226057
+ ## Historical Architecture Context
226058
+ These are relevant findings from past reviews:
226059
+ `;
226060
+ input.memories.forEach((mem, i) => {
226061
+ prompt += `- Memory ${i + 1}: ${mem}
226062
+ `;
226063
+ });
226064
+ }
226065
+ return prompt;
226066
+ }
226067
+ parseOutput(rawResponse) {
226068
+ const parsed = this.extractJSON(rawResponse);
226069
+ if (parsed) {
226070
+ const validated = ArchitectureOutputSchema.safeParse(parsed);
226071
+ if (validated.success) {
226072
+ return {
226073
+ agentName: this.name,
226074
+ findings: validated.data.findings,
226075
+ summary: validated.data.summary,
226076
+ consistencyScore: validated.data.consistencyScore
226077
+ };
226078
+ }
226079
+ }
226080
+ return {
226081
+ agentName: this.name,
226082
+ findings: [],
226083
+ summary: rawResponse.slice(0, 500),
226084
+ consistencyScore: "good"
226085
+ };
226086
+ }
226087
+ };
226088
+ var import_shared7 = require_dist3();
225910
226089
  var AutonomousAgent2 = class extends BaseAgent {
225911
226090
  name = "Vortex";
225912
226091
  maxToolIterations = 30;
225913
226092
  // overridden by maxSteps if provided
225914
- systemPrompt = import_shared6.Prompts.autonomousSystemPrompt;
226093
+ systemPrompt = import_shared7.Prompts.autonomousSystemPrompt;
225915
226094
  buildPrompt(input) {
225916
226095
  const { diff, contextChunks } = input;
225917
226096
  let prompt = `USER TASK:
@@ -225940,7 +226119,7 @@ ${chunk.content}
225940
226119
  };
225941
226120
  var import_db22 = require_dist2();
225942
226121
  var import_retrieval22 = require_dist4();
225943
- var import_shared7 = require_dist3();
226122
+ var import_shared8 = require_dist3();
225944
226123
  var MemoryService3 = class {
225945
226124
  embedder;
225946
226125
  constructor() {
@@ -225969,7 +226148,7 @@ ${chunk.content}
225969
226148
  let embedding;
225970
226149
  try {
225971
226150
  const embeddings = await this.embedder.embedChunks([
225972
- (0, import_shared7.createQueryChunk)(memoryContent, "memory")
226151
+ (0, import_shared8.createQueryChunk)(memoryContent, "memory")
225973
226152
  ]);
225974
226153
  if (embeddings[0]) {
225975
226154
  embedding = JSON.stringify(embeddings[0]);
@@ -225994,7 +226173,7 @@ ${chunk.content}
225994
226173
  let embedding;
225995
226174
  try {
225996
226175
  const embeddings = await this.embedder.embedChunks([
225997
- (0, import_shared7.createQueryChunk)(content, "memory")
226176
+ (0, import_shared8.createQueryChunk)(content, "memory")
225998
226177
  ]);
225999
226178
  if (embeddings[0]) {
226000
226179
  embedding = JSON.stringify(embeddings[0]);
@@ -226016,7 +226195,7 @@ ${chunk.content}
226016
226195
  let queryEmbedding = null;
226017
226196
  try {
226018
226197
  const embeddings = await this.embedder.embedChunks([
226019
- (0, import_shared7.createQueryChunk)(query)
226198
+ (0, import_shared8.createQueryChunk)(query)
226020
226199
  ]);
226021
226200
  queryEmbedding = embeddings[0] ?? null;
226022
226201
  } catch {
@@ -226036,7 +226215,7 @@ ${chunk.content}
226036
226215
  if (mem.embedding) {
226037
226216
  try {
226038
226217
  const memEmbedding = JSON.parse(mem.embedding);
226039
- similarity = (0, import_shared7.cosineSimilarity)(queryEmbedding, memEmbedding);
226218
+ similarity = (0, import_shared8.cosineSimilarity)(queryEmbedding, memEmbedding);
226040
226219
  } catch {
226041
226220
  }
226042
226221
  }
@@ -226556,33 +226735,50 @@ URL: ${r.url}
226556
226735
  * Builds both the vector store (for semantic search) and the BM25 index (for keyword search).
226557
226736
  */
226558
226737
  async indexRepository(cwd) {
226559
- if (!(0, import_git4.isGitRepo)(cwd)) {
226560
- throw new Error(`Directory ${cwd} is not a git repository.`);
226738
+ let root = cwd;
226739
+ if ((0, import_git4.isGitRepo)(cwd)) {
226740
+ try {
226741
+ root = (0, import_git4.getGitRoot)(cwd);
226742
+ } catch {
226743
+ }
226561
226744
  }
226562
- const root = (0, import_git4.getGitRoot)(cwd);
226563
226745
  await (0, import_db3.initDatabase)();
226564
- const files = (0, import_git4.listTrackedFiles)(root).filter((file) => {
226565
- const ext = path42.extname(file);
226566
- const supportedExts = [
226567
- ".ts",
226568
- ".tsx",
226569
- ".js",
226570
- ".jsx",
226571
- ".py",
226572
- ".go",
226573
- ".rs",
226574
- ".java",
226575
- ".cpp",
226576
- ".hpp",
226577
- ".c",
226578
- ".h",
226579
- ".rb",
226580
- ".php",
226581
- ".html",
226582
- ".css"
226583
- ];
226584
- return supportedExts.includes(ext) && !file.includes("node_modules");
226585
- });
226746
+ let files = [];
226747
+ if ((0, import_git4.isGitRepo)(cwd)) {
226748
+ try {
226749
+ const tracked = (0, import_git4.listTrackedFiles)(root).filter((file) => {
226750
+ const ext = path42.extname(file);
226751
+ const supportedExts = [
226752
+ ".ts",
226753
+ ".tsx",
226754
+ ".js",
226755
+ ".jsx",
226756
+ ".py",
226757
+ ".go",
226758
+ ".rs",
226759
+ ".java",
226760
+ ".cpp",
226761
+ ".hpp",
226762
+ ".c",
226763
+ ".h",
226764
+ ".rb",
226765
+ ".php",
226766
+ ".html",
226767
+ ".css"
226768
+ ];
226769
+ return supportedExts.includes(ext) && !file.includes("node_modules");
226770
+ });
226771
+ if (tracked.length > 0) {
226772
+ files = tracked;
226773
+ }
226774
+ } catch (e) {
226775
+ }
226776
+ }
226777
+ if (files.length === 0) {
226778
+ for await (const file of (0, import_retrieval5.scanFiles)(root)) {
226779
+ files.push(file);
226780
+ }
226781
+ }
226586
226782
  let totalChunks = 0;
226587
226783
  for (const file of files) {
226588
226784
  try {
@@ -226618,7 +226814,7 @@ URL: ${r.url}
226618
226814
  console.log(`Generating embedding for query: "${query}"...`);
226619
226815
  await (0, import_db3.initDatabase)();
226620
226816
  const queryEmbedding = await this.embedder.embedChunks([
226621
- (0, import_shared8.createQueryChunk)(query)
226817
+ (0, import_shared9.createQueryChunk)(query)
226622
226818
  ]);
226623
226819
  if (queryEmbedding.length === 0) {
226624
226820
  return [];
@@ -226901,7 +227097,7 @@ var require_package = __commonJS({
226901
227097
  "package.json"(exports, module) {
226902
227098
  module.exports = {
226903
227099
  name: "@vortex-ai/cli",
226904
- version: "0.1.45",
227100
+ version: "0.1.50",
226905
227101
  description: "Vortex CLI - The main entry point",
226906
227102
  main: "./dist/index.js",
226907
227103
  bin: {
@@ -227018,17 +227214,25 @@ async function searchCommand(options) {
227018
227214
  const spinner = ora2(`Searching codebase for: "${options.query}"...`).start();
227019
227215
  const indexer = new import_engine2.Indexer();
227020
227216
  try {
227021
- spinner.text = "Running hybrid search (vector + BM25 + cross-encoder)...";
227022
- const results = await indexer.hybridSearch(options.query, parseInt(options.limit, 10));
227023
- if (results.length === 0) {
227217
+ spinner.text = options.expandQuery ? "Expanding query and running parallel hybrid search..." : "Running hybrid search (vector + BM25 + cross-encoder)...";
227218
+ let queriesToSearch = [options.query];
227219
+ const agent = new import_engine2.IntelligenceAgent();
227220
+ if (options.expandQuery) {
227221
+ queriesToSearch = await agent.expandQuery(options.query);
227222
+ }
227223
+ const allResults = await Promise.all(
227224
+ queriesToSearch.map((q) => indexer.hybridSearch(q, parseInt(options.limit, 10)))
227225
+ );
227226
+ const flatResults = allResults.flat();
227227
+ 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));
227228
+ if (uniqueResults.length === 0) {
227024
227229
  spinner.fail("No relevant code found.");
227025
227230
  return;
227026
227231
  }
227027
- spinner.text = `Found ${results.length} relevant code chunks. Analyzing with AI engine...`;
227232
+ spinner.text = `Found ${uniqueResults.length} relevant code chunks. Analyzing with AI engine...`;
227028
227233
  const memoryService = new import_engine2.MemoryService();
227029
227234
  const memories = await memoryService.recallRelevantMemories(options.query, 3);
227030
- const agent = new import_engine2.IntelligenceAgent();
227031
- const answer = await agent.answerQueryWithContext(options.query, results);
227235
+ const answer = await agent.answerQueryWithContext(options.query, uniqueResults);
227032
227236
  spinner.succeed("Analysis complete!\n");
227033
227237
  const parsedAnswer = await marked.parse(answer);
227034
227238
  const formatted = boxen(parsedAnswer.trim(), {
@@ -227041,7 +227245,7 @@ async function searchCommand(options) {
227041
227245
  });
227042
227246
  console.log(formatted);
227043
227247
  console.log(chalk2.cyan.dim(" Reference Material (Hybrid Retrieval)"));
227044
- results.forEach((res, i) => {
227248
+ uniqueResults.forEach((res, i) => {
227045
227249
  const sources = res.sources ? res.sources.join("+") : "vector";
227046
227250
  const scoreStr = res.score ? (res.score * 100).toFixed(1) + "%" : "N/A";
227047
227251
  console.log(chalk2.gray(` \u2502 [${i + 1}] ${res.file.replace(process.cwd(), "")} \u2794 ${res.symbolPath || "(anonymous)"} (${scoreStr}) [${sources}]`));
@@ -227120,11 +227324,11 @@ async function reviewCommand(options) {
227120
227324
  const queries = await agent.extractSearchQueriesFromDiff(diff);
227121
227325
  const allChunks = [];
227122
227326
  if (queries.length > 0) {
227123
- spinner.text = `Hybrid searching for context...`;
227124
- for (const query of queries) {
227125
- const results = await indexer.hybridSearch(query, 3);
227126
- allChunks.push(...results);
227127
- }
227327
+ spinner.text = `Hybrid searching for context (parallel)...`;
227328
+ const allResults = await Promise.all(
227329
+ queries.map((query) => indexer.hybridSearch(query, 3))
227330
+ );
227331
+ allChunks.push(...allResults.flat());
227128
227332
  }
227129
227333
  const uniqueChunks = Array.from(
227130
227334
  new Map(allChunks.map((c) => [c.id, c])).values()
@@ -227377,7 +227581,7 @@ Created new project folder: ${projectPath}`));
227377
227581
  if (!options.contextChunks || options.contextChunks.length === 0) {
227378
227582
  try {
227379
227583
  const indexer = new import_engine5.Indexer();
227380
- const relevantContext = await indexer.hybridSearch(prompt, 5);
227584
+ const relevantContext = await indexer.hybridSearch(prompt, 15);
227381
227585
  options.contextChunks = relevantContext.map((c) => ({
227382
227586
  file: c.file,
227383
227587
  symbolPath: c.symbolPath || "anonymous",
@@ -227400,7 +227604,11 @@ Created new project folder: ${projectPath}`));
227400
227604
  const parsedPlan = JSON.parse(executionPlanStr);
227401
227605
  planSummary = parsedPlan.summary || planSummary;
227402
227606
  stepCount = parsedPlan.steps ? parsedPlan.steps.length : 0;
227403
- executionPlan = parsedPlan.steps ? parsedPlan.steps.map((s, i) => `${i + 1}. ${s}`).join("\n") : executionPlanStr;
227607
+ executionPlan = parsedPlan.steps ? parsedPlan.steps.map((s, i) => {
227608
+ if (typeof s === "string") return `${i + 1}. ${s}`;
227609
+ return `${i + 1}. [${(s.action || "MODIFY").toUpperCase()}] ${s.file || "General"}
227610
+ ${s.description}`;
227611
+ }).join("\n\n") : executionPlanStr;
227404
227612
  filesToRead = parsedPlan.filesToRead || [];
227405
227613
  } catch {
227406
227614
  executionPlan = executionPlanStr;
@@ -227497,6 +227705,7 @@ Allow? (y/N) `;
227497
227705
  },
227498
227706
  {
227499
227707
  initialState,
227708
+ verifyCommand: options.verify,
227500
227709
  onToolCall: (toolName, args) => {
227501
227710
  if (toolName === "write_file") {
227502
227711
  spinner.text = `Writing ${args.path}...`;
@@ -227632,7 +227841,8 @@ Please fix this issue in the codebase.`;
227632
227841
  await solveCommand(prompt, {
227633
227842
  autoApprove: options.autoApprove,
227634
227843
  maxSteps: options.maxSteps,
227635
- contextChunks
227844
+ contextChunks,
227845
+ verify: options.verify
227636
227846
  });
227637
227847
  } catch (err) {
227638
227848
  spinner.fail("Failed to setup solve-issue");
@@ -227649,9 +227859,9 @@ cacheCommand.command("stats").description("View LLM cache statistics").action(as
227649
227859
  const stats = await import_engine7.LLMCacheManager.getStats();
227650
227860
  console.log("\nLLM Cache Statistics\n");
227651
227861
  console.log(`Entries: ${stats.entries.toLocaleString()}`);
227652
- console.log(`Hits: ${stats.hits.toLocaleString()}`);
227862
+ console.log(`Saved API Calls: ${stats.hits.toLocaleString()}`);
227653
227863
  const storageMB = (stats.storage / 1024 / 1024).toFixed(2);
227654
- console.log(`Storage: ${storageMB} MB
227864
+ console.log(`Storage Used: ${storageMB} MB
227655
227865
  `);
227656
227866
  } catch (err) {
227657
227867
  console.error("Failed to retrieve cache stats:", err);
@@ -227716,12 +227926,12 @@ program.hook("preAction", async (thisCommand, actionCommand) => {
227716
227926
  }
227717
227927
  });
227718
227928
  program.command("init").description("Initialize repository intelligence, embeddings, and PR history").option("--reindex", "Rebuild repository embeddings while preserving historical PR intelligence").action(initCommand);
227719
- 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);
227929
+ 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);
227720
227930
  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);
227721
227931
  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);
227722
227932
  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);
227723
- 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));
227724
- 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);
227933
+ 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));
227934
+ 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);
227725
227935
  program.addCommand(cacheCommand);
227726
227936
  program.parse();
227727
227937
  /*! Bundled license information: