N picked the globally freshest project by git-mtime, ignoring the session the operator was actually on. Plain n's spawn form already seeds from the focused session (spawnSimilarProjectSeed); fast-forward now matches that, falling back to recency only when nothing is focused.
71 lines
2.6 KiB
TypeScript
71 lines
2.6 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;
|
|
name?: 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 name is freshly
|
|
* generated when the daemon supplied one. */
|
|
export function resolveFastForward(
|
|
projects: Project[],
|
|
presets: SpawnPreset[],
|
|
name?: 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, name: name || undefined };
|
|
}
|
|
|
|
/** The immediate spawn request — worktree on, new branch named after the
|
|
* generated session name — or `null` when any default is missing. */
|
|
export function buildFastForwardRequest(
|
|
res: FastForwardResolution,
|
|
): CreateSessionRequest | null {
|
|
const { project, command, name } = res;
|
|
if (!project || !command?.trim() || !name) return null;
|
|
return {
|
|
cwd: project.path,
|
|
command,
|
|
metadata: { name },
|
|
worktree: {
|
|
base_repo: project.base_repo ?? project.path,
|
|
branch: name,
|
|
create_branch: true,
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Seed for the fallback spawn form: whatever did resolve goes into the
|
|
* form; the rest stays with its own defaults. */
|
|
export function fastForwardSeed(res: FastForwardResolution) {
|
|
return {
|
|
cwd: res.project?.path ?? "",
|
|
command: res.command ?? "",
|
|
name: res.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.name ? res.name : undefined,
|
|
};
|
|
}
|