feat: terminal reattach, explorer tab memory, worktree terminals
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import {
|
||||
createWorktree,
|
||||
deletePaseoWorktree,
|
||||
getWorktreeTerminalSpecs,
|
||||
isPaseoOwnedWorktreeCwd,
|
||||
listPaseoWorktrees,
|
||||
slugify,
|
||||
@@ -262,6 +263,43 @@ describe("createWorktree", () => {
|
||||
// Verify worktree was cleaned up
|
||||
expect(existsSync(expectedWorktreePath)).toBe(false);
|
||||
});
|
||||
|
||||
it("reads worktree terminal specs from paseo.json with optional name", async () => {
|
||||
const paseoConfig = {
|
||||
worktree: {
|
||||
terminals: [
|
||||
{ name: "Dev Server", command: "npm run dev" },
|
||||
{ command: "cd packages/app && npm run dev" },
|
||||
],
|
||||
},
|
||||
};
|
||||
writeFileSync(join(repoDir, "paseo.json"), JSON.stringify(paseoConfig));
|
||||
|
||||
expect(getWorktreeTerminalSpecs(repoDir)).toEqual([
|
||||
{ name: "Dev Server", command: "npm run dev" },
|
||||
{ command: "cd packages/app && npm run dev" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("filters invalid worktree terminal specs", async () => {
|
||||
const paseoConfig = {
|
||||
worktree: {
|
||||
terminals: [
|
||||
null,
|
||||
{},
|
||||
{ name: " ", command: " " },
|
||||
{ name: " Watch ", command: "npm run watch", cwd: "packages/app" },
|
||||
{ name: 123, command: "npm run test" },
|
||||
],
|
||||
},
|
||||
};
|
||||
writeFileSync(join(repoDir, "paseo.json"), JSON.stringify(paseoConfig));
|
||||
|
||||
expect(getWorktreeTerminalSpecs(repoDir)).toEqual([
|
||||
{ name: "Watch", command: "npm run watch" },
|
||||
{ command: "npm run test" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("paseo worktree manager", () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ interface PaseoConfig {
|
||||
worktree?: {
|
||||
setup?: string[];
|
||||
destroy?: string[];
|
||||
terminals?: WorktreeTerminalConfig[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,6 +34,11 @@ export type WorktreeSetupCommandResult = {
|
||||
exitCode: number | null;
|
||||
};
|
||||
|
||||
export interface WorktreeTerminalConfig {
|
||||
name?: string;
|
||||
command: string;
|
||||
}
|
||||
|
||||
export class WorktreeSetupError extends Error {
|
||||
readonly results: WorktreeSetupCommandResult[];
|
||||
|
||||
@@ -107,6 +113,42 @@ export function getWorktreeDestroyCommands(repoRoot: string): string[] {
|
||||
return destroyCommands.filter((cmd) => typeof cmd === "string" && cmd.trim().length > 0);
|
||||
}
|
||||
|
||||
export function getWorktreeTerminalSpecs(repoRoot: string): WorktreeTerminalConfig[] {
|
||||
const config = readPaseoConfig(repoRoot);
|
||||
const terminals = config?.worktree?.terminals;
|
||||
if (!Array.isArray(terminals) || terminals.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const specs: WorktreeTerminalConfig[] = [];
|
||||
for (const terminal of terminals) {
|
||||
if (!terminal || typeof terminal !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawCommand = terminal.command;
|
||||
if (typeof rawCommand !== "string") {
|
||||
continue;
|
||||
}
|
||||
const command = rawCommand.trim();
|
||||
if (!command) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawName = terminal.name;
|
||||
const name = typeof rawName === "string" && rawName.trim().length > 0
|
||||
? rawName.trim()
|
||||
: undefined;
|
||||
|
||||
specs.push({
|
||||
...(name ? { name } : {}),
|
||||
command,
|
||||
});
|
||||
}
|
||||
|
||||
return specs;
|
||||
}
|
||||
|
||||
async function execSetupCommand(
|
||||
command: string,
|
||||
options: { cwd: string; env: NodeJS.ProcessEnv }
|
||||
|
||||
Reference in New Issue
Block a user