refactor: extract auto-mode-service into modular services

Reduce auto-mode-service.ts from 1308 to 516 lines (60% reduction) by
extracting reusable functionality into shared packages and services:

- Add feature prompt builders to @automaker/prompts (buildFeaturePrompt,
  buildFollowUpPrompt, buildContinuationPrompt, extractTitleFromDescription)
- Add planning prompts and task parsing to @automaker/prompts
- Add stream processor utilities to @automaker/utils (sleep, processStream)
- Add git commit utilities to @automaker/git-utils (commitAll, hasUncommittedChanges)
- Create ProjectAnalyzer service for project analysis
- Create FeatureVerificationService for verify/commit operations
- Extend FeatureLoader with updateStatus, updatePlanSpec, getPending methods
- Expand FeatureStatus type to include all used statuses
- Add PlanSpec and ParsedTask types to @automaker/types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-22 23:25:22 +01:00
parent c4a2f2c2a8
commit 79ef8c8510
25 changed files with 3048 additions and 2306 deletions

View File

@@ -0,0 +1,112 @@
/**
* Git Commit Utilities - Commit operations for git repositories
*
* Provides utilities for staging and committing changes.
*/
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
/**
* Check if there are uncommitted changes in the working directory
*
* @param workDir - The working directory to check
* @returns True if there are uncommitted changes
*/
export async function hasUncommittedChanges(workDir: string): Promise<boolean> {
try {
const { stdout } = await execAsync('git status --porcelain', { cwd: workDir });
return stdout.trim().length > 0;
} catch {
return false;
}
}
/**
* Stage all changes and commit with a message
*
* @param workDir - The working directory
* @param message - The commit message
* @returns The commit hash if successful, null otherwise
*/
export async function commitAll(workDir: string, message: string): Promise<string | null> {
try {
// Check for changes first
const hasChanges = await hasUncommittedChanges(workDir);
if (!hasChanges) {
return null;
}
// Stage all changes
await execAsync('git add -A', { cwd: workDir });
// Commit with message (escape double quotes)
const escapedMessage = message.replace(/"/g, '\\"');
await execAsync(`git commit -m "${escapedMessage}"`, { cwd: workDir });
// Get the commit hash
const { stdout } = await execAsync('git rev-parse HEAD', { cwd: workDir });
return stdout.trim();
} catch {
return null;
}
}
/**
* Get the current HEAD commit hash
*
* @param workDir - The working directory
* @returns The commit hash or null if not a git repo
*/
export async function getHeadHash(workDir: string): Promise<string | null> {
try {
const { stdout } = await execAsync('git rev-parse HEAD', { cwd: workDir });
return stdout.trim();
} catch {
return null;
}
}
/**
* Get the short version of a commit hash
*
* @param hash - The full commit hash
* @param length - Length of short hash (default 8)
* @returns The shortened hash
*/
export function shortHash(hash: string, length = 8): string {
return hash.substring(0, length);
}
/**
* Run verification commands (lint, typecheck, test, build)
*
* @param workDir - The working directory
* @param checks - Optional custom checks (defaults to npm scripts)
* @returns Object with success status and failed check name if any
*/
export async function runVerificationChecks(
workDir: string,
checks?: Array<{ cmd: string; name: string }>
): Promise<{ success: boolean; failedCheck?: string }> {
const defaultChecks = [
{ cmd: 'npm run lint', name: 'Lint' },
{ cmd: 'npm run typecheck', name: 'Type check' },
{ cmd: 'npm test', name: 'Tests' },
{ cmd: 'npm run build', name: 'Build' },
];
const checksToRun = checks || defaultChecks;
for (const check of checksToRun) {
try {
await execAsync(check.cmd, { cwd: workDir, timeout: 120000 });
} catch {
return { success: false, failedCheck: check.name };
}
}
return { success: true };
}

View File

@@ -17,3 +17,12 @@ export {
generateDiffsForNonGitDirectory,
getGitRepositoryDiffs,
} from './diff.js';
// Export commit utilities
export {
hasUncommittedChanges,
commitAll,
getHeadHash,
shortHash,
runVerificationChecks,
} from './commit.js';