tmux-ide 2.6.1 → 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 (154) hide show
  1. package/README.md +36 -14
  2. package/bin/cli.js +4174 -1235
  3. package/bin/cli.ts +426 -72
  4. package/package.json +10 -6
  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/agent-explain.d.ts +8 -1
  9. package/packages/daemon/dist/agent-explain.js +19 -3
  10. package/packages/daemon/dist/control/client.d.ts +23 -0
  11. package/packages/daemon/dist/control/client.js +105 -0
  12. package/packages/daemon/dist/control/dispatch.d.ts +34 -0
  13. package/packages/daemon/dist/control/dispatch.js +83 -0
  14. package/packages/daemon/dist/control/fanout.d.ts +19 -0
  15. package/packages/daemon/dist/control/fanout.js +37 -0
  16. package/packages/daemon/dist/control/frames.d.ts +23 -0
  17. package/packages/daemon/dist/control/frames.js +37 -0
  18. package/packages/daemon/dist/control/lifecycle.d.ts +45 -0
  19. package/packages/daemon/dist/control/lifecycle.js +114 -0
  20. package/packages/daemon/dist/control/server.d.ts +16 -0
  21. package/packages/daemon/dist/control/server.js +214 -0
  22. package/packages/daemon/dist/control/verbs.d.ts +11 -0
  23. package/packages/daemon/dist/control/verbs.js +91 -0
  24. package/packages/daemon/dist/doctor.d.ts +18 -0
  25. package/packages/daemon/dist/doctor.js +105 -15
  26. package/packages/daemon/dist/lib/agent-discovery.d.ts +27 -2
  27. package/packages/daemon/dist/lib/agent-discovery.js +29 -14
  28. package/packages/daemon/dist/lib/app-config.d.ts +106 -0
  29. package/packages/daemon/dist/lib/app-config.js +104 -5
  30. package/packages/daemon/dist/lib/manifest-pack.d.ts +79 -0
  31. package/packages/daemon/dist/lib/manifest-pack.js +232 -0
  32. package/packages/daemon/dist/lib/state-home.d.ts +2 -0
  33. package/packages/daemon/dist/lib/state-home.js +12 -0
  34. package/packages/daemon/dist/lib/tui-binary.d.ts +57 -0
  35. package/packages/daemon/dist/lib/tui-binary.js +130 -0
  36. package/packages/daemon/dist/lib/update-check.js +5 -0
  37. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +34 -0
  38. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  39. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/PkgInfo +1 -0
  40. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
  41. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  42. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +139 -0
  43. package/packages/daemon/dist/restore.d.ts +35 -8
  44. package/packages/daemon/dist/restore.js +52 -15
  45. package/packages/daemon/dist/send.d.ts +33 -1
  46. package/packages/daemon/dist/send.js +32 -19
  47. package/packages/daemon/dist/widgets/explorer/breadcrumbs.d.ts +1 -1
  48. package/packages/daemon/dist/widgets/explorer/footer.d.ts +1 -1
  49. package/packages/daemon/dist/widgets/explorer/tree.d.ts +1 -1
  50. package/packages/daemon/dist/widgets/lib/help-overlay.d.ts +1 -1
  51. package/packages/daemon/dist/widgets/setup/agent-naming.d.ts +1 -1
  52. package/packages/daemon/dist/widgets/setup/config-tree.d.ts +1 -1
  53. package/packages/daemon/dist/widgets/setup/detect-panel.d.ts +1 -1
  54. package/packages/daemon/dist/widgets/setup/field-editor.d.ts +1 -1
  55. package/packages/daemon/dist/widgets/setup/footer.d.ts +1 -1
  56. package/packages/daemon/dist/widgets/setup/layout-picker.d.ts +1 -1
  57. package/packages/daemon/src/agent-explain.ts +34 -6
  58. package/packages/daemon/src/control/client.ts +128 -0
  59. package/packages/daemon/src/control/dispatch.ts +107 -0
  60. package/packages/daemon/src/control/fanout.ts +44 -0
  61. package/packages/daemon/src/control/frames.ts +40 -0
  62. package/packages/daemon/src/control/lifecycle.ts +151 -0
  63. package/packages/daemon/src/control/server.ts +237 -0
  64. package/packages/daemon/src/control/verbs.ts +118 -0
  65. package/packages/daemon/src/doctor.ts +113 -28
  66. package/packages/daemon/src/lib/agent-discovery.ts +53 -13
  67. package/packages/daemon/src/lib/app-config.ts +194 -5
  68. package/packages/daemon/src/lib/manifest-pack.ts +255 -0
  69. package/packages/daemon/src/lib/state-home.ts +13 -0
  70. package/packages/daemon/src/lib/tui-binary.ts +165 -0
  71. package/packages/daemon/src/lib/update-check.ts +5 -0
  72. package/packages/daemon/src/restore.ts +53 -15
  73. package/packages/daemon/src/send.ts +55 -21
  74. package/packages/daemon/src/tui/chrome/events.ts +4 -4
  75. package/packages/daemon/src/tui/chrome/front-door.ts +39 -0
  76. package/packages/daemon/src/tui/chrome/notify-prefs.ts +58 -0
  77. package/packages/daemon/src/tui/chrome/notify-state.ts +76 -0
  78. package/packages/daemon/src/tui/chrome/notify.ts +737 -52
  79. package/packages/daemon/src/tui/chrome/updater.ts +318 -30
  80. package/packages/daemon/src/tui/compiled.ts +11 -3
  81. package/packages/daemon/src/tui/detect/classify.ts +49 -0
  82. package/packages/daemon/src/tui/detect/manifest-loader.ts +56 -6
  83. package/packages/daemon/src/tui/detect/manifest.ts +39 -3
  84. package/packages/daemon/src/tui/detect/manifests.ts +395 -33
  85. package/packages/daemon/src/tui/detect/process-tree.ts +33 -3
  86. package/packages/daemon/src/tui/detect/session-id.ts +503 -0
  87. package/packages/daemon/src/tui/integrations/opencode.ts +121 -0
  88. package/packages/daemon/src/tui/main.ts +13 -1
  89. package/packages/daemon/src/tui/mirror/ack-writer.ts +77 -0
  90. package/packages/daemon/src/tui/mirror/agent-chip.ts +126 -0
  91. package/packages/daemon/src/tui/mirror/agent-lifecycle.ts +437 -0
  92. package/packages/daemon/src/tui/mirror/agent-rows.ts +155 -0
  93. package/packages/daemon/src/tui/mirror/app-state.ts +342 -0
  94. package/packages/daemon/src/tui/mirror/app.tsx +7048 -0
  95. package/packages/daemon/src/tui/mirror/attention.ts +110 -0
  96. package/packages/daemon/src/tui/mirror/blit.ts +186 -0
  97. package/packages/daemon/src/tui/mirror/control-client.ts +80 -9
  98. package/packages/daemon/src/tui/mirror/dialog-model.ts +298 -0
  99. package/packages/daemon/src/tui/mirror/dialog-stack.ts +367 -0
  100. package/packages/daemon/src/tui/mirror/diff-model.ts +387 -0
  101. package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
  102. package/packages/daemon/src/tui/mirror/file-tree.ts +322 -0
  103. package/packages/daemon/src/tui/mirror/focus-border.ts +57 -0
  104. package/packages/daemon/src/tui/mirror/folder-picker.ts +124 -0
  105. package/packages/daemon/src/tui/mirror/home-model.ts +174 -0
  106. package/packages/daemon/src/tui/mirror/host-terminal.ts +49 -0
  107. package/packages/daemon/src/tui/mirror/hosted.ts +205 -0
  108. package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
  109. package/packages/daemon/src/tui/mirror/layout-parse.ts +154 -0
  110. package/packages/daemon/src/tui/mirror/menu-model.ts +210 -0
  111. package/packages/daemon/src/tui/mirror/palette.ts +564 -0
  112. package/packages/daemon/src/tui/mirror/pane-mirror.ts +578 -20
  113. package/packages/daemon/src/tui/mirror/pane-surface.tsx +422 -0
  114. package/packages/daemon/src/tui/mirror/perf-tap.ts +186 -0
  115. package/packages/daemon/src/tui/mirror/resize-model.ts +85 -0
  116. package/packages/daemon/src/tui/mirror/scrollbar-model.ts +88 -0
  117. package/packages/daemon/src/tui/mirror/search-model.ts +70 -0
  118. package/packages/daemon/src/tui/mirror/selection.ts +376 -0
  119. package/packages/daemon/src/tui/mirror/session-mirror.ts +724 -0
  120. package/packages/daemon/src/tui/mirror/settings-model.ts +425 -0
  121. package/packages/daemon/src/tui/mirror/sidebar.tsx +218 -0
  122. package/packages/daemon/src/tui/mirror/size-truth.ts +130 -0
  123. package/packages/daemon/src/tui/mirror/spans.ts +46 -0
  124. package/packages/daemon/src/tui/mirror/status-grammar.ts +32 -0
  125. package/packages/daemon/src/tui/mirror/theme.ts +45 -0
  126. package/packages/daemon/src/tui/team/entry.ts +34 -7
  127. package/packages/daemon/src/tui/team/fuzzy.ts +20 -0
  128. package/packages/daemon/src/tui/team/report.ts +11 -1
  129. package/packages/daemon/src/tui/team/sessions.ts +184 -17
  130. package/packages/daemon/src/tui/team/wait.ts +144 -0
  131. package/scripts/build-macos-notifier.mjs +160 -0
  132. package/scripts/build-tui.mjs +11 -4
  133. package/scripts/perf-mirror.mjs +313 -0
  134. package/scripts/postinstall.js +8 -1
  135. package/scripts/prepublish-check.mjs +37 -1
  136. package/scripts/publish-tap.sh +55 -0
  137. package/skill/SKILL.md +110 -2
  138. package/templates/AGENTS.md +14 -7
  139. package/templates/agent-team-monorepo.yml +8 -0
  140. package/templates/agent-team-nextjs.yml +8 -0
  141. package/templates/agent-team.yml +10 -0
  142. package/templates/convex.yml +2 -0
  143. package/templates/default.yml +11 -5
  144. package/templates/go.yml +4 -0
  145. package/templates/missions.yml +6 -0
  146. package/templates/nextjs.yml +4 -0
  147. package/templates/python.yml +4 -0
  148. package/templates/skills/backend.md +5 -12
  149. package/templates/skills/frontend.md +5 -12
  150. package/templates/skills/general-worker.md +5 -12
  151. package/templates/skills/researcher.md +7 -12
  152. package/templates/skills/reviewer.md +7 -16
  153. package/templates/vite.yml +4 -0
  154. package/packages/daemon/src/tui/mirror/viewer.tsx +0 -166
