build: color-code open and close outputs

This commit is contained in:
2026-03-21 01:05:17 +05:30
parent 17bb1130c8
commit 787fa75df4
6 changed files with 64 additions and 67 deletions
+3 -15
View File
@@ -54,21 +54,9 @@ program
force: options.force,
});
if (result.success) {
console.log(`Closed session: ${result.sessionName}`);
if (result.cleanupPerformed) {
console.log(` Removed ${result.worktreesRemoved?.length || 0} worktrees`);
if (result.tagsCreated && result.tagsCreated.length > 0) {
console.log(` Created backup tags: ${result.tagsCreated.join(", ")}`);
}
if (result.warnings && result.warnings.length > 0) {
console.log(` Warnings: ${result.warnings.join("; ")}`);
}
if (result.branchesDeleted && result.branchesDeleted.length > 0) {
console.log(` Deleted ${result.branchesDeleted.length} branches`);
}
}
} else {
if (result.success) {
console.log(result.instructions);
} else {
console.error(`Failed: ${result.error}`);
process.exit(1);
}
+47 -32
View File
@@ -1,6 +1,7 @@
import * as readline from "node:readline";
import chalk from 'chalk'; // removed console usage
import type { CloseSessionOptions, CloseSessionResult } from "../types";
import { runCommand, getSessionPath, getWorktreesForSession } from "./utils";
import { runCommand, getSessionPath, getWorktreesForSession, sanitizeName } from "./utils";
/**
* Prompts the user for input on the terminal.
@@ -45,33 +46,42 @@ async function promptUser(question: string): Promise<string> {
*/
export async function closeMultiModel(options: CloseSessionOptions): Promise<CloseSessionResult> {
const worktreesRemoved: string[] = [];
const instructionsArr: string[] = [];
const branchesDeleted: string[] = [];
const tagsCreated: string[] = [];
const warnings: string[] = [];
const safeSessionName = sanitizeName(options.sessionName);
try {
// Check if session exists
const { ok: sessionExists } = await runCommand(["tmux", "has-session", "-t", options.sessionName]);
const { ok: sessionExists } = await runCommand(["tmux", "has-session", "-t", safeSessionName]);
// Kill tmux session if it exists
if (sessionExists) {
await runCommand(["tmux", "kill-session", "-t", options.sessionName]);
await runCommand(["tmux", "kill-session", "-t", safeSessionName]);
instructionsArr.push(chalk.green(`Closed session '${safeSessionName}'`));
} else {
instructionsArr.push(chalk.yellow(`Session '${safeSessionName}' does not exist`));
}
// Get list of worktrees for this session before killing tmux
const worktrees = await getWorktreesForSession(options.sessionName);
const worktrees = await getWorktreesForSession(safeSessionName);
// If cleanup requested and not forced, ask for confirmation
if (options.cleanupWorktrees && !options.force && worktrees.length > 0) {
console.log(`\nThe following worktrees and branches will be removed:`);
worktrees.forEach((wt) => console.log(` - ${wt.path} (branch: ${wt.branch})`));
instructionsArr.push(chalk.red('The following worktrees and branches will be removed:'));
worktrees.forEach((wt) => {
const msg = ` - ${wt.path} (branch: ${wt.branch})`;
instructionsArr.push(msg.trim());
});
const answer = await promptUser("\nDo you want to proceed? (y/N): ");
const answer = await promptUser(chalk.bold("\nDo you want to proceed? (y/N): "));
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
return {
success: false,
sessionName: options.sessionName,
sessionName: safeSessionName,
error: "Cleanup cancelled by user",
instructions: instructionsArr.join("\n"),
};
}
}
@@ -94,20 +104,19 @@ export async function closeMultiModel(options: CloseSessionOptions): Promise<Clo
if (tagResult.ok) {
tagsCreated.push(archiveTag);
} else {
const warningMsg = `Failed to create tag ${archiveTag} for branch ${worktree.branch}.`;
warnings.push(warningMsg);
console.warn(`Warning: ${warningMsg}`);
const warningMsg = chalk.red(`Warning: Failed to create tag ${archiveTag} for branch ${worktree.branch}.`);
instructionsArr.push(warningMsg);
}
await runCommand(["git", "branch", "-D", worktree.branch]);
branchesDeleted.push(worktree.branch);
} catch (err) {
console.warn(`Warning: Failed to cleanup worktree ${worktree.path}: ${err}`);
instructionsArr.push(chalk.red(`Failed to cleanup worktree ${worktree.path}: ${err}`));
}
}
// Also remove the base directory
const worktreeBase = getSessionPath(options.sessionName);
const worktreeBase = getSessionPath(safeSessionName);
try {
await runCommand(["rm", "-rf", worktreeBase]);
} catch {
@@ -115,26 +124,32 @@ export async function closeMultiModel(options: CloseSessionOptions): Promise<Clo
}
cleanupPerformed = worktreesRemoved.length > 0;
if (cleanupPerformed) {
instructionsArr.push(chalk.dim(`Removed ${worktreesRemoved.length} worktrees, alongwith their branches.`));
if (tagsCreated.length > 0) {
instructionsArr.push(chalk.dim(`Created archive tags: ${tagsCreated.join(", ")}.`));
}
}
}
return {
success: true,
sessionName: options.sessionName,
cleanupPerformed,
worktreesRemoved,
branchesDeleted,
tagsCreated,
warnings,
};
return {
success: true,
sessionName: safeSessionName,
cleanupPerformed,
worktreesRemoved,
branchesDeleted,
tagsCreated,
instructions: instructionsArr.join("\n"),
};
} catch (error) {
return {
success: false,
sessionName: options.sessionName,
error: String(error),
worktreesRemoved,
branchesDeleted,
tagsCreated,
warnings,
};
return {
success: false,
sessionName: safeSessionName,
error: String(error),
worktreesRemoved,
branchesDeleted,
tagsCreated,
instructions: instructionsArr.join("\n"),
};
}
}
+6 -2
View File
@@ -154,7 +154,9 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
if (!sessionCreateResult.ok) {
const undoErrors = await undoWorktree(worktreePath, branchName);
let errorMsg = `Error: Failed to create tmux session '${sessionName}'.${sessionCreateResult.stderr ? ` ${sessionCreateResult.stderr}` : ""}`;
if (undoErrors.length > 0) errorMsg += ` Cleanup errors: ${undoErrors.join(", ")}`;
if (undoErrors.length > 0) {
errorMsg += ` Cleanup errors: ${undoErrors.join(", ")}`;
}
return { success: false, sessionName, error: errorMsg };
}
} else {
@@ -173,7 +175,9 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
if (!windowCreateResult.ok) {
failedModels.push(`${plan.model} (${windowCreateResult.stderr || "failed to create window"})`);
const undoErrors = await undoWorktree(worktreePath, branchName);
if (undoErrors.length > 0) failedModels.push(`cleanup failed for ${plan.model}: ${undoErrors.join(", ")}`);
if (undoErrors.length > 0) {
failedModels.push(`cleanup failed for ${plan.model}: ${undoErrors.join(", ")}`);
}
continue;
}
}
+1 -12
View File
@@ -25,17 +25,6 @@ export const closeTool = tool({
return result.error!;
}
let details = "";
if (result.cleanupPerformed) {
details += ` Removed ${result.worktreesRemoved?.length || 0} worktrees.`;
if (result.tagsCreated && result.tagsCreated.length > 0) {
details += ` Created backup tags: ${result.tagsCreated.join(", ")}.`;
}
if (result.warnings && result.warnings.length > 0) {
details += ` Warnings: ${result.warnings.join("; ")}.`;
}
}
return `Session "${result.sessionName}" has been closed.${details}`;
return result.instructions;
},
});
+3 -2
View File
@@ -44,6 +44,8 @@ export interface CloseSessionOptions {
* Result returned after attempting to close a multi-model session.
*/
export interface CloseSessionResult {
/** Console messages and instructions. */
instructions: string;
/** Whether the close succeeded. */
success: boolean;
/** The session name that was closed. */
@@ -58,8 +60,7 @@ export interface CloseSessionResult {
branchesDeleted?: string[];
/** Archive tags that were created before branch deletion. */
tagsCreated?: string[];
/** Warning messages. */
warnings?: string[];
}
/**