@atercates/claude-deck 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/app/api/sessions/[id]/fork/route.ts +0 -1
  2. package/app/api/sessions/[id]/route.ts +0 -5
  3. package/app/api/sessions/[id]/summarize/route.ts +2 -3
  4. package/app/api/sessions/route.ts +2 -11
  5. package/app/api/sessions/status/acknowledge/route.ts +8 -0
  6. package/app/api/sessions/status/route.ts +2 -233
  7. package/app/page.tsx +6 -13
  8. package/components/ClaudeProjects/ClaudeProjectCard.tsx +19 -31
  9. package/components/ClaudeProjects/ClaudeSessionCard.tsx +20 -31
  10. package/components/NewSessionDialog/AdvancedSettings.tsx +3 -12
  11. package/components/NewSessionDialog/NewSessionDialog.types.ts +0 -10
  12. package/components/NewSessionDialog/ProjectSelector.tsx +2 -7
  13. package/components/NewSessionDialog/hooks/useNewSessionForm.ts +3 -36
  14. package/components/NewSessionDialog/index.tsx +0 -7
  15. package/components/Pane/DesktopTabBar.tsx +62 -28
  16. package/components/Pane/index.tsx +5 -0
  17. package/components/Projects/index.ts +0 -1
  18. package/components/QuickSwitcher.tsx +63 -11
  19. package/components/SessionList/ActiveSessionsSection.tsx +116 -0
  20. package/components/SessionList/hooks/useSessionListMutations.ts +0 -35
  21. package/components/SessionList/index.tsx +9 -1
  22. package/components/SessionStatusBar.tsx +155 -0
  23. package/components/WaitingBanner.tsx +122 -0
  24. package/components/views/DesktopView.tsx +27 -8
  25. package/components/views/MobileView.tsx +6 -1
  26. package/components/views/types.ts +2 -0
  27. package/data/sessions/index.ts +0 -1
  28. package/data/sessions/queries.ts +1 -27
  29. package/data/statuses/queries.ts +68 -34
  30. package/hooks/useSessions.ts +0 -12
  31. package/lib/claude/watcher.ts +28 -5
  32. package/lib/db/queries.ts +4 -64
  33. package/lib/db/types.ts +0 -8
  34. package/lib/hooks/reporter.ts +116 -0
  35. package/lib/hooks/setup.ts +164 -0
  36. package/lib/orchestration.ts +16 -23
  37. package/lib/providers/registry.ts +3 -57
  38. package/lib/providers.ts +19 -100
  39. package/lib/status-monitor.ts +303 -0
  40. package/package.json +1 -1
  41. package/server.ts +5 -1
  42. package/app/api/groups/[...path]/route.ts +0 -136
  43. package/app/api/groups/route.ts +0 -93
  44. package/components/NewSessionDialog/AgentSelector.tsx +0 -37
  45. package/components/Projects/ProjectCard.tsx +0 -276
  46. package/components/TmuxSessions.tsx +0 -132
  47. package/data/groups/index.ts +0 -1
  48. package/data/groups/mutations.ts +0 -95
  49. package/hooks/useGroups.ts +0 -37
  50. package/hooks/useKeybarVisibility.ts +0 -42
  51. package/lib/claude/process-manager.ts +0 -278
  52. package/lib/status-detector.ts +0 -375
