mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 00:14:54 +00:00
* fix(cli): pass cwd to worktree ls and archive
worktree ls/archive called getPaseoWorktreeList({}) which fails because
the daemon requires cwd or repoRoot. Default to process.cwd(), accept
optional --cwd, and cover both with unit and CLI help tests.
Closes #2010
* test(cli): reproduce worktree listing without repository scope
* fix(cli): resolve local worktree paths from the calling directory
Local instance paths belong to the CLI working directory. Explicit endpoint paths remain daemon-relative, matching the project command convention. Cover both through the real daemon.
* refactor(cli): make repository directory selection explicit
---------
Co-authored-by: Jason <mildreins@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import type { Command } from "commander";
|
|
import { basename } from "node:path";
|
|
import { connectToDaemon } from "../../utils/client.js";
|
|
import { resolveRepositoryDirectory } from "./repository-directory.js";
|
|
import type { CommandOptions, ListResult, OutputSchema, CommandError } from "../../output/index.js";
|
|
|
|
/** Worktree list item for display */
|
|
export interface WorktreeListItem {
|
|
name: string;
|
|
branch: string;
|
|
cwd: string;
|
|
agent: string;
|
|
}
|
|
|
|
/** Shorten home directory in path */
|
|
function shortenPath(path: string): string {
|
|
const home = process.env.HOME;
|
|
if (home && path.startsWith(home)) {
|
|
return "~" + path.slice(home.length);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/** Extract worktree name from path */
|
|
function extractWorktreeName(path: string): string {
|
|
return basename(path);
|
|
}
|
|
|
|
/** Schema for worktree ls output */
|
|
export const worktreeLsSchema: OutputSchema<WorktreeListItem> = {
|
|
idField: "name",
|
|
columns: [
|
|
{ header: "NAME", field: "name", width: 20 },
|
|
{ header: "BRANCH", field: "branch", width: 25 },
|
|
{ header: "CWD", field: "cwd", width: 45 },
|
|
{ header: "AGENT", field: "agent", width: 10 },
|
|
],
|
|
};
|
|
|
|
export type WorktreeLsResult = ListResult<WorktreeListItem>;
|
|
|
|
export interface WorktreeLsOptions extends CommandOptions {
|
|
host?: string;
|
|
cwd?: string;
|
|
}
|
|
|
|
export async function runLsCommand(
|
|
options: WorktreeLsOptions,
|
|
_command: Command,
|
|
): Promise<WorktreeLsResult> {
|
|
const cwd = resolveRepositoryDirectory(options);
|
|
const client = await connectToDaemon({ target: options.daemonTarget });
|
|
|
|
try {
|
|
const agentsPayload = await client.fetchAgents({ filter: { includeArchived: true } });
|
|
const agents = agentsPayload.entries.map((entry) => entry.agent);
|
|
|
|
// Get worktree list from daemon
|
|
const response = await client.getPaseoWorktreeList({ cwd });
|
|
|
|
await client.close();
|
|
|
|
if (response.error) {
|
|
const error: CommandError = {
|
|
code: "WORKTREE_LIST_FAILED",
|
|
message: `Failed to list worktrees: ${response.error.message}`,
|
|
};
|
|
throw error;
|
|
}
|
|
|
|
// Build a map of worktree paths to agent IDs
|
|
const worktreeAgentMap = new Map<string, string>();
|
|
for (const agent of agents) {
|
|
worktreeAgentMap.set(agent.cwd, agent.id.slice(0, 7));
|
|
}
|
|
|
|
const items: WorktreeListItem[] = response.worktrees.map((wt) => ({
|
|
name: extractWorktreeName(wt.worktreePath),
|
|
branch: wt.branchName ?? "-",
|
|
cwd: shortenPath(wt.worktreePath),
|
|
agent: worktreeAgentMap.get(wt.worktreePath) ?? "-",
|
|
}));
|
|
|
|
return {
|
|
type: "list",
|
|
data: items,
|
|
schema: worktreeLsSchema,
|
|
};
|
|
} catch (err) {
|
|
await client.close().catch(() => {});
|
|
|
|
// Re-throw CommandError as-is
|
|
if (err && typeof err === "object" && "code" in err) {
|
|
throw err;
|
|
}
|
|
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
const error: CommandError = {
|
|
code: "WORKTREE_LIST_FAILED",
|
|
message: `Failed to list worktrees: ${message}`,
|
|
};
|
|
throw error;
|
|
}
|
|
}
|