tmux-team 4.0.0 → 4.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.
- package/README.md +123 -25
- package/package.json +15 -16
- package/src/cli.test.ts +15 -1
- package/src/cli.ts +15 -1
- package/src/commands/add.ts +17 -32
- package/src/commands/basic-commands.test.ts +534 -17
- package/src/commands/check.ts +20 -0
- package/src/commands/completion.ts +6 -8
- package/src/commands/config-command.test.ts +9 -8
- package/src/commands/config.ts +1 -5
- package/src/commands/help.ts +8 -3
- package/src/commands/install.test.ts +15 -1
- package/src/commands/list.ts +21 -2
- package/src/commands/migrate.ts +84 -0
- package/src/commands/preamble.test.ts +15 -2
- package/src/commands/preamble.ts +61 -16
- package/src/commands/remove.ts +10 -6
- package/src/commands/talk.test.ts +132 -22
- package/src/commands/talk.ts +28 -3
- package/src/commands/team.ts +361 -0
- package/src/commands/update.ts +45 -14
- package/src/config.test.ts +24 -0
- package/src/config.ts +37 -3
- package/src/context.test.ts +76 -1
- package/src/context.ts +8 -1
- package/src/identity.test.ts +3 -9
- package/src/identity.ts +7 -9
- package/src/registry.test.ts +61 -0
- package/src/registry.ts +29 -0
- package/src/tmux.test.ts +190 -1
- package/src/tmux.ts +289 -9
- package/src/types.ts +55 -0
- package/src/ui.test.ts +7 -1
package/src/context.ts
CHANGED
|
@@ -18,9 +18,15 @@ export function createContext(options: CreateContextOptions): Context {
|
|
|
18
18
|
const { argv, flags, cwd = process.cwd() } = options;
|
|
19
19
|
|
|
20
20
|
const paths = resolvePaths(cwd, flags.team);
|
|
21
|
-
const config = loadConfig(paths);
|
|
22
21
|
const ui = createUI(flags.json);
|
|
23
22
|
const tmux = createTmux();
|
|
23
|
+
const registryScope = flags.team
|
|
24
|
+
? { type: 'team' as const, teamName: flags.team }
|
|
25
|
+
: {
|
|
26
|
+
type: 'workspace' as const,
|
|
27
|
+
workspaceRoot: paths.workspaceRoot ?? cwd,
|
|
28
|
+
};
|
|
29
|
+
const config = loadConfig(paths, tmux.getAgentRegistry(registryScope));
|
|
24
30
|
|
|
25
31
|
return {
|
|
26
32
|
argv,
|
|
@@ -29,6 +35,7 @@ export function createContext(options: CreateContextOptions): Context {
|
|
|
29
35
|
config,
|
|
30
36
|
tmux,
|
|
31
37
|
paths,
|
|
38
|
+
registryScope,
|
|
32
39
|
exit(code: number): never {
|
|
33
40
|
process.exit(code);
|
|
34
41
|
},
|
package/src/identity.test.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import type { PaneEntry } from './types.js';
|
|
3
3
|
import { resolveActor } from './identity.js';
|
|
4
|
-
import { execSync } from 'child_process';
|
|
5
4
|
|
|
6
5
|
vi.mock('child_process', () => ({
|
|
7
6
|
execSync: vi.fn(),
|
|
8
7
|
}));
|
|
9
8
|
|
|
10
|
-
const mockedExec = vi.mocked(execSync);
|
|
11
|
-
|
|
12
9
|
describe('resolveActor', () => {
|
|
13
10
|
const paneRegistry: Record<string, PaneEntry> = {
|
|
14
|
-
claude: { pane: '
|
|
15
|
-
codex: { pane: '
|
|
11
|
+
claude: { pane: '%98' },
|
|
12
|
+
codex: { pane: '%99' },
|
|
16
13
|
};
|
|
17
14
|
|
|
18
15
|
beforeEach(() => {
|
|
@@ -41,7 +38,6 @@ describe('resolveActor', () => {
|
|
|
41
38
|
it('uses pane identity when in tmux and pane matches registry', () => {
|
|
42
39
|
process.env.TMUX = '1';
|
|
43
40
|
process.env.TMUX_PANE = '%99';
|
|
44
|
-
mockedExec.mockReturnValue('10.1\n');
|
|
45
41
|
const res = resolveActor(paneRegistry);
|
|
46
42
|
expect(res.actor).toBe('codex');
|
|
47
43
|
expect(res.source).toBe('pane');
|
|
@@ -51,7 +47,6 @@ describe('resolveActor', () => {
|
|
|
51
47
|
process.env.TMUX = '1';
|
|
52
48
|
process.env.TMUX_PANE = '%99';
|
|
53
49
|
process.env.TMT_AGENT_NAME = 'claude';
|
|
54
|
-
mockedExec.mockReturnValue('10.1\n');
|
|
55
50
|
const res = resolveActor(paneRegistry);
|
|
56
51
|
expect(res.actor).toBe('codex');
|
|
57
52
|
expect(res.warning).toContain('Identity mismatch');
|
|
@@ -59,9 +54,8 @@ describe('resolveActor', () => {
|
|
|
59
54
|
|
|
60
55
|
it('uses env actor with warning when pane is unregistered', () => {
|
|
61
56
|
process.env.TMUX = '1';
|
|
62
|
-
process.env.TMUX_PANE = '%
|
|
57
|
+
process.env.TMUX_PANE = '%100';
|
|
63
58
|
process.env.TMT_AGENT_NAME = 'someone';
|
|
64
|
-
mockedExec.mockReturnValue('99.9\n');
|
|
65
59
|
const res = resolveActor(paneRegistry);
|
|
66
60
|
expect(res.actor).toBe('someone');
|
|
67
61
|
expect(res.source).toBe('env');
|
package/src/identity.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface ActorResolution {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Get current tmux pane ID (e.g., "
|
|
15
|
+
* Get current tmux pane ID (e.g., "%12").
|
|
16
16
|
*/
|
|
17
17
|
function getCurrentPane(): string | null {
|
|
18
18
|
if (!process.env.TMUX) {
|
|
@@ -20,16 +20,14 @@ function getCurrentPane(): string | null {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const tmuxPane = process.env.TMUX_PANE;
|
|
23
|
-
if (
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
23
|
+
if (tmuxPane) return tmuxPane;
|
|
26
24
|
|
|
27
25
|
try {
|
|
28
|
-
const result = execSync(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
return result.trim();
|
|
26
|
+
const result = execSync(`tmux display-message -p '#{pane_id}'`, {
|
|
27
|
+
encoding: 'utf-8',
|
|
28
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
29
|
+
});
|
|
30
|
+
return result.trim() || null;
|
|
33
31
|
} catch {
|
|
34
32
|
return null;
|
|
35
33
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Context } from './types.js';
|
|
3
|
+
import { getRegistryScope, registrationFromEntry, scopeLabel } from './registry.js';
|
|
4
|
+
|
|
5
|
+
function ctx(overrides: Partial<Context>): Context {
|
|
6
|
+
return {
|
|
7
|
+
argv: [],
|
|
8
|
+
flags: { json: false, verbose: false },
|
|
9
|
+
ui: {} as Context['ui'],
|
|
10
|
+
config: {} as Context['config'],
|
|
11
|
+
tmux: {} as Context['tmux'],
|
|
12
|
+
paths: {
|
|
13
|
+
globalDir: '/g',
|
|
14
|
+
globalConfig: '/g/c',
|
|
15
|
+
localConfig: '/r/tmux-team.json',
|
|
16
|
+
stateFile: '/g/s',
|
|
17
|
+
workspaceRoot: '/repo',
|
|
18
|
+
},
|
|
19
|
+
exit: (() => {
|
|
20
|
+
throw new Error('exit');
|
|
21
|
+
}) as Context['exit'],
|
|
22
|
+
...overrides,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('registry helpers', () => {
|
|
27
|
+
it('prefers explicit context registry scope', () => {
|
|
28
|
+
const scope = { type: 'team' as const, teamName: 'egp' };
|
|
29
|
+
expect(getRegistryScope(ctx({ registryScope: scope }))).toBe(scope);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('uses --team when no explicit scope exists', () => {
|
|
33
|
+
expect(getRegistryScope(ctx({ flags: { json: false, verbose: false, team: 'egp' } }))).toEqual({
|
|
34
|
+
type: 'team',
|
|
35
|
+
teamName: 'egp',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('falls back to workspace scope', () => {
|
|
40
|
+
expect(getRegistryScope(ctx({}))).toEqual({ type: 'workspace', workspaceRoot: '/repo' });
|
|
41
|
+
expect(scopeLabel({ type: 'workspace', workspaceRoot: '/repo' })).toBe('workspace /repo');
|
|
42
|
+
expect(scopeLabel({ type: 'team', teamName: 'egp' })).toBe('team "egp"');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('converts pane entries into registrations', () => {
|
|
46
|
+
expect(
|
|
47
|
+
registrationFromEntry('codex', {
|
|
48
|
+
pane: '%1',
|
|
49
|
+
remark: 'review',
|
|
50
|
+
preamble: 'Be strict',
|
|
51
|
+
deny: ['x'],
|
|
52
|
+
})
|
|
53
|
+
).toEqual({
|
|
54
|
+
name: 'codex',
|
|
55
|
+
remark: 'review',
|
|
56
|
+
preamble: 'Be strict',
|
|
57
|
+
deny: ['x'],
|
|
58
|
+
});
|
|
59
|
+
expect(registrationFromEntry('codex')).toEqual({ name: 'codex' });
|
|
60
|
+
});
|
|
61
|
+
});
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// Registry helpers for workspace/team scoped agent metadata
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
import type { AgentRegistration, Context, PaneEntry, RegistryScope } from './types.js';
|
|
6
|
+
|
|
7
|
+
export function getRegistryScope(ctx: Context): RegistryScope {
|
|
8
|
+
if (ctx.registryScope) return ctx.registryScope;
|
|
9
|
+
if (ctx.flags.team) {
|
|
10
|
+
return { type: 'team', teamName: ctx.flags.team };
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
type: 'workspace',
|
|
14
|
+
workspaceRoot: ctx.paths.workspaceRoot ?? process.cwd(),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function scopeLabel(scope: RegistryScope): string {
|
|
19
|
+
return scope.type === 'team' ? `team "${scope.teamName}"` : `workspace ${scope.workspaceRoot}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function registrationFromEntry(name: string, entry?: PaneEntry): AgentRegistration {
|
|
23
|
+
return {
|
|
24
|
+
name,
|
|
25
|
+
...(entry?.remark !== undefined && { remark: entry.remark }),
|
|
26
|
+
...(entry?.preamble !== undefined && { preamble: entry.preamble }),
|
|
27
|
+
...(entry?.deny !== undefined && { deny: entry.deny }),
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/tmux.test.ts
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
// ─────────────────────────────────────────────────────────────
|
|
4
4
|
|
|
5
5
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
6
|
-
import { execSync } from 'child_process';
|
|
6
|
+
import { execFileSync, execSync } from 'child_process';
|
|
7
7
|
import { createTmux } from './tmux.js';
|
|
8
8
|
|
|
9
9
|
// Mock child_process
|
|
10
10
|
vi.mock('child_process', () => ({
|
|
11
11
|
execSync: vi.fn(),
|
|
12
|
+
execFileSync: vi.fn(),
|
|
12
13
|
}));
|
|
13
14
|
|
|
14
15
|
const mockedExecSync = vi.mocked(execSync);
|
|
16
|
+
const mockedExecFileSync = vi.mocked(execFileSync);
|
|
15
17
|
|
|
16
18
|
describe('createTmux', () => {
|
|
17
19
|
beforeEach(() => {
|
|
@@ -192,6 +194,56 @@ describe('createTmux', () => {
|
|
|
192
194
|
{ id: '%2', command: 'codex', suggestedName: 'codex' },
|
|
193
195
|
]);
|
|
194
196
|
});
|
|
197
|
+
|
|
198
|
+
it('ignores invalid metadata and duplicate pane IDs', () => {
|
|
199
|
+
mockedExecSync.mockReturnValue('%1\tcodex\tbad-json\n%1\tcodex\tbad-json\n%2\tzsh\t{}\n');
|
|
200
|
+
const tmux = createTmux();
|
|
201
|
+
expect(tmux.listPanes()).toEqual([
|
|
202
|
+
{ id: '%1', command: 'codex', suggestedName: 'codex' },
|
|
203
|
+
{ id: '%2', command: 'zsh', suggestedName: null },
|
|
204
|
+
]);
|
|
205
|
+
expect(tmux.getAgentRegistry({ type: 'workspace', workspaceRoot: '/repo' })).toEqual({
|
|
206
|
+
paneRegistry: {},
|
|
207
|
+
agents: {},
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('parses tmux-team pane metadata', () => {
|
|
212
|
+
mockedExecSync.mockReturnValue(
|
|
213
|
+
'%1\tcodex\t{"version":1,"workspaces":{"/repo":{"name":"codex","remark":"review"}}}\n'
|
|
214
|
+
);
|
|
215
|
+
const tmux = createTmux();
|
|
216
|
+
expect(tmux.getAgentRegistry({ type: 'workspace', workspaceRoot: '/repo' })).toEqual({
|
|
217
|
+
paneRegistry: { codex: { pane: '%1', remark: 'review' } },
|
|
218
|
+
agents: {},
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('parses pane target and cwd from modern list-panes output', () => {
|
|
223
|
+
mockedExecSync.mockReturnValue('%1\tmain:2.0\t/repo\tcodex\t{"version":1}\n');
|
|
224
|
+
const tmux = createTmux();
|
|
225
|
+
expect(tmux.listPanes()).toEqual([
|
|
226
|
+
{
|
|
227
|
+
id: '%1',
|
|
228
|
+
target: 'main:2.0',
|
|
229
|
+
cwd: '/repo',
|
|
230
|
+
command: 'codex',
|
|
231
|
+
suggestedName: 'codex',
|
|
232
|
+
metadata: { version: 1 },
|
|
233
|
+
},
|
|
234
|
+
]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('builds team registries and agent config from metadata', () => {
|
|
238
|
+
mockedExecSync.mockReturnValue(
|
|
239
|
+
'%1\tcodex\t{"version":1,"teams":{"egp":{"name":"codex","preamble":"Be strict","deny":["x"]}}}\n'
|
|
240
|
+
);
|
|
241
|
+
const tmux = createTmux();
|
|
242
|
+
expect(tmux.getAgentRegistry({ type: 'team', teamName: 'egp' })).toEqual({
|
|
243
|
+
paneRegistry: { codex: { pane: '%1', preamble: 'Be strict', deny: ['x'] } },
|
|
244
|
+
agents: { codex: { preamble: 'Be strict', deny: ['x'] } },
|
|
245
|
+
});
|
|
246
|
+
});
|
|
195
247
|
});
|
|
196
248
|
|
|
197
249
|
describe('getCurrentPaneId', () => {
|
|
@@ -233,6 +285,143 @@ describe('createTmux', () => {
|
|
|
233
285
|
});
|
|
234
286
|
});
|
|
235
287
|
|
|
288
|
+
describe('metadata registry writes', () => {
|
|
289
|
+
it('resolves pane targets to canonical pane IDs', () => {
|
|
290
|
+
mockedExecFileSync.mockReturnValue('%9\n');
|
|
291
|
+
const tmux = createTmux();
|
|
292
|
+
expect(tmux.resolvePaneTarget('1.2')).toBe('%9');
|
|
293
|
+
expect(mockedExecFileSync).toHaveBeenCalledWith(
|
|
294
|
+
'tmux',
|
|
295
|
+
['display-message', '-p', '-t', '1.2', '#{pane_id}'],
|
|
296
|
+
expect.any(Object)
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('sets workspace registration on pane metadata', () => {
|
|
301
|
+
mockedExecFileSync.mockReturnValueOnce('');
|
|
302
|
+
const tmux = createTmux();
|
|
303
|
+
tmux.setAgentRegistration(
|
|
304
|
+
'%9',
|
|
305
|
+
{ type: 'workspace', workspaceRoot: '/repo' },
|
|
306
|
+
{ name: 'codex', preamble: 'Be strict' }
|
|
307
|
+
);
|
|
308
|
+
expect(mockedExecFileSync).toHaveBeenLastCalledWith(
|
|
309
|
+
'tmux',
|
|
310
|
+
[
|
|
311
|
+
'set-option',
|
|
312
|
+
'-p',
|
|
313
|
+
'-t',
|
|
314
|
+
'%9',
|
|
315
|
+
'@tmux-team.agent',
|
|
316
|
+
'{"version":1,"workspaces":{"/repo":{"name":"codex","preamble":"Be strict"}}}',
|
|
317
|
+
],
|
|
318
|
+
expect.any(Object)
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('sets team registration while preserving existing metadata', () => {
|
|
323
|
+
mockedExecFileSync.mockReturnValueOnce(
|
|
324
|
+
'{"version":1,"workspaces":{"/repo":{"name":"codex"}}}\n'
|
|
325
|
+
);
|
|
326
|
+
const tmux = createTmux();
|
|
327
|
+
tmux.setAgentRegistration('%9', { type: 'team', teamName: 'egp' }, { name: 'reviewer' });
|
|
328
|
+
expect(mockedExecFileSync).toHaveBeenLastCalledWith(
|
|
329
|
+
'tmux',
|
|
330
|
+
[
|
|
331
|
+
'set-option',
|
|
332
|
+
'-p',
|
|
333
|
+
'-t',
|
|
334
|
+
'%9',
|
|
335
|
+
'@tmux-team.agent',
|
|
336
|
+
'{"version":1,"workspaces":{"/repo":{"name":"codex"}},"teams":{"egp":{"name":"reviewer"}}}',
|
|
337
|
+
],
|
|
338
|
+
expect.any(Object)
|
|
339
|
+
);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('clears scoped registration and unsets empty metadata', () => {
|
|
343
|
+
mockedExecSync.mockReturnValue(
|
|
344
|
+
'%1\tcodex\t{"version":1,"workspaces":{"/repo":{"name":"codex"}}}\n'
|
|
345
|
+
);
|
|
346
|
+
const tmux = createTmux();
|
|
347
|
+
expect(
|
|
348
|
+
tmux.clearAgentRegistration('codex', { type: 'workspace', workspaceRoot: '/repo' })
|
|
349
|
+
).toBe(true);
|
|
350
|
+
expect(mockedExecFileSync).toHaveBeenCalledWith(
|
|
351
|
+
'tmux',
|
|
352
|
+
['set-option', '-p', '-u', '-t', '%1', '@tmux-team.agent'],
|
|
353
|
+
expect.any(Object)
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('returns false when clearing a missing registration', () => {
|
|
358
|
+
mockedExecSync.mockReturnValue(
|
|
359
|
+
'%1\tcodex\t{"version":1,"workspaces":{"/repo":{"name":"codex"}}}\n'
|
|
360
|
+
);
|
|
361
|
+
const tmux = createTmux();
|
|
362
|
+
expect(
|
|
363
|
+
tmux.clearAgentRegistration('claude', { type: 'workspace', workspaceRoot: '/repo' })
|
|
364
|
+
).toBe(false);
|
|
365
|
+
expect(mockedExecFileSync).not.toHaveBeenCalled();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('lists teams from pane metadata', () => {
|
|
369
|
+
mockedExecSync.mockReturnValue(
|
|
370
|
+
'%1\tcodex\t{"version":1,"teams":{"egp":{"name":"codex"},"checkout":{"name":"claude"}}}\n'
|
|
371
|
+
);
|
|
372
|
+
const tmux = createTmux();
|
|
373
|
+
expect(tmux.listTeams()).toEqual({ checkout: ['claude'], egp: ['codex'] });
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('lists pane team and workspace details', () => {
|
|
377
|
+
mockedExecSync.mockReturnValue(
|
|
378
|
+
'%1\tmain:1.0\t/repo\tclaude\t{"version":1,"workspaces":{"/repo":{"name":"claude","remark":"lead"}},"teams":{"egp":{"name":"reviewer"}}}\n%2\tmain:1.1\t/tmp\tzsh\t\n'
|
|
379
|
+
);
|
|
380
|
+
const tmux = createTmux();
|
|
381
|
+
expect(tmux.listTeamPanes()).toEqual([
|
|
382
|
+
{
|
|
383
|
+
pane: '%1',
|
|
384
|
+
target: 'main:1.0',
|
|
385
|
+
cwd: '/repo',
|
|
386
|
+
command: 'claude',
|
|
387
|
+
suggestedName: 'claude',
|
|
388
|
+
registrations: [
|
|
389
|
+
{ scopeType: 'team', scope: 'egp', agent: 'reviewer' },
|
|
390
|
+
{ scopeType: 'workspace', scope: '/repo', agent: 'claude', remark: 'lead' },
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
pane: '%2',
|
|
395
|
+
target: 'main:1.1',
|
|
396
|
+
cwd: '/tmp',
|
|
397
|
+
command: 'zsh',
|
|
398
|
+
suggestedName: null,
|
|
399
|
+
registrations: [],
|
|
400
|
+
},
|
|
401
|
+
]);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('removes one team while preserving other registrations', () => {
|
|
405
|
+
mockedExecSync.mockReturnValue(
|
|
406
|
+
'%1\tcodex\t{"version":1,"teams":{"egp":{"name":"codex"},"checkout":{"name":"claude"}}}\n'
|
|
407
|
+
);
|
|
408
|
+
const tmux = createTmux();
|
|
409
|
+
expect(tmux.removeTeam('egp')).toEqual({ removed: 1, agents: ['codex'] });
|
|
410
|
+
expect(mockedExecFileSync).toHaveBeenCalledWith(
|
|
411
|
+
'tmux',
|
|
412
|
+
[
|
|
413
|
+
'set-option',
|
|
414
|
+
'-p',
|
|
415
|
+
'-t',
|
|
416
|
+
'%1',
|
|
417
|
+
'@tmux-team.agent',
|
|
418
|
+
'{"version":1,"teams":{"checkout":{"name":"claude"}}}',
|
|
419
|
+
],
|
|
420
|
+
expect.any(Object)
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
236
425
|
describe('pane ID handling', () => {
|
|
237
426
|
it('accepts window.pane format', () => {
|
|
238
427
|
mockedExecSync.mockReturnValue('');
|