mirror of
https://github.com/bendtherules/opencode-multi-model.git
synced 2026-08-18 13:42:21 +00:00
Impl: tool-npm-package
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import * as readline from "node:readline";
|
||||
import type { CloseSessionOptions, CloseSessionResult } from "../types";
|
||||
import { runCommand, getSessionPath, getWorktreesForSession } from "./utils";
|
||||
|
||||
/**
|
||||
* Prompts the user for input on the terminal.
|
||||
*
|
||||
* @param question The question to display.
|
||||
* @returns The user's answer.
|
||||
*/
|
||||
async function promptUser(question: string): Promise<string> {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
rl.close();
|
||||
resolve(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a multi-model tmux session and optionally cleans up worktrees and branches.
|
||||
*
|
||||
* When `cleanupWorktrees` is true, removes git worktrees, creates archive tags
|
||||
* for recovery, and deletes the associated branches.
|
||||
*
|
||||
* @param options Close configuration including session name and cleanup flags.
|
||||
* @returns Result with details about what was cleaned up.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await closeMultiModel({
|
||||
* sessionName: "compare",
|
||||
* cleanupWorktrees: true,
|
||||
* force: true,
|
||||
* });
|
||||
* if (result.success) {
|
||||
* console.log(`Closed ${result.sessionName}, removed ${result.worktreesRemoved?.length} worktrees`);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export async function closeMultiModel(options: CloseSessionOptions): Promise<CloseSessionResult> {
|
||||
const worktreesRemoved: string[] = [];
|
||||
const branchesDeleted: string[] = [];
|
||||
const tagsCreated: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
try {
|
||||
// Check if session exists
|
||||
const { ok: sessionExists } = await runCommand(["tmux", "has-session", "-t", options.sessionName]);
|
||||
|
||||
// Get list of worktrees for this session before killing tmux
|
||||
const worktrees = await getWorktreesForSession(options.sessionName);
|
||||
|
||||
// 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})`));
|
||||
|
||||
const answer = await promptUser("\nDo you want to proceed? (y/N): ");
|
||||
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
||||
return {
|
||||
success: false,
|
||||
sessionName: options.sessionName,
|
||||
error: "Cleanup cancelled by user",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Kill tmux session if it exists
|
||||
if (sessionExists) {
|
||||
await runCommand(["tmux", "kill-session", "-t", options.sessionName]);
|
||||
}
|
||||
|
||||
let cleanupPerformed = false;
|
||||
|
||||
// Cleanup worktrees and optionally branches
|
||||
if (options.cleanupWorktrees) {
|
||||
for (const worktree of worktrees) {
|
||||
try {
|
||||
// Remove worktree
|
||||
await runCommand(["git", "worktree", "remove", "-f", worktree.path]);
|
||||
worktreesRemoved.push(worktree.path);
|
||||
|
||||
// Create an archive tag before deleting the branch for safe recovery
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const archiveTag = `archive/${worktree.branch}-${timestamp}`;
|
||||
const tagResult = await runCommand(["git", "tag", archiveTag, worktree.branch]);
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
await runCommand(["git", "branch", "-D", worktree.branch]);
|
||||
branchesDeleted.push(worktree.branch);
|
||||
} catch (err) {
|
||||
console.warn(`Warning: Failed to cleanup worktree ${worktree.path}: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Also remove the base directory if empty
|
||||
const worktreeBase = getSessionPath(options.sessionName);
|
||||
try {
|
||||
await runCommand(["rmdir", worktreeBase]);
|
||||
} catch {
|
||||
// Ignore errors if directory not empty
|
||||
}
|
||||
|
||||
cleanupPerformed = worktreesRemoved.length > 0;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
sessionName: options.sessionName,
|
||||
cleanupPerformed,
|
||||
worktreesRemoved,
|
||||
branchesDeleted,
|
||||
tagsCreated,
|
||||
warnings,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
sessionName: options.sessionName,
|
||||
error: String(error),
|
||||
worktreesRemoved,
|
||||
branchesDeleted,
|
||||
tagsCreated,
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user