Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions cli/commands/batch-evaluate-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
createEvaluationDirectory,
EvaluationMetadata,
printBatchCompletionMessage,
getEvaluationRoot,
} from '../utils/shared.utils';
import { ProgressTracker } from '../utils/progress-tracker';
import { CostEstimatorService } from '../../src/services/cost-estimator.service';
Expand All @@ -17,6 +18,14 @@ import { consoleManager } from '../../src/common/utils/console-manager';
import { getCommitDiff, extractFilesFromDiff } from '../utils/git-utils';
import { isDiagnosticLog } from '../utils/diagnostic-filter';
import { spawnSync } from 'child_process';
import {
calculateWeightedAverage,
PillarName,
SEVEN_PILLARS,
} from '../../src/constants/agent-weights.constants';
import { promptAndGenerateOkrs } from '../utils/okr-prompt.utils';
import inquirer from 'inquirer';
import pLimit from 'p-limit';

interface CommitInfo {
hash: string;
Expand Down Expand Up @@ -56,7 +65,7 @@ export async function runBatchEvaluateCommand(args: string[]) {
// Load configuration
const config = loadConfig();
if (!config) {
console.error('❌ Config file not found. Run `npm run config` to create one.');
console.error('❌ Config file not found. Run `codewave config --init` to create one.');
process.exit(1);
}
validateConfig(config);
Expand Down Expand Up @@ -89,7 +98,6 @@ export async function runBatchEvaluateCommand(args: string[]) {
if (costEstimate !== null) {
estimator.printEstimate(costEstimate, commits.length);

const { default: inquirer } = await import('inquirer');
const { proceed } = await inquirer.prompt([
{
type: 'confirm',
Expand All @@ -112,7 +120,6 @@ export async function runBatchEvaluateCommand(args: string[]) {
const orchestrator = new CommitEvaluationOrchestrator(agentRegistry, config);

// Configure concurrency limit (10 concurrent evaluations)
const { default: pLimit } = await import('p-limit');
const limit = pLimit(10);

// Buffer for storing suppressed output (warnings, errors)
Expand Down Expand Up @@ -248,8 +255,6 @@ export async function runBatchEvaluateCommand(args: string[]) {

const authors = Array.from(uniqueAuthors);
if (authors.length > 0) {
const { promptAndGenerateOkrs } = await import('../utils/okr-prompt.utils.js');
const { getEvaluationRoot } = await import('../utils/shared.utils.js');
const evalRoot = getEvaluationRoot();
await promptAndGenerateOkrs(config, authors, evalRoot, {
sinceDate: options.since ? new Date(options.since) : undefined,
Expand Down Expand Up @@ -540,7 +545,7 @@ async function getCommitsToEvaluate(options: any): Promise<CommitInfo[]> {
}

function calculateAggregateMetrics(agentResults: any[]): any {
const metrics: any = {
const metrics: Record<PillarName, number> = {
functionalImpact: 0,
idealTimeHours: 0,
testCoverage: 0,
Expand All @@ -552,7 +557,6 @@ function calculateAggregateMetrics(agentResults: any[]): any {
};

// Import weighted aggregation
const { calculateWeightedAverage } = require('../../src/constants/agent-weights.constants');

// Get latest metrics from each agent
const agentMetricsMap = new Map<string, any>();
Expand All @@ -563,8 +567,7 @@ function calculateAggregateMetrics(agentResults: any[]): any {
});

// Calculate weighted average for each metric
const metricNames = Object.keys(metrics);
metricNames.forEach((metricName) => {
SEVEN_PILLARS.forEach((metricName: PillarName) => {
const contributors: Array<{ agentName: string; score: number }> = [];
agentMetricsMap.forEach((agentMetrics, agentRole) => {
if (agentMetrics[metricName] !== undefined) {
Expand All @@ -576,7 +579,10 @@ function calculateAggregateMetrics(agentResults: any[]): any {
});

if (contributors.length > 0) {
metrics[metricName] = calculateWeightedAverage(contributors, metricName);
const weightedValue = calculateWeightedAverage(contributors, metricName);
if (weightedValue !== null) {
metrics[metricName] = weightedValue;
}
}
});

Expand Down
2 changes: 1 addition & 1 deletion cli/commands/evaluate-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
getEvaluationRoot,
} from '../utils/shared.utils';
import { parseCommitStats } from '../../src/common/utils/commit-utils';
import { promptAndGenerateOkrs } from '../utils/okr-prompt.utils';
import {
getCommitDiff,
getDiffFromStaged,
Expand Down Expand Up @@ -312,7 +313,7 @@
const evaluationResult = await orchestrator.evaluateCommit(context, {
streaming: streamingEnabled,
threadId: `eval-${Date.now()}`,
onProgress: (state: any) => {

Check warning on line 316 in cli/commands/evaluate-command.ts

View workflow job for this annotation

GitHub Actions / Test, Lint & Build (20.x)

'state' is defined but never used. Allowed unused args must match /^_/u
// For single evaluate with LangSmith (no streaming), onProgress is called once at end
// Just track final state for summary
},
Expand Down Expand Up @@ -384,7 +385,6 @@
printEvaluateCompletionMessage(outputDir);

if (commitAuthor) {
const { promptAndGenerateOkrs } = await import('../utils/okr-prompt.utils.js');
const evalRoot = getEvaluationRoot();
await promptAndGenerateOkrs(config, [commitAuthor], evalRoot);
}
Expand Down
3 changes: 2 additions & 1 deletion cli/commands/generate-okr-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '../../src/services/author-stats-aggregator.service';
import { OkrOrchestrator } from '../../src/orchestrator/okr-orchestrator';
import { OkrProgressTracker } from '../utils/okr-progress-tracker';
import { consoleManager } from '../../src/common/utils/console-manager';

/**
* CLI command for generating OKRs
Expand Down Expand Up @@ -62,7 +63,7 @@ export async function runGenerateOkrCommand(args: string[]) {
console.log(chalk.gray(`⚡ Using concurrency: ${concurrency}`));

// Suppress logs that interfere with progress bar
const { consoleManager } = await import('../../src/common/utils/console-manager.js');

const originalStdoutWrite = process.stdout.write.bind(process.stdout);

(process.stdout.write as any) = function (str: string, ...args: any[]): boolean {
Expand Down
5 changes: 3 additions & 2 deletions cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { runEvaluateCommand } from './commands/evaluate-command';
import { runConfigCommand } from './commands/config.command';
import { runBatchEvaluateCommand } from './commands/batch-evaluate-command';
import { runGenerateOkrCommand } from './commands/generate-okr-command';
import * as path from 'path';
import * as fs from 'fs';

async function main() {
const [, , command, ...args] = process.argv;
Expand All @@ -34,10 +36,9 @@ async function main() {
if (command === '--version' || command === '-v') {
try {
// Try to load package.json from the project root
const path = require('path');
// __dirname is dist/cli, so go up 2 levels to reach root
const packagePath = path.resolve(__dirname, '../../package.json');
const packageJson = require(packagePath);
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
console.log(`codewave version ${packageJson.version}`);
} catch (error) {
console.log('codewave version unknown');
Expand Down
56 changes: 47 additions & 9 deletions cli/utils/okr-prompt.utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import chalk from 'chalk';
import { AppConfig } from '../../src/config/config.interface';
import { OkrOrchestrator } from '../../src/orchestrator/okr-orchestrator';
import { AggregationOptions } from '../../src/services/author-stats-aggregator.service';
import {
AggregationOptions,
AuthorStatsAggregatorService,
} from '../../src/services/author-stats-aggregator.service';
import { MetricsCalculationService } from '../../src/services/metrics-calculation.service';
import { OkrProgressTracker } from './okr-progress-tracker';
import inquirer from 'inquirer';
import fs from 'fs';
import path from 'path';
import { generateAuthorPage } from './shared.utils';
import { consoleManager } from '../../src/common/utils/console-manager';

/**
* Prompt user and generate OKRs for authors
Expand Down Expand Up @@ -31,7 +40,6 @@ export async function promptAndGenerateOkrs(
console.log(chalk.gray(`Estimated cost: $${estimatedCost.toFixed(4)}`));
console.log(chalk.gray(`Authors: ${authors.join(', ')}`));

const { default: inquirer } = await import('inquirer');
const { proceed } = await inquirer.prompt([
{
type: 'confirm',
Expand Down Expand Up @@ -59,7 +67,7 @@ export async function promptAndGenerateOkrs(
tracker.initialize(authors);

// Suppress logs that interfere with progress bar
const { consoleManager } = await import('../../src/common/utils/console-manager.js');

const originalStdoutWrite = process.stdout.write.bind(process.stdout);

(process.stdout.write as any) = function (str: string, ...args: any[]): boolean {
Expand Down Expand Up @@ -103,12 +111,9 @@ export async function promptAndGenerateOkrs(

/**
* Regenerate author pages to include latest OKR data
* Uses consistent team summary calculation for BACI score accuracy
*/
async function regenerateAuthorPages(evalRoot: string, authors: string[]): Promise<void> {
const fs = await import('fs');
const path = await import('path');
const { generateAuthorPage } = await import('./shared.utils.js');

// Read index.json to get commit data for each author
const indexPath = path.join(evalRoot, 'index.json');
if (!fs.existsSync(indexPath)) {
Expand All @@ -128,11 +133,44 @@ async function regenerateAuthorPages(evalRoot: string, authors: string[]): Promi
byAuthor.get(author)!.push(item);
});

// Regenerate pages only for authors with new OKRs
// Calculate team summary for BACI score consistency
const allAuthorData = await AuthorStatsAggregatorService.aggregateAuthorStats(evalRoot);
const allMetrics: any[] = [];

// Convert all authors' evaluations to metrics format
for (const [authorName, authorEvaluations] of allAuthorData.entries()) {
const authorMetrics = authorEvaluations.map((evaluation) => {
// Calculate averaged metrics from agent results
const averagedMetrics =
evaluation.averagedMetrics ||
MetricsCalculationService.calculateWeightedMetrics(evaluation.agents);

return {
createdBy: authorName,
commitScore: averagedMetrics?.commitScore || 0,
testingQuality: averagedMetrics?.testCoverage || 0,
technicalDebtRate: 0,
deliveryRate: 0,
functionalImpact: averagedMetrics?.functionalImpact || 0,
codeQuality: averagedMetrics?.codeQuality || 0,
codeComplexity: averagedMetrics?.codeComplexity || 0,
actualTimeHours: averagedMetrics?.actualTimeHours || 0,
idealTimeHours: averagedMetrics?.idealTimeHours || 0,
technicalDebtHours: averagedMetrics?.technicalDebtHours || 0,
debtReductionHours: averagedMetrics?.debtReductionHours || 0,
};
});
allMetrics.push(...authorMetrics);
}

// Get comprehensive team summary
const teamSummary = MetricsCalculationService.calculateTeamSummary(allMetrics);

// Regenerate pages with consistent team summary for BACI score accuracy
for (const author of authors) {
const commits = byAuthor.get(author);
if (commits && commits.length > 0) {
await generateAuthorPage(evalRoot, author, commits);
await generateAuthorPage(evalRoot, author, commits, teamSummary);
console.log(chalk.gray(` ✓ Updated dashboard for ${author}`));
}
}
Expand Down
Loading
Loading