Skip to content

Commit 7d7a545

Browse files
committed
readme
1 parent c45ac9e commit 7d7a545

File tree

5 files changed

+143
-3
lines changed

5 files changed

+143
-3
lines changed

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# FleetCode
2+
3+
A desktop terminal application for running multiple CLI coding agents simultaneously, each in isolated git worktrees.
4+
5+
## Features
6+
7+
- **Multiple Sessions**: Run multiple coding agent sessions (Claude, Codex) in parallel
8+
- **Git Worktree Isolation**: Each session runs in its own git worktree, keeping work isolated
9+
- **Persistent Sessions**: Sessions persist across app restarts with automatic resumption
10+
- **Terminal Theming**: Choose from preset themes (macOS Light/Dark, Solarized Dark, Dracula, One Dark, GitHub Dark)
11+
- **Setup Commands**: Configure shell commands to run before the coding agent starts
12+
- **MCP Server Management**: Add and configure Model Context Protocol (MCP) servers
13+
- **Session Management**: Rename, close, and delete sessions with automatic worktree cleanup
14+
15+
## Prerequisites
16+
17+
- Node.js 16+
18+
- Git
19+
- Claude CLI (`npm install -g @anthropic-ai/claude-cli`) or Codex
20+
21+
## Installation
22+
23+
```bash
24+
npm install
25+
```
26+
27+
## Usage
28+
29+
### Development
30+
31+
```bash
32+
npm run dev
33+
```
34+
35+
### Production Build
36+
37+
```bash
38+
npm run build
39+
npm start
40+
```
41+
42+
## How It Works
43+
44+
### Session Creation
45+
46+
1. Select a project directory (must be a git repository)
47+
2. Choose a parent branch for the worktree
48+
3. Select your coding agent (Claude or Codex)
49+
4. Optionally add setup commands (e.g., environment variables, source files)
50+
5. FleetCode creates a new git worktree and spawns a terminal session
51+
52+
### Session Management
53+
54+
- **New Sessions**: Use `--session-id <uuid>` for first-time Claude sessions
55+
- **Reopened Sessions**: Automatically resume with `--resume <uuid>`
56+
- **Worktrees**: Each session gets its own isolated git worktree
57+
- **Persistence**: Sessions are saved and can be reopened after closing the app
58+
59+
### Terminal Settings
60+
61+
Access settings via the gear icon (⚙️) in the sidebar:
62+
63+
- **Font Family**: Choose from common monospace fonts
64+
- **Font Size**: Adjust terminal text size
65+
- **Theme**: Select from preset color themes
66+
- **Cursor Blink**: Toggle cursor blinking
67+
68+
### MCP Servers
69+
70+
Configure Model Context Protocol servers for enhanced agent capabilities:
71+
72+
- **stdio**: Direct process communication
73+
- **SSE**: Server-sent events via HTTP
74+
75+
## Project Structure
76+
77+
```
78+
FleetCode/
79+
├── main.ts # Main Electron process (PTY management, git operations)
80+
├── renderer.ts # Renderer process (UI, terminal instances)
81+
├── index.html # Application layout
82+
├── styles.css # Tailwind CSS styles
83+
└── dist/ # Compiled output
84+
```
85+
86+
## Technology Stack
87+
88+
- **Electron**: Desktop application framework
89+
- **xterm.js**: Terminal emulator
90+
- **node-pty**: PTY (pseudo-terminal) spawning
91+
- **simple-git**: Git operations (worktree management)
92+
- **electron-store**: Persistent configuration storage
93+
- **Tailwind CSS**: Styling
94+
95+
## License
96+
97+
ISC

main.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { simpleGit } from "simple-git";
77
import Store from "electron-store";
88
import { exec } from "child_process";
99
import { promisify } from "util";
10+
import { v4 as uuidv4 } from "uuid";
1011

1112
const execAsync = promisify(exec);
1213

@@ -25,6 +26,7 @@ interface PersistedSession {
2526
config: SessionConfig;
2627
worktreePath: string;
2728
createdAt: number;
29+
sessionUuid: string;
2830
}
2931

3032
let mainWindow: BrowserWindow;
@@ -182,6 +184,9 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
182184
// Create git worktree
183185
const worktreePath = await createWorktree(config.projectDir, config.parentBranch, sessionNumber);
184186

187+
// Generate UUID for this session
188+
const sessionUuid = uuidv4();
189+
185190
// Create persisted session metadata
186191
const persistedSession: PersistedSession = {
187192
id: sessionId,
@@ -190,6 +195,7 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
190195
config,
191196
worktreePath,
192197
createdAt: Date.now(),
198+
sessionUuid,
193199
};
194200

195201
// Save to store
@@ -235,7 +241,11 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
235241

236242
// Auto-run the selected coding agent
237243
if (config.codingAgent === "claude") {
238-
const claudeCmd = config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
244+
// New session always uses --session-id
245+
const sessionFlag = `--session-id ${sessionUuid}`;
246+
const skipPermissionsFlag = config.skipPermissions ? "--dangerously-skip-permissions" : "";
247+
const flags = [sessionFlag, skipPermissionsFlag].filter(f => f).join(" ");
248+
const claudeCmd = `claude ${flags}\r`;
239249
ptyProcess.write(claudeCmd);
240250
} else if (config.codingAgent === "codex") {
241251
ptyProcess.write("codex\r");
@@ -261,7 +271,11 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
261271

262272
// Auto-run the selected coding agent
263273
if (config.codingAgent === "claude") {
264-
const claudeCmd = config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
274+
// New session always uses --session-id
275+
const sessionFlag = `--session-id ${sessionUuid}`;
276+
const skipPermissionsFlag = config.skipPermissions ? "--dangerously-skip-permissions" : "";
277+
const flags = [sessionFlag, skipPermissionsFlag].filter(f => f).join(" ");
278+
const claudeCmd = `claude ${flags}\r`;
265279
ptyProcess.write(claudeCmd);
266280
} else if (config.codingAgent === "codex") {
267281
ptyProcess.write("codex\r");
@@ -350,7 +364,11 @@ ipcMain.on("reopen-session", (event, sessionId: string) => {
350364
}
351365

352366
if (session.config.codingAgent === "claude") {
353-
const claudeCmd = session.config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
367+
// Reopened session always uses --resume
368+
const sessionFlag = `--resume ${session.sessionUuid}`;
369+
const skipPermissionsFlag = session.config.skipPermissions ? "--dangerously-skip-permissions" : "";
370+
const flags = [sessionFlag, skipPermissionsFlag].filter(f => f).join(" ");
371+
const claudeCmd = `claude ${flags}\r`;
354372
ptyProcess.write(claudeCmd);
355373
} else if (session.config.codingAgent === "codex") {
356374
ptyProcess.write("codex\r");

package-lock.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"devDependencies": {
1717
"@electron/rebuild": "^4.0.1",
1818
"@types/node": "^24.6.2",
19+
"@types/uuid": "^10.0.0",
1920
"autoprefixer": "^10.4.21",
2021
"electron": "^38.2.0",
2122
"electron-rebuild": "^3.2.9",
@@ -28,6 +29,7 @@
2829
"electron-store": "^11.0.0",
2930
"node-pty": "^1.0.0",
3031
"simple-git": "^3.28.0",
32+
"uuid": "^13.0.0",
3133
"xterm": "^5.3.0",
3234
"xterm-addon-fit": "^0.8.0"
3335
}

renderer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface PersistedSession {
1717
config: SessionConfig;
1818
worktreePath: string;
1919
createdAt: number;
20+
sessionUuid: string;
2021
}
2122

2223
interface Session {

0 commit comments

Comments
 (0)