@@ -0,0 +1,210 @@
1
+ /**
2
+ * The right-click context-menu model (M19.2) — PURE so it unit-tests without
3
+ * OpenTUI. app.tsx opens a small overlay at the pointer on a right-button press
4
+ * (SGR button 2); the ITEM DESCRIPTORS per region and all the geometry (dims,
5
+ * on-screen clamp, click hit-test) live here, while the closures that actually
6
+ * attach / kill / split / rename stay in the app (a `run` switch over the
7
+ * item's `id`).
8
+ *
9
+ * The overlay is late-mounted inside a <Show>, so — per the app's mouse
10
+ * landmine laws — it carries NO per-item handlers: clicks are routed centrally
11
+ * by coordinate math against {@link menuItemAt}, exactly the way the surface
12
+ * tab bar and window strip resolve their clicks by x-span math. Keeping that
13
+ * math in one tested place is the whole point.
14
+ */
15
+
16
+ /** The surfaces a right-click can target. Each maps to a fixed item list; the
17
+ * concrete payload (session name, file path, pane id) rides in the app's menu
18
+ * state, not here. */
19
+ export type MenuRegion = "session" | "agent" | "file" | "difffile" | "pane" | "window";
20
+
21
+ /** One menu entry. `id` is what the app dispatches on; `danger` items rearm to
22
+ * a "confirm: y" state instead of firing immediately; `input` items open an
23
+ * inline text line (the string is the prompt) before firing; `children` items
24
+ * open a SUBMENU column beside the row (one nesting level); `checkbox` items
25
+ * carry a live ✓/✗ state the app supplies (the toggle keeps the menu open). */
26
+ export interface MenuItem {
27
+ id: string;
28
+ label: string;
29
+ danger?: boolean;
30
+ input?: string;
31
+ children?: MenuItem[];
32
+ checkbox?: boolean;
33
+ }
34
+
35
+ /** PURE — the fixed item list for each region. The app supplies the context. */
36
+ export const MENU_ITEMS: Record<MenuRegion, MenuItem[]> = {
37
+ session: [
38
+ { id: "attach", label: "Attach" },
39
+ { id: "new-agent", label: "New agent…" },
40
+ { id: "rename", label: "Rename", input: "rename to" },
41
+ { id: "kill", label: "Kill session", danger: true },
42
+ ],
43
+ // Sidebar AGENT rows (M23.1): a left-click jumps; the context menu carries
44
+ // the lifecycle verbs. "Stop agent" only interrupts (the pane survives);
45
+ // "Close pane" is the destructive twin that also kills the pane. restart/
46
+ // stop confirm via DialogConfirm in the app (plain-language body), so only
47
+ // close — where there is no dialog — uses the inline confirm rearm.
48
+ agent: [
49
+ { id: "jump", label: "Go to agent" },
50
+ { id: "restart", label: "Restart agent" },
51
+ { id: "stop", label: "Stop agent" },
52
+ { id: "close", label: "Close pane", danger: true },
53
+ ],
54
+ file: [
55
+ { id: "open", label: "Open" },
56
+ { id: "newfile", label: "New file", input: "new file" },
57
+ { id: "rename", label: "Rename", input: "rename to" },
58
+ { id: "delete", label: "Delete", danger: true },
59
+ ],
60
+ difffile: [
61
+ { id: "open", label: "Open in editor" },
62
+ { id: "copypath", label: "Copy path" },
63
+ ],
64
+ pane: [
65
+ { id: "split-h", label: "Split horizontal" },
66
+ { id: "split-v", label: "Split vertical" },
67
+ { id: "zoom", label: "Zoom toggle" },
68
+ { id: "swap-next", label: "Swap with next" },
69
+ { id: "break", label: "Break to window" },
70
+ { id: "rotate", label: "Rotate panes" },
71
+ {
72
+ id: "layouts",
73
+ label: "Layouts",
74
+ children: [
75
+ { id: "layout:even-horizontal", label: "even-horizontal" },
76
+ { id: "layout:even-vertical", label: "even-vertical" },
77
+ { id: "layout:main-horizontal", label: "main-horizontal" },
78
+ { id: "layout:main-vertical", label: "main-vertical" },
79
+ { id: "layout:tiled", label: "tiled" },
80
+ ],
81
+ },
82
+ { id: "sync-toggle", label: "Synchronize panes", checkbox: true },
83
+ { id: "kill", label: "Kill pane", danger: true },
84
+ ],
85
+ window: [
86
+ { id: "new", label: "New window" },
87
+ { id: "rename", label: "Rename window", input: "rename to" },
88
+ { id: "kill", label: "Kill window", danger: true },
89
+ ],
90
+ };
91
+
92
+ /** PURE — the pane menu's item list for a concrete pane (M22.9). App-mouse
93
+ * panes (the app turned mouse reporting on — presses are forwarded, so a drag
94
+ * can't start a selection) get a leading "Select text…" verb that pauses
95
+ * forwarding for that pane; while its select mode is active the entry flips to
96
+ * the exit verb. They also get the drag-default toggle (M24.2): the label is
97
+ * always the OTHER default (`dragDefault` is the pane's current one, override
98
+ * and config applied), so picking it flips the pane for the session. Ordinary
99
+ * panes keep the fixed list untouched. */
100
+ export function paneMenuItems(
101
+ appMouse: boolean,
102
+ selectModeOn: boolean,
103
+ dragDefault: "select" | "forward",
104
+ ): MenuItem[] {
105
+ if (!appMouse) return MENU_ITEMS.pane;
106
+ const entry: MenuItem = selectModeOn
107
+ ? { id: "select-text-off", label: "Stop selecting" }
108
+ : { id: "select-text", label: "Select text…" };
109
+ const dragToggle: MenuItem =
110
+ dragDefault === "select"
111
+ ? { id: "drag-forward", label: "Forward mouse drags" }
112
+ : { id: "drag-select", label: "Select on drag" };
113
+ return [entry, dragToggle, ...MENU_ITEMS.pane];
114
+ }
115
+
116
+ /** The overlay's border (1) + horizontal padding (1 each side). The header row
117
+ * and every item row live INSIDE this frame. */
118
+ const BORDER = 1;
119
+ const PAD = 1;
120
+ /** A minimum inner text width so a short menu still reads as a panel. */
121
+ const MIN_INNER = 16;
122
+ /** The suffix a destructive item rearms to (label + this). Reserved in the
123
+ * width so "confirm: y" is never clipped — the render appends the SAME string. */
124
+ export const CONFIRM_SUFFIX = " confirm: y";
125
+ /** The caret a `children` item renders flush-right ("› Layouts ▸"): a
126
+ * leading space + the glyph, reserved in the width so a submenu row's caret is
127
+ * never clipped. The base "› " prefix (2) already sits on every row. */
128
+ export const SUBMENU_CARET = " ▸";
129
+
130
+ /** PURE — the overlay's cell dimensions for `title` + `items`. Width fits the
131
+ * longest of the title and item rows (each item reserves 2 cells for the "› "
132
+ * selection prefix); height is the top border + header + items + bottom border.
133
+ * Item labels leave slack for the longer "confirm: y" / input renderings, which
134
+ * never need to exceed the base width in practice. */
135
+ export function menuDims(title: string, items: MenuItem[]): { width: number; height: number } {
136
+ const inner = Math.max(
137
+ MIN_INNER,
138
+ title.length,
139
+ // A danger item's rearmed "label confirm: y" must fit; a `children` item
140
+ // reserves the "› " prefix AND the flush-right submenu caret; a `checkbox`
141
+ // item's ✓/✗ mark is the same width as the "› " prefix it replaces. Others
142
+ // reserve 2 cells for the "› " selection prefix.
143
+ ...items.map((it) => {
144
+ if (it.danger) return it.label.length + CONFIRM_SUFFIX.length;
145
+ if (it.children) return it.label.length + 2 + SUBMENU_CARET.length;
146
+ return it.label.length + 2;
147
+ }),
148
+ );
149
+ return { width: inner + PAD * 2 + BORDER * 2, height: items.length + 3 };
150
+ }
151
+
152
+ /** PURE — the on-screen top-left for a menu of `width`×`height` opened at
153
+ * (`x`,`y`), clamped so the whole box stays on a `screenW`×`screenH` grid. */
154
+ export function clampMenuPos(
155
+ x: number,
156
+ y: number,
157
+ width: number,
158
+ height: number,
159
+ screenW: number,
160
+ screenH: number,
161
+ ): { left: number; top: number } {
162
+ const left = Math.max(0, Math.min(x, screenW - width));
163
+ const top = Math.max(0, Math.min(y, screenH - height));
164
+ return { left, top };
165
+ }
166
+
167
+ /** The placed overlay's geometry — what the hit-tests need. */
168
+ export interface MenuGeom {
169
+ left: number;
170
+ top: number;
171
+ width: number;
172
+ height: number;
173
+ itemCount: number;
174
+ }
175
+
176
+ /** PURE — the item index under (`x`,`y`), or -1 when the point is not on an item
177
+ * row (the border, the header, or outside the box). Item `i` renders one row
178
+ * below the top border + header, i.e. at screen y = top + 2 + i. */
179
+ export function menuItemAt(m: MenuGeom, x: number, y: number): number {
180
+ if (x < m.left || x >= m.left + m.width) return -1;
181
+ const i = y - (m.top + BORDER + 1);
182
+ return i >= 0 && i < m.itemCount ? i : -1;
183
+ }
184
+
185
+ /** PURE — is (`x`,`y`) anywhere inside the overlay box (item, header, or border)?
186
+ * A down INSIDE that isn't on an item is a no-op (menu stays); a down OUTSIDE
187
+ * closes the menu. */
188
+ export function pointInMenu(m: MenuGeom, x: number, y: number): boolean {
189
+ return x >= m.left && x < m.left + m.width && y >= m.top && y < m.top + m.height;
190
+ }
191
+
192
+ /** PURE — the on-screen top-left for the SUBMENU column that opens beside the
193
+ * `parent` item at `parentItemIndex`. It opens to the RIGHT of the parent, its
194
+ * top border aligned with the parent item's row, then clamps fully on-screen
195
+ * (sliding left over the parent if the right edge would overflow — acceptable
196
+ * since the keyboard/coordinate router owns whichever column is focused). */
197
+ export function submenuPos(
198
+ parent: MenuGeom,
199
+ parentItemIndex: number,
200
+ width: number,
201
+ height: number,
202
+ screenW: number,
203
+ screenH: number,
204
+ ): { left: number; top: number } {
205
+ const x = parent.left + parent.width;
206
+ // The parent item renders at y = parent.top + BORDER + header(1) + index; put
207
+ // the submenu's top border on that same row so the columns read as connected.
208
+ const y = parent.top + BORDER + 1 + parentItemIndex;
209
+ return clampMenuPos(x, y, width, height, screenW, screenH);
210
+ }