@contextrail/code-review-agent 0.1.2-alpha.1 → 0.1.2-alpha.2

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.
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import type { ReviewInputs } from '../review-inputs/index.js';
3
3
  import type { McpClient } from '../mcp/client.js';
4
4
  import type { Logger } from '../logging/logger.js';
5
+ import type { LlmService } from '../llm/service.js';
5
6
  declare const orchestratorOutputSchema: z.ZodObject<{
6
7
  understanding: z.ZodString;
7
8
  reviewers: z.ZodArray<z.ZodString, "many">;
@@ -22,6 +23,7 @@ export type AgenticOrchestratorConfig = {
22
23
  };
23
24
  export type AgenticOrchestratorDeps = {
24
25
  mcpClient: McpClient;
26
+ llmService: LlmService;
25
27
  config: AgenticOrchestratorConfig;
26
28
  logger?: Logger;
27
29
  };
@@ -1,11 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { DEFAULT_MAX_STEPS } from '../config/defaults.js';
3
- import { generateUserMessagePrompt } from './prompts.js';
3
+ import { generateUserMessagePrompt } from '../prompts/index.js';
4
4
  import { writeOrchestratorOutputs } from './writer.js';
5
5
  import { filterValidReviewers, formatReviewerSelectionError } from './validation.js';
6
6
  import { extractFilePatterns } from '../reviewers/executor.js';
7
7
  import { fileMatchesPatterns } from '../review-inputs/file-patterns.js';
8
- import { createLlmService } from '../llm/factory.js';
9
8
  const extractSystemPrompt = (text) => {
10
9
  const start = text.indexOf('<system_prompt>');
11
10
  const end = text.indexOf('</system_prompt>');
@@ -20,7 +19,7 @@ const orchestratorOutputSchema = z.object({
20
19
  reviewers: z.array(z.string()).min(1).describe('Selected reviewer names from available list'),
21
20
  });
22
21
  export const runOrchestrator = async (inputs, outputDir, deps) => {
23
- const { mcpClient, config, logger } = deps;
22
+ const { mcpClient, llmService, config, logger } = deps;
24
23
  // Fetch review-orchestration prompt from MCP
25
24
  const promptResult = await mcpClient.getPrompt({ name: 'review-orchestration' });
26
25
  const promptMessages = [];
@@ -34,10 +33,13 @@ export const runOrchestrator = async (inputs, outputDir, deps) => {
34
33
  const role = message.role === 'user' ? 'user' : message.role === 'assistant' ? 'assistant' : 'system';
35
34
  promptMessages.push({ role, content: raw });
36
35
  }
37
- // Extract reviewer catalog from prompt
38
- const promptText = promptMessages.map((m) => m.content).join('\n');
39
- const reviewerMatches = Array.from(promptText.matchAll(/reviewer-[a-z-]+/g)).map((match) => match[0]);
40
- const availableReviewers = Array.from(new Set(reviewerMatches));
36
+ // Extract reviewer catalog from prompt metadata
37
+ const promptMeta = promptResult.metadata;
38
+ if (!Array.isArray(promptMeta?.availableReviewers) || promptMeta.availableReviewers.length === 0) {
39
+ throw new Error('[ORCH] Orchestration prompt is missing metadata.availableReviewers or it is empty. ' +
40
+ 'Please update review-orchestration.yaml to include a metadata.availableReviewers array.');
41
+ }
42
+ const availableReviewers = promptMeta.availableReviewers;
41
43
  // Pre-fetch reviewer file patterns before orchestrator runs
42
44
  logger?.debug(`Pre-fetching file patterns for ${availableReviewers.length} reviewers...`);
43
45
  const reviewerFilePatterns = new Map();
@@ -58,12 +60,6 @@ export const runOrchestrator = async (inputs, outputDir, deps) => {
58
60
  logger?.debug(`Pre-fetched file patterns for ${reviewerFilePatterns.size} reviewers`);
59
61
  // Build user message with change summary and reviewer file patterns
60
62
  const userMessage = generateUserMessagePrompt(inputs, availableReviewers, config.prDescription, reviewerFilePatterns, config.reviewDomains);
61
- // Create LLM service with MCP tools
62
- const { service: llmService } = createLlmService({
63
- openRouterApiKey: config.openRouterApiKey,
64
- mcpClient,
65
- logger,
66
- });
67
63
  // Prepare messages
68
64
  const messages = [
69
65
  ...promptMessages,
@@ -1,6 +1,6 @@
1
1
  import { mkdir, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { generateActivityContentPrompt } from './prompts.js';
3
+ import { generateActivityContentPrompt } from '../prompts/index.js';
4
4
  export const writeOrchestratorOutputs = async (outputDir, understanding, reviewers, toolCalls, contextIds) => {
5
5
  const orchestratorDir = path.join(outputDir, 'orchestrator');
6
6
  await mkdir(orchestratorDir, { recursive: true });
@@ -1,7 +1,8 @@
1
1
  import type { ReviewerResult, ReviewDecision, AggregationTokenUsage, SynthesisResult } from './schema.js';
2
+ import type { LlmService } from '../llm/service.js';
2
3
  import type { Logger } from '../logging/logger.js';
3
4
  export type AggregationConfig = {
4
- openRouterApiKey: string;
5
+ llmService: LlmService;
5
6
  model: string;
6
7
  maxSteps?: number;
7
8
  logger?: Logger;
@@ -1,6 +1,5 @@
1
1
  import { reviewDecisionSchema, synthesisResultSchema, cleanFinding } from './schema.js';
2
- import { buildReviewDecisionPrompt, buildSynthesisPrompt } from './prompts.js';
3
- import { createLlmService } from '../llm/factory.js';
2
+ import { buildReviewDecisionPrompt, buildSynthesisPrompt } from '../prompts/index.js';
4
3
  const DEFAULT_AGGREGATION_STEPS = 5;
5
4
  const MIN_AGGREGATION_STEPS = 1;
6
5
  const MAX_AGGREGATION_STEPS = 20;
@@ -20,11 +19,7 @@ export const validateAggregationMaxSteps = (value) => {
20
19
  * @returns Synthesis result with deduplicated findings, contradictions, and compound risks
21
20
  */
22
21
  export const synthesizeFindings = async (reviewerResults, config) => {
23
- const { service: llmService } = createLlmService({
24
- openRouterApiKey: config.openRouterApiKey,
25
- logger: config.logger,
26
- enableMetrics: true,
27
- });
22
+ const llmService = config.llmService;
28
23
  const maxSteps = validateAggregationMaxSteps(config.maxSteps);
29
24
  const totalInputFindings = reviewerResults.reduce((sum, rr) => sum + rr.findings.length, 0);
30
25
  config.logger?.debug(`Synthesis evidence input: reviewers=${reviewerResults.length}, totalFindings=${totalInputFindings}, byReviewer=${JSON.stringify(reviewerResults.map((rr) => ({
@@ -75,11 +70,7 @@ export const synthesizeFindings = async (reviewerResults, config) => {
75
70
  return { synthesis: cleanedSynthesis, usage };
76
71
  };
77
72
  export const generateReviewDecision = async (understanding, synthesisResult, config) => {
78
- const { service: llmService } = createLlmService({
79
- openRouterApiKey: config.openRouterApiKey,
80
- logger: config.logger,
81
- enableMetrics: true,
82
- });
73
+ const llmService = config.llmService;
83
74
  const maxSteps = validateAggregationMaxSteps(config.maxSteps);
84
75
  const blockingFindings = synthesisResult.findings.filter((finding) => finding.severity === 'critical' || finding.severity === 'major');
85
76
  config.logger?.debug(`Decision evidence input: findings=${synthesisResult.findings.length}, blockingFindings=${blockingFindings.length}, contradictions=${synthesisResult.contradictions.length}, compoundRisks=${synthesisResult.compoundRisks.length}`);