Files
claude-task-master/apps/cli/src/commands/autopilot/shared.ts
2025-10-16 22:32:21 +02:00

263 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @fileoverview Shared utilities for autopilot commands
*/
import {
WorkflowOrchestrator,
WorkflowStateManager,
GitAdapter,
CommitMessageGenerator
} from '@tm/core';
import type { WorkflowState, WorkflowContext, SubtaskInfo } from '@tm/core';
import chalk from 'chalk';
/**
* Base options interface for all autopilot commands
*/
export interface AutopilotBaseOptions {
projectRoot?: string;
json?: boolean;
verbose?: boolean;
}
/**
* Load workflow state from disk using WorkflowStateManager
*/
export async function loadWorkflowState(
projectRoot: string
): Promise<WorkflowState | null> {
const stateManager = new WorkflowStateManager(projectRoot);
if (!(await stateManager.exists())) {
return null;
}
try {
return await stateManager.load();
} catch (error) {
throw new Error(
`Failed to load workflow state: ${(error as Error).message}`
);
}
}
/**
* Save workflow state to disk using WorkflowStateManager
*/
export async function saveWorkflowState(
projectRoot: string,
state: WorkflowState
): Promise<void> {
const stateManager = new WorkflowStateManager(projectRoot);
try {
await stateManager.save(state);
} catch (error) {
throw new Error(
`Failed to save workflow state: ${(error as Error).message}`
);
}
}
/**
* Delete workflow state from disk using WorkflowStateManager
*/
export async function deleteWorkflowState(projectRoot: string): Promise<void> {
const stateManager = new WorkflowStateManager(projectRoot);
await stateManager.delete();
}
/**
* Check if workflow state exists using WorkflowStateManager
*/
export async function hasWorkflowState(projectRoot: string): Promise<boolean> {
const stateManager = new WorkflowStateManager(projectRoot);
return await stateManager.exists();
}
/**
* Initialize WorkflowOrchestrator with persistence
*/
export function createOrchestrator(
context: WorkflowContext,
projectRoot: string
): WorkflowOrchestrator {
const orchestrator = new WorkflowOrchestrator(context);
const stateManager = new WorkflowStateManager(projectRoot);
// Enable auto-persistence
orchestrator.enableAutoPersist(async (state: WorkflowState) => {
await stateManager.save(state);
});
return orchestrator;
}
/**
* Initialize GitAdapter for project
*/
export function createGitAdapter(projectRoot: string): GitAdapter {
return new GitAdapter(projectRoot);
}
/**
* Initialize CommitMessageGenerator
*/
export function createCommitMessageGenerator(): CommitMessageGenerator {
return new CommitMessageGenerator();
}
/**
* Output formatter for JSON and text modes
*/
export class OutputFormatter {
constructor(private useJson: boolean) {}
/**
* Output data in appropriate format
*/
output(data: Record<string, unknown>): void {
if (this.useJson) {
console.log(JSON.stringify(data, null, 2));
} else {
this.outputText(data);
}
}
/**
* Output data in human-readable text format
*/
private outputText(data: Record<string, unknown>): void {
for (const [key, value] of Object.entries(data)) {
if (typeof value === 'object' && value !== null) {
console.log(chalk.cyan(`${key}:`));
this.outputObject(value as Record<string, unknown>, ' ');
} else {
console.log(chalk.white(`${key}: ${value}`));
}
}
}
/**
* Output nested object with indentation
*/
private outputObject(obj: Record<string, unknown>, indent: string): void {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object' && value !== null) {
console.log(chalk.cyan(`${indent}${key}:`));
this.outputObject(value as Record<string, unknown>, indent + ' ');
} else {
console.log(chalk.gray(`${indent}${key}: ${value}`));
}
}
}
/**
* Output error message
*/
error(message: string, details?: Record<string, unknown>): void {
if (this.useJson) {
console.error(
JSON.stringify(
{
error: message,
...details
},
null,
2
)
);
} else {
console.error(chalk.red(`Error: ${message}`));
if (details) {
for (const [key, value] of Object.entries(details)) {
console.error(chalk.gray(` ${key}: ${value}`));
}
}
}
}
/**
* Output success message
*/
success(message: string, data?: Record<string, unknown>): void {
if (this.useJson) {
console.log(
JSON.stringify(
{
success: true,
message,
...data
},
null,
2
)
);
} else {
console.log(chalk.green(`${message}`));
if (data) {
this.output(data);
}
}
}
/**
* Output warning message
*/
warning(message: string): void {
if (this.useJson) {
console.warn(
JSON.stringify(
{
warning: message
},
null,
2
)
);
} else {
console.warn(chalk.yellow(`${message}`));
}
}
/**
* Output info message
*/
info(message: string): void {
if (this.useJson) {
// Don't output info messages in JSON mode
return;
}
console.log(chalk.blue(` ${message}`));
}
}
/**
* Validate task ID format
*/
export function validateTaskId(taskId: string): boolean {
// Task ID should be in format: number or number.number (e.g., "1" or "1.2")
const pattern = /^\d+(\.\d+)*$/;
return pattern.test(taskId);
}
/**
* Parse subtasks from task data
*/
export function parseSubtasks(
task: any,
maxAttempts: number = 3
): SubtaskInfo[] {
if (!task.subtasks || !Array.isArray(task.subtasks)) {
return [];
}
return task.subtasks.map((subtask: any) => ({
id: subtask.id,
title: subtask.title,
status: subtask.status === 'done' ? 'completed' : 'pending',
attempts: 0,
maxAttempts
}));
}