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.
@@ -43,6 +43,25 @@ function createMockTmux(): Tmux & {
43
43
  getCurrentPaneId() {
44
44
  return null;
45
45
  },
46
+ resolvePaneTarget(target: string) {
47
+ return target;
48
+ },
49
+ getAgentRegistry() {
50
+ return { paneRegistry: {}, agents: {} };
51
+ },
52
+ setAgentRegistration() {},
53
+ clearAgentRegistration() {
54
+ return false;
55
+ },
56
+ listTeams() {
57
+ return {};
58
+ },
59
+ listTeamPanes() {
60
+ return [];
61
+ },
62
+ removeTeam() {
63
+ return { removed: 0, agents: [] };
64
+ },
46
65
  };
47
66
  return mock;
48
67
  }
@@ -239,7 +258,8 @@ describe('buildMessage (via cmdTalk)', () => {
239
258
  pollInterval: 0.1,
240
259
  captureLines: 100,
241
260
  maxCaptureLines: 2000,
242
- preambleEvery: 3, pasteEnterDelayMs: 500,
261
+ preambleEvery: 3,
262
+ pasteEnterDelayMs: 500,
243
263
  },
244
264
  };
245
265
 
@@ -448,6 +468,27 @@ describe('cmdTalk - basic send', () => {
448
468
  expect(ui.errors[0]).toContain("Agent 'unknown' not found");
449
469
  });
450
470
 
471
+ it('requires explicit team when a missing agent appears in shared teams', async () => {
472
+ const tmux = createMockTmux();
473
+ vi.spyOn(tmux, 'listTeams').mockReturnValue({
474
+ frontend: ['codex'],
475
+ release: ['codex'],
476
+ });
477
+ const ui = createMockUI();
478
+ const ctx = createContext({
479
+ tmux,
480
+ ui,
481
+ paths: createTestPaths(testDir),
482
+ config: { paneRegistry: {} },
483
+ });
484
+
485
+ await expect(cmdTalk(ctx, 'codex', 'Hello')).rejects.toThrow('exit(3)');
486
+
487
+ expect(ui.errors).toEqual([
488
+ "Agent 'codex' is in multiple shared teams: frontend, release. Specify one with --team <team>.",
489
+ ]);
490
+ });
491
+
451
492
  it('outputs JSON when --json flag is set', async () => {
452
493
  const tmux = createMockTmux();
453
494
  const ui = createMockUI();
@@ -551,7 +592,8 @@ describe('cmdTalk - --wait mode', () => {
551
592
  pollInterval: 0.01,
552
593
  captureLines: 100,
553
594
  maxCaptureLines: 2000,
554
- preambleEvery: 3, pasteEnterDelayMs: 500,
595
+ preambleEvery: 3,
596
+ pasteEnterDelayMs: 500,
555
597
  },
556
598
  },
557
599
  });
@@ -595,7 +637,8 @@ describe('cmdTalk - --wait mode', () => {
595
637
  pollInterval: 0.01,
596
638
  captureLines: 100,
597
639
  maxCaptureLines: 2000,
598
- preambleEvery: 3, pasteEnterDelayMs: 500,
640
+ preambleEvery: 3,
641
+ pasteEnterDelayMs: 500,
599
642
  },
600
643
  },
601
644
  });
@@ -626,7 +669,8 @@ describe('cmdTalk - --wait mode', () => {
626
669
  pollInterval: 0.02,
627
670
  captureLines: 100,
628
671
  maxCaptureLines: 2000,
629
- preambleEvery: 3, pasteEnterDelayMs: 500,
672
+ preambleEvery: 3,
673
+ pasteEnterDelayMs: 500,
630
674
  },
631
675
  },
632
676
  });
@@ -674,7 +718,8 @@ describe('cmdTalk - --wait mode', () => {
674
718
  pollInterval: 0.01,
675
719
  captureLines: 100,
676
720
  maxCaptureLines: 2000,
677
- preambleEvery: 3, pasteEnterDelayMs: 500,
721
+ preambleEvery: 3,
722
+ pasteEnterDelayMs: 500,
678
723
  },
679
724
  },
680
725
  });
@@ -710,7 +755,8 @@ describe('cmdTalk - --wait mode', () => {
710
755
  pollInterval: 0.01,
711
756
  captureLines: 100,
712
757
  maxCaptureLines: 2000,
713
- preambleEvery: 3, pasteEnterDelayMs: 500,
758
+ preambleEvery: 3,
759
+ pasteEnterDelayMs: 500,
714
760
  },
715
761
  },
