The web N path sent the generated handle as the session name as well as the worktree branch, so the session was born named and never got its Haiku name from its first prompt. The TUI already spawns unnamed (#131); the handle now only seeds the branch.
77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
// Fast-forward spawn (#67): one keystroke, every default resolved, no form.
|
|
// Pure resolution only — the caller (main.ts) owns the fetches and the POST,
|
|
// and falls back to a seeded spawn form when anything is missing.
|
|
|
|
import type { CreateSessionRequest, Project, SpawnPreset } from "./types.js";
|
|
import { sortProjects } from "./projectList.js";
|
|
|
|
/** What one fast-forward keystroke resolved. Anything absent means "open a
|
|
* form pre-filled with the rest instead of spawning". */
|
|
export interface FastForwardResolution {
|
|
project?: Project;
|
|
command?: string;
|
|
/** A generated handle for the worktree branch (#131). Not a session name:
|
|
* the fast path names no session. */
|
|
handle?: string;
|
|
}
|
|
|
|
/** The defaults: the currently focused session's project when it's still a
|
|
* known project, else the most recent one (fresh-first, same ordering the
|
|
* picker shows) — so `N` lands where the operator currently is, matching
|
|
* what plain `n` seeds from `spawnSimilarProjectSeed`. Its last command
|
|
* falls back to the first non-custom preset, and the branch handle is
|
|
* freshly generated when the daemon supplied one. */
|
|
export function resolveFastForward(
|
|
projects: Project[],
|
|
presets: SpawnPreset[],
|
|
handle?: string,
|
|
focusedCwd?: string,
|
|
): FastForwardResolution {
|
|
const project =
|
|
projects.find((p) => p.path === focusedCwd) ?? sortProjects(projects, new Date())[0];
|
|
const lastCommand = project?.last_command?.trim();
|
|
const command =
|
|
lastCommand || presets.find((p) => p.command !== "")?.command || undefined;
|
|
return { project, command, handle: handle || undefined };
|
|
}
|
|
|
|
/** The immediate spawn request — worktree on, new branch named after the
|
|
* generated handle — or `null` when any default is missing.
|
|
*
|
|
* The request carries no name (#131): the daemon assigns a provisional one
|
|
* and replaces it once the session sees its first prompt. A name here would
|
|
* also collide with the branch handle's own session. */
|
|
export function buildFastForwardRequest(
|
|
res: FastForwardResolution,
|
|
): CreateSessionRequest | null {
|
|
const { project, command, handle } = res;
|
|
if (!project || !command?.trim() || !handle) return null;
|
|
return {
|
|
cwd: project.path,
|
|
command,
|
|
worktree: {
|
|
base_repo: project.base_repo ?? project.path,
|
|
branch: handle,
|
|
create_branch: true,
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Seed for the fallback spawn form: whatever did resolve goes into the
|
|
* form; the rest stays with its own defaults. The name field stays empty
|
|
* (#131) — the handle seeds the branch, not the session name. */
|
|
export function fastForwardSeed(res: FastForwardResolution) {
|
|
return {
|
|
cwd: res.project?.path ?? "",
|
|
command: res.command ?? "",
|
|
name: "",
|
|
// Left undefined rather than false when nothing resolved: the form
|
|
// defaults the toggle on, and `false` would stick, so the fallback form
|
|
// would disagree with both a normally-opened form and the TUI's fast path
|
|
// (#67 review).
|
|
worktreeEnabled: res.project ? true : undefined,
|
|
worktreeBranch:
|
|
res.project && res.handle ? res.handle : undefined,
|
|
};
|
|
}
|