tmux-ide 2.7.0 → 2.8.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 (101) hide show
  1. package/README.md +22 -5
  2. package/bin/cli.js +3532 -1090
  3. package/bin/cli.ts +368 -71
  4. package/package.json +2 -1
  5. package/packages/contracts/src/__tests__/control.test.ts +154 -0
  6. package/packages/contracts/src/control.ts +217 -0
  7. package/packages/contracts/src/index.ts +1 -0
  8. package/packages/daemon/dist/control/client.d.ts +23 -0
  9. package/packages/daemon/dist/control/client.js +105 -0
  10. package/packages/daemon/dist/control/dispatch.d.ts +34 -0
  11. package/packages/daemon/dist/control/dispatch.js +83 -0
  12. package/packages/daemon/dist/control/fanout.d.ts +19 -0
  13. package/packages/daemon/dist/control/fanout.js +37 -0
  14. package/packages/daemon/dist/control/frames.d.ts +23 -0
  15. package/packages/daemon/dist/control/frames.js +37 -0
  16. package/packages/daemon/dist/control/lifecycle.d.ts +45 -0
  17. package/packages/daemon/dist/control/lifecycle.js +114 -0
  18. package/packages/daemon/dist/control/server.d.ts +16 -0
  19. package/packages/daemon/dist/control/server.js +214 -0
  20. package/packages/daemon/dist/control/verbs.d.ts +11 -0
  21. package/packages/daemon/dist/control/verbs.js +91 -0
  22. package/packages/daemon/dist/doctor.d.ts +18 -0
  23. package/packages/daemon/dist/doctor.js +105 -15
  24. package/packages/daemon/dist/lib/agent-discovery.d.ts +27 -2
  25. package/packages/daemon/dist/lib/agent-discovery.js +29 -14
  26. package/packages/daemon/dist/lib/app-config.d.ts +106 -0
  27. package/packages/daemon/dist/lib/app-config.js +104 -5
  28. package/packages/daemon/dist/lib/manifest-pack.d.ts +79 -0
  29. package/packages/daemon/dist/lib/manifest-pack.js +232 -0
  30. package/packages/daemon/dist/lib/state-home.d.ts +2 -0
  31. package/packages/daemon/dist/lib/state-home.js +12 -0
  32. package/packages/daemon/dist/lib/update-check.js +5 -0
  33. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +34 -0
  34. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  35. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/PkgInfo +1 -0
  36. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
  37. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  38. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +139 -0
  39. package/packages/daemon/dist/restore.d.ts +35 -8
  40. package/packages/daemon/dist/restore.js +52 -15
  41. package/packages/daemon/dist/send.d.ts +33 -1
  42. package/packages/daemon/dist/send.js +32 -19
  43. package/packages/daemon/src/control/client.ts +128 -0
  44. package/packages/daemon/src/control/dispatch.ts +107 -0
  45. package/packages/daemon/src/control/fanout.ts +44 -0
  46. package/packages/daemon/src/control/frames.ts +40 -0
  47. package/packages/daemon/src/control/lifecycle.ts +151 -0
  48. package/packages/daemon/src/control/server.ts +237 -0
  49. package/packages/daemon/src/control/verbs.ts +118 -0
  50. package/packages/daemon/src/doctor.ts +113 -28
  51. package/packages/daemon/src/lib/agent-discovery.ts +53 -13
  52. package/packages/daemon/src/lib/app-config.ts +103 -5
  53. package/packages/daemon/src/lib/manifest-pack.ts +255 -0
  54. package/packages/daemon/src/lib/state-home.ts +13 -0
  55. package/packages/daemon/src/lib/update-check.ts +5 -0
  56. package/packages/daemon/src/restore.ts +53 -15
  57. package/packages/daemon/src/send.ts +55 -21
  58. package/packages/daemon/src/tui/chrome/events.ts +4 -4
  59. package/packages/daemon/src/tui/chrome/front-door.ts +39 -0
  60. package/packages/daemon/src/tui/chrome/notify-prefs.ts +58 -0
  61. package/packages/daemon/src/tui/chrome/notify-state.ts +76 -0
  62. package/packages/daemon/src/tui/chrome/notify.ts +582 -84
  63. package/packages/daemon/src/tui/chrome/updater.ts +268 -62
  64. package/packages/daemon/src/tui/detect/classify.ts +34 -0
  65. package/packages/daemon/src/tui/detect/manifest-loader.ts +54 -5
  66. package/packages/daemon/src/tui/detect/manifest.ts +24 -3
  67. package/packages/daemon/src/tui/detect/manifests.ts +240 -6
  68. package/packages/daemon/src/tui/detect/process-tree.ts +13 -3
  69. package/packages/daemon/src/tui/detect/session-id.ts +503 -0
  70. package/packages/daemon/src/tui/integrations/opencode.ts +121 -0
  71. package/packages/daemon/src/tui/mirror/agent-chip.ts +40 -11
  72. package/packages/daemon/src/tui/mirror/agent-lifecycle.ts +437 -0
  73. package/packages/daemon/src/tui/mirror/agent-rows.ts +27 -5
  74. package/packages/daemon/src/tui/mirror/app-state.ts +171 -8
  75. package/packages/daemon/src/tui/mirror/app.tsx +2182 -399
  76. package/packages/daemon/src/tui/mirror/attention.ts +110 -0
  77. package/packages/daemon/src/tui/mirror/dialog-stack.ts +17 -4
  78. package/packages/daemon/src/tui/mirror/diff-model.ts +279 -4
  79. package/packages/daemon/src/tui/mirror/file-tree.ts +231 -6
  80. package/packages/daemon/src/tui/mirror/host-terminal.ts +49 -0
  81. package/packages/daemon/src/tui/mirror/hosted.ts +205 -0
  82. package/packages/daemon/src/tui/mirror/layout-parse.ts +154 -0
  83. package/packages/daemon/src/tui/mirror/menu-model.ts +27 -4
  84. package/packages/daemon/src/tui/mirror/palette.ts +299 -9
  85. package/packages/daemon/src/tui/mirror/pane-mirror.ts +82 -4
  86. package/packages/daemon/src/tui/mirror/pane-surface.tsx +18 -11
  87. package/packages/daemon/src/tui/mirror/perf-tap.ts +29 -3
  88. package/packages/daemon/src/tui/mirror/selection.ts +122 -8
  89. package/packages/daemon/src/tui/mirror/session-mirror.ts +349 -68
  90. package/packages/daemon/src/tui/mirror/settings-model.ts +96 -16
  91. package/packages/daemon/src/tui/mirror/sidebar.tsx +218 -0
  92. package/packages/daemon/src/tui/mirror/size-truth.ts +53 -0
  93. package/packages/daemon/src/tui/mirror/theme.ts +45 -0
  94. package/packages/daemon/src/tui/team/fuzzy.ts +20 -0
  95. package/packages/daemon/src/tui/team/sessions.ts +85 -7
  96. package/packages/daemon/src/tui/team/wait.ts +144 -0
  97. package/scripts/build-macos-notifier.mjs +160 -0
  98. package/scripts/postinstall.js +8 -1
  99. package/scripts/prepublish-check.mjs +37 -1
  100. package/scripts/publish-tap.sh +55 -0
  101. package/skill/SKILL.md +88 -2