716
762
  });
@@ -739,7 +785,8 @@ describe('cmdTalk - --wait mode', () => {
739
785
  pollInterval: 0.01,
740
786
  captureLines: 100,
741
787
  maxCaptureLines: 2000,
742
- preambleEvery: 3, pasteEnterDelayMs: 500,
788
+ preambleEvery: 3,
789
+ pasteEnterDelayMs: 500,
743
790
  },
744
791
  },
745
792
  });
@@ -791,7 +838,8 @@ describe('cmdTalk - --wait mode', () => {
791
838
  pollInterval: 0.05,
792
839
  captureLines: 100,
793
840
  maxCaptureLines: 2000,
794
- preambleEvery: 3, pasteEnterDelayMs: 500,
841
+ preambleEvery: 3,
842
+ pasteEnterDelayMs: 500,
795
843
  },
796
844
  paneRegistry: {
797
845
  codex: { pane: '10.1' },
@@ -837,7 +885,8 @@ describe('cmdTalk - --wait mode', () => {
837
885
  pollInterval: 0.02,
838
886
  captureLines: 100,
839
887
  maxCaptureLines: 2000,
840
- preambleEvery: 3, pasteEnterDelayMs: 500,
888
+ preambleEvery: 3,
889
+ pasteEnterDelayMs: 500,
841
890
  },
842
891
  paneRegistry: {
843
892
  codex: { pane: '10.1' },
@@ -892,7 +941,8 @@ describe('cmdTalk - --wait mode', () => {
892
941
  pollInterval: 0.02,
893
942
  captureLines: 100,
894
943
  maxCaptureLines: 2000,
895
- preambleEvery: 3, pasteEnterDelayMs: 500,
944
+ preambleEvery: 3,
945
+ pasteEnterDelayMs: 500,
896
946
  },
897
947
  paneRegistry: {
898
948
  codex: { pane: '10.1' },
@@ -1003,7 +1053,8 @@ describe('cmdTalk - nonce collision handling', () => {
1003
1053
  pollInterval: 0.01,
1004
1054
  captureLines: 100,
1005
1055
  maxCaptureLines: 2000,
1006
- preambleEvery: 3, pasteEnterDelayMs: 500,
1056
+ preambleEvery: 3,
1057
+ pasteEnterDelayMs: 500,
1007
1058
  },
1008
1059
  },
1009
1060
  });
@@ -1058,7 +1109,8 @@ describe('cmdTalk - JSON output contract', () => {
1058
1109
  pollInterval: 0.01,
1059
1110
  captureLines: 100,
1060
1111
  maxCaptureLines: 2000,
1061
- preambleEvery: 3, pasteEnterDelayMs: 500,
1112
+ preambleEvery: 3,
1113
+ pasteEnterDelayMs: 500,
1062
1114
  },
1063
1115
  },
1064
1116
  });
@@ -1099,7 +1151,8 @@ describe('cmdTalk - JSON output contract', () => {
1099
1151
  pollInterval: 0.01,
1100
1152
  captureLines: 100,
1101
1153
  maxCaptureLines: 2000,
1102
- preambleEvery: 3, pasteEnterDelayMs: 500,
1154
+ preambleEvery: 3,
1155
+ pasteEnterDelayMs: 500,
1103
1156
  },
1104
1157
  },
1105
1158
  });
@@ -1141,7 +1194,8 @@ describe('cmdTalk - JSON output contract', () => {
1141
1194
  pollInterval: 0.01,
1142
1195
  captureLines: 100,
1143
1196
  maxCaptureLines: 2000,
1144
- preambleEvery: 3, pasteEnterDelayMs: 500,
1197
+ preambleEvery: 3,
1198
+ pasteEnterDelayMs: 500,
1145
1199
  },
1146
1200
  },
1147
1201
  });
@@ -1178,7 +1232,8 @@ describe('cmdTalk - JSON output contract', () => {
1178
1232
  pollInterval: 0.01,
1179
1233
  captureLines: 100,
1180
1234
  maxCaptureLines: 2000,
1181
- preambleEvery: 3, pasteEnterDelayMs: 500,
1235
+ preambleEvery: 3,
1236
+ pasteEnterDelayMs: 500,
1182
1237
  },
1183
1238
  },
1184
1239
  });
@@ -1228,7 +1283,8 @@ describe('cmdTalk - JSON output contract', () => {
1228
1283
  pollInterval: 0.02,
1229
1284
  captureLines: 100,
1230
1285
  maxCaptureLines: 2000,
1231
- preambleEvery: 3, pasteEnterDelayMs: 500,
1286
+ preambleEvery: 3,
1287
+ pasteEnterDelayMs: 500,
1232
1288
  },
1233
1289
  paneRegistry: {
1234
1290
  codex: { pane: '10.1' },
@@ -1310,7 +1366,16 @@ describe('cmdTalk - end marker detection', () => {
1310
1366
  ui,
1311
1367
  paths: createTestPaths(testDir),
1312
1368
  flags: { wait: true, json: true, timeout: 0.5 },
1313
- config: { defaults: { timeout: 0.5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3, pasteEnterDelayMs: 500 } },
1369
+ config: {
1370
+ defaults: {
1371
+ timeout: 0.5,
1372
+ pollInterval: 0.01,
1373
+ captureLines: 100,
1374
+ maxCaptureLines: 2000,
1375
+ preambleEvery: 3,
1376
+ pasteEnterDelayMs: 500,
1377
+ },
1378
+ },
1314
1379
  });
1315
1380
 
1316
1381
  await cmdTalk(ctx, 'claude', 'Test message');
@@ -1344,7 +1409,16 @@ describe('cmdTalk - end marker detection', () => {
1344
1409
  ui,
1345
1410
  paths: createTestPaths(testDir),
1346
1411
  flags: { wait: true, json: true, timeout: 0.5 },
1347
- config: { defaults: { timeout: 0.5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3, pasteEnterDelayMs: 500 } },
1412
+ config: {
1413
+ defaults: {
1414
+ timeout: 0.5,
1415
+ pollInterval: 0.01,
1416
+ captureLines: 100,
1417
+ maxCaptureLines: 2000,
1418
+ preambleEvery: 3,
1419
+ pasteEnterDelayMs: 500,
1420
+ },
1421
+ },
1348
1422
  });
1349
1423
 
1350
1424
  await cmdTalk(ctx, 'claude', 'Test');
@@ -1378,7 +1452,16 @@ Line 4 final`;
1378
1452
  ui,
1379
1453
  paths: createTestPaths(testDir),
1380
1454
  flags: { wait: true, json: true, timeout: 0.5 },
1381
- config: { defaults: { timeout: 0.5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3, pasteEnterDelayMs: 500 } },
1455
+ config: {
1456
+ defaults: {
1457
+ timeout: 0.5,
1458
+ pollInterval: 0.01,
1459
+ captureLines: 100,
1460
+ maxCaptureLines: 2000,
1461
+ preambleEvery: 3,
1462
+ pasteEnterDelayMs: 500,
1463
+ },
1464
+ },
1382
1465
  });
1383
1466
 
1384
1467
  await cmdTalk(ctx, 'claude', 'Test');
@@ -1409,7 +1492,16 @@ Line 4 final`;
1409
1492
  ui,
1410
1493
  paths: createTestPaths(testDir),
1411
1494
  flags: { wait: true, json: true, timeout: 0.5 },
1412
- config: { defaults: { timeout: 0.5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3, pasteEnterDelayMs: 500 } },
1495
+ config: {
1496
+ defaults: {
1497
+ timeout: 0.5,
1498
+ pollInterval: 0.01,
1499
+ captureLines: 100,
1500
+ maxCaptureLines: 2000,
1501
+ preambleEvery: 3,
1502
+ pasteEnterDelayMs: 500,
1503
+ },
1504
+ },
1413
1505
  });
1414
1506
 
1415
1507
  await cmdTalk(ctx, 'claude', 'Test');
@@ -1446,7 +1538,16 @@ Line 4 final`;
1446
1538
  ui,
1447
1539
  paths: createTestPaths(testDir),
1448
1540
  flags: { wait: true, json: true, timeout: 0.5 },
1449
- config: { defaults: { timeout: 0.5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3, pasteEnterDelayMs: 500 } },
1541
+ config: {
1542
+ defaults: {
1543
+ timeout: 0.5,
1544
+ pollInterval: 0.01,
1545
+ captureLines: 100,
1546
+ maxCaptureLines: 2000,
1547
+ preambleEvery: 3,
1548
+ pasteEnterDelayMs: 500,
1549
+ },
1550
+ },
1450
1551
  });
1451
1552
 
1452
1553
  await cmdTalk(ctx, 'claude', 'Test');
@@ -1482,7 +1583,16 @@ Line 4 final`;
1482
1583
  ui,
1483
1584
  paths: createTestPaths(testDir),
1484
1585
  flags: { wait: true, json: true, timeout: 0.5 },
1485
- config: { defaults: { timeout: 0.5, pollInterval: 0.01, captureLines: 200, maxCaptureLines: 2000, preambleEvery: 3, pasteEnterDelayMs: 500 } },
1586
+ config: {
1587
+ defaults: {
1588
+ timeout: 0.5,
1589
+ pollInterval: 0.01,
1590
+ captureLines: 200,
1591
+ maxCaptureLines: 2000,
1592
+ preambleEvery: 3,
1593
+ pasteEnterDelayMs: 500,
1594
+ },
1595
+ },
1486
1596
  });
1487
1597
 
1488
1598
  await cmdTalk(ctx, 'claude', 'Test');
@@ -186,7 +186,10 @@ function extractWithExpandableCapture(
186
186
 
187
187
  if (instructionLineIndex !== -1 && instructionLineIndex < endMarkerLineIndex) {
188
188
  // Instruction visible: extract from after instruction to marker
189
- const response = lines.slice(instructionLineIndex + 1, endMarkerLineIndex).join('\n').trim();
189
+ const response = lines
190
+ .slice(instructionLineIndex + 1, endMarkerLineIndex)
191
+ .join('\n')
192
+ .trim();
190
193
  return { response, truncated: false };
191
194
  }
192
195
 
@@ -316,6 +319,21 @@ function buildMessage(message: string, agentName: string, ctx: Context): string
316
319
  return `[SYSTEM: ${preamble}]\n\n${message}`;
317
320
  }
318
321
 
322
+ function teamHintForMissingAgent(ctx: Context, target: string): string | null {
323
+ if (ctx.flags.team) return null;
324
+
325
+ const matches = Object.entries(ctx.tmux.listTeams())
326
+ .filter(([, agents]) => agents.includes(target))
327
+ .map(([teamName]) => teamName)
328
+ .sort();
329
+
330
+ if (matches.length === 0) return null;
331
+ if (matches.length === 1) {
332
+ return `Agent '${target}' is in shared team '${matches[0]}'. Specify it: tmt talk ${target} "..." --team ${matches[0]}`;
333
+ }
334
+ return `Agent '${target}' is in multiple shared teams: ${matches.join(', ')}. Specify one with --team <team>.`;
335
+ }
336
+
319
337
  export async function cmdTalk(ctx: Context, target: string, message: string): Promise<void> {
320
338
  const { ui, config, tmux, flags, exit } = ctx;
321
339
  const waitEnabled = Boolean(flags.wait) || config.mode === 'wait';
@@ -386,6 +404,11 @@ export async function cmdTalk(ctx: Context, target: string, message: string): Pr
386
404
  // Single agent
387
405
  if (!config.paneRegistry[target]) {
388
406
  const available = Object.keys(config.paneRegistry).join(', ');
407
+ const teamHint = teamHintForMissingAgent(ctx, target);
408
+ if (teamHint) {
409
+ ui.error(teamHint);
410
+ exit(ExitCodes.PANE_NOT_FOUND);
411
+ }
389
412
  ui.error(`Agent '${target}' not found. Available: ${available || 'none'}`);
390
413
  exit(ExitCodes.PANE_NOT_FOUND);
391
414
  }
@@ -619,7 +642,8 @@ export async function cmdTalk(ctx: Context, target: string, message: string): Pr
619
642
  );
620
643
 
621
644
  // Clean Gemini CLI UI artifacts
622
- const response = target === 'gemini' ? cleanGeminiResponse(extractedResponse) : extractedResponse;
645
+ const response =
646
+ target === 'gemini' ? cleanGeminiResponse(extractedResponse) : extractedResponse;
623
647
 
624
648
  if (!flags.json && isTTY) {
625
649
  process.stdout.write('\r' + ' '.repeat(80) + '\r');
@@ -883,7 +907,8 @@ async function cmdTalkAllWait(
883
907
  );
884
908
 
885
909
  // Clean Gemini CLI UI artifacts
886
- const response = state.agent === 'gemini' ? cleanGeminiResponse(extractedResponse) : extractedResponse;
910
+ const response =
911
+ state.agent === 'gemini' ? cleanGeminiResponse(extractedResponse) : extractedResponse;
887
912
  state.response = response;
888
913
  state.truncated = truncated;
889
914
  state.status = 'completed';