package/lib/db/queries.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { getDb } from "./index";
2
2
  import type {
3
3
  Session,
4
- Group,
5
4
  Project,
6
5
  ProjectDevServer,
7
6
  ProjectRepository,
@@ -39,14 +38,13 @@ export const queries = {
39
38
  parentSessionId: string | null,
40
39
  model: string | null,
41
40
  systemPrompt: string | null,
42
- groupPath: string,
43
41
  agentType: string,
44
42
  autoApprove: boolean,
45
43
  projectId: string | null
46
44
  ) =>
47
45
  execute(
48
- `INSERT INTO sessions (id, name, tmux_name, working_directory, parent_session_id, model, system_prompt, group_path, agent_type, auto_approve, project_id)
49
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
46
+ `INSERT INTO sessions (id, name, tmux_name, working_directory, parent_session_id, model, system_prompt, agent_type, auto_approve, project_id)
47
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
50
48
  [
51
49
  id,
52
50
  name,
@@ -55,7 +53,6 @@ export const queries = {
55
53
  parentSessionId,
56
54
  model,
57
55
  systemPrompt,
58
- groupPath,
59
56
  agentType,
60
57
  autoApprove ? 1 : 0,
61
58
  projectId,
@@ -68,12 +65,6 @@ export const queries = {
68
65
  getAllSessions: () =>
69
66
  query<Session>("SELECT * FROM sessions ORDER BY updated_at DESC"),
70
67
 
71
- updateSessionStatus: (status: string, id: string) =>
72
- execute(
73
- "UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE id = ?",
74
- [status, id]
75
- ),
76
-
77
68
  updateSessionClaudeId: (claudeSessionId: string, id: string) =>
78
69
  execute(
79
70
  "UPDATE sessions SET claude_session_id = ?, updated_at = datetime('now') WHERE id = ?",
@@ -112,24 +103,6 @@ export const queries = {
112
103
  [prUrl, prNumber, prStatus, id]
113
104
  ),
114
105
 
115
- updateSessionGroup: (groupPath: string, id: string) =>
116
- execute(
117
- "UPDATE sessions SET group_path = ?, updated_at = datetime('now') WHERE id = ?",
118
- [groupPath, id]
119
- ),
120
-
121
- getSessionsByGroup: (groupPath: string) =>
122
- query<Session>(
123
- "SELECT * FROM sessions WHERE group_path = ? ORDER BY updated_at DESC",
124
- [groupPath]
125
- ),
126
-
127
- moveSessionsToGroup: (newGroupPath: string, oldGroupPath: string) =>
128
- execute(
129
- "UPDATE sessions SET group_path = ?, updated_at = datetime('now') WHERE group_path = ?",
130
- [newGroupPath, oldGroupPath]
131
- ),
132
-
133
106
  updateSessionProject: (projectId: string, id: string) =>
134
107
  execute(
135
108
  "UPDATE sessions SET project_id = ?, updated_at = datetime('now') WHERE id = ?",
@@ -162,13 +135,12 @@ export const queries = {
162
135
  conductorSessionId: string,
163
136
  workerTask: string,
164
137
  model: string | null,
165
- groupPath: string,
166
138
  agentType: string,
167
139
  projectId: string | null
168
140
  ) =>
169
141
  execute(
170
- `INSERT INTO sessions (id, name, tmux_name, working_directory, conductor_session_id, worker_task, worker_status, model, group_path, agent_type, project_id)
171
- VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`,
142
+ `INSERT INTO sessions (id, name, tmux_name, working_directory, conductor_session_id, worker_task, worker_status, model, agent_type, project_id)
143
+ VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)`,
172
144
  [
173
145
  id,
174
146
  name,
@@ -177,43 +149,11 @@ export const queries = {
177
149
  conductorSessionId,
178
150
  workerTask,
179
151
  model,
180
- groupPath,
181
152
  agentType,
182
153
  projectId,
183
154
  ]
184
155
  ),
185
156
 
186
- getAllGroups: () =>
187
- query<Group>("SELECT * FROM groups ORDER BY sort_order ASC, name ASC"),
188
-
189
- getGroup: (path: string) =>
190
- queryOne<Group>("SELECT * FROM groups WHERE path = ?", [path]),
191
-
192
- createGroup: (path: string, name: string, sortOrder: number) =>
193
- execute("INSERT INTO groups (path, name, sort_order) VALUES (?, ?, ?)", [
194
- path,
195
- name,
196
- sortOrder,
197
- ]),
198
-
199
- updateGroupName: (name: string, path: string) =>
200
- execute("UPDATE groups SET name = ? WHERE path = ?", [name, path]),
201
-
202
- updateGroupExpanded: (expanded: boolean, path: string) =>
203
- execute("UPDATE groups SET expanded = ? WHERE path = ?", [
204
- expanded ? 1 : 0,
205
- path,
206
- ]),
207
-
208
- updateGroupOrder: (sortOrder: number, path: string) =>
209
- execute("UPDATE groups SET sort_order = ? WHERE path = ?", [
210
- sortOrder,
211
- path,
212
- ]),
213
-
214
- deleteGroup: (path: string) =>
215
- execute("DELETE FROM groups WHERE path = ?", [path]),
216
-
217
157
  createProject: (
218
158
  id: string,
219
159
  name: string,
package/lib/db/types.ts CHANGED
@@ -31,14 +31,6 @@ export interface Session {
31
31
  worker_status: "pending" | "running" | "completed" | "failed" | null;
32
32
  }
33
33
 
34
- export interface Group {
35
- path: string;
36
- name: string;
37
- expanded: boolean;
38
- sort_order: number;
39
- created_at: string;
40
- }
41
-
42
34
  export interface Project {
43
35
  id: string;
44
36
  name: string;
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Code hook reporter.
4
+ *
5
+ * Invoked by Claude Code hooks on state transitions. Reads JSON from stdin
6
+ * and writes a session state file to ~/.claude-deck/session-states/{session_id}.json.
7
+ *
8
+ * Installed to ~/.claude-deck/hooks/state-reporter by the setup module.
9
+ */
10
+
11
+ import * as fs from "fs";
12
+ import * as path from "path";
13
+ import * as os from "os";
14
+
15
+ const STATES_DIR = path.join(os.homedir(), ".claude-deck", "session-states");
16
+
17
+ interface HookInput {
18
+ session_id: string;
19
+ hook_event_name: string;
20
+ tool_name?: string;
21
+ tool_input?: unknown;
22
+ last_assistant_message?: string;
23
+ stop_hook_active?: boolean;
24
+ reason?: string;
25
+ }
26
+
27
+ interface StateFile {
28
+ status: "running" | "waiting" | "idle";
29
+ lastLine: string;
30
+ waitingContext?: string;
31
+ ts: number;
32
+ }
33
+
34
+ function readStdin(): Promise<string> {
35
+ return new Promise((resolve) => {
36
+ let data = "";
37
+ process.stdin.setEncoding("utf-8");
38
+ process.stdin.on("data", (chunk) => (data += chunk));
39
+ process.stdin.on("end", () => resolve(data));
40
+ // Safety timeout — don't hang if stdin never closes
41
+ setTimeout(() => resolve(data), 1000);
42
+ });
43
+ }
44
+
45
+ function getStatePath(sessionId: string): string {
46
+ return path.join(STATES_DIR, `${sessionId}.json`);
47
+ }
48
+
49
+ function writeState(sessionId: string, state: StateFile): void {
50
+ fs.mkdirSync(STATES_DIR, { recursive: true });
51
+ fs.writeFileSync(getStatePath(sessionId), JSON.stringify(state));
52
+ }
53
+
54
+ function deleteState(sessionId: string): void {
55
+ try {
56
+ fs.unlinkSync(getStatePath(sessionId));
57
+ } catch {
58
+ // file may not exist
59
+ }
60
+ }
61
+
62
+ async function main(): Promise<void> {
63
+ const raw = await readStdin();
64
+ if (!raw.trim()) return;
65
+
66
+ let input: HookInput;
67
+ try {
68
+ input = JSON.parse(raw);
69
+ } catch {
70
+ return;
71
+ }
72
+
73
+ const { session_id, hook_event_name } = input;
74
+ if (!session_id) return;
75
+
76
+ switch (hook_event_name) {
77
+ case "SessionEnd":
78
+ deleteState(session_id);
79
+ break;
80
+
81
+ case "PermissionRequest":
82
+ writeState(session_id, {
83
+ status: "waiting",
84
+ lastLine: `Waiting: ${input.tool_name || "permission"}`,
85
+ waitingContext: input.tool_name
86
+ ? `Permission requested for ${input.tool_name}`
87
+ : "Permission requested",
88
+ ts: Date.now(),
89
+ });
90
+ break;
91
+
92
+ case "Stop":
93
+ // Only transition to idle if not in a stop-hook re-run loop
94
+ if (!input.stop_hook_active) {
95
+ writeState(session_id, {
96
+ status: "idle",
97
+ lastLine: input.last_assistant_message?.slice(0, 200) || "",
98
+ ts: Date.now(),
99
+ });
100
+ }
101
+ break;
102
+
103
+ default:
104
+ // UserPromptSubmit, SessionStart, PreToolUse, PostToolUse, PermissionDenied
105
+ writeState(session_id, {
106
+ status: "running",
107
+ lastLine: input.tool_name
108
+ ? `Running: ${input.tool_name}`
109
+ : "Running...",
110
+ ts: Date.now(),
111
+ });
112
+ break;
113
+ }
114
+ }
115
+
116
+ main().catch(() => process.exit(0));
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Hooks setup module.
3
+ *
4
+ * Installs the state-reporter script to ~/.claude-deck/hooks/ and
5
+ * merges hook configuration into ~/.claude/settings.json idempotently.
6
+ */
7
+
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import * as os from "os";
11
+
12
+ const CLAUDE_DECK_DIR = path.join(os.homedir(), ".claude-deck");
13
+ const HOOKS_DIR = path.join(CLAUDE_DECK_DIR, "hooks");
14
+ const STATES_DIR = path.join(CLAUDE_DECK_DIR, "session-states");
15
+ const REPORTER_PATH = path.join(HOOKS_DIR, "state-reporter");
16
+ const SETTINGS_PATH = path.join(os.homedir(), ".claude", "settings.json");
17
+
18
+ // Events we hook into and whether they run async
19
+ const HOOK_EVENTS: Array<{ event: string; async: boolean }> = [
20
+ { event: "UserPromptSubmit", async: true },
21
+ { event: "PreToolUse", async: true },
22
+ { event: "PermissionRequest", async: false },
23
+ { event: "Elicitation", async: false },
24
+ { event: "Stop", async: true },
25
+ { event: "SessionStart", async: true },
26
+ { event: "SessionEnd", async: true },
27
+ ];
28
+
29
+ // The reporter is a self-contained Node.js script (no tsx, no ESM, no deps)
30
+ const REPORTER_SCRIPT = `#!/usr/bin/env node
31
+ "use strict";
32
+ var fs = require("fs");
33
+ var path = require("path");
34
+ var os = require("os");
35
+ var STATES_DIR = path.join(os.homedir(), ".claude-deck", "session-states");
36
+
37
+ var data = "";
38
+ process.stdin.setEncoding("utf-8");
39
+ process.stdin.on("data", function(c) { data += c; });
40
+ process.stdin.on("end", function() {
41
+ try {
42
+ var input = JSON.parse(data);
43
+ var id = input.session_id;
44
+ if (!id) return;
45
+ fs.mkdirSync(STATES_DIR, { recursive: true });
46
+ var fp = path.join(STATES_DIR, id + ".json");
47
+ var evt = input.hook_event_name;
48
+
49
+ if (evt === "SessionEnd") {
50
+ try { fs.unlinkSync(fp); } catch(e) {}
51
+ return;
52
+ }
53
+
54
+ var status = "running";
55
+ var lastLine = input.tool_name ? "Running: " + input.tool_name : "Running...";
56
+ var state = { status: status, lastLine: lastLine, ts: Date.now() };
57
+
58
+ if (evt === "SessionStart") {
59
+ state.status = "idle";
60
+ state.lastLine = "";
61
+ } else if (evt === "PermissionRequest" || evt === "Elicitation") {
62
+ state.status = "waiting";
63
+ state.lastLine = "Waiting: " + (input.tool_name || "input required");
64
+ state.waitingContext = input.tool_name
65
+ ? "Permission requested for " + input.tool_name
66
+ : "Input required";
67
+ } else if (evt === "Stop" && !input.stop_hook_active) {
68
+ state.status = "idle";
69
+ state.lastLine = (input.last_assistant_message || "").slice(0, 200);
70
+ }
71
+
72
+ fs.writeFileSync(fp, JSON.stringify(state));
73
+ } catch(e) {}
74
+ });
75
+ setTimeout(function() { process.exit(0); }, 2000);
76
+ `;
77
+
78
+ function installReporterScript(): void {
79
+ fs.mkdirSync(HOOKS_DIR, { recursive: true });
80
+ fs.mkdirSync(STATES_DIR, { recursive: true });
81
+ fs.writeFileSync(REPORTER_PATH, REPORTER_SCRIPT, { mode: 0o755 });
82
+ }
83
+
84
+ interface HookCommand {
85
+ type: "command";
86
+ command: string;
87
+ async?: boolean;
88
+ }
89
+
90
+ interface HookMatcher {
91
+ matcher?: string;
92
+ hooks: HookCommand[];
93
+ }
94
+
95
+ type SettingsHooks = Record<string, HookMatcher[]>;
96
+
97
+ interface Settings {
98
+ hooks?: SettingsHooks;
99
+ [key: string]: unknown;
100
+ }
101
+
102
+ function isOurHook(hook: HookCommand): boolean {
103
+ return hook.command === REPORTER_PATH;
104
+ }
105
+
106
+ function mergeHooksIntoSettings(): void {
107
+ let settings: Settings = {};
108
+
109
+ try {
110
+ const raw = fs.readFileSync(SETTINGS_PATH, "utf-8");
111
+ settings = JSON.parse(raw);
112
+ } catch {
113
+ // File doesn't exist or is invalid
114
+ }
115
+
116
+ if (!settings.hooks) {
117
+ settings.hooks = {};
118
+ }
119
+
120
+ let changed = false;
121
+
122
+ for (const { event, async: isAsync } of HOOK_EVENTS) {
123
+ if (!settings.hooks[event]) {
124
+ settings.hooks[event] = [];
125
+ }
126
+
127
+ const eventHooks = settings.hooks[event];
128
+ const alreadyInstalled = eventHooks.some((matcher) =>
129
+ matcher.hooks?.some((h) => isOurHook(h))
130
+ );
131
+
132
+ if (!alreadyInstalled) {
133
+ const hookDef: HookCommand = {
134
+ type: "command",
135
+ command: REPORTER_PATH,
136
+ };
137
+ if (isAsync) {
138
+ hookDef.async = true;
139
+ }
140
+ eventHooks.push({ hooks: [hookDef] });
141
+ changed = true;
142
+ }
143
+ }
144
+
145
+ if (changed) {
146
+ fs.mkdirSync(path.dirname(SETTINGS_PATH), { recursive: true });
147
+ fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
148
+ console.log("> Hooks configured in ~/.claude/settings.json");
149
+ }
150
+ }
151
+
152
+ export function setupHooks(): void {
153
+ try {
154
+ installReporterScript();
155
+ mergeHooksIntoSettings();
156
+ console.log(
157
+ "> Hook reporter installed at ~/.claude-deck/hooks/state-reporter"
158
+ );
159
+ } catch (err) {
160
+ console.error("Failed to setup hooks:", err);
161
+ }
162
+ }
163
+
164
+ export { STATES_DIR };
@@ -11,8 +11,9 @@ import { promisify } from "util";
11
11
  import { queries, type Session } from "./db";
12
12
  import { createWorktree, deleteWorktree } from "./worktrees";
13
13
  import { setupWorktree } from "./env-setup";
14
- import { type AgentType, getProvider } from "./providers";
15
- import { statusDetector } from "./status-detector";
14
+ import { type AgentType, CLAUDE_COMMAND, buildClaudeFlags } from "./providers";
15
+ import { getStatusSnapshot } from "./status-monitor";
16
+ import { getSessionIdFromName } from "./providers/registry";
16
17
  import { wrapWithBanner } from "./banner";
17
18
  import { runInBackground } from "./async-operations";
18
19
 
@@ -94,7 +95,7 @@ export async function spawnWorker(
94
95
 
95
96
  const sessionId = randomUUID();
96
97
  const sessionName = taskToSessionName(task);
97
- const provider = getProvider(agentType);
98
+ // Provider is always claude
98
99
 
99
100
  let worktreePath: string | null = null;
100
101
  let actualWorkingDir = workingDirectory;
@@ -131,7 +132,7 @@ export async function spawnWorker(
131
132
  }
132
133
 
133
134
  // Create session in database
134
- const tmuxName = `${provider.id}-${sessionId}`;
135
+ const tmuxName = `claude-${sessionId}`;
135
136
  await queries.createWorkerSession(
136
137
  sessionId,
137
138
  sessionName,
@@ -140,7 +141,6 @@ export async function spawnWorker(
140
141
  conductorSessionId,
141
142
  task,
142
143
  model,
143
- "sessions", // group_path
144
144
  agentType,
145
145
  "uncategorized" // project_id
146
146
  );
@@ -157,15 +157,15 @@ export async function spawnWorker(
157
157
  }
158
158
 
159
159
  // Create tmux session and start the agent
160
- const tmuxSessionName = `${provider.id}-${sessionId}`;
160
+ const tmuxSessionName = `claude-${sessionId}`;
161
161
  const cwd = actualWorkingDir.replace("~", "$HOME");
162
162
 
163
163
  // Build the initial prompt command (workers use auto-approve by default for automation)
164
- const flags = provider.buildFlags({ model, autoApprove: true });
164
+ const flags = buildClaudeFlags({ model, autoApprove: true });
165
165
  const flagsStr = flags.join(" ");
166
166
 
167
167
  // Create tmux session with the agent and banner
168
- const agentCmd = `${provider.command} ${flagsStr}`;
168
+ const agentCmd = `${CLAUDE_COMMAND} ${flagsStr}`;
169
169
  const newSessionCmd = wrapWithBanner(agentCmd);
170
170
  const createCmd = `tmux set -g mouse on 2>/dev/null; tmux new-session -d -s "${tmuxSessionName}" -c "${cwd}" "${newSessionCmd}"`;
171
171
 
@@ -270,16 +270,12 @@ export async function getWorkers(
270
270
  const workerInfos: WorkerInfo[] = [];
271
271
 
272
272
  for (const worker of workers) {
273
- const provider = getProvider(worker.agent_type || "claude");
274
- const tmuxSessionName = worker.tmux_name || `${provider.id}-${worker.id}`;
273
+ const tmuxSessionName = worker.tmux_name || `claude-${worker.id}`;
275
274
 
276
- // Get live status from tmux
277
- let liveStatus: string;
278
- try {
279
- liveStatus = await statusDetector.getStatus(tmuxSessionName);
280
- } catch {
281
- liveStatus = "dead";
282
- }
275
+ // Get live status from cached monitor snapshot
276
+ const sessionId = getSessionIdFromName(tmuxSessionName);
277
+ const snapshot = getStatusSnapshot();
278
+ const liveStatus = snapshot[sessionId]?.status || "dead";
283
279
 
284
280
  // Combine DB status with live status
285
281
  let status: WorkerInfo["status"];
@@ -320,8 +316,7 @@ export async function getWorkerOutput(
320
316
  throw new Error(`Worker ${workerId} not found`);
321
317
  }
322
318
 
323
- const provider = getProvider(session.agent_type || "claude");
324
- const tmuxSessionName = session.tmux_name || `${provider.id}-${workerId}`;
319
+ const tmuxSessionName = session.tmux_name || `claude-${workerId}`;
325
320
 
326
321
  try {
327
322
  const { stdout } = await execAsync(
@@ -345,8 +340,7 @@ export async function sendToWorker(
345
340
  throw new Error(`Worker ${workerId} not found`);
346
341
  }
347
342
 
348
- const provider = getProvider(session.agent_type || "claude");
349
- const tmuxSessionName = session.tmux_name || `${provider.id}-${workerId}`;
343
+ const tmuxSessionName = session.tmux_name || `claude-${workerId}`;
350
344
 
351
345
  try {
352
346
  const escapedMessage = message.replace(/"/g, '\\"').replace(/\$/g, "\\$");
@@ -385,8 +379,7 @@ export async function killWorker(
385
379
  return;
386
380
  }
387
381
 
388
- const provider = getProvider(session.agent_type || "claude");
389
- const tmuxSessionName = session.tmux_name || `${provider.id}-${workerId}`;
382
+ const tmuxSessionName = session.tmux_name || `claude-${workerId}`;
390
383
 
391
384
  // Kill tmux session
392
385
  try {
@@ -1,67 +1,13 @@
1
- export const PROVIDER_IDS = ["claude"] as const;
2
-
3
- export type ProviderId = (typeof PROVIDER_IDS)[number];
4
-
5
- export interface ProviderDefinition {
6
- id: ProviderId;
7
- name: string;
8
- description: string;
9
- cli: string;
10
- configDir: string;
11
- autoApproveFlag?: string;
12
- supportsResume: boolean;
13
- supportsFork: boolean;
14
- resumeFlag?: string;
15
- modelFlag?: string;
16
- initialPromptFlag?: string;
17
- defaultArgs?: string[];
18
- }
19
-
20
- export const PROVIDERS: ProviderDefinition[] = [
21
- {
22
- id: "claude",
23
- name: "Claude Code",
24
- description: "Anthropic's official CLI",
25
- cli: "claude",
26
- configDir: "~/.claude",
27
- autoApproveFlag: "--dangerously-skip-permissions",
28
- supportsResume: true,
29
- supportsFork: true,
30
- resumeFlag: "--resume",
31
- initialPromptFlag: "",
32
- },
33
- ];
34
-
35
- export const PROVIDER_MAP = new Map<ProviderId, ProviderDefinition>(
36
- PROVIDERS.map((provider) => [provider.id, provider])
37
- );
38
-
39
- export function getProviderDefinition(id: ProviderId): ProviderDefinition {
40
- const provider = PROVIDER_MAP.get(id);
41
- if (!provider) {
42
- throw new Error(`Unknown provider: ${id}`);
43
- }
44
- return provider;
45
- }
46
-
47
- export function getAllProviderDefinitions(): ProviderDefinition[] {
48
- return PROVIDERS;
49
- }
50
-
51
- export function isValidProviderId(value: string): value is ProviderId {
52
- return PROVIDER_MAP.has(value as ProviderId);
53
- }
1
+ export type ProviderId = "claude";
54
2
 
55
3
  export function getManagedSessionPattern(): RegExp {
56
- return /^claude-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
+ return /^claude-(new-)?[0-9a-z]{4,}/i;
57
5
  }
58
6
 
59
7
  export function getProviderIdFromSessionName(
60
8
  sessionName: string
61
9
  ): ProviderId | null {
62
- if (sessionName.startsWith("claude-")) {
63
- return "claude";
64
- }
10
+ if (sessionName.startsWith("claude-")) return "claude";
65
11
  return null;
66
12
  }
67
13