Files
gh-jschulte-claude-plugins-…/skills/analyze/batch-session-state.ts
2025-11-30 08:29:31 +08:00

199 lines
5.6 KiB
TypeScript

/**
* Batch Session State Management
*
* Stores answers from initial batch configuration and reuses them
* across all widgets in the batch session, eliminating repetitive questions.
*/
import * as fs from 'fs';
import * as path from 'path';
export interface BatchSessionAnswers {
detection_type?: 'generic' | 'monorepo-service' | 'nx-app' | 'turborepo-package' | 'lerna-package';
route?: 'greenfield' | 'brownfield';
brownfield_mode?: 'standard' | 'upgrade';
transmission?: 'manual' | 'cruise-control';
clarifications_strategy?: 'defer' | 'prompt' | 'skip';
implementation_scope?: 'none' | 'p0' | 'p0_p1' | 'all';
spec_output_location?: string;
target_stack?: string;
build_location?: string;
build_location_type?: 'subfolder' | 'separate' | 'replace';
}
export interface BatchSessionState {
sessionId: string;
startedAt: string;
batchRootDirectory: string;
totalRepos: number;
batchSize: number;
answers: BatchSessionAnswers;
processedRepos: string[];
}
const BATCH_SESSION_FILENAME = '.stackshift-batch-session.json';
/**
* Find batch session file by checking current directory and walking up
* Similar to how .git directories are found
*/
function findBatchSessionFile(startDir: string = process.cwd()): string | null {
let currentDir = path.resolve(startDir);
const root = path.parse(currentDir).root;
while (currentDir !== root) {
const batchSessionPath = path.join(currentDir, BATCH_SESSION_FILENAME);
if (fs.existsSync(batchSessionPath)) {
return batchSessionPath;
}
currentDir = path.dirname(currentDir);
}
return null;
}
/**
* Get batch session file path for a directory
*/
function getBatchSessionPath(directory: string = process.cwd()): string {
return path.join(directory, BATCH_SESSION_FILENAME);
}
/**
* Create a new batch session with initial answers
*/
export function createBatchSession(
batchRootDirectory: string,
totalRepos: number,
batchSize: number,
answers: BatchSessionAnswers
): BatchSessionState {
const session: BatchSessionState = {
sessionId: `batch-${Date.now()}`,
startedAt: new Date().toISOString(),
batchRootDirectory: path.resolve(batchRootDirectory),
totalRepos,
batchSize,
answers,
processedRepos: []
};
const sessionPath = getBatchSessionPath(batchRootDirectory);
fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
return session;
}
/**
* Get current batch session if it exists
* Searches current directory and walks up to find batch session
*/
export function getBatchSession(startDir: string = process.cwd()): BatchSessionState | null {
try {
const sessionPath = findBatchSessionFile(startDir);
if (!sessionPath) {
return null;
}
const content = fs.readFileSync(sessionPath, 'utf-8');
return JSON.parse(content);
} catch (error) {
console.error('Error reading batch session:', error);
return null;
}
}
/**
* Check if we're currently in a batch session
* Searches current directory and walks up
*/
export function hasBatchSession(startDir: string = process.cwd()): boolean {
return findBatchSessionFile(startDir) !== null;
}
/**
* Update batch session with processed repo
*/
export function markRepoProcessed(repoName: string, startDir: string = process.cwd()): void {
const sessionPath = findBatchSessionFile(startDir);
if (!sessionPath) {
return;
}
try {
const content = fs.readFileSync(sessionPath, 'utf-8');
const session: BatchSessionState = JSON.parse(content);
if (!session.processedRepos.includes(repoName)) {
session.processedRepos.push(repoName);
}
fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
} catch (error) {
console.error('Error updating batch session:', error);
}
}
/**
* Clear batch session in specific directory
*/
export function clearBatchSession(directory: string = process.cwd()): boolean {
try {
const sessionPath = getBatchSessionPath(directory);
if (fs.existsSync(sessionPath)) {
fs.unlinkSync(sessionPath);
return true;
}
return false;
} catch (error) {
console.error('Error clearing batch session:', error);
return false;
}
}
/**
* Get batch session progress
*/
export function getBatchProgress(startDir: string = process.cwd()): string {
const session = getBatchSession(startDir);
if (!session) {
return 'No active batch session';
}
const processed = session.processedRepos.length;
const total = session.totalRepos;
const percentage = Math.round((processed / total) * 100);
return `Batch Progress: ${processed}/${total} repos (${percentage}%)`;
}
/**
* Format batch session for display
*/
export function formatBatchSession(session: BatchSessionState): string {
const duration = Date.now() - new Date(session.startedAt).getTime();
const hours = Math.floor(duration / 3600000);
const minutes = Math.floor((duration % 3600000) / 60000);
return `
📦 Active Batch Session
Session ID: ${session.sessionId}
Batch Root: ${session.batchRootDirectory}
Started: ${new Date(session.startedAt).toLocaleString()}
Duration: ${hours}h ${minutes}m
Total Repos: ${session.totalRepos}
Batch Size: ${session.batchSize}
Processed: ${session.processedRepos.length}/${session.totalRepos}
Configuration:
Route: ${session.answers.route || 'not set'}
Transmission: ${session.answers.transmission || 'not set'}
Spec Output: ${session.answers.spec_output_location || 'current directory'}
Build Location: ${session.answers.build_location || 'greenfield/'}
Target Stack: ${session.answers.target_stack || 'not set'}
Session File: ${session.batchRootDirectory}/${BATCH_SESSION_FILENAME}
`.trim();
}