Add custom branch name feature
Users can now specify a custom branch name when creating a session. If provided, the custom name is used as both the git branch name and the session display name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9405824d7b
commit
fcd62c27bb
|
|
@ -71,6 +71,12 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Branch Name (optional)</label>
|
||||||
|
<input type="text" id="branch-name" class="form-input" placeholder="e.g., feature/add-login" />
|
||||||
|
<span class="text-xs text-gray-400 mt-1 block">Custom branch name (will also be used as session name)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Coding Agent</label>
|
<label class="form-label">Coding Agent</label>
|
||||||
<select id="coding-agent" class="form-select">
|
<select id="coding-agent" class="form-select">
|
||||||
|
|
|
||||||
24
main.ts
24
main.ts
|
|
@ -14,6 +14,7 @@ const execAsync = promisify(exec);
|
||||||
interface SessionConfig {
|
interface SessionConfig {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
parentBranch: string;
|
parentBranch: string;
|
||||||
|
branchName?: string;
|
||||||
codingAgent: string;
|
codingAgent: string;
|
||||||
skipPermissions: boolean;
|
skipPermissions: boolean;
|
||||||
setupCommands?: string[];
|
setupCommands?: string[];
|
||||||
|
|
@ -343,14 +344,21 @@ async function ensureFleetcodeExcluded(projectDir: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createWorktree(projectDir: string, parentBranch: string, sessionNumber: number, sessionUuid: string): Promise<string> {
|
async function createWorktree(projectDir: string, parentBranch: string, sessionNumber: number, sessionUuid: string, customBranchName?: string): Promise<string> {
|
||||||
const git = simpleGit(projectDir);
|
const git = simpleGit(projectDir);
|
||||||
const fleetcodeDir = path.join(projectDir, ".fleetcode");
|
const fleetcodeDir = path.join(projectDir, ".fleetcode");
|
||||||
const worktreeName = `session${sessionNumber}`;
|
const worktreeName = `session${sessionNumber}`;
|
||||||
const worktreePath = path.join(fleetcodeDir, worktreeName);
|
const worktreePath = path.join(fleetcodeDir, worktreeName);
|
||||||
// Include short UUID to ensure branch uniqueness across deletes/recreates
|
|
||||||
const shortUuid = sessionUuid.split('-')[0];
|
// Use custom branch name if provided, otherwise generate default
|
||||||
const branchName = `fleetcode/session${sessionNumber}-${shortUuid}`;
|
let branchName: string;
|
||||||
|
if (customBranchName) {
|
||||||
|
branchName = customBranchName;
|
||||||
|
} else {
|
||||||
|
// Include short UUID to ensure branch uniqueness across deletes/recreates
|
||||||
|
const shortUuid = sessionUuid.split('-')[0];
|
||||||
|
branchName = `fleetcode/session${sessionNumber}-${shortUuid}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Create .fleetcode directory if it doesn't exist
|
// Create .fleetcode directory if it doesn't exist
|
||||||
if (!fs.existsSync(fleetcodeDir)) {
|
if (!fs.existsSync(fleetcodeDir)) {
|
||||||
|
|
@ -434,7 +442,9 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
||||||
try {
|
try {
|
||||||
const sessionNumber = getNextSessionNumber();
|
const sessionNumber = getNextSessionNumber();
|
||||||
const sessionId = `session-${Date.now()}`;
|
const sessionId = `session-${Date.now()}`;
|
||||||
const sessionName = `Session ${sessionNumber}`;
|
|
||||||
|
// Use custom branch name as session name if provided, otherwise default
|
||||||
|
const sessionName = config.branchName || `Session ${sessionNumber}`;
|
||||||
|
|
||||||
// Generate UUID for this session (before creating worktree)
|
// Generate UUID for this session (before creating worktree)
|
||||||
const sessionUuid = uuidv4();
|
const sessionUuid = uuidv4();
|
||||||
|
|
@ -442,8 +452,8 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
||||||
// Ensure .fleetcode is excluded (async, don't wait)
|
// Ensure .fleetcode is excluded (async, don't wait)
|
||||||
ensureFleetcodeExcluded(config.projectDir);
|
ensureFleetcodeExcluded(config.projectDir);
|
||||||
|
|
||||||
// Create git worktree with unique branch name
|
// Create git worktree with custom or default branch name
|
||||||
const worktreePath = await createWorktree(config.projectDir, config.parentBranch, sessionNumber, sessionUuid);
|
const worktreePath = await createWorktree(config.projectDir, config.parentBranch, sessionNumber, sessionUuid, config.branchName);
|
||||||
|
|
||||||
// Extract and write MCP config
|
// Extract and write MCP config
|
||||||
const mcpServers = extractProjectMcpConfig(config.projectDir);
|
const mcpServers = extractProjectMcpConfig(config.projectDir);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { FitAddon } from "xterm-addon-fit";
|
||||||
interface SessionConfig {
|
interface SessionConfig {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
parentBranch: string;
|
parentBranch: string;
|
||||||
|
branchName?: string;
|
||||||
codingAgent: string;
|
codingAgent: string;
|
||||||
skipPermissions: boolean;
|
skipPermissions: boolean;
|
||||||
setupCommands?: string[];
|
setupCommands?: string[];
|
||||||
|
|
@ -761,6 +762,7 @@ ipcRenderer.on("session-created", (_event, sessionId: string, persistedSession:
|
||||||
const modal = document.getElementById("config-modal");
|
const modal = document.getElementById("config-modal");
|
||||||
const projectDirInput = document.getElementById("project-dir") as HTMLInputElement;
|
const projectDirInput = document.getElementById("project-dir") as HTMLInputElement;
|
||||||
const parentBranchSelect = document.getElementById("parent-branch") as HTMLSelectElement;
|
const parentBranchSelect = document.getElementById("parent-branch") as HTMLSelectElement;
|
||||||
|
const branchNameInput = document.getElementById("branch-name") as HTMLInputElement;
|
||||||
const setupCommandsTextarea = document.getElementById("setup-commands") as HTMLTextAreaElement;
|
const setupCommandsTextarea = document.getElementById("setup-commands") as HTMLTextAreaElement;
|
||||||
|
|
||||||
if (createBtn) {
|
if (createBtn) {
|
||||||
|
|
@ -775,6 +777,9 @@ ipcRenderer.on("session-created", (_event, sessionId: string, persistedSession:
|
||||||
projectDirInput.value = "";
|
projectDirInput.value = "";
|
||||||
selectedDirectory = "";
|
selectedDirectory = "";
|
||||||
parentBranchSelect.innerHTML = '<option value="">Loading branches...</option>';
|
parentBranchSelect.innerHTML = '<option value="">Loading branches...</option>';
|
||||||
|
if (branchNameInput) {
|
||||||
|
branchNameInput.value = "";
|
||||||
|
}
|
||||||
if (setupCommandsTextarea) {
|
if (setupCommandsTextarea) {
|
||||||
setupCommandsTextarea.value = "";
|
setupCommandsTextarea.value = "";
|
||||||
}
|
}
|
||||||
|
|
@ -934,9 +939,13 @@ createBtn?.addEventListener("click", () => {
|
||||||
? setupCommandsText.split("\n").filter(cmd => cmd.trim())
|
? setupCommandsText.split("\n").filter(cmd => cmd.trim())
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const branchNameInput = document.getElementById("branch-name") as HTMLInputElement;
|
||||||
|
const branchName = branchNameInput?.value.trim() || undefined;
|
||||||
|
|
||||||
const config: SessionConfig = {
|
const config: SessionConfig = {
|
||||||
projectDir: selectedDirectory,
|
projectDir: selectedDirectory,
|
||||||
parentBranch: parentBranchSelect.value,
|
parentBranch: parentBranchSelect.value,
|
||||||
|
branchName,
|
||||||
codingAgent: codingAgentSelect.value,
|
codingAgent: codingAgentSelect.value,
|
||||||
skipPermissions: codingAgentSelect.value === "claude" ? skipPermissionsCheckbox.checked : false,
|
skipPermissions: codingAgentSelect.value === "claude" ? skipPermissionsCheckbox.checked : false,
|
||||||
setupCommands,
|
setupCommands,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue