build: add biome formatter and linter

This commit is contained in:
2026-03-23 08:17:27 +05:30
parent 068db04355
commit 7a164c705c
21 changed files with 1620 additions and 510 deletions
+121 -34
View File
@@ -1,17 +1,17 @@
import * as fs from "node:fs";
import chalk from 'chalk';
import chalk from "chalk";
import type { MultiModelOptions, MultiModelResult } from "../types";
import {
runCommand,
normalizeModels,
findDuplicates,
createWindowPlans,
findDuplicates,
formatInvalidModelError,
sanitizeName,
getWorktreePath,
getSessionPath,
undoWorktree,
getWorktreePath,
launchModelInWindow,
normalizeModels,
runCommand,
sanitizeName,
undoWorktree,
} from "./utils";
/**
@@ -35,7 +35,9 @@ import {
* }
* ```
*/
export async function openMultiModel(options: MultiModelOptions): Promise<MultiModelResult> {
export async function openMultiModel(
options: MultiModelOptions,
): Promise<MultiModelResult> {
const sessionName = options.sessionName.trim();
const safeSessionName = sanitizeName(sessionName);
const models = normalizeModels(options.models);
@@ -43,11 +45,19 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
const mode = options.mode || "cli";
if (!sessionName) {
return { success: false, sessionName: "", error: "Error: `sessionName` must not be empty." };
return {
success: false,
sessionName: "",
error: "Error: `sessionName` must not be empty.",
};
}
if (models.length === 0) {
return { success: false, sessionName, error: "Error: Model names must not be empty." };
return {
success: false,
sessionName,
error: "Error: Model names must not be empty.",
};
}
const duplicateModels = findDuplicates(models);
@@ -59,19 +69,35 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
};
}
const gitRepoCheck = await runCommand(["git", "rev-parse", "--is-inside-work-tree"]);
const gitRepoCheck = await runCommand([
"git",
"rev-parse",
"--is-inside-work-tree",
]);
if (!gitRepoCheck.ok) {
return { success: false, sessionName, error: "Error: `multi-model` requires a git repository." };
return {
success: false,
sessionName,
error: "Error: `multi-model` requires a git repository.",
};
}
const tmuxExists = await runCommand(["command", "-v", "tmux"]);
if (!tmuxExists.ok) {
return { success: false, sessionName, error: "Error: `tmux` is not installed or not on `PATH`." };
return {
success: false,
sessionName,
error: "Error: `tmux` is not installed or not on `PATH`.",
};
}
const binaryExists = await runCommand(["command", "-v", binaryName]);
if (!binaryExists.ok) {
return { success: false, sessionName, error: `Error: \`${binaryName}\` is not installed or not on \`PATH\`.` };
return {
success: false,
sessionName,
error: `Error: \`${binaryName}\` is not installed or not on \`PATH\`.`,
};
}
const modelListResult = await runCommand([binaryName, "models"]);
@@ -90,12 +116,25 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
const invalidModels = models.filter((model) => !allowlist.includes(model));
if (invalidModels.length > 0) {
return { success: false, sessionName, error: formatInvalidModelError(invalidModels, allowlist) };
return {
success: false,
sessionName,
error: formatInvalidModelError(invalidModels, allowlist),
};
}
const sessionExists = await runCommand(["tmux", "has-session", "-t", sessionName]);
const sessionExists = await runCommand([
"tmux",
"has-session",
"-t",
sessionName,
]);
if (sessionExists.ok) {
return { success: false, sessionName, error: "Error: Session already exists. Use a different `sessionName`." };
return {
success: false,
sessionName,
error: "Error: Session already exists. Use a different `sessionName`.",
};
}
const windowPlans = createWindowPlans(models);
@@ -111,30 +150,61 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
if (fs.existsSync(worktreePath)) {
if (!isFirst) {
failedModels.push(`${plan.model} (Worktree path already exists at ${worktreePath})`);
failedModels.push(
`${plan.model} (Worktree path already exists at ${worktreePath})`,
);
continue;
} else {
return { success: false, sessionName, error: `Error: Worktree path already exists at ${worktreePath}. Clean it up first.` };
return {
success: false,
sessionName,
error: `Error: Worktree path already exists at ${worktreePath}. Clean it up first.`,
};
}
}
const branchExists = await runCommand(["git", "show-ref", "--verify", "--quiet", `refs/heads/${branchName}`]);
const branchExists = await runCommand([
"git",
"show-ref",
"--verify",
"--quiet",
`refs/heads/${branchName}`,
]);
if (branchExists.ok) {
if (!isFirst) {
failedModels.push(`${plan.model} (Branch '${branchName}' already exists)`);
failedModels.push(
`${plan.model} (Branch '${branchName}' already exists)`,
);
continue;
} else {
return { success: false, sessionName, error: `Error: Branch '${branchName}' already exists.` };
return {
success: false,
sessionName,
error: `Error: Branch '${branchName}' already exists.`,
};
}
}
const worktreeResult = await runCommand(["git", "worktree", "add", "-b", branchName, worktreePath]);
const worktreeResult = await runCommand([
"git",
"worktree",
"add",
"-b",
branchName,
worktreePath,
]);
if (!worktreeResult.ok) {
if (!isFirst) {
failedModels.push(`${plan.model} (Failed to create worktree: ${worktreeResult.stderr})`);
failedModels.push(
`${plan.model} (Failed to create worktree: ${worktreeResult.stderr})`,
);
continue;
} else {
return { success: false, sessionName, error: `Error: Failed to create worktree for ${plan.model}: ${worktreeResult.stderr}` };
return {
success: false,
sessionName,
error: `Error: Failed to create worktree for ${plan.model}: ${worktreeResult.stderr}`,
};
}
}
@@ -173,22 +243,35 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
]);
if (!windowCreateResult.ok) {
failedModels.push(`${plan.model} (${windowCreateResult.stderr || "failed to create window"})`);
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(", ")}`);
failedModels.push(
`cleanup failed for ${plan.model}: ${undoErrors.join(", ")}`,
);
}
continue;
}
}
const launchResult = await launchModelInWindow(sessionName, plan, binaryName);
const launchResult = await launchModelInWindow(
sessionName,
plan,
binaryName,
);
if (launchResult.ok) {
succeededModels.push(plan.model);
} else {
failedModels.push(`${plan.model} (${launchResult.stderr || "failed to send launch command"})`);
failedModels.push(
`${plan.model} (${launchResult.stderr || "failed to send launch command"})`,
);
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(", ")}`,
);
}
}
@@ -206,9 +289,13 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
sessionName,
windows: windowNames,
instructions: [
chalk.red(`Error: Created tmux session '${sessionName}', but some model launches failed.`),
chalk.red(
`Error: Created tmux session '${sessionName}', but some model launches failed.`,
),
chalk.red(`Failed: ${failedModels.join(", ")}.`),
chalk.dim(`Succeeded: ${succeededModels.length > 0 ? succeededModels.join(", ") : "none"}.`),
chalk.dim(
`Succeeded: ${succeededModels.length > 0 ? succeededModels.join(", ") : "none"}.`,
),
"",
chalk.bold(`Attach with \`${attachCommand}\` to inspect the session.`),
"",
@@ -216,7 +303,7 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
primaryCleanup,
"",
chalk.dim(`Alternate manual cleanup - `),
chalk.dim(cleanupCommand)
chalk.dim(cleanupCommand),
].join("\n"),
};
}
@@ -232,7 +319,7 @@ export async function openMultiModel(options: MultiModelOptions): Promise<MultiM
primaryCleanup,
"",
chalk.dim(`Alternate manual cleanup - `),
chalk.dim(cleanupCommand)
chalk.dim(cleanupCommand),
].join("\n"),
};
}