claude-code-session-manager 0.10.2 → 0.11.0

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 (34) hide show
  1. package/dist/assets/TiptapBody-CAJSNRPs.js +189 -0
  2. package/dist/assets/{cssMode-DyodRfD-.js → cssMode-o7rZCrm4.js} +1 -1
  3. package/dist/assets/{freemarker2-D1H1ixRK.js → freemarker2-CgmCS5Wh.js} +1 -1
  4. package/dist/assets/{handlebars-wnlxpTlt.js → handlebars-BcPLqhPv.js} +1 -1
  5. package/dist/assets/{html-Dv_oA_OQ.js → html-CC9xWnC3.js} +1 -1
  6. package/dist/assets/{htmlMode-DGXsu2-V.js → htmlMode-DEgCqH7k.js} +1 -1
  7. package/dist/assets/{index-oiSqLrkZ.js → index-C7ljEoqc.js} +1223 -1192
  8. package/dist/assets/{index-CcRP2nIC.css → index-CH3K1pkS.css} +1 -1
  9. package/dist/assets/{javascript-CxejmYhM.js → javascript-CjwqkQrn.js} +1 -1
  10. package/dist/assets/{jsonMode-ztPfF7kI.js → jsonMode-BYTLu76d.js} +4 -4
  11. package/dist/assets/{liquid-DvtfrYeo.js → liquid-wbQUuJwT.js} +1 -1
  12. package/dist/assets/{lspLanguageFeatures-mIBTKOZq.js → lspLanguageFeatures-BJGMI7Xu.js} +1 -1
  13. package/dist/assets/{mdx-DTebMWEJ.js → mdx-DcDstgPF.js} +1 -1
  14. package/dist/assets/{python-zea5QgfT.js → python-B96yyM_5.js} +1 -1
  15. package/dist/assets/{razor-DODk3om_.js → razor-C7aRIxIE.js} +1 -1
  16. package/dist/assets/{tsMode-BQGo_Gc8.js → tsMode-B3UYlGaL.js} +1 -1
  17. package/dist/assets/{typescript-Cfo1NBg6.js → typescript-CV587TvC.js} +1 -1
  18. package/dist/assets/{xml-D1RKIHcE.js → xml-PWUJecBf.js} +1 -1
  19. package/dist/assets/{yaml-B8MoJlND.js → yaml-D8bBNHE4.js} +1 -1
  20. package/dist/index.html +2 -2
  21. package/package.json +5 -1
  22. package/src/main/agentMemory.cjs +267 -0
  23. package/src/main/docEditor.cjs +92 -0
  24. package/src/main/files.cjs +346 -0
  25. package/src/main/git.cjs +333 -0
  26. package/src/main/historyAggregator.cjs +70 -0
  27. package/src/main/index.cjs +66 -0
  28. package/src/main/ipcSchemas.cjs +75 -0
  29. package/src/main/projectSkills.cjs +124 -0
  30. package/src/main/scheduler.cjs +155 -11
  31. package/src/main/superagent.cjs +202 -0
  32. package/src/main/transcripts.cjs +8 -1
  33. package/src/preload/api.d.ts +215 -0
  34. package/src/preload/index.cjs +54 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * SuperAgent — "boss" runner that dispatches specialist subagents.
