refactor: replace console.log/chalk with ActivityLogger across services

- Add ActivityLogger interface wrapping Temporal's Context.current().log
- Thread logger parameter through claude-executor, message-handlers, git-manager, prompt-manager, reporting, and agent validators
- Remove chalk dependency from all service/activity files; CLI files keep console.log for terminal output
- Replace colorFn: ChalkInstance parameter with structured logger.info/warn/error calls
- Use replay-safe `log` import from @temporalio/workflow in workflows.ts
This commit is contained in:
ajmallesh
2026-02-16 17:16:27 -08:00
parent d3816a29fa
commit bb89d6f458
17 changed files with 322 additions and 296 deletions
+40 -37
View File
@@ -5,9 +5,9 @@
// as published by the Free Software Foundation.
import { $ } from 'zx';
import chalk from 'chalk';
import { PentestError } from '../error-handling.js';
import { ErrorCode } from '../types/errors.js';
import type { ActivityLogger } from '../temporal/activity-logger.js';
/**
* Check if a directory is a git repository.
@@ -53,17 +53,19 @@ function logChangeSummary(
changes: string[],
messageWithChanges: string,
messageWithoutChanges: string,
color: typeof chalk.green,
logger: ActivityLogger,
level: 'info' | 'warn' = 'info',
maxToShow: number = 5
): void {
if (changes.length > 0) {
console.log(color(messageWithChanges.replace('{count}', String(changes.length))));
changes.slice(0, maxToShow).forEach((change) => console.log(chalk.gray(` ${change}`)));
if (changes.length > maxToShow) {
console.log(chalk.gray(` ... and ${changes.length - maxToShow} more files`));
}
const msg = messageWithChanges.replace('{count}', String(changes.length));
const fileList = changes.slice(0, maxToShow).map((c) => ` ${c}`).join(', ');
const suffix = changes.length > maxToShow
? ` ... and ${changes.length - maxToShow} more files`
: '';
logger[level](`${msg} ${fileList}${suffix}`);
} else {
console.log(color(messageWithoutChanges));
logger[level](messageWithoutChanges);
}
}
@@ -138,10 +140,10 @@ export async function executeGitCommandWithRetry(
if (isGitLockError(errMsg) && attempt < maxRetries) {
const delay = Math.pow(2, attempt - 1) * 1000;
console.log(
chalk.yellow(
` ⚠️ Git lock conflict during ${description} (attempt ${attempt}/${maxRetries}). Retrying in ${delay}ms...`
)
// executeGitCommandWithRetry is also called outside activity context
// (e.g., from resume logic), so we use console.warn as a fallback here
console.warn(
`Git lock conflict during ${description} (attempt ${attempt}/${maxRetries}). Retrying in ${delay}ms...`
);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
@@ -165,15 +167,16 @@ export async function executeGitCommandWithRetry(
// Two-phase reset: hard reset (tracked files) + clean (untracked files)
export async function rollbackGitWorkspace(
sourceDir: string,
reason: string = 'retry preparation'
reason: string = 'retry preparation',
logger: ActivityLogger
): Promise<GitOperationResult> {
// Skip git operations if not a git repository
if (!(await isGitRepository(sourceDir))) {
console.log(chalk.gray(` ⏭️ Skipping git rollback (not a git repository)`));
logger.info('Skipping git rollback (not a git repository)');
return { success: true };
}
console.log(chalk.yellow(` 🔄 Rolling back workspace for ${reason}`));
logger.info(`Rolling back workspace for ${reason}`);
try {
const changes = await getChangedFiles(sourceDir, 'status check for rollback');
@@ -190,15 +193,16 @@ export async function rollbackGitWorkspace(
logChangeSummary(
changes,
'Rollback completed - removed {count} contaminated changes:',
'Rollback completed - no changes to remove',
chalk.yellow,
'Rollback completed - removed {count} contaminated changes:',
'Rollback completed - no changes to remove',
logger,
'info',
3
);
return { success: true };
} catch (error) {
const errMsg = error instanceof Error ? error.message : String(error);
console.log(chalk.red(`Rollback failed after retries: ${errMsg}`));
logger.error(`Rollback failed after retries: ${errMsg}`);
return {
success: false,
error: new PentestError(
@@ -216,23 +220,22 @@ export async function rollbackGitWorkspace(
export async function createGitCheckpoint(
sourceDir: string,
description: string,
attempt: number
attempt: number,
logger: ActivityLogger
): Promise<GitOperationResult> {
// Skip git operations if not a git repository
if (!(await isGitRepository(sourceDir))) {
console.log(chalk.gray(` ⏭️ Skipping git checkpoint (not a git repository)`));
logger.info('Skipping git checkpoint (not a git repository)');
return { success: true };
}
console.log(chalk.blue(` 📍 Creating checkpoint for ${description} (attempt ${attempt})`));
logger.info(`Creating checkpoint for ${description} (attempt ${attempt})`);
try {
// First attempt: preserve existing deliverables. Retries: clean workspace to prevent pollution
if (attempt > 1) {
const cleanResult = await rollbackGitWorkspace(sourceDir, `${description} (retry cleanup)`);
const cleanResult = await rollbackGitWorkspace(sourceDir, `${description} (retry cleanup)`, logger);
if (!cleanResult.success) {
console.log(
chalk.yellow(` ⚠️ Workspace cleanup failed, continuing anyway: ${cleanResult.error?.message}`)
);
logger.warn(`Workspace cleanup failed, continuing anyway: ${cleanResult.error?.message}`);
}
}
@@ -247,29 +250,30 @@ export async function createGitCheckpoint(
);
if (hasChanges) {
console.log(chalk.blue(`Checkpoint created with uncommitted changes staged`));
logger.info('Checkpoint created with uncommitted changes staged');
} else {
console.log(chalk.blue(`Empty checkpoint created (no workspace changes)`));
logger.info('Empty checkpoint created (no workspace changes)');
}
return { success: true };
} catch (error) {
const result = toErrorResult(error);
console.log(chalk.yellow(` ⚠️ Checkpoint creation failed after retries: ${result.error?.message}`));
logger.warn(`Checkpoint creation failed after retries: ${result.error?.message}`);
return result;
}
}
export async function commitGitSuccess(
sourceDir: string,
description: string
description: string,
logger: ActivityLogger
): Promise<GitOperationResult> {
// Skip git operations if not a git repository
if (!(await isGitRepository(sourceDir))) {
console.log(chalk.gray(` ⏭️ Skipping git commit (not a git repository)`));
logger.info('Skipping git commit (not a git repository)');
return { success: true };
}
console.log(chalk.green(` 💾 Committing successful results for ${description}`));
logger.info(`Committing successful results for ${description}`);
try {
const changes = await getChangedFiles(sourceDir, 'status check for success commit');
@@ -286,15 +290,14 @@ export async function commitGitSuccess(
logChangeSummary(
changes,
'Success commit created with {count} file changes:',
'Empty success commit created (agent made no file changes)',
chalk.green,
5
'Success commit created with {count} file changes:',
'Empty success commit created (agent made no file changes)',
logger
);
return { success: true };
} catch (error) {
const result = toErrorResult(error);
console.log(chalk.yellow(` ⚠️ Success commit failed after retries: ${result.error?.message}`));
logger.warn(`Success commit failed after retries: ${result.error?.message}`);
return result;
}
}