@berthdev/berth 0.0.0-stage → 0.2.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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -2
  3. package/dist/src/agents/claude-name.d.ts +1 -0
  4. package/dist/src/agents/claude-name.js +57 -0
  5. package/dist/src/agents/claude-name.js.map +1 -0
  6. package/dist/src/agents/commands.d.ts +23 -0
  7. package/dist/src/agents/commands.js +48 -0
  8. package/dist/src/agents/commands.js.map +1 -0
  9. package/dist/src/agents/hooks.d.ts +13 -0
  10. package/dist/src/agents/hooks.js +141 -0
  11. package/dist/src/agents/hooks.js.map +1 -0
  12. package/dist/src/agents/registry.d.ts +33 -0
  13. package/dist/src/agents/registry.js +141 -0
  14. package/dist/src/agents/registry.js.map +1 -0
  15. package/dist/src/cli/main.d.ts +4 -0
  16. package/dist/src/cli/main.js +236 -0
  17. package/dist/src/cli/main.js.map +1 -0
  18. package/dist/src/config/index.d.ts +21 -0
  19. package/dist/src/config/index.js +77 -0
  20. package/dist/src/config/index.js.map +1 -0
  21. package/dist/src/context.d.ts +9 -0
  22. package/dist/src/context.js +12 -0
  23. package/dist/src/context.js.map +1 -0
  24. package/dist/src/errors.d.ts +13 -0
  25. package/dist/src/errors.js +17 -0
  26. package/dist/src/errors.js.map +1 -0
  27. package/dist/src/index.d.ts +17 -0
  28. package/dist/src/index.js +18 -0
  29. package/dist/src/index.js.map +1 -0
  30. package/dist/src/iterm2/install.d.ts +29 -0
  31. package/dist/src/iterm2/install.js +112 -0
  32. package/dist/src/iterm2/install.js.map +1 -0
  33. package/dist/src/present/attach.d.ts +16 -0
  34. package/dist/src/present/attach.js +31 -0
  35. package/dist/src/present/attach.js.map +1 -0
  36. package/dist/src/present/state.d.ts +14 -0
  37. package/dist/src/present/state.js +63 -0
  38. package/dist/src/present/state.js.map +1 -0
  39. package/dist/src/registry/doctor.d.ts +7 -0
  40. package/dist/src/registry/doctor.js +31 -0
  41. package/dist/src/registry/doctor.js.map +1 -0
  42. package/dist/src/registry/sessions.d.ts +47 -0
  43. package/dist/src/registry/sessions.js +138 -0
  44. package/dist/src/registry/sessions.js.map +1 -0
  45. package/dist/src/restore/ensure.d.ts +17 -0
  46. package/dist/src/restore/ensure.js +57 -0
  47. package/dist/src/restore/ensure.js.map +1 -0
  48. package/dist/src/restore/lock.d.ts +6 -0
  49. package/dist/src/restore/lock.js +74 -0
  50. package/dist/src/restore/lock.js.map +1 -0
  51. package/dist/src/shell.d.ts +6 -0
  52. package/dist/src/shell.js +68 -0
  53. package/dist/src/shell.js.map +1 -0
  54. package/dist/src/snapshot/store.d.ts +29 -0
  55. package/dist/src/snapshot/store.js +90 -0
  56. package/dist/src/snapshot/store.js.map +1 -0
  57. package/dist/src/snapshot/take.d.ts +5 -0
  58. package/dist/src/snapshot/take.js +33 -0
  59. package/dist/src/snapshot/take.js.map +1 -0
  60. package/dist/src/tmux/client.d.ts +49 -0
  61. package/dist/src/tmux/client.js +111 -0
  62. package/dist/src/tmux/client.js.map +1 -0
  63. package/dist/src/tmux/install.d.ts +4 -0
  64. package/dist/src/tmux/install.js +64 -0
  65. package/dist/src/tmux/install.js.map +1 -0
  66. package/dist/src/tools.d.ts +5 -0
  67. package/dist/src/tools.js +16 -0
  68. package/dist/src/tools.js.map +1 -0
  69. package/package.json +49 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 maxzyma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
