Skip to content

Commit d01e715

Browse files
committed
mcp servers and a bunch of stuff
1 parent 1c614c9 commit d01e715

File tree

5 files changed

+370
-11
lines changed

5 files changed

+370
-11
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,5 @@ dist
139139
vite.config.js.timestamp-*
140140
vite.config.ts.timestamp-*
141141
.vite/
142+
143+
.fleetcode/

index.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,36 @@ <h2 class="modal-title">New Session Configuration</h2>
7878
</select>
7979
</div>
8080

81+
<div class="form-group">
82+
<label class="flex items-center space-x-2 cursor-pointer">
83+
<input type="checkbox" id="skip-permissions" class="form-checkbox" checked />
84+
<span class="text-sm text-gray-300">Skip permissions (use --dangerously-skip-permissions)</span>
85+
</label>
86+
</div>
87+
8188
<div class="btn-group">
8289
<button id="cancel-session" class="btn-secondary">Cancel</button>
8390
<button id="create-session" class="btn-primary">Create Session</button>
8491
</div>
8592
</div>
8693
</div>
8794

95+
<!-- MCP Server Details Modal -->
96+
<div id="mcp-details-modal" class="modal-overlay hidden">
97+
<div class="modal">
98+
<h2 class="modal-title" id="mcp-details-title">Server Details</h2>
99+
100+
<div id="mcp-details-content" class="space-y-3 text-sm text-gray-300">
101+
<!-- Details will be populated here -->
102+
</div>
103+
104+
<div class="btn-group">
105+
<button id="close-mcp-details" class="btn-secondary">Close</button>
106+
<button id="remove-mcp-details" class="btn-danger">Remove Server</button>
107+
</div>
108+
</div>
109+
</div>
110+
88111
<!-- MCP Server Modal -->
89112
<div id="mcp-modal" class="modal-overlay hidden">
90113
<div class="modal">

main.ts

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface SessionConfig {
1414
projectDir: string;
1515
parentBranch: string;
1616
codingAgent: string;
17+
skipPermissions: boolean;
1718
}
1819

1920
interface PersistedSession {
@@ -120,6 +121,7 @@ ipcMain.handle("get-last-settings", () => {
120121
projectDir: "",
121122
parentBranch: "",
122123
codingAgent: "claude",
124+
skipPermissions: true,
123125
});
124126
});
125127

@@ -181,7 +183,8 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
181183

182184
// Auto-run the selected coding agent
183185
if (config.codingAgent === "claude") {
184-
ptyProcess.write("claude\r");
186+
const claudeCmd = config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
187+
ptyProcess.write(claudeCmd);
185188
} else if (config.codingAgent === "codex") {
186189
ptyProcess.write("codex\r");
187190
}
@@ -199,7 +202,8 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
199202

200203
// Auto-run the selected coding agent
201204
if (config.codingAgent === "claude") {
202-
ptyProcess.write("claude\r");
205+
const claudeCmd = config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
206+
ptyProcess.write(claudeCmd);
203207
} else if (config.codingAgent === "codex") {
204208
ptyProcess.write("codex\r");
205209
}
@@ -277,7 +281,8 @@ ipcMain.on("reopen-session", (event, sessionId: string) => {
277281
terminalReady = true;
278282

279283
if (session.config.codingAgent === "claude") {
280-
ptyProcess.write("claude\r");
284+
const claudeCmd = session.config.skipPermissions ? "claude --dangerously-skip-permissions\r" : "claude\r";
285+
ptyProcess.write(claudeCmd);
281286
} else if (session.config.codingAgent === "codex") {
282287
ptyProcess.write("codex\r");
283288
}
@@ -332,6 +337,17 @@ ipcMain.handle("get-all-sessions", () => {
332337
return getPersistedSessions();
333338
});
334339

340+
// Rename session
341+
ipcMain.on("rename-session", (_event, sessionId: string, newName: string) => {
342+
const sessions = getPersistedSessions();
343+
const session = sessions.find(s => s.id === sessionId);
344+
345+
if (session) {
346+
session.name = newName;
347+
savePersistedSessions(sessions);
348+
}
349+
});
350+
335351
// MCP Server management functions
336352
async function listMcpServers() {
337353
try {
@@ -355,12 +371,18 @@ async function listMcpServers() {
355371
}
356372

357373
// Parse format: "name: url (type) - status" or just "name"
358-
// Extract just the server name (before the colon)
374+
// Extract server name (before the colon) and status
359375
const colonIndex = line.indexOf(":");
360376
const serverName = colonIndex > 0 ? line.substring(0, colonIndex).trim() : line.trim();
361377

378+
// Check if server is connected (✓ Connected or similar)
379+
const isConnected = line.includes("✓") || line.includes("Connected");
380+
362381
if (serverName) {
363-
servers.push({ name: serverName });
382+
servers.push({
383+
name: serverName,
384+
connected: isConnected
385+
});
364386
}
365387
}
366388

@@ -374,13 +396,45 @@ async function listMcpServers() {
374396
async function addMcpServer(name: string, config: any) {
375397
// Use add-json to support full configuration including env vars, headers, etc.
376398
const jsonConfig = JSON.stringify(config);
377-
await execAsync(`claude mcp add-json "${name}" '${jsonConfig}'`);
399+
await execAsync(`claude mcp add-json --scope user "${name}" '${jsonConfig}'`);
378400
}
379401

380402
async function removeMcpServer(name: string) {
381403
await execAsync(`claude mcp remove "${name}"`);
382404
}
383405

406+
async function getMcpServerDetails(name: string) {
407+
try {
408+
const { stdout } = await execAsync(`claude mcp get "${name}"`);
409+
410+
// Parse the output to extract details
411+
const details: any = { name };
412+
const lines = stdout.split("\n");
413+
414+
for (const line of lines) {
415+
const trimmed = line.trim();
416+
if (trimmed.includes("Scope:")) {
417+
details.scope = trimmed.replace("Scope:", "").trim();
418+
} else if (trimmed.includes("Status:")) {
419+
details.status = trimmed.replace("Status:", "").trim();
420+
} else if (trimmed.includes("Type:")) {
421+
details.type = trimmed.replace("Type:", "").trim();
422+
} else if (trimmed.includes("URL:")) {
423+
details.url = trimmed.replace("URL:", "").trim();
424+
} else if (trimmed.includes("Command:")) {
425+
details.command = trimmed.replace("Command:", "").trim();
426+
} else if (trimmed.includes("Args:")) {
427+
details.args = trimmed.replace("Args:", "").trim();
428+
}
429+
}
430+
431+
return details;
432+
} catch (error) {
433+
console.error("Error getting MCP server details:", error);
434+
throw error;
435+
}
436+
}
437+
384438
ipcMain.handle("list-mcp-servers", async () => {
385439
try {
386440
return await listMcpServers();
@@ -408,6 +462,15 @@ ipcMain.handle("remove-mcp-server", async (_event, name: string) => {
408462
}
409463
});
410464

465+
ipcMain.handle("get-mcp-server-details", async (_event, name: string) => {
466+
try {
467+
return await getMcpServerDetails(name);
468+
} catch (error) {
469+
console.error("Error getting MCP server details:", error);
470+
throw error;
471+
}
472+
});
473+
411474
const createWindow = () => {
412475
mainWindow = new BrowserWindow({
413476
width: 1400,
@@ -430,6 +493,14 @@ const createWindow = () => {
430493
app.whenReady().then(() => {
431494
createWindow();
432495

496+
// Refresh MCP server list every minute and broadcast to all windows
497+
setInterval(async () => {
498+
const servers = await listMcpServers();
499+
BrowserWindow.getAllWindows().forEach(window => {
500+
window.webContents.send("mcp-servers-updated", servers);
501+
});
502+
}, 60000); // 60000ms = 1 minute
503+
433504
app.on("activate", () => {
434505
if (BrowserWindow.getAllWindows().length === 0) {
435506
createWindow();

0 commit comments

Comments
 (0)