readme
This commit is contained in:
parent
c45ac9ea98
commit
7d7a545ef8
97
README.md
Normal file
97
README.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# FleetCode
|
||||
|
||||
A desktop terminal application for running multiple CLI coding agents simultaneously, each in isolated git worktrees.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple Sessions**: Run multiple coding agent sessions (Claude, Codex) in parallel
|
||||
- **Git Worktree Isolation**: Each session runs in its own git worktree, keeping work isolated
|
||||
- **Persistent Sessions**: Sessions persist across app restarts with automatic resumption
|
||||
- **Terminal Theming**: Choose from preset themes (macOS Light/Dark, Solarized Dark, Dracula, One Dark, GitHub Dark)
|
||||
- **Setup Commands**: Configure shell commands to run before the coding agent starts
|
||||
- **MCP Server Management**: Add and configure Model Context Protocol (MCP) servers
|
||||
- **Session Management**: Rename, close, and delete sessions with automatic worktree cleanup
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 16+
|
||||
- Git
|
||||
- Claude CLI (`npm install -g @anthropic-ai/claude-cli`) or Codex
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Production Build
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Session Creation
|
||||
|
||||
1. Select a project directory (must be a git repository)
|
||||
2. Choose a parent branch for the worktree
|
||||
3. Select your coding agent (Claude or Codex)
|
||||
4. Optionally add setup commands (e.g., environment variables, source files)
|
||||
5. FleetCode creates a new git worktree and spawns a terminal session
|
||||
|
||||
### Session Management
|
||||
|
||||
- **New Sessions**: Use `--session-id <uuid>` for first-time Claude sessions
|
||||
- **Reopened Sessions**: Automatically resume with `--resume <uuid>`
|
||||
- **Worktrees**: Each session gets its own isolated git worktree
|
||||
- **Persistence**: Sessions are saved and can be reopened after closing the app
|
||||
|
||||
### Terminal Settings
|
||||
|
||||
Access settings via the gear icon (⚙️) in the sidebar:
|
||||
|
||||
- **Font Family**: Choose from common monospace fonts
|
||||
- **Font Size**: Adjust terminal text size
|
||||
- **Theme**: Select from preset color themes
|
||||
- **Cursor Blink**: Toggle cursor blinking
|
||||
|
||||
### MCP Servers
|
||||
|
||||
Configure Model Context Protocol servers for enhanced agent capabilities:
|
||||
|
||||
- **stdio**: Direct process communication
|
||||
- **SSE**: Server-sent events via HTTP
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
FleetCode/
|
||||
├── main.ts # Main Electron process (PTY management, git operations)
|
||||
├── renderer.ts # Renderer process (UI, terminal instances)
|
||||
├── index.html # Application layout
|
||||
├── styles.css # Tailwind CSS styles
|
||||
└── dist/ # Compiled output
|
||||
```
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **Electron**: Desktop application framework
|
||||
- **xterm.js**: Terminal emulator
|
||||
- **node-pty**: PTY (pseudo-terminal) spawning
|
||||
- **simple-git**: Git operations (worktree management)
|
||||
- **electron-store**: Persistent configuration storage
|
||||
- **Tailwind CSS**: Styling
|
||||
|
||||
## License
|
||||
|
||||
ISC
|
||||
24
main.ts
24
main.ts
|
|
@ -7,6 +7,7 @@ import { simpleGit } from "simple-git";
|
|||
import Store from "electron-store";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ interface PersistedSession {
|
|||
config: SessionConfig;
|
||||
worktreePath: string;
|
||||
createdAt: number;
|
||||
sessionUuid: string;
|
||||
}
|
||||
|
||||
let mainWindow: BrowserWindow;
|
||||
|
|
@ -182,6 +184,9 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
|||
// Create git worktree
|
||||
const worktreePath = await createWorktree(config.projectDir, config.parentBranch, sessionNumber);
|
||||
|
||||
// Generate UUID for this session
|
||||
const sessionUuid = uuidv4();
|
||||
|
||||
// Create persisted session metadata
|
||||
const persistedSession: PersistedSession = {
|
||||
id: sessionId,
|
||||
|
|
@ -190,6 +195,7 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
|||
config,
|
||||
worktreePath,
|
||||
createdAt: Date.now(),
|
||||
sessionUuid,
|
||||
};
|
||||
|
||||
// Save to store
|
||||
|
|
@ -235,7 +241,11 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
|||
|
||||
// Auto-run the selected coding agent
|
||||
if (config.codingAgent === "claude") {
|
||||
const claudeCmd = config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
|
||||
// New session always uses --session-id
|
||||
const sessionFlag = `--session-id ${sessionUuid}`;
|
||||
const skipPermissionsFlag = config.skipPermissions ? "--dangerously-skip-permissions" : "";
|
||||
const flags = [sessionFlag, skipPermissionsFlag].filter(f => f).join(" ");
|
||||
const claudeCmd = `claude ${flags}\r`;
|
||||
ptyProcess.write(claudeCmd);
|
||||
} else if (config.codingAgent === "codex") {
|
||||
ptyProcess.write("codex\r");
|
||||
|
|
@ -261,7 +271,11 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
|||
|
||||
// Auto-run the selected coding agent
|
||||
if (config.codingAgent === "claude") {
|
||||
const claudeCmd = config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
|
||||
// New session always uses --session-id
|
||||
const sessionFlag = `--session-id ${sessionUuid}`;
|
||||
const skipPermissionsFlag = config.skipPermissions ? "--dangerously-skip-permissions" : "";
|
||||
const flags = [sessionFlag, skipPermissionsFlag].filter(f => f).join(" ");
|
||||
const claudeCmd = `claude ${flags}\r`;
|
||||
ptyProcess.write(claudeCmd);
|
||||
} else if (config.codingAgent === "codex") {
|
||||
ptyProcess.write("codex\r");
|
||||
|
|
@ -350,7 +364,11 @@ ipcMain.on("reopen-session", (event, sessionId: string) => {
|
|||
}
|
||||
|
||||
if (session.config.codingAgent === "claude") {
|
||||
const claudeCmd = session.config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
|
||||
// Reopened session always uses --resume
|
||||
const sessionFlag = `--resume ${session.sessionUuid}`;
|
||||
const skipPermissionsFlag = session.config.skipPermissions ? "--dangerously-skip-permissions" : "";
|
||||
const flags = [sessionFlag, skipPermissionsFlag].filter(f => f).join(" ");
|
||||
const claudeCmd = `claude ${flags}\r`;
|
||||
ptyProcess.write(claudeCmd);
|
||||
} else if (session.config.codingAgent === "codex") {
|
||||
ptyProcess.write("codex\r");
|
||||
|
|
|
|||
22
package-lock.json
generated
22
package-lock.json
generated
|
|
@ -12,12 +12,14 @@
|
|||
"electron-store": "^11.0.0",
|
||||
"node-pty": "^1.0.0",
|
||||
"simple-git": "^3.28.0",
|
||||
"uuid": "^13.0.0",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron/rebuild": "^4.0.1",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"electron": "^38.2.0",
|
||||
"electron-rebuild": "^3.2.9",
|
||||
|
|
@ -1017,6 +1019,13 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||
|
|
@ -5038,6 +5047,19 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"devDependencies": {
|
||||
"@electron/rebuild": "^4.0.1",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"electron": "^38.2.0",
|
||||
"electron-rebuild": "^3.2.9",
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
"electron-store": "^11.0.0",
|
||||
"node-pty": "^1.0.0",
|
||||
"simple-git": "^3.28.0",
|
||||
"uuid": "^13.0.0",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ interface PersistedSession {
|
|||
config: SessionConfig;
|
||||
worktreePath: string;
|
||||
createdAt: number;
|
||||
sessionUuid: string;
|
||||
}
|
||||
|
||||
interface Session {
|
||||
|
|
|
|||
Loading…
Reference in a new issue