3
+ *
4
+ * Ported (in MVP form) from ClaudeCodeUnleashed's SuperAgent feature.
5
+ * Differs significantly from upstream:
6
+ * - We don't call Groq/OpenAI. The boss IS Claude — we send a structured
7
+ * prompt to the active PTY tab asking Claude to act as the boss, pick
8
+ * specialists, and report progress in a parseable format.
9
+ * - No autonomous output-parsing / fast-path / WAIT logic. State machine
10
+ * is renderer-driven via transcript events (see live.ts pattern).
11
+ * - Per-tab run state lives here so the renderer can poll status across
12
+ * navigation, and so a future supervisor probe can act on stuck runs.
13
+ *
14
+ * Channels:
15
+ * superagent:start({ tabId, prompt, specialistCount, depth })
16
+ * → { ok, error? } and broadcasts the boss prompt to the tab's PTY.
17
+ * superagent:status(tabId) → SuperAgentRunState | null
18
+ * superagent:stop(tabId) → { ok }
19
+ *
20
+ * Broadcasts:
21
+ * superagent:state-changed → { tabId, state } whenever a run starts/stops.
22
+ *
23
+ * The PTY write is intentionally done via the existing ptyManager.write
24
+ * indirection, NOT a fresh spawn — SuperAgent runs inside the user's current
25
+ * Claude session.
26
+ */
27
+
28
+ const { ipcMain } = require('electron');
29
+ const { schemas } = require('./ipcSchemas.cjs');
30
+ const { manager: ptyManager } = require('./pty.cjs');
31
+ const logs = require('./logs.cjs');
32
+
33
+ // Per-tab run state. Single live run per tab — starting a new run on a tab
34
+ // that's already running stops the existing one first.
35
+ //
36
+ // Shape (also exposed on the renderer via api.d.ts SuperAgentRunState):
37
+ // { status: 'idle' | 'running' | 'done' | 'error',
38
+ // prompt, specialistCount, depth,
39
+ // startedAt: number | null, finishedAt: number | null,
40
+ // error?: string }
41
+ const runs = new Map();
42
+
43
+ let mainWindow = null;
44
+
45
+ function attachWindow(win) {
46
+ mainWindow = win;
47
+ }
48
+
49
+ function broadcast(tabId) {
50
+ if (!mainWindow || mainWindow.isDestroyed()) return;
51
+ const state = runs.get(tabId) ?? null;
52
+ try {
53
+ mainWindow.webContents.send('superagent:state-changed', { tabId, state });
54
+ } catch { /* renderer gone */ }
55
+ }
56
+
57
+ /**
58
+ * Build the prompt we send to Claude to act as the boss. Structured so the
59
+ * renderer can (in a follow-up) pattern-match progress markers off the
60
+ * transcript without us having to parse PTY output ourselves.
61
+ *
62
+ * O(1) string concat — the prompt is bounded by the zod max(8KiB).
63
+ */
64
+ function buildBossPrompt({ prompt, specialistCount, depth }) {
65
+ const depthLine = depth === 'quick'
66
+ ? 'Quick pass — surface-level analysis, single-shot specialist work.'
67
+ : depth === 'deep'
68
+ ? 'Deep pass — multi-step plans per specialist, recurse on findings.'
69
+ : 'Standard pass — moderate detail, one or two iterations per specialist.';
70
+
71
+ return [
72
+ '## SuperAgent boss mode',
73
+ '',
74
+ `You are coordinating ${specialistCount} specialist subagent(s) for the user's task.`,
75
+ depthLine,
76
+ '',
77
+ 'Workflow:',
78
+ ` 1. Pick ${specialistCount} specialist subagent type(s) that best match the task.`,
79
+ ' Report them as: [SUPERAGENT] specialists: <type1>, <type2>, ...',
80
+ ' 2. Dispatch them via the Task tool, one at a time or in parallel as appropriate.',
81
+ ' 3. After each specialist returns, report: [SUPERAGENT] progress: <specialist> done',
82
+ ' 4. When the task is complete, summarize results and report: [SUPERAGENT] complete',
83
+ '',
84
+ 'User task:',
85
+ prompt,
86
+ ].join('\n');
87
+ }
88
+
89
+ function startRun(payload) {
90
+ const { tabId, prompt, specialistCount, depth } = payload;
91
+
92
+ // Stop any in-flight run on this tab — single run-per-tab invariant.
93
+ const existing = runs.get(tabId);
94
+ if (existing && existing.status === 'running') {
95
+ runs.set(tabId, {
96
+ ...existing,
97
+ status: 'done',
98
+ finishedAt: Date.now(),
99
+ });
100
+ }
101
+
102
+ const fullPrompt = buildBossPrompt({ prompt, specialistCount, depth });
103
+
104
+ // PTY write — exactly the pattern docstring'd at the top of the task brief.
105
+ // The trailing carriage-return submits the prompt in Claude's TUI.
106
+ let writeErr = null;
107
+ try {
108
+ ptyManager.write({ tabId, data: fullPrompt + '\r' });
109
+ } catch (e) {
110
+ writeErr = e?.message ?? String(e);
111
+ }
112
+
113
+ if (writeErr) {
114
+ const errState = {
115
+ status: 'error',
116
+ prompt,
117
+ specialistCount,
118
+ depth,
119
+ startedAt: Date.now(),
120
+ finishedAt: Date.now(),
121
+ error: writeErr,
122
+ };
123
+ runs.set(tabId, errState);
124
+ broadcast(tabId);
125
+ logs.writeLine({
126
+ scope: 'superagent',
127
+ level: 'error',
128
+ message: 'start failed',
129
+ meta: { tabId, error: writeErr },
130
+ });
131
+ return { ok: false, error: writeErr };
132
+ }
133
+
134
+ const state = {
135
+ status: 'running',
136
+ prompt,
137
+ specialistCount,
138
+ depth,
139
+ startedAt: Date.now(),
140
+ finishedAt: null,
141
+ };
142
+ runs.set(tabId, state);
143
+ broadcast(tabId);
144
+
145
+ logs.writeLine({
146
+ scope: 'superagent',
147
+ level: 'info',
148
+ message: 'start',
149
+ meta: { tabId, specialistCount, depth, promptBytes: prompt.length },
150
+ });
151
+
152
+ return { ok: true };
153
+ }
154
+
155
+ function stopRun(tabId) {
156
+ const existing = runs.get(tabId);
157
+ if (!existing || existing.status !== 'running') {
158
+ return { ok: true };
159
+ }
160
+ runs.set(tabId, {
161
+ ...existing,
162
+ status: 'done',
163
+ finishedAt: Date.now(),
164
+ });
165
+ broadcast(tabId);
166
+ logs.writeLine({
167
+ scope: 'superagent',
168
+ level: 'info',
169
+ message: 'stop',
170
+ meta: { tabId },
171
+ });
172
+ return { ok: true };
173
+ }
174
+
175
+ function getStatus(tabId) {
176
+ return runs.get(tabId) ?? null;
177
+ }
178
+
179
+ function registerSuperAgentHandlers() {
180
+ ipcMain.handle('superagent:start', (_e, payload) => {
181
+ const parsed = schemas.superagentStart.parse(payload);
182
+ return startRun(parsed);
183
+ });
184
+
185
+ ipcMain.handle('superagent:status', (_e, payload) => {
186
+ const { tabId } = schemas.superagentTabId.parse(payload);
187
+ return getStatus(tabId);
188
+ });
189
+
190
+ ipcMain.handle('superagent:stop', (_e, payload) => {
191
+ const { tabId } = schemas.superagentTabId.parse(payload);
192
+ return stopRun(tabId);
193
+ });
194
+ }
195
+
196
+ module.exports = {
197
+ attachWindow,
198
+ registerSuperAgentHandlers,
199
+ // Exposed for tests.
200
+ buildBossPrompt,
201
+ _runs: runs,
202
+ };
@@ -69,7 +69,9 @@ function classifyLine(obj) {
69
69
  return { kind: 'plan', data: block.input, raw: obj };
70
70
  }