- # Temporary Holding Version
1
+ # @berthdev/berth
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Core library and `berth` CLI of Berth: keep terminal-based coding agents
4
+ (Claude Code, Codex CLI, Gemini CLI, OpenCode, Copilot CLI, Cursor CLI, Kimi
5
+ CLI, Qoder CLI) running in tmux, list them all, and bring each back —
6
+ conversation included — after an editor restart or a reboot.
7
+
8
+ ```sh
9
+ berth new claude --origin vscode # start an agent session
10
+ berth ls # every session, managed or not
11
+ berth hooks install claude # let the agent report its conversation id and state
12
+ berth tmux install # snapshot automatically as sessions change
13
+ berth ensure # after a reboot: rebuild and resume
14
+ ```
15
+
16
+ Requires Node ≥ 20 and tmux ≥ 3.0. Behaviour is specified in the repository's
17
+ `design/spec.md`.
@@ -0,0 +1 @@
1
+ export declare function claudeConversationByName(cwd: string, name: string, home?: string): string | undefined;
@@ -0,0 +1,57 @@
1
+ import { closeSync, existsSync, openSync, readdirSync, readSync, realpathSync, statSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ /**
5
+ * Name key (spec §4.2): a Claude Code session started with `--name X` records
6
+ * X as a `custom-title` entry near the top of its transcript. The newest
7
+ * transcript in the session's directory carrying that title is the session's
8
+ * conversation. Claude keeps a directory's transcripts under
9
+ * ~/.claude/projects/<cwd with every non-alphanumeric character as '-'>.
10
+ */
11
+ const HEADER_LINES = 40;
12
+ function projectDirs(cwd, home) {
13
+ const encode = (p) => join(home, '.claude', 'projects', p.replace(/[^A-Za-z0-9]/g, '-'));
14
+ const dirs = [encode(cwd)];
15
+ try {
16
+ const real = realpathSync(cwd);
17
+ if (real !== cwd)
18
+ dirs.push(encode(real));
19
+ }
20
+ catch {
21
+ // The directory may be gone; the recorded path still names its transcripts.
22
+ }
23
+ return dirs.filter((d) => existsSync(d));
24
+ }
25
+ function titleOf(file) {
26
+ const fd = openSync(file, 'r');
27
+ try {
28
+ const buffer = Buffer.alloc(64 * 1024);
29
+ const read = readSync(fd, buffer, 0, buffer.length, 0);
30
+ for (const line of buffer.subarray(0, read).toString('utf8').split('\n').slice(0, HEADER_LINES)) {
31
+ try {
32
+ const entry = JSON.parse(line);
33
+ if (entry.type === 'custom-title' && entry.customTitle)
34
+ return entry.customTitle;
35
+ }
36
+ catch {
37
+ // Partial or non-JSON line.
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ finally {
43
+ closeSync(fd);
44
+ }
45
+ }
46
+ export function claudeConversationByName(cwd, name, home = process.env.HOME || homedir()) {
47
+ const files = projectDirs(cwd, home)
48
+ .flatMap((dir) => readdirSync(dir).filter((f) => f.endsWith('.jsonl')).map((f) => join(dir, f)))
49
+ .map((file) => ({ file, mtime: statSync(file).mtimeMs }))
50
+ .sort((a, b) => b.mtime - a.mtime);
51
+ for (const { file } of files) {
52
+ if (titleOf(file) === name)
53
+ return file.replace(/^.*\//, '').replace(/\.jsonl$/, '');
54
+ }
55
+ return undefined;
56
+ }
57
+ //# sourceMappingURL=claude-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-name.js","sourceRoot":"","sources":["../../../src/agents/claude-name.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { AgentOverride } from '../config/index.js';
2
+ import { type Adapter } from './registry.js';
3
+ export declare function requireAdapter(id: string): Adapter;
4
+ /**
5
+ * Options from an original launch command that are kept on resume
6
+ * (implementation §3.2). Anything not in the adapter's keep-set is dropped:
7
+ * prompts, positional arguments, session selectors, one-shot modes.
8
+ */
9
+ export declare function keptArgs(adapter: Adapter, argv: string): string[];
10
+ export interface CommandInput {
11
+ adapter: Adapter;
12
+ name: string;
13
+ cwd: string;
14
+ /** Extra arguments for a fresh launch, already split. */
15
+ extraArgs?: string[];
16
+ overrides: Record<string, AgentOverride>;
17
+ }
18
+ export declare function launchCommand(input: CommandInput): string;
19
+ /** Resume command, or undefined when the agent has no resume template. */
20
+ export declare function resumeCommand(input: CommandInput & {
21
+ conversation: string;
22
+ originalArgv: string;
23
+ }): string | undefined;
@@ -0,0 +1,48 @@
1
+ import { BerthError, ExitCode } from '../errors.js';
2
+ import { renderTemplate, shellQuote, shellSplit } from '../shell.js';
3
+ import { getAdapter } from './registry.js';
4
+ export function requireAdapter(id) {
5
+ const adapter = getAdapter(id);
6
+ if (!adapter)
7
+ throw new BerthError(`unknown agent: ${id}`, ExitCode.usage);
8
+ return adapter;
9
+ }
10
+ /**
11
+ * Options from an original launch command that are kept on resume
12
+ * (implementation §3.2). Anything not in the adapter's keep-set is dropped:
13
+ * prompts, positional arguments, session selectors, one-shot modes.
14
+ */
15
+ export function keptArgs(adapter, argv) {
16
+ const words = argv ? shellSplit(argv).slice(1) : [];
17
+ const kept = [];
18
+ for (let i = 0; i < words.length; i++) {
19
+ const word = words[i];
20
+ if (!word.startsWith('-'))
21
+ continue;
22
+ const [flag] = word.split('=', 1);
23
+ if (!(flag in adapter.keep))
24
+ continue;
25
+ kept.push(word);
26
+ const takesValue = adapter.keep[flag];
27
+ if (takesValue && !word.includes('=') && i + 1 < words.length)
28
+ kept.push(words[++i]);
29
+ }
30
+ return kept;
31
+ }
32
+ function template(adapter, kind, overrides) {
33
+ return overrides[adapter.id]?.[kind] ?? adapter[kind];
34
+ }
35
+ export function launchCommand(input) {
36
+ const tpl = template(input.adapter, 'launch', input.overrides) ?? '';
37
+ const args = (input.extraArgs ?? []).map(shellQuote).join(' ');
38
+ return renderTemplate(tpl, { name: input.name, cwd: input.cwd, args });
39
+ }
40
+ /** Resume command, or undefined when the agent has no resume template. */
41
+ export function resumeCommand(input) {
42
+ const tpl = template(input.adapter, 'resume', input.overrides);
43
+ if (!tpl)
44
+ return undefined;
45
+ const args = keptArgs(input.adapter, input.originalArgv).map(shellQuote).join(' ');
46
+ return renderTemplate(tpl, { name: input.name, cwd: input.cwd, conversation: input.conversation, args });
47
+ }
48
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../../src/agents/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type Adapter } from './registry.js';
2
+ /** Command an agent runs for an event: `BERTH_HOOK=1 <node> <cli> hook <agent> <event>`. */
3
+ export declare function hookCommand(cliPath: string, agent: string, event: string): string;
4
+ export declare function installHooks(agentId: string, cliPath: string, env?: NodeJS.ProcessEnv): string;
5
+ export declare function uninstallHooks(agentId: string, env?: NodeJS.ProcessEnv): string;
6
+ export declare function hooksInstalled(agentId: string, env?: NodeJS.ProcessEnv): boolean;
7
+ /** What a hook event changes on the session, or nothing when the event does not matter. */
8
+ export declare function metaForEvent(adapter: Adapter, event: string, payload: Record<string, unknown>): Record<string, string>;
9
+ /**
10
+ * Handle `berth hook <agent> <event>` inside an agent's pane. Never fails the
11
+ * agent: problems are swallowed and the function returns what it stamped.
12
+ */
13
+ export declare function handleHook(agentId: string, event: string, stdin: string, env?: NodeJS.ProcessEnv): Record<string, string>;
@@ -0,0 +1,141 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { BerthError, ExitCode } from '../errors.js';
5
+ import { shellQuote } from '../shell.js';
6
+ import { Tmux } from '../tmux/client.js';
7
+ import { presentState } from '../present/state.js';
8
+ import { createContext } from '../context.js';
9
+ import { snapshotQuietly } from '../snapshot/take.js';
10
+ import { requireAdapter } from './commands.js';
11
+ import { stateForEvent } from './registry.js';
12
+ /** Marker that identifies hook entries written by Berth. */
13
+ const MARKER = 'BERTH_HOOK=1';
14
+ function hookFile(adapter, env) {
15
+ const home = env.HOME || homedir();
16
+ switch (adapter.hookTarget) {
17
+ case 'claude-settings':
18
+ return join(home, '.claude', 'settings.json');
19
+ case 'codex-hooks':
20
+ return join(home, '.codex', 'hooks.json');
21
+ default:
22
+ throw new BerthError(`${adapter.displayName} has no hook integration in Berth yet`, ExitCode.usage);
23
+ }
24
+ }
25
+ function hookEvents(adapter) {
26
+ const { start, working, waiting, done } = adapter.events;
27
+ return [...new Set([...start, ...working, ...waiting, ...done])];
28
+ }
29
+ function readHooks(file) {
30
+ if (!existsSync(file))
31
+ return {};
32
+ try {
33
+ return JSON.parse(readFileSync(file, 'utf8'));
34
+ }
35
+ catch (error) {
36
+ throw new BerthError(`cannot parse ${file}: ${error.message}`);
37
+ }
38
+ }
39
+ function withoutBerth(groups = []) {
40
+ return groups
41
+ .map((g) => ({ ...g, hooks: (g.hooks ?? []).filter((h) => !h.command?.includes(MARKER)) }))
42
+ .filter((g) => g.hooks.length > 0);
43
+ }
44
+ function writeHooks(file, data) {
45
+ mkdirSync(dirname(file), { recursive: true });
46
+ writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`);
47
+ }
48
+ /** Command an agent runs for an event: `BERTH_HOOK=1 <node> <cli> hook <agent> <event>`. */
49
+ export function hookCommand(cliPath, agent, event) {
50
+ return `${MARKER} ${shellQuote(process.execPath)} ${shellQuote(cliPath)} hook ${agent} ${shellQuote(event)}`;
51
+ }
52
+ export function installHooks(agentId, cliPath, env = process.env) {
53
+ const adapter = requireAdapter(agentId);
54
+ const file = hookFile(adapter, env);
55
+ const data = readHooks(file);
56
+ const hooks = { ...(data.hooks ?? {}) };
57
+ for (const event of hookEvents(adapter)) {
58
+ hooks[event] = [...withoutBerth(hooks[event]), { hooks: [{ type: 'command', command: hookCommand(cliPath, adapter.id, event) }] }];
59
+ }
60
+ writeHooks(file, { ...data, hooks });
61
+ return file;
62
+ }
63
+ export function uninstallHooks(agentId, env = process.env) {
64
+ const adapter = requireAdapter(agentId);
65
+ const file = hookFile(adapter, env);
66
+ if (!existsSync(file))
67
+ return file;
68
+ const data = readHooks(file);
69
+ const hooks = {};
70
+ for (const [event, groups] of Object.entries(data.hooks ?? {})) {
71
+ const kept = withoutBerth(groups);
72
+ if (kept.length > 0)
73
+ hooks[event] = kept;
74
+ }
75
+ writeHooks(file, { ...data, hooks });
76
+ return file;
77
+ }
78
+ export function hooksInstalled(agentId, env = process.env) {
79
+ const adapter = requireAdapter(agentId);
80
+ if (!adapter.hookTarget)
81
+ return false;
82
+ const data = readHooks(hookFile(adapter, env));
83
+ return Object.values(data.hooks ?? {}).some((groups) => groups.some((g) => g.hooks?.some((h) => h.command?.includes(MARKER))));
84
+ }
85
+ /** What a hook event changes on the session, or nothing when the event does not matter. */
86
+ export function metaForEvent(adapter, event, payload) {
87
+ const meta = {};
88
+ const id = payload.session_id ?? payload.sessionID ?? payload.conversation_id;
89
+ if (adapter.events.start.includes(event) && typeof id === 'string' && id)
90
+ meta.conversation = id;
91
+ let state = stateForEvent(adapter, event);
92
+ // Claude Code also sends Notification for an idle reminder; only a permission request means waiting.
93
+ if (adapter.id === 'claude' && event === 'Notification' && !/permission/i.test(String(payload.message ?? '')))
94
+ state = undefined;
95
+ if (state)
96
+ meta.state = state;
97
+ // Latest notification text, shown in session lists (spec §6).
98
+ if (state && typeof payload.message === 'string' && payload.message.trim())
99
+ meta.note = payload.message.trim().slice(0, 200);
100
+ return meta;
101
+ }
102
+ /**
103
+ * Handle `berth hook <agent> <event>` inside an agent's pane. Never fails the
104
+ * agent: problems are swallowed and the function returns what it stamped.
105
+ */
106
+ export function handleHook(agentId, event, stdin, env = process.env) {
107
+ try {
108
+ const adapter = requireAdapter(agentId);
109
+ const pane = env.TMUX_PANE;
110
+ const socketPath = env.TMUX?.split(',')[0];
111
+ if (!pane || !socketPath)
112
+ return {};
113
+ let payload = {};
114
+ try {
115
+ payload = stdin.trim() ? JSON.parse(stdin) : {};
116
+ }
117
+ catch {
118
+ payload = {};
119
+ }
120
+ const meta = metaForEvent(adapter, event, payload);
121
+ if (Object.keys(meta).length === 0)
122
+ return {};
123
+ const tmux = new Tmux({ bin: env.BERTH_TMUX || 'tmux', socketPath });
124
+ const session = tmux.sessionOfPane(pane);
125
+ if (!session)
126
+ return {};
127
+ tmux.setMeta(session, meta);
128
+ if (meta.state)
129
+ presentState(tmux, session, meta.state, { note: meta.note });
130
+ // A new conversation id must reach the snapshot before any reboot.
131
+ if (meta.conversation) {
132
+ const ctx = createContext({ ...env, BERTH_TMUX_SOCKET_PATH: socketPath });
133
+ snapshotQuietly(ctx);
134
+ }
135
+ return meta;
136
+ }
137
+ catch {
138
+ return {};
139
+ }
140
+ }
141
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/agents/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAgB,MAAM,eAAe,CAAC;AAE5D,4DAA4D;AAC5D,MAAM,MAAM,GAAG,cAAc,CAAC;AAM9B,SAAS,QAAQ,CAAC,OAAgB,EAAE,GAAuC;IACzE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;IACnC,QAAQ,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3B,KAAK,iBAAiB;YACpB,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QAChD,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC5C;YACE,MAAM,IAAI,UAAU,CAAC,GAAG,OAAO,CAAC,WAAW,uCAAuC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxG,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB;IAClC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IACzD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAa,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAAC,gBAAgB,IAAI,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,SAAsB,EAAE;IAC5C,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;SAC1F,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,IAAc;IAC9C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,KAAa,EAAE,KAAa;IACvE,OAAO,GAAG,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;AAC/G,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,OAAe,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG;IAC9E,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAgC,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;IACrE,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACrI,CAAC;IACD,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG;IAC/D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAgC,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC3C,CAAC;IACD,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG;IAC/D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACjI,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,KAAa,EAAE,OAAgC;IAC5F,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAC9E,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE;QAAE,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IACjG,IAAI,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC1C,qGAAqG;IACrG,IAAI,OAAO,CAAC,EAAE,KAAK,QAAQ,IAAI,KAAK,KAAK,cAAc,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAAE,KAAK,GAAG,SAAS,CAAC;IACjI,IAAI,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9B,8DAA8D;IAC9D,IAAI,KAAK,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7H,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,KAAa,EAAE,KAAa,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG;IACzF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC;QAC3B,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,CAAC;QACpC,IAAI,OAAO,GAA4B,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAA6B,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;QACD,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,UAAU,IAAI,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC5B,IAAI,IAAI,CAAC,KAAK;YAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7E,mEAAmE;QACnE,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,GAAG,GAAG,EAAE,sBAAsB,EAAE,UAAU,EAAE,CAAC,CAAC;YAC1E,eAAe,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Agent adapters (spec §8, implementation §3).
3
+ *
4
+ * `source` records where each row's commands and events come from:
5
+ * - `help <version>`: checked against the CLI's own --help on that version;
6
+ * - `cmux`: follows cmux's handling of that agent (reference commit in spec §8).
7
+ * No code is taken from other projects; facts about the CLIs are restated here.
8
+ */
9
+ export type AgentState = 'working' | 'waiting' | 'done';
10
+ export interface HookEvents {
11
+ /** Events whose payload carries the conversation id. */
12
+ start: string[];
13
+ working: string[];
14
+ waiting: string[];
15
+ done: string[];
16
+ }
17
+ export interface Adapter {
18
+ id: string;
19
+ displayName: string;
20
+ binary: string;
21
+ launch: string;
22
+ /** Absent when the agent cannot resume a conversation. */
23
+ resume?: string;
24
+ /** Options kept on resume; value says whether the option takes an argument. */
25
+ keep: Record<string, boolean>;
26
+ /** Where Berth writes hooks for this agent, when it supports them. */
27
+ hookTarget?: 'claude-settings' | 'codex-hooks';
28
+ events: HookEvents;
29
+ source: string;
30
+ }
31
+ export declare const ADAPTERS: Adapter[];
32
+ export declare function getAdapter(id: string): Adapter | undefined;
33
+ export declare function stateForEvent(adapter: Adapter, event: string): AgentState | undefined;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Agent adapters (spec §8, implementation §3).
3
+ *
4
+ * `source` records where each row's commands and events come from:
5
+ * - `help <version>`: checked against the CLI's own --help on that version;
6
+ * - `cmux`: follows cmux's handling of that agent (reference commit in spec §8).
7
+ * No code is taken from other projects; facts about the CLIs are restated here.
8
+ */
9
+ const noEvents = { start: [], working: [], waiting: [], done: [] };
10
+ const claudeEvents = {
11
+ start: ['SessionStart'],
12
+ working: ['UserPromptSubmit'],
13
+ waiting: ['Notification'],
14
+ done: ['Stop'],
15
+ };
16
+ export const ADAPTERS = [
17
+ {
18
+ id: 'claude',
19
+ displayName: 'Claude Code',
20
+ binary: 'claude',
21
+ launch: 'claude --name {name} {args}',
22
+ resume: 'claude --resume {conversation} --name {name} {args}',
23
+ keep: {
24
+ '--model': true,
25
+ '--permission-mode': true,
26
+ '--dangerously-skip-permissions': false,
27
+ '--add-dir': true,
28
+ '--settings': true,
29
+ '--mcp-config': true,
30
+ '--agent': true,
31
+ },
32
+ hookTarget: 'claude-settings',
33
+ events: claudeEvents,
34
+ source: 'help 2.1.310',
35
+ },
36
+ {
37
+ id: 'codex',
38
+ displayName: 'Codex CLI',
39
+ binary: 'codex',
40
+ launch: 'codex {args}',
41
+ resume: 'codex resume {conversation} {args}',
42
+ keep: {
43
+ '--model': true,
44
+ '-m': true,
45
+ '--sandbox': true,
46
+ '-s': true,
47
+ '--ask-for-approval': true,
48
+ '-a': true,
49
+ '--profile': true,
50
+ '-p': true,
51
+ '--config': true,
52
+ '-c': true,
53
+ '--dangerously-bypass-approvals-and-sandbox': false,
54
+ },
55
+ hookTarget: 'codex-hooks',
56
+ events: { start: ['SessionStart'], working: [], waiting: [], done: ['Stop'] },
57
+ source: 'help 0.162.1 (commands); cmux (hook events)',
58
+ },
59
+ {
60
+ id: 'gemini',
61
+ displayName: 'Gemini CLI',
62
+ binary: 'gemini',
63
+ launch: 'gemini {args}',
64
+ resume: 'gemini --resume {conversation} {args}',
65
+ keep: { '--model': true, '-m': true, '--yolo': false, '--approval-mode': true },
66
+ events: noEvents,
67
+ source: 'cmux',
68
+ },
69
+ {
70
+ id: 'opencode',
71
+ displayName: 'OpenCode',
72
+ binary: 'opencode',
73
+ launch: 'opencode {args}',
74
+ resume: 'opencode --session {conversation} {args}',
75
+ keep: { '--model': true, '-m': true, '--agent': true },
76
+ events: { start: ['session.created'], working: [], waiting: [], done: ['session.idle'] },
77
+ source: 'cmux',
78
+ },
79
+ {
80
+ id: 'copilot',
81
+ displayName: 'GitHub Copilot CLI',
82
+ binary: 'copilot',
83
+ launch: 'copilot {args}',
84
+ resume: 'copilot --resume {conversation} {args}',
85
+ keep: { '--model': true, '--allow-all-tools': false, '--add-dir': true },
86
+ events: { start: [], working: [], waiting: [], done: ['Notification'] },
87
+ source: 'cmux',
88
+ },
89
+ {
90
+ id: 'cursor',
91
+ displayName: 'Cursor CLI',
92
+ binary: 'cursor-agent',
93
+ launch: 'cursor-agent {args}',
94
+ resume: 'cursor-agent --resume {conversation} {args}',
95
+ keep: { '--model': true, '--force': false },
96
+ events: noEvents,
97
+ source: 'help 2025.09.12 (commands); cmux',
98
+ },
99
+ {
100
+ id: 'kimi',
101
+ displayName: 'Kimi CLI',
102
+ binary: 'kimi',
103
+ launch: 'kimi {args}',
104
+ resume: 'kimi --session {conversation} {args}',
105
+ keep: { '--agent': true, '--add-dir': true, '--model': true, '-m': true },
106
+ events: noEvents,
107
+ source: 'help 0.31.1',
108
+ },
109
+ {
110
+ id: 'qoder',
111
+ displayName: 'Qoder CLI',
112
+ binary: 'qodercli',
113
+ launch: 'qodercli --name {name} {args}',
114
+ resume: 'qodercli --resume {conversation} {args}',
115
+ keep: { '--model': true, '--yolo': false },
116
+ events: noEvents,
117
+ source: 'help 1.0.48 (commands); cmux',
118
+ },
119
+ {
120
+ id: 'shell',
121
+ displayName: 'Shell',
122
+ binary: '',
123
+ launch: '',
124
+ keep: {},
125
+ events: noEvents,
126
+ source: 'built-in',
127
+ },
128
+ ];
129
+ export function getAdapter(id) {
130
+ return ADAPTERS.find((adapter) => adapter.id === id);
131
+ }
132
+ export function stateForEvent(adapter, event) {
133
+ if (adapter.events.working.includes(event))
134
+ return 'working';
135
+ if (adapter.events.waiting.includes(event))
136
+ return 'waiting';
137
+ if (adapter.events.done.includes(event))
138
+ return 'done';
139
+ return undefined;
140
+ }
141
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/agents/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ type Out = (text: string) => void;
3
+ export declare function main(argv: string[], out?: Out): number;
4
+ export {};