@@ -16,12 +16,16 @@
16
16
  * in its installed source (`io.py` confirm_ask, `waiting.py` spinner).
17
17
  * Each matcher cites the invariant it was built from ("seen: …").
18
18
  * - `conservative` — best-effort from public docs/common knowledge
19
- * (`opencode`, `gemini`, `copilot`, `cursor`, `goose`, `amp`). Every
20
- * matcher is HIGH-PRECISION (esc-to-interrupt / spinner / explicit y-n /
21
- * "Do you want") so it can never false-positive on a plain prompt, but it
22
- * may miss real states until a live capture upgrades it. `opencode` was
23
- * attempted live but its local auth DB errored and the TUI rendered blank,
24
- * so it stays conservative.
19
+ * (`opencode`, `gemini`, `copilot`, `cursor`, `goose`, `amp`, and the
20
+ * M25.4 breadth set: `devin`, `kimi`, `pi`, `grok`, `kiro`, `cline`,
21
+ * `droid`, `kilo`). Every matcher is HIGH-PRECISION (esc-to-interrupt /
22
+ * spinner / explicit y-n / "Do you want") so it can never false-positive
23
+ * on a plain prompt, but it may miss real states until a live capture
24
+ * upgrades it. `opencode` was attempted live but its local auth DB errored
25
+ * and the TUI rendered blank, so it stays conservative. Several M25.4
26
+ * manifests carry source/binary-VERBATIM markers (see each manifest's
27
+ * provenance comment) but stay conservative until a live capture confirms
28
+ * the surrounding chrome.
25
29
  *