71
71
  if (block.name === 'Agent' || block.name === 'Task') {
72
- return { kind: 'agent_spawn', data: block.input, raw: obj };
72
+ // Include block.id as toolUseId so the live store can match the
73
+ // corresponding tool_result and update per-agent lastActivityAt.
74
+ return { kind: 'agent_spawn', data: { ...block.input, toolUseId: block.id }, raw: obj };
73
75
  }
74
76
  return {
75
77
  kind: 'tool_use',
@@ -77,6 +79,11 @@ function classifyLine(obj) {
77
79
  raw: obj,
78
80
  };
79
81
  }
82
+ // tool_result carries the tool_use_id of the completed Task/Agent call.
83
+ // The live store uses this to update the agent's lastActivityAt bookend.
84
+ if (block?.type === 'tool_result' && block.tool_use_id) {
85
+ return { kind: 'tool_result', data: { toolUseId: block.tool_use_id }, raw: obj };
86
+ }
80
87
  }
81
88
  }
82
89
 
@@ -377,6 +377,46 @@ export interface HistoryAggregateResult {
377
377
  scannedMs: number;
378
378
  }
379
379
 
380
+ export interface ConversationSummary {
381
+ /** ISO 8601 timestamp of the first event in the conversation (or file mtime fallback). */
382
+ timestamp: string;
383
+ /** Decoded project cwd, e.g. /home/user/Projects/foo. */
384
+ projectFolder: string;
385
+ stats: {
386
+ /** Wall-clock duration in ms (first event ts → last event ts). Omitted when unknown. */
387
+ duration?: number;
388
+ /** Sum of input + output tokens across the file. 0 when no usage blocks present. */
389
+ estimatedTokens: number;
390
+ };
391
+ }
392
+
393
+ export interface ListConversationsResult {
394
+ conversations: ConversationSummary[];
395
+ scannedMs: number;
396
+ }
397
+
398
+ export interface ProjectSkillState {
399
+ skillId: string;
400
+ enabled: boolean;
401
+ }
402
+
403
+ export interface FileEntry {
404
+ name: string;
405
+ path: string;
406
+ isDirectory: boolean;
407
+ isFile: boolean;
408
+ size: number;
409
+ mtimeMs: number;
410
+ }
411
+
412
+ export interface FilesListResult { ok: boolean; entries: FileEntry[]; error: string | null }
413
+ export interface FilesReadResult { ok: boolean; text: string; error: string | null; size: number }
414
+ export interface FilesWriteResult { ok: boolean; error: string | null }
415
+ export interface FilesCreateResult { ok: boolean; path?: string; error: string | null }
416
+ export interface FilesRenameResult { ok: boolean; newPath?: string; error: string | null }
417
+ export interface FilesDeleteResult { ok: boolean; error: string | null }
418
+ export interface FilesShellResult { ok: boolean; error?: string }
419
+
380
420
  export interface WatcherInfo {
381
421
  watcherId: string;
382
422
  tabId: string;
@@ -506,6 +546,83 @@ export interface MemoryMutationResult {
506
546
  error: string | null;
507
547
  }
508
548
 
549
+ // ────────────────────────────────────────────── Per-subagent memory
550
+ // Stored at ~/.claude/session-manager/agent-memory/<agentId>.json. Keyed by
551
+ // agent name (the .md filename in ~/.claude/agents/), not by workspace cwd.
552
+
553
+ export type AgentMemoryCategory = 'command' | 'preference' | 'pattern' | 'failure' | 'workflow';
554
+
555
+ export interface AgentMemoryEntry {
556
+ id: string;
557
+ body: string;
558
+ category: AgentMemoryCategory | null;
559
+ createdAt: number;
560
+ updatedAt: number;
561
+ bytes: number;
562
+ }
563
+
564
+ export interface AgentMemoryListResult {
565
+ entries: AgentMemoryEntry[];
566
+ agentId: string;
567
+ error: string | null;
568
+ }
569
+
570
+ export interface AgentMemoryGetResult {
571
+ entry: AgentMemoryEntry | null;
572
+ error: string | null;
573
+ }
574
+
575
+ export interface AgentMemoryMutationResult {
576
+ ok: boolean;
577
+ error: string | null;
578
+ }
579
+
580
+ export interface AgentMemoryAgentSummary {
581
+ agentId: string;
582
+ bytes: number;
583
+ mtimeMs: number;
584
+ }
585
+
586
+ export interface AgentMemoryListAgentsResult {
587
+ agents: AgentMemoryAgentSummary[];
588
+ error: string | null;
589
+ }
590
+
591
+ // ────────────────────────────────────────────── Git status (richer than app:git-branch)
592
+
593
+ /** Mirrors the status returned by src/main/git.cjs mapStatus(). */
594
+ export type GitFileStatusType =
595
+ | 'modified'
596
+ | 'added'
597
+ | 'deleted'
598
+ | 'renamed'
599
+ | 'untracked'
600
+ | 'staged'
601
+ | 'conflict';
602
+
603
+ export interface GitFileStatus {
604
+ /** Absolute path. Always inside cwd (we resolve relative paths against it). */
605
+ path: string;
606
+ /** Path as git reported it, relative to the repo root. */
607
+ relativePath: string;
608
+ status: GitFileStatusType;
609
+ /** Raw porcelain X (index) character. ' ', 'M', 'A', 'D', 'R', 'C', 'U', '?'. */
610
+ indexStatus: string;
611
+ /** Raw porcelain Y (worktree) character. ' ', 'M', 'D', 'U', '?'. */
612
+ workTreeStatus: string;
613
+ }
614
+
615
+ export interface GitStatusResult {
616
+ branch: string;
617
+ ahead: number;
618
+ behind: number;
619
+ uncommittedCount: number;
620
+ files: GitFileStatus[];
621
+ }
622
+
623
+ /** Map keyed by absolute path. Same enum as GitFileStatus.status. */
624
+ export type GitFileStatusMap = Record<string, GitFileStatusType>;
625
+
509
626
  // ────────────────────────────────────────────── Bundle F — plugins install
510
627
 
511
628
  export interface PluginInstallResult {
@@ -519,6 +636,40 @@ export interface PluginInstallProgressEvent {
519
636
  line: string;
520
637
  }
521
638
 
639
+ // ────────────────────────────────────────────── SuperAgent
640
+ // "Boss" run that dispatches specialist subagents on the active tab's claude
641
+ // session. Renderer-driven progress — main only owns lifecycle + prompt write.
642
+
643
+ export type SuperAgentDepth = 'quick' | 'standard' | 'deep';
644
+ export type SuperAgentStatus = 'idle' | 'running' | 'done' | 'error';
645
+
646
+ export interface SuperAgentRunState {
647
+ status: SuperAgentStatus;
648
+ prompt: string;
649
+ specialistCount: number;
650
+ depth: SuperAgentDepth;
651
+ startedAt: number | null;
652
+ finishedAt: number | null;
653
+ error?: string;
654
+ }
655
+
656
+ export interface SuperAgentStartArgs {
657
+ tabId: string;
658
+ prompt: string;
659
+ specialistCount: number;
660
+ depth: SuperAgentDepth;
661
+ }
662
+
663
+ export interface SuperAgentStartResult {
664
+ ok: boolean;
665
+ error?: string;
666
+ }
667
+
668
+ export interface SuperAgentStateChangedEvent {
669
+ tabId: string;
670
+ state: SuperAgentRunState | null;
671
+ }
672
+
522
673
  export interface SessionManagerAPI {
523
674
  app: {
524
675
  version: () => Promise<string>;
@@ -540,6 +691,13 @@ export interface SessionManagerAPI {
540
691
  onNewSession: (handler: () => void) => () => void;
541
692
  onRebootSession: (handler: () => void) => () => void;
542
693
  openInEditor: (cwd: string, editor?: string | null) => Promise<{ ok: boolean; editor?: string; error?: string }>;
694
+ /** Open an http/https URL in the OS default browser. file://, javascript:,
695
+ * and other schemes are rejected with `ok:false` to prevent abuse. */
696
+ openExternal: (url: string) => Promise<{ ok: boolean; error?: string }>;
697
+ /** Open a specific file at line:col in the user's editor. Editors with
698
+ * goto-line support (code/cursor/subl) get the `-g file:line:col` form;
699
+ * others open the file alone. */
700
+ openFileInEditor: (filePath: string, line?: number, col?: number, editor?: string | null) => Promise<{ ok: boolean; editor?: string; error?: string }>;
543
701
  openInFinder: (cwd: string) => Promise<{ ok: boolean; error?: string }>;
544
702
  openInTerminal: (cwd: string) => Promise<{ ok: boolean; terminal?: string; error?: string }>;
545
703
  archiveProject: (encoded: string) => Promise<{ ok: boolean; error?: string }>;
@@ -616,8 +774,23 @@ export interface SessionManagerAPI {
616
774
  status: () => Promise<OtelStatus>;
617
775
  configPath: () => Promise<string>;
618
776
  };
777
+ projectSkills: {
778
+ get: (cwd: string) => Promise<ProjectSkillState[]>;
779
+ set: (cwd: string, skillId: string, enabled: boolean) => Promise<{ ok: boolean }>;
780
+ };
781
+ files: {
782
+ list: (path: string, showHidden?: boolean) => Promise<FilesListResult>;
783
+ read: (path: string) => Promise<FilesReadResult>;
784
+ write: (path: string, content: string) => Promise<FilesWriteResult>;
785
+ create: (parentPath: string, name: string, kind: 'file' | 'folder') => Promise<FilesCreateResult>;
786
+ rename: (path: string, newName: string) => Promise<FilesRenameResult>;
787
+ delete: (path: string) => Promise<FilesDeleteResult>;
788
+ openExternal: (path: string) => Promise<FilesShellResult>;
789
+ showInFinder: (path: string) => Promise<FilesShellResult>;
790
+ };
619
791
  history: {
620
792
  aggregate: (req?: HistoryAggregateRequest) => Promise<HistoryAggregateResult>;
793
+ listConversations: () => Promise<ListConversationsResult>;
621
794
  };
622
795
  schedule: {
623
796
  state: () => Promise<ScheduleStateSnapshot>;
@@ -683,6 +856,48 @@ export interface SessionManagerAPI {
683
856
  /** Create a new memory entry with starter frontmatter + body. */
684
857
  create: (name: string, description?: string, workspace?: string) => Promise<MemoryMutationResult>;
685
858
  };
859
+ agentMemory: {
860
+ /** List all memory entries for one subagent. Sorted newest first. */
861
+ list: (agentId: string) => Promise<AgentMemoryListResult>;
862
+ /** Get one entry's full body. Returns `{entry:null}` if missing. */
863
+ get: (agentId: string, entryId: string) => Promise<AgentMemoryGetResult>;
864
+ /** Upsert one entry. Atomic write through config.cjs; max body 1 MiB. */
865
+ set: (
866
+ agentId: string,
867
+ entryId: string,
868
+ body: string,
869
+ category?: AgentMemoryCategory,
870
+ ) => Promise<AgentMemoryMutationResult>;
871
+ /** Delete one entry. Removes the file outright when last entry is removed. */
872
+ delete: (agentId: string, entryId: string) => Promise<AgentMemoryMutationResult>;
873
+ /** List all agents that currently have a memory file on disk. */
874
+ listAgents: () => Promise<AgentMemoryListAgentsResult>;
875
+ };
876
+ docEditor: {
877
+ pickFile: (payload?: { lastDir?: string }) => Promise<{ path: string | null; error?: string }>;
878
+ readFile: (path: string) => Promise<{ ok: boolean; text?: string; mtimeMs?: number; error?: string }>;
879
+ writeFile: (path: string, text: string) => Promise<{ ok: boolean; mtimeMs?: number; error?: string }>;
880
+ };
881
+ git: {
882
+ /** Full git status for `cwd`. Returns null when not a git repo, git is
883
+ * missing, or the call times out (5s ceiling). Cached per-cwd for 5s. */
884
+ status: (cwd: string) => Promise<GitStatusResult | null>;
885
+ /** `{ absPath: status }` map. Returns `{}` for non-git / errored cwds.
886
+ * Same 5s cache as status(). Designed for a file-tree sidebar where the
887
+ * renderer needs per-row badges without a separate git call per file. */
888
+ fileStatus: (cwd: string) => Promise<GitFileStatusMap>;
889
+ };
890
+ superagent: {
891
+ /** Start a SuperAgent boss run — writes a structured prompt to the tab's
892
+ * PTY asking Claude to pick + dispatch specialists. Single run per tab. */
893
+ start: (args: SuperAgentStartArgs) => Promise<SuperAgentStartResult>;
894
+ /** Current run state for tab, or null if no run has been started. */
895
+ status: (tabId: string) => Promise<SuperAgentRunState | null>;
896
+ /** Mark the run as done. Does not interrupt Claude — the user can stop
897
+ * the in-PTY work via Ctrl-C; this only flips the renderer indicator. */
898
+ stop: (tabId: string) => Promise<{ ok: boolean }>;
899
+ onStateChanged: (handler: (ev: SuperAgentStateChangedEvent) => void) => () => void;
900
+ };
686
901
  }
687
902
 
688
903
  declare global {
@@ -10,6 +10,8 @@ contextBridge.exposeInMainWorld('api', {
10
10
  gitBranch: (cwd) => ipcRenderer.invoke('app:git-branch', { cwd }),
11
11
  rebootApp: () => ipcRenderer.send('app:reboot-app'),
12
12
  openInEditor: (cwd, editor) => ipcRenderer.invoke('app:open-in-editor', { cwd, editor }),
13
+ openExternal: (url) => ipcRenderer.invoke('app:open-external', { url }),
14
+ openFileInEditor: (filePath, line, col, editor) => ipcRenderer.invoke('app:open-file-in-editor', { path: filePath, line, col, editor }),
13
15
  openInFinder: (cwd) => ipcRenderer.invoke('app:open-in-finder', { cwd }),
14
16
  openInTerminal: (cwd) => ipcRenderer.invoke('app:open-in-terminal', { cwd }),
15
17
  archiveProject: (encoded) => ipcRenderer.invoke('app:archive-project', { encoded }),
@@ -144,6 +146,22 @@ contextBridge.exposeInMainWorld('api', {
144
146
  },
145
147
  history: {
146
148
  aggregate: (req) => ipcRenderer.invoke('history:aggregate', req),
149
+ listConversations: () => ipcRenderer.invoke('history:list-conversations'),
150
+ },
151
+ projectSkills: {
152
+ get: (cwd) => ipcRenderer.invoke('project-skills:get', { cwd }),
153
+ set: (cwd, skillId, enabled) =>
154
+ ipcRenderer.invoke('project-skills:set', { cwd, skillId, enabled }),
155
+ },
156
+ files: {
157
+ list: (path, showHidden) => ipcRenderer.invoke('files:list', { path, showHidden }),
158
+ read: (path) => ipcRenderer.invoke('files:read', { path }),
159
+ write: (path, content) => ipcRenderer.invoke('files:write', { path, content }),
160
+ create: (parentPath, name, kind) => ipcRenderer.invoke('files:create', { parentPath, name, kind }),
161
+ rename: (path, newName) => ipcRenderer.invoke('files:rename', { path, newName }),
162
+ delete: (path) => ipcRenderer.invoke('files:delete', { path }),
163
+ openExternal: (path) => ipcRenderer.invoke('files:open-external', { path }),
164
+ showInFinder: (path) => ipcRenderer.invoke('files:show-in-finder', { path }),
147
165
  },
148
166
  schedule: {
149
167
  state: () => ipcRenderer.invoke('schedule:state'),
@@ -203,4 +221,40 @@ contextBridge.exposeInMainWorld('api', {
203
221
  return ipcRenderer.invoke('memory:create', payload);
204
222
  },
205
223
  },
224
+ agentMemory: {
225
+ list: (agentId) => ipcRenderer.invoke('agent-memory:list', { agentId }),
226
+ get: (agentId, entryId) => ipcRenderer.invoke('agent-memory:get', { agentId, entryId }),
227
+ set: (agentId, entryId, body, category) => {
228
+ const payload = { agentId, entryId, body };
229
+ if (category) payload.category = category;
230
+ return ipcRenderer.invoke('agent-memory:set', payload);
231
+ },
232
+ delete: (agentId, entryId) => ipcRenderer.invoke('agent-memory:delete', { agentId, entryId }),
233
+ listAgents: () => ipcRenderer.invoke('agent-memory:list-agents'),
234
+ },
235
+ docEditor: {
236
+ pickFile: (payload) => ipcRenderer.invoke('doc-editor:pick-file', payload),
237
+ readFile: (p) => ipcRenderer.invoke('doc-editor:read-file', { path: p }),
238
+ writeFile: (p, text) => ipcRenderer.invoke('doc-editor:write-file', { path: p, text }),
239
+ },
240
+ git: {
241
+ // Returns null when cwd is not a git repo, git is missing, or the call
242
+ // times out (5s ceiling). The existing `app.gitBranch` is intentionally
243
+ // kept — StatusBar still uses it for the cheap per-tab branch readout.
244
+ status: (cwd) => ipcRenderer.invoke('git:status', { cwd }),
245
+ fileStatus: (cwd) => ipcRenderer.invoke('git:file-status', { cwd }),
246
+ },
247
+ superagent: {
248
+ /** Start a SuperAgent boss run on a tab — writes a structured prompt to
249
+ * the PTY asking Claude to pick + dispatch specialists. Single live run
250
+ * per tab; starting again on a running tab terminates the prior one. */
251
+ start: (payload) => ipcRenderer.invoke('superagent:start', payload),
252
+ status: (tabId) => ipcRenderer.invoke('superagent:status', { tabId }),
253
+ stop: (tabId) => ipcRenderer.invoke('superagent:stop', { tabId }),
254
+ onStateChanged: (handler) => {
255
+ const listener = (_e, payload) => handler(payload);
256
+ ipcRenderer.on('superagent:state-changed', listener);
257
+ return () => ipcRenderer.removeListener('superagent:state-changed', listener);
258
+ },
259
+ },
206
260
  });