26
30
  * Users can override any of these with a JSON file in
27
31
  * `~/.tmux-ide/agent-detection/` (see `manifest-loader.ts`).
@@ -317,6 +321,228 @@ const AMP: AgentManifest = {
317
321
  },
318
322
  };
319
323
 
324
+ const DEVIN: AgentManifest = {
325
+ id: "devin",
326
+ commands: ["devin"],
327
+ confidence: "conservative",
328
+ states: {
329
+ // conservative — Devin CLI (Cognition; closed-source native Rust binary,
330
+ // needs an account). Blocked markers are VERBATIM from docs.devin.ai
331
+ // (cli/changelog + essential-commands via llms-full.txt): the plan-mode
332
+ // approval menu and the always-allow option. Devin has NO verified working
333
+ // text (its spinner is unlabeled; docs say interrupt is Ctrl+C, not esc),
334
+ // so working keeps only the shared spinner probe.
335
+ blocked: {
336
+ any: [
337
+ // seen (docs): "Yes, implement plan and accept edits" / "…and bypass
338
+ // permissions" — the shared prefix covers both.
339
+ { region: "bottom", contains: "Yes, implement plan and" },
340
+ { region: "bottom", contains: "No, plan needs changes" },
341
+ { region: "bottom", contains: "Yes, always allow" },
342
+ ],
343
+ },
344
+ working: {
345
+ any: [
346
+ { region: "bottom", regex: BRAILLE_SPINNER },
347
+ { region: "title", regex: BRAILLE_SPINNER },
348
+ ],
349
+ },
350
+ },
351
+ };
352
+
353
+ const KIMI: AgentManifest = {
354
+ id: "kimi",
355
+ commands: ["kimi"],
356
+ confidence: "conservative",
357
+ states: {
358
+ // conservative — kimi CLI (MoonshotAI/kimi-cli, Python/prompt_toolkit).
359
+ // Blocked markers are VERBATIM from its source (_approval_panel.py): the
360
+ // approval panel's "<tool> is requesting approval to …" header and its
361
+ // option labels. Its working spinner is a text-less moon animation (not
362
+ // braille), so working may under-detect until tuned — the braille probe is
363
+ // kept only as the shared fallback. ("Thinking"/"Thought for" deliberately
364
+ // NOT matched: they persist in the transcript after the turn.)
365
+ blocked: {
366
+ any: [
367
+ { region: "bottom", contains: " is requesting approval to " },
368
+ { region: "bottom", contains: "Approve for this session" },
369
+ { region: "bottom", contains: "Reject, tell the model" },
370
+ ],
371
+ },
372
+ working: {
373
+ any: [
374
+ { region: "bottom", regex: BRAILLE_SPINNER },
375
+ { region: "title", regex: BRAILLE_SPINNER },
376
+ ],
377
+ },
378
+ },
379
+ };
380
+
381
+ const PI: AgentManifest = {
382
+ id: "pi",
383
+ commands: ["pi"],
384
+ confidence: "conservative",
385
+ states: {
386
+ // conservative — pi (@mariozechner/pi-coding-agent, node). Working marker
387
+ // is VERBATIM from its source (interactive-mode.ts): the status line is
388
+ // `Working... (esc to interrupt)`, with `(esc to cancel)` retry/compacting
389
+ // variants. Its core has NO distinctive approval wording (generic Yes/No
390
+ // via extensions), so blocked is deliberately ABSENT — authority
391
+ // self-reporting or the classifier's fallback carry it.
392
+ working: {
393
+ any: [
394
+ { region: "bottom", contains: "Working... (esc to interrupt)" },
395
+ { region: "bottom", contains: "(esc to cancel)" },
396
+ { region: "bottom", regex: BRAILLE_SPINNER },
397
+ { region: "title", regex: BRAILLE_SPINNER },
398
+ ],
399
+ },
400
+ },
401
+ };
402
+
403
+ const GROK: AgentManifest = {
404
+ id: "grok",
405
+ commands: ["grok"],
406
+ confidence: "conservative",
407
+ states: {
408
+ // conservative — the community grok CLI (@vibe-kit/grok-cli /
409
+ // superagent-ai/grok-cli, node/OpenTUI). Working markers are VERBATIM from
410
+ // its source (app.tsx / headless output.ts): the mid-turn placeholder
411
+ // carries "(esc to interrupt)" and headless prints "⏳ Processing...".
412
+ // Its ConfirmationDialog wording could not be verified, so blocked keeps
413
+ // only a high-precision generic. NOTE: xAI's separate official
414
+ // `grok-build` binary substring-matches this manifest but has no verified
415
+ // strings — it will simply read idle until evidence exists.
416
+ blocked: {
417
+ any: [{ region: "bottom", contains: "(y/n)", caseInsensitive: true }],
418
+ },
419
+ working: {
420
+ any: [
421
+ { region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
422
+ { region: "bottom", contains: "⏳ Processing" },
423
+ { region: "bottom", regex: BRAILLE_SPINNER },
424
+ { region: "title", regex: BRAILLE_SPINNER },
425
+ ],
426
+ },
427
+ },
428
+ };
429
+
430
+ const KIRO: AgentManifest = {
431
+ id: "kiro",
432
+ commands: ["kiro-cli", "kiro"],
433
+ confidence: "conservative",
434
+ states: {
435
+ // conservative — Kiro CLI (AWS; the rebranded Amazon Q Developer CLI, a
436
+ // native Rust binary). Markers are VERBATIM from the open-source
437
+ // predecessor (aws/amazon-q-developer-cli chat-cli/src/cli/chat/mod.rs):
438
+ // the tool-approval question and the Spinners::Dots "Thinking..." status.
439
+ // The closed Kiro build may drift — same lineage, pending live capture.
440
+ // NOTE: the predecessor's 1-char `q` binary is deliberately NOT a command
441
+ // token (a single letter substring-matches far too much).
442
+ blocked: {
443
+ any: [
444
+ { region: "bottom", contains: "Allow this action?" },
445
+ { region: "bottom", contains: "to trust (always allow) this tool" },
446
+ ],
447
+ },
448
+ working: {
449
+ any: [
450
+ { region: "bottom", contains: "Thinking..." },
451
+ { region: "bottom", regex: BRAILLE_SPINNER },
452
+ { region: "title", regex: BRAILLE_SPINNER },
453
+ ],
454
+ },
455
+ },
456
+ };
457
+
458
+ const CLINE: AgentManifest = {
459
+ id: "cline",
460
+ commands: ["cline"],
461
+ confidence: "conservative",
462
+ states: {
463
+ // conservative — cline CLI (cline.bot; CLI 2.0 ships prebuilt platform
464
+ // binaries, OpenTUI). Markers are VERBATIM from its source: the approval
465
+ // dialog's "Cline needs permission" title + "Approve tool call?" header
466
+ // (tui/components/dialogs/tool-approval.tsx) and the streaming
467
+ // "Thinking... (esc to cancel)" status (chat-message-list.tsx).
468
+ blocked: {
469
+ any: [
470
+ { region: "bottom", contains: "Cline needs permission" },
471
+ { region: "bottom", contains: "Approve tool call?" },
472
+ { region: "bottom", contains: "[y] Approve" },
473
+ ],
474
+ },
475
+ working: {
476
+ any: [
477
+ { region: "bottom", contains: "esc to cancel", caseInsensitive: true },
478
+ { region: "bottom", regex: BRAILLE_SPINNER },
479
+ { region: "title", regex: BRAILLE_SPINNER },
480
+ ],
481
+ },
482
+ },
483
+ };
484
+
485
+ const DROID: AgentManifest = {
486
+ id: "droid",
487
+ commands: ["droid"],
488
+ confidence: "conservative",
489
+ states: {
490
+ // conservative — droid (Factory CLI) was launched live (v0.114.1) but sits
491
+ // on an auth-gated login menu without a Factory account, so only the login
492
+ // splash could be captured (it reads IDLE and is deliberately not matched:
493
+ // "Please login with your Factory account to continue." / "> Login").
494
+ // Verified live: droid runs as its OWN process (`pane_current_command` =
495
+ // "droid"), so command matching needs no process-tree help.
496
+ // Blocked markers are docs-derived (docs.factory.ai auto-run levels,
497
+ // corroborated third-party walkthrough) — the Spec-mode proceed menu names
498
+ // its autonomy levels verbatim. Pending live-capture confirmation.
499
+ blocked: {
500
+ any: [
501
+ { region: "bottom", contains: "Proceed, manual approval" },
502
+ { region: "bottom", contains: "Proceed, allow safe commands" },
503
+ { region: "bottom", contains: "Proceed, allow all commands" },
504
+ ],
505
+ },
506
+ working: {
507
+ any: [
508
+ { region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
509
+ { region: "bottom", regex: BRAILLE_SPINNER },
510
+ { region: "title", regex: BRAILLE_SPINNER },
511
+ ],
512
+ },
513
+ },
514
+ };
515
+
516
+ const KILO: AgentManifest = {
517
+ id: "kilo",
518
+ commands: ["kilo", ".kilo", "kilocode"],
519
+ confidence: "conservative",
520
+ states: {
521
+ // conservative, but the blocked markers are VERBATIM from the shipped
522
+ // @kilocode/cli binary (v7.4.5, strings-extracted — no account to drive a
523
+ // live capture): the permission dialog renders a "△ Permission required"
524
+ // header over an "Allow once" / "Allow always" / "Reject" option list.
525
+ // NOTE: the CLI's npm launcher (`bin/kilo`, node) execs the platform
526
+ // binary installed as `bin/.kilo`, hence the ".kilo" command token.
527
+ blocked: {
528
+ any: [
529
+ { region: "bottom", contains: "Permission required" },
530
+ { region: "bottom", contains: "Allow once" },
531
+ { region: "bottom", contains: "Allow always" },
532
+ ],
533
+ },
534
+ // No working-state string was found in the binary (its status UI renders
535
+ // from dynamic parts) — only the shared spinner probe, so kilo may read
536
+ // idle while working until a live capture tunes it.
537
+ working: {
538
+ any: [
539
+ { region: "bottom", regex: BRAILLE_SPINNER },
540
+ { region: "title", regex: BRAILLE_SPINNER },
541
+ ],
542
+ },
543
+ },
544
+ };
545
+
320
546
  const SHELL: AgentManifest = {
321
547
  id: "shell",
322
548
  commands: ["bash", "zsh", "sh", "fish", "nu"],
@@ -350,5 +576,13 @@ export const BUNDLED_MANIFESTS: AgentManifest[] = [
350
576
  CURSOR,
351
577
  GOOSE,
352
578
  AMP,
579
+ DEVIN,
580
+ KIMI,
581
+ PI,
582
+ GROK,
583
+ KIRO,
584
+ CLINE,
585
+ DROID,
586
+ KILO,
353
587
  SHELL,
354
588
  ];
@@ -53,6 +53,16 @@ export function parsePsOutput(raw: string): ProcEntry[] {
53
53
  * depth are guarded.
54
54
  */
55
55
  export function subtreeCommands(entries: ProcEntry[], rootPid: number, maxDepth = 6): string[] {
56
+ return subtreeEntries(entries, rootPid, maxDepth).map((entry) => entry.command);
57
+ }
58
+
59
+ /**
60
+ * The full {@link ProcEntry} rows of the subtree rooted at `rootPid`, in the
61
+ * same deepest-first order as {@link subtreeCommands}. Callers that need PIDs
62
+ * (e.g. the session-id capture probing a pane's agent process by open files)
63
+ * use this; the command-only view above stays the common path.
64
+ */
65
+ export function subtreeEntries(entries: ProcEntry[], rootPid: number, maxDepth = 6): ProcEntry[] {
56
66
  const childrenByParent = new Map<number, ProcEntry[]>();
57
67
  const byPid = new Map<number, ProcEntry>();
58
68
  for (const entry of entries) {
@@ -65,7 +75,7 @@ export function subtreeCommands(entries: ProcEntry[], rootPid: number, maxDepth
65
75
  const root = byPid.get(rootPid);
66
76
  if (!root) return [];
67
77
 
68
- const commands: string[] = [];
78
+ const out: ProcEntry[] = [];
69
79
  const visited = new Set<number>();
70
80
 
71
81
  const walk = (pid: number, depth: number): void => {
@@ -76,11 +86,11 @@ export function subtreeCommands(entries: ProcEntry[], rootPid: number, maxDepth
76
86
  walk(child.pid, depth + 1);
77
87
  }
78
88
  const self = byPid.get(pid);
79
- if (self) commands.push(self.command);
89
+ if (self) out.push(self);
80
90
  };
81
91
 
82
92
  walk(rootPid, 0);
83
- return commands;
93
+ return out;
84
94
  }
85
95
 
86
96
  /**