tmux-ide 2.6.0 → 2.7.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 (271) hide show
  1. package/README.md +14 -9
  2. package/bin/cli.js +1024 -519
  3. package/bin/cli.ts +63 -6
  4. package/bunfig.toml +4 -0
  5. package/package.json +18 -7
  6. package/packages/contracts/package.json +22 -0
  7. package/packages/contracts/src/__tests__/ide-config.test.ts +46 -0
  8. package/packages/contracts/src/__tests__/terminals.test.ts +87 -0
  9. package/packages/contracts/src/actions-contract.ts +310 -0
  10. package/packages/contracts/src/actions-errors.ts +41 -0
  11. package/packages/contracts/src/domain.ts +36 -0
  12. package/packages/contracts/src/ide-config.ts +170 -0
  13. package/packages/contracts/src/index.ts +24 -0
  14. package/packages/contracts/src/lib-internal/auth.ts +13 -0
  15. package/packages/contracts/src/lib-internal/hq.ts +38 -0
  16. package/packages/contracts/src/terminals.ts +116 -0
  17. package/packages/contracts/src/tmux.ts +60 -0
  18. package/packages/contracts/src/workspace.ts +67 -0
  19. package/packages/daemon/dist/agent-explain.d.ts +8 -1
  20. package/packages/daemon/dist/agent-explain.js +19 -3
  21. package/packages/daemon/dist/lib/tui-binary.d.ts +57 -0
  22. package/packages/daemon/dist/lib/tui-binary.js +130 -0
  23. package/packages/daemon/dist/widgets/explorer/breadcrumbs.d.ts +1 -1
  24. package/packages/daemon/dist/widgets/explorer/footer.d.ts +1 -1
  25. package/packages/daemon/dist/widgets/explorer/tree.d.ts +1 -1
  26. package/packages/daemon/dist/widgets/lib/help-overlay.d.ts +1 -1
  27. package/packages/daemon/dist/widgets/setup/agent-naming.d.ts +1 -1
  28. package/packages/daemon/dist/widgets/setup/config-tree.d.ts +1 -1
  29. package/packages/daemon/dist/widgets/setup/detect-panel.d.ts +1 -1
  30. package/packages/daemon/dist/widgets/setup/field-editor.d.ts +1 -1
  31. package/packages/daemon/dist/widgets/setup/footer.d.ts +1 -1
  32. package/packages/daemon/dist/widgets/setup/layout-picker.d.ts +1 -1
  33. package/packages/daemon/src/agent-explain.ts +298 -0
  34. package/packages/daemon/src/attach.ts +20 -0
  35. package/packages/daemon/src/bin.ts +4 -0
  36. package/packages/daemon/src/canonical.ts +7 -0
  37. package/packages/daemon/src/cli.ts +499 -0
  38. package/packages/daemon/src/command-center/actions/contract.ts +2 -0
  39. package/packages/daemon/src/command-center/actions/dispatcher.ts +137 -0
  40. package/packages/daemon/src/command-center/actions/errors.ts +105 -0
  41. package/packages/daemon/src/command-center/actions/handlers/_project-context.ts +30 -0
  42. package/packages/daemon/src/command-center/actions/handlers/_resolve-project.ts +78 -0
  43. package/packages/daemon/src/command-center/actions/handlers/app-set-remote-access.ts +118 -0
  44. package/packages/daemon/src/command-center/actions/handlers/config-actions.ts +113 -0
  45. package/packages/daemon/src/command-center/actions/handlers/daemon-shutdown.ts +38 -0
  46. package/packages/daemon/src/command-center/actions/handlers/project-activate.ts +30 -0
  47. package/packages/daemon/src/command-center/actions/handlers/project-launch.ts +70 -0
  48. package/packages/daemon/src/command-center/actions/handlers/project-open-terminal.ts +87 -0
  49. package/packages/daemon/src/command-center/actions/handlers/project-restart.ts +38 -0
  50. package/packages/daemon/src/command-center/actions/handlers/project-stop.ts +62 -0
  51. package/packages/daemon/src/command-center/actions/handlers/terminal-respawn.ts +119 -0
  52. package/packages/daemon/src/command-center/actions/handlers/terminal-stop.ts +35 -0
  53. package/packages/daemon/src/command-center/actions/registry.ts +149 -0
  54. package/packages/daemon/src/command-center/discovery.ts +96 -0
  55. package/packages/daemon/src/command-center/index.ts +31 -0
  56. package/packages/daemon/src/command-center/schemas.ts +85 -0
  57. package/packages/daemon/src/command-center/server.ts +1260 -0
  58. package/packages/daemon/src/command-center/ws-events.ts +316 -0
  59. package/packages/daemon/src/config.ts +549 -0
  60. package/packages/daemon/src/detect.ts +248 -0
  61. package/packages/daemon/src/doctor.ts +242 -0
  62. package/packages/daemon/src/embed.ts +5 -0
  63. package/packages/daemon/src/index.ts +12 -0
  64. package/packages/daemon/src/init.ts +211 -0
  65. package/packages/daemon/src/inspect.ts +178 -0
  66. package/packages/daemon/src/js-yaml.d.ts +10 -0
  67. package/packages/daemon/src/launch.ts +349 -0
  68. package/packages/daemon/src/lib/active-projects.ts +49 -0
  69. package/packages/daemon/src/lib/agent-discovery.ts +121 -0
  70. package/packages/daemon/src/lib/app-config.ts +427 -0
  71. package/packages/daemon/src/lib/app-settings.ts +53 -0
  72. package/packages/daemon/src/lib/auth/auth-service.ts +227 -0
  73. package/packages/daemon/src/lib/auth/middleware.ts +56 -0
  74. package/packages/daemon/src/lib/auth/types.ts +2 -0
  75. package/packages/daemon/src/lib/auth-token.ts +5 -0
  76. package/packages/daemon/src/lib/authorship.ts +280 -0
  77. package/packages/daemon/src/lib/canonical-daemon.ts +122 -0
  78. package/packages/daemon/src/lib/cli-action-bridge.ts +216 -0
  79. package/packages/daemon/src/lib/daemon-embed.ts +782 -0
  80. package/packages/daemon/src/lib/daemon-watchdog.ts +111 -0
  81. package/packages/daemon/src/lib/daemon.ts +79 -0
  82. package/packages/daemon/src/lib/dot-path.ts +17 -0
  83. package/packages/daemon/src/lib/errors.ts +67 -0
  84. package/packages/daemon/src/lib/filesystem-browser.ts +292 -0
  85. package/packages/daemon/src/lib/launch-plan.ts +90 -0
  86. package/packages/daemon/src/lib/log.ts +134 -0
  87. package/packages/daemon/src/lib/output.ts +76 -0
  88. package/packages/daemon/src/lib/project-init-runner.ts +150 -0
  89. package/packages/daemon/src/lib/project-inspect.ts +80 -0
  90. package/packages/daemon/src/lib/project-onboard.ts +149 -0
  91. package/packages/daemon/src/lib/project-probe.ts +92 -0
  92. package/packages/daemon/src/lib/project-registry.ts +296 -0
  93. package/packages/daemon/src/lib/session-monitor.ts +122 -0
  94. package/packages/daemon/src/lib/session-options.ts +100 -0
  95. package/packages/daemon/src/lib/shell.ts +8 -0
  96. package/packages/daemon/src/lib/sizes.ts +36 -0
  97. package/packages/daemon/src/lib/skill-sync.ts +155 -0
  98. package/packages/daemon/src/lib/slugify.ts +10 -0
  99. package/packages/daemon/src/lib/terminals-store.ts +125 -0
  100. package/packages/daemon/src/lib/tui-binary.ts +165 -0
  101. package/packages/daemon/src/lib/update-check.ts +298 -0
  102. package/packages/daemon/src/lib/update.ts +158 -0
  103. package/packages/daemon/src/lib/workspace-registry.ts +229 -0
  104. package/packages/daemon/src/lib/worktree.ts +289 -0
  105. package/packages/daemon/src/lib/yaml-io.ts +27 -0
  106. package/packages/daemon/src/ls.ts +40 -0
  107. package/packages/daemon/src/restart.ts +24 -0
  108. package/packages/daemon/src/restore.ts +514 -0
  109. package/packages/daemon/src/schemas/domain.ts +2 -0
  110. package/packages/daemon/src/schemas/filesystem.ts +34 -0
  111. package/packages/daemon/src/schemas/ide-config.ts +2 -0
  112. package/packages/daemon/src/schemas/index.ts +59 -0
  113. package/packages/daemon/src/schemas/inspect.ts +67 -0
  114. package/packages/daemon/src/schemas/registry.ts +55 -0
  115. package/packages/daemon/src/schemas/ws-events.ts +135 -0
  116. package/packages/daemon/src/send.ts +171 -0
  117. package/packages/daemon/src/server/README.md +15 -0
  118. package/packages/daemon/src/server/index.ts +74 -0
  119. package/packages/daemon/src/server/pty-bridge.ts +532 -0
  120. package/packages/daemon/src/server/standalone.ts +18 -0
  121. package/packages/daemon/src/server/ws-route.ts +483 -0
  122. package/packages/daemon/src/status.ts +59 -0
  123. package/packages/daemon/src/stop.ts +28 -0
  124. package/packages/daemon/src/terminal/NodePtyAdapter.ts +271 -0
  125. package/packages/daemon/src/terminal/PtyAdapter.ts +140 -0
  126. package/packages/daemon/src/terminal/README.md +92 -0
  127. package/packages/daemon/src/tui/chrome/cheatsheet.ts +260 -0
  128. package/packages/daemon/src/tui/chrome/chip.ts +30 -0
  129. package/packages/daemon/src/tui/chrome/events.ts +119 -0
  130. package/packages/daemon/src/tui/chrome/kitty-keys.ts +55 -0
  131. package/packages/daemon/src/tui/chrome/menu.ts +289 -0
  132. package/packages/daemon/src/tui/chrome/notify.ts +382 -0
  133. package/packages/daemon/src/tui/chrome/panels.ts +111 -0
  134. package/packages/daemon/src/tui/chrome/sidebar.ts +222 -0
  135. package/packages/daemon/src/tui/chrome/snapshot.ts +425 -0
  136. package/packages/daemon/src/tui/chrome/statusline.ts +595 -0
  137. package/packages/daemon/src/tui/chrome/updater.ts +510 -0
  138. package/packages/daemon/src/tui/chrome/welcome.ts +124 -0
  139. package/packages/daemon/src/tui/compiled.ts +121 -0
  140. package/packages/daemon/src/tui/detect/classify.ts +208 -0
  141. package/packages/daemon/src/tui/detect/manifest-loader.ts +193 -0
  142. package/packages/daemon/src/tui/detect/manifest.ts +199 -0
  143. package/packages/daemon/src/tui/detect/manifests.ts +354 -0
  144. package/packages/daemon/src/tui/detect/process-tree.ts +217 -0
  145. package/packages/daemon/src/tui/detect/snapshot.ts +70 -0
  146. package/packages/daemon/src/tui/integrations/claude.ts +176 -0
  147. package/packages/daemon/src/tui/integrations/offer.ts +145 -0
  148. package/packages/daemon/src/tui/main.ts +82 -0
  149. package/packages/daemon/src/tui/mirror/ack-writer.ts +77 -0
  150. package/packages/daemon/src/tui/mirror/agent-chip.ts +97 -0
  151. package/packages/daemon/src/tui/mirror/agent-rows.ts +133 -0
  152. package/packages/daemon/src/tui/mirror/app-state.ts +179 -0
  153. package/packages/daemon/src/tui/mirror/app.tsx +5265 -0
  154. package/packages/daemon/src/tui/mirror/blit.ts +186 -0
  155. package/packages/daemon/src/tui/mirror/control-client.ts +214 -0
  156. package/packages/daemon/src/tui/mirror/control.ts +97 -0
  157. package/packages/daemon/src/tui/mirror/dialog-model.ts +298 -0
  158. package/packages/daemon/src/tui/mirror/dialog-stack.ts +354 -0
  159. package/packages/daemon/src/tui/mirror/diff-model.ts +112 -0
  160. package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
  161. package/packages/daemon/src/tui/mirror/file-tree.ts +97 -0
  162. package/packages/daemon/src/tui/mirror/focus-border.ts +57 -0
  163. package/packages/daemon/src/tui/mirror/folder-picker.ts +124 -0
  164. package/packages/daemon/src/tui/mirror/home-model.ts +174 -0
  165. package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
  166. package/packages/daemon/src/tui/mirror/menu-model.ts +187 -0
  167. package/packages/daemon/src/tui/mirror/palette.ts +274 -0
  168. package/packages/daemon/src/tui/mirror/pane-mirror.ts +561 -0
  169. package/packages/daemon/src/tui/mirror/pane-surface.tsx +415 -0
  170. package/packages/daemon/src/tui/mirror/perf-tap.ts +160 -0
  171. package/packages/daemon/src/tui/mirror/resize-model.ts +85 -0
  172. package/packages/daemon/src/tui/mirror/scrollbar-model.ts +88 -0
  173. package/packages/daemon/src/tui/mirror/search-model.ts +70 -0
  174. package/packages/daemon/src/tui/mirror/selection.ts +262 -0
  175. package/packages/daemon/src/tui/mirror/session-mirror.ts +443 -0
  176. package/packages/daemon/src/tui/mirror/settings-model.ts +345 -0
  177. package/packages/daemon/src/tui/mirror/size-truth.ts +77 -0
  178. package/packages/daemon/src/tui/mirror/spans.ts +46 -0
  179. package/packages/daemon/src/tui/mirror/status-grammar.ts +32 -0
  180. package/packages/daemon/src/tui/team/CONTROL.md +50 -0
  181. package/packages/daemon/src/tui/team/entry.ts +38 -0
  182. package/packages/daemon/src/tui/team/fuzzy.ts +133 -0
  183. package/packages/daemon/src/tui/team/home.ts +170 -0
  184. package/packages/daemon/src/tui/team/index.tsx +1521 -0
  185. package/packages/daemon/src/tui/team/input.ts +34 -0
  186. package/packages/daemon/src/tui/team/keymap.ts +127 -0
  187. package/packages/daemon/src/tui/team/mouse.ts +29 -0
  188. package/packages/daemon/src/tui/team/nav.ts +31 -0
  189. package/packages/daemon/src/tui/team/preview.ts +34 -0
  190. package/packages/daemon/src/tui/team/projects.ts +191 -0
  191. package/packages/daemon/src/tui/team/report.ts +83 -0
  192. package/packages/daemon/src/tui/team/sessions.ts +433 -0
  193. package/packages/daemon/src/tui/team/tree.ts +62 -0
  194. package/packages/daemon/src/types.ts +13 -0
  195. package/packages/daemon/src/ui/index.ts +32 -0
  196. package/packages/daemon/src/ui/terminal/index.ts +9 -0
  197. package/packages/daemon/src/ui/types.ts +91 -0
  198. package/packages/daemon/src/ui/web/base.css +80 -0
  199. package/packages/daemon/src/ui/web/components/Box.tsx +59 -0
  200. package/packages/daemon/src/ui/web/components/Input.tsx +32 -0
  201. package/packages/daemon/src/ui/web/components/ScrollBox.tsx +60 -0
  202. package/packages/daemon/src/ui/web/components/Text.tsx +28 -0
  203. package/packages/daemon/src/ui/web/hooks.ts +106 -0
  204. package/packages/daemon/src/ui/web/index.ts +27 -0
  205. package/packages/daemon/src/ui/web/render.ts +77 -0
  206. package/packages/daemon/src/ui/web/utils/color.ts +27 -0
  207. package/packages/daemon/src/validate.ts +217 -0
  208. package/packages/daemon/src/widgets/changes/README.md +3 -0
  209. package/packages/daemon/src/widgets/changes/index.tsx +691 -0
  210. package/packages/daemon/src/widgets/config/README.md +3 -0
  211. package/packages/daemon/src/widgets/config/index.tsx +481 -0
  212. package/packages/daemon/src/widgets/explorer/README.md +3 -0
  213. package/packages/daemon/src/widgets/explorer/breadcrumbs.tsx +77 -0
  214. package/packages/daemon/src/widgets/explorer/footer.tsx +20 -0
  215. package/packages/daemon/src/widgets/explorer/header.tsx +23 -0
  216. package/packages/daemon/src/widgets/explorer/index.tsx +456 -0
  217. package/packages/daemon/src/widgets/explorer/tree-model.ts +103 -0
  218. package/packages/daemon/src/widgets/explorer/tree.tsx +165 -0
  219. package/packages/daemon/src/widgets/lib/config-model.ts +116 -0
  220. package/packages/daemon/src/widgets/lib/files.ts +88 -0
  221. package/packages/daemon/src/widgets/lib/git.ts +88 -0
  222. package/packages/daemon/src/widgets/lib/grammar.ts +126 -0
  223. package/packages/daemon/src/widgets/lib/help-overlay.tsx +101 -0
  224. package/packages/daemon/src/widgets/lib/pane-comms.ts +209 -0
  225. package/packages/daemon/src/widgets/lib/theme.ts +194 -0
  226. package/packages/daemon/src/widgets/lib/watcher.ts +132 -0
  227. package/packages/daemon/src/widgets/preview/README.md +3 -0
  228. package/packages/daemon/src/widgets/preview/index.tsx +416 -0
  229. package/packages/daemon/src/widgets/resolve.ts +121 -0
  230. package/packages/daemon/src/widgets/setup/README.md +3 -0
  231. package/packages/daemon/src/widgets/setup/agent-naming.tsx +112 -0
  232. package/packages/daemon/src/widgets/setup/config-tree.tsx +246 -0
  233. package/packages/daemon/src/widgets/setup/detect-panel.tsx +72 -0
  234. package/packages/daemon/src/widgets/setup/field-editor.tsx +265 -0
  235. package/packages/daemon/src/widgets/setup/footer.tsx +107 -0
  236. package/packages/daemon/src/widgets/setup/index.tsx +341 -0
  237. package/packages/daemon/src/widgets/setup/layout-picker.tsx +96 -0
  238. package/packages/daemon/src/widgets/setup/orchestrator-panel.tsx +200 -0
  239. package/packages/daemon/src/widgets/setup/review-panel.tsx +140 -0
  240. package/packages/daemon/src/widgets/setup/setup-model.ts +188 -0
  241. package/packages/daemon/src/widgets/sidebar/index.tsx +527 -0
  242. package/packages/tmux-bridge/package.json +22 -0
  243. package/packages/tmux-bridge/src/errors.ts +28 -0
  244. package/packages/tmux-bridge/src/index.ts +31 -0
  245. package/packages/tmux-bridge/src/monitor.ts +77 -0
  246. package/packages/tmux-bridge/src/panes.ts +136 -0
  247. package/packages/tmux-bridge/src/runner.test.ts +501 -0
  248. package/packages/tmux-bridge/src/runner.ts +91 -0
  249. package/packages/tmux-bridge/src/sessions.ts +126 -0
  250. package/packages/tmux-bridge/src/targeting.test.ts +107 -0
  251. package/packages/tmux-bridge/src/targeting.ts +90 -0
  252. package/scripts/build-tui.mjs +11 -4
  253. package/scripts/perf-mirror.mjs +313 -0
  254. package/scripts/postinstall.js +26 -2
  255. package/skill/SKILL.md +22 -0
  256. package/templates/AGENTS.md +14 -7
  257. package/templates/agent-team-monorepo.yml +8 -0
  258. package/templates/agent-team-nextjs.yml +8 -0
  259. package/templates/agent-team.yml +10 -0
  260. package/templates/convex.yml +2 -0
  261. package/templates/default.yml +11 -5
  262. package/templates/go.yml +4 -0
  263. package/templates/missions.yml +6 -0
  264. package/templates/nextjs.yml +4 -0
  265. package/templates/python.yml +4 -0
  266. package/templates/skills/backend.md +5 -12
  267. package/templates/skills/frontend.md +5 -12
  268. package/templates/skills/general-worker.md +5 -12
  269. package/templates/skills/researcher.md +7 -12
  270. package/templates/skills/reviewer.md +7 -16
  271. package/templates/vite.yml +4 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * The tmux-ide widget PANELS — every widget is a floating app dialog.
3
+ *
4
+ * The one-app milestone: instead of living as dedicated tmux panes, each widget
5
+ * opens as a `display-popup -E` over your work, one root-table keystroke away,
6
+ * esc/q to close (the popup exits when the widget process does). This mirrors the
7
+ * chrome popups (switcher `M-p` / cheat sheet `M-k`) but points each key at
8
+ * `tmux-ide popup <widget>`, which execs the bun widget synchronously inside the
9
+ * popup's PTY (see the `popup` case in bin/cli.ts).
10
+ *
11
+ * {@link PANEL_POPUPS} is the single source of truth — the widget name (the
12
+ * `tmux-ide popup <widget>` arg AND the `keys.panels.<widget>` config key), a
13
+ * menu label, and per-widget popup sizing. The status bar's adopt binds one key
14
+ * per entry, the cheat sheet lists them, and the actions menu opens them — all
15
+ * derived from this list so they never drift.
16
+ *
17
+ * Only widgets that EXIST and stand alone are here: `preview` renders whatever
18
+ * the explorer selected (via a shared `@preview_file` tmux option — nothing to
19
+ * show on its own) and `setup` is the onboarding wizard, so neither is a panel.
20
+ * `tasks`/`warroom`/`costs` were removed in the v2.6.0 front-end trim.
21
+ *
22
+ * Every builder here is PURE (tested); the io (running the popup) is the CLI's.
23
+ */
24
+ import type { AppPanelKeys } from "../../lib/app-config.ts";
25
+
26
+ /** A widget that can float as a panel popup. */
27
+ export interface PanelPopup {
28
+ /** Widget name — the `tmux-ide popup <widget>` arg + `keys.panels` field. */
29
+ widget: "explorer" | "changes" | "config";
30
+ /** Human label for the actions menu row. */
31
+ label: string;
32
+ /**
33
+ * `display-popup` size tokens (tmux `-w`/`-h`). Sized per widget: the explorer
34
+ * is a tall, narrower file column; changes is a wide diff surface; config is a
35
+ * roomy tree editor. All large — a panel floats OVER your work, so it should
36
+ * dominate while open, then vanish on esc.
37
+ */
38
+ width: string;
39
+ height: string;
40
+ }
41
+
42
+ /**
43
+ * The panel registry — the widgets bound as floating popups, in menu order.
44
+ * Keys are configurable via `~/.tmux-ide/config.json` (`keys.panels.*`); the
45
+ * defaults live in {@link ../../lib/app-config.ts}. Sizing is fixed here (tuned,
46
+ * not configurable — a deliberate scope line).
47
+ */
48
+ export const PANEL_POPUPS: PanelPopup[] = [
49
+ { widget: "explorer", label: "⊞ Files", width: "60%", height: "85%" },
50
+ { widget: "changes", label: "± Changes", width: "85%", height: "90%" },
51
+ { widget: "config", label: "⚙ Config", width: "80%", height: "85%" },
52
+ ];
53
+
54
+ /** The widget names that `tmux-ide popup <widget>` accepts (the panel set). */
55
+ export const POPUP_WIDGETS: string[] = PANEL_POPUPS.map((p) => p.widget);
56
+
57
+ /** The CLI invocation a panel popup runs (`tmux-ide popup <widget>`). */
58
+ export function panelPopupCli(widget: string): string {
59
+ return `tmux-ide popup ${widget}`;
60
+ }
61
+
62
+ /**
63
+ * The configured root-table key for a panel. `AppPanelKeys` fields are named
64
+ * for the widgets, so this is a typed lookup — the panel registry and the config
65
+ * shape stay in lockstep.
66
+ */
67
+ export function panelKey(panel: PanelPopup, keys: AppPanelKeys): string {
68
+ return keys[panel.widget];
69
+ }
70
+
71
+ /**
72
+ * PURE — the `display-popup` command STRING that floats a panel (shared by the
73
+ * actions menu's Panels rows, so a menu pick opens an identical popup to the
74
+ * key bind). `-d '#{pane_current_path}'` opens the widget on the pane's cwd, so
75
+ * the explorer/changes/config land on the project you're looking at.
76
+ */
77
+ export function panelPopupCommand(panel: PanelPopup, cli = panelPopupCli(panel.widget)): string {
78
+ return `display-popup -E -d '#{pane_current_path}' -w ${panel.width} -h ${panel.height} "${cli}"`;
79
+ }
80
+
81
+ /**
82
+ * PURE — the tmux argv that binds a panel's root-table key to its
83
+ * `display-popup`. Server-wide root-table bind (tmux has no per-session
84
+ * bind-key), mirroring the switcher's popup key — see the note on
85
+ * `unadoptSession`. Idempotent: re-binding a key overwrites it.
86
+ */
87
+ export function panelPopupBindCommand(
88
+ panel: PanelPopup,
89
+ key: string,
90
+ cli = panelPopupCli(panel.widget),
91
+ ): string[] {
92
+ return [
93
+ "bind-key",
94
+ "-n",
95
+ key,
96
+ "display-popup",
97
+ "-E",
98
+ "-d",
99
+ "#{pane_current_path}",
100
+ "-w",
101
+ panel.width,
102
+ "-h",
103
+ panel.height,
104
+ cli,
105
+ ];
106
+ }
107
+
108
+ /** PURE — the tmux argv that removes a panel's key binding. */
109
+ export function panelPopupUnbindCommand(key: string): string[] {
110
+ return ["unbind-key", "-n", key];
111
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * The tmux-ide SIDEBAR — the app's persistent nav column.
3
+ *
4
+ * Unlike the widget PANELS (floating popups) this is a real, always-visible
5
+ * tmux pane: a narrow full-height left column that lists the fleet
6
+ * (project → session → window) with live status glyphs and jumps the viewing
7
+ * client anywhere on enter. It's summoned per-session with a toggle key
8
+ * (`keys.sidebar`, default `M-b`) that runs `tmux-ide sidebar-toggle`, so the
9
+ * app column appears wherever you call for it — in ANY adopted session.
10
+ *
11
+ * This module owns the tmux mechanics: the pure toggle-key bind builders (bound
12
+ * server-wide by `adoptSession`, like the other chrome keys) and the io that
13
+ * opens/finds/closes the column pane. The pane is marked with
14
+ * {@link SIDEBAR_PANE_OPTION} so the data layer excludes it from status rollups
15
+ * (see `../team/sessions.ts`) and the chip updater never labels it an agent.
16
+ *
17
+ * The command builders are PURE (tested); the io wrappers are thin.
18
+ */
19
+ import { existsSync } from "node:fs";
20
+ import { dirname, resolve } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+ import { runTmux } from "@tmux-ide/tmux-bridge";
23
+ import type { ThemeConfig } from "../../types.ts";
24
+ import { shellEscape } from "../../lib/shell.ts";
25
+ import { SIDEBAR_PANE_OPTION } from "../team/sessions.ts";
26
+ import { resolveTuiLaunch, findCompiledTui, isBunAvailable } from "../compiled.ts";
27
+
28
+ export { SIDEBAR_PANE_OPTION };
29
+
30
+ const __dirname = dirname(fileURLToPath(import.meta.url));
31
+
32
+ /**
33
+ * Resolve the sidebar widget's `.tsx` entry, robust across BOTH run modes. The
34
+ * widget sources ship uncompiled and are spawned by `bun`, so a bundled caller
35
+ * (this module inlined into `bin/cli.js`) must reach them via a bin-anchored
36
+ * path, while a dev caller (this module at `tui/chrome/`) reaches them relatively
37
+ * — the two differ, so we probe candidates and take the first that exists. Falls
38
+ * back to the dev path so the returned command is at least well-formed.
39
+ */
40
+ export function sidebarWidgetScript(): string {
41
+ const candidates = [
42
+ resolve(__dirname, "../../widgets/sidebar/index.tsx"),
43
+ resolve(__dirname, "../packages/daemon/src/widgets/sidebar/index.tsx"),
44
+ ];
45
+ return candidates.find((p) => existsSync(p)) ?? candidates[0]!;
46
+ }
47
+
48
+ /**
49
+ * PURE — the shell command the sidebar pane runs. `cd`s into the project dir
50
+ * first so bun finds `bunfig.toml` (the @opentui/solid JSX preload), then boots
51
+ * the widget with the current session, dir, and (optional) theme. Mirrors
52
+ * `resolveWidgetCommand`'s shape but self-locates the script so it survives the
53
+ * bundle (see {@link sidebarWidgetScript}).
54
+ */
55
+ export function sidebarWidgetCommand(
56
+ scriptPath: string,
57
+ session: string,
58
+ dir: string,
59
+ theme: ThemeConfig | null,
60
+ ): string {
61
+ const args = [`--session=${session}`, `--dir=${dir}`];
62
+ if (theme) args.push(`--theme=${JSON.stringify(theme)}`);
63
+
64
+ const launch = resolveTuiLaunch({
65
+ surface: "sidebar",
66
+ scriptPath,
67
+ args,
68
+ checkoutExists: existsSync(scriptPath),
69
+ bunAvailable: isBunAvailable(),
70
+ compiledBinary: findCompiledTui(),
71
+ });
72
+ if (launch.mode === "unavailable") {
73
+ // Keep the pane command well-formed; the pane shows bun's own error. This
74
+ // path only trips on a broken install (no checkout, no bun, no binary).
75
+ return `cd ${shellEscape(dir)} && bun ${shellEscape(scriptPath)} ${args.map(shellEscape).join(" ")}`;
76
+ }
77
+
78
+ const escaped = launch.argv.map(shellEscape).join(" ");
79
+ // Both modes cd into the project dir first: bun needs a nearby bunfig only in
80
+ // a checkout (which sits above dir); the binary is self-contained.
81
+ return `cd ${shellEscape(dir)} && ${shellEscape(launch.bin)} ${escaped}`;
82
+ }
83
+
84
+ /**
85
+ * Root-table key that toggles the sidebar: `M-b` (Alt+b). Like the other chrome
86
+ * keys it lives in the ROOT table so it fires without the prefix from any
87
+ * adopted session; Alt-letter keys are low-collision in terminal apps. Default
88
+ * for {@link sidebarToggleBindCommand}; overridable via `keys.sidebar`.
89
+ */
90
+ export const SIDEBAR_KEY = "M-b";
91
+
92
+ /** Default sidebar column width in tmux columns — narrow enough to leave the
93
+ * work area room, wide enough for a glyph + a truncated name. */
94
+ export const DEFAULT_SIDEBAR_WIDTH = 30;
95
+
96
+ /** Normalized sidebar settings. */
97
+ export interface ResolvedSidebar {
98
+ enabled: boolean;
99
+ width: number;
100
+ }
101
+
102
+ /**
103
+ * PURE — normalize the ide.yml `sidebar` sugar (`true` | `{ width }` | `false` |
104
+ * undefined) into `{ enabled, width }`. A missing/invalid width falls back to
105
+ * {@link DEFAULT_SIDEBAR_WIDTH}; width is clamped to a sane floor so a typo can't
106
+ * produce a zero-width column.
107
+ */
108
+ export function resolveSidebarConfig(raw: unknown): ResolvedSidebar {
109
+ if (raw === true) return { enabled: true, width: DEFAULT_SIDEBAR_WIDTH };
110
+ if (!raw || typeof raw !== "object") return { enabled: false, width: DEFAULT_SIDEBAR_WIDTH };
111
+ const width = parseSidebarWidth((raw as { width?: unknown }).width);
112
+ return { enabled: true, width };
113
+ }
114
+
115
+ /** PURE — parse a width value (string or number) to a positive int column count,
116
+ * falling back to the default and flooring at 10. */
117
+ export function parseSidebarWidth(value: unknown): number {
118
+ const n =
119
+ typeof value === "number" ? value : typeof value === "string" ? parseInt(value, 10) : NaN;
120
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_SIDEBAR_WIDTH;
121
+ return Math.max(10, Math.floor(n));
122
+ }
123
+
124
+ /**
125
+ * PURE — the tmux argv that binds the sidebar toggle key. `run-shell`
126
+ * format-expands `#{session_name}` into `--session` (bind args themselves do NOT
127
+ * expand; run-shell DOES — the same trick the menu bind uses), so the CLI toggles
128
+ * the column in whichever session the client is viewing. Server-wide root-table
129
+ * bind, mirroring the switcher's M-p — see the note on `unadoptSession`.
130
+ */
131
+ export function sidebarToggleBindCommand(
132
+ cli = "tmux-ide sidebar-toggle",
133
+ key = SIDEBAR_KEY,
134
+ ): string[] {
135
+ return ["bind-key", "-n", key, "run-shell", `${cli} --session '#{session_name}'`];
136
+ }
137
+
138
+ /** PURE — the tmux argv that removes the sidebar toggle binding. */
139
+ export function sidebarToggleUnbindCommand(key = SIDEBAR_KEY): string[] {
140
+ return ["unbind-key", "-n", key];
141
+ }
142
+
143
+ /**
144
+ * PURE — the `split-window` argv that opens the sidebar column. `-h -b -f`
145
+ * creates a horizontal split placed BEFORE (left of) the target that spans the
146
+ * FULL window height (`-f`) regardless of which pane is active, `-l <width>`
147
+ * fixes its column width, and `-P -F '#{pane_id}'` prints the new pane id. The
148
+ * trailing `widgetCmd` runs the sidebar widget in the pane.
149
+ */
150
+ export function sidebarSplitCommand(
151
+ session: string,
152
+ dir: string,
153
+ width: number,
154
+ widgetCmd: string,
155
+ ): string[] {
156
+ return [
157
+ "split-window",
158
+ "-P",
159
+ "-F",
160
+ "#{pane_id}",
161
+ "-t",
162
+ session,
163
+ "-h",
164
+ "-b",
165
+ "-f",
166
+ "-l",
167
+ String(width),
168
+ "-c",
169
+ dir,
170
+ widgetCmd,
171
+ ];
172
+ }
173
+
174
+ /**
175
+ * io — the sidebar pane id already open in `session`, or null. Reads the
176
+ * {@link SIDEBAR_PANE_OPTION} marker off each pane so a toggle can find and close
177
+ * an existing column.
178
+ */
179
+ export function findSidebarPane(session: string): string | null {
180
+ try {
181
+ const raw = runTmux(
182
+ ["list-panes", "-t", session, "-F", `#{pane_id}\t#{${SIDEBAR_PANE_OPTION}}`],
183
+ { encoding: "utf-8" },
184
+ )
185
+ .toString()
186
+ .trim();
187
+ for (const line of raw.split("\n").filter(Boolean)) {
188
+ const [id = "", flag = ""] = line.split("\t");
189
+ if (flag === "1" && id) return id;
190
+ }
191
+ } catch {
192
+ // no such session / tmux unavailable — treat as "no sidebar"
193
+ }
194
+ return null;
195
+ }
196
+
197
+ /**
198
+ * io — open the sidebar column in `session` on `dir`, mark the pane so the data
199
+ * layer excludes it, title it "sidebar" (the pane-border chip falls back to the
200
+ * title for non-agent panes), and return the new pane id.
201
+ */
202
+ export function openSidebarPane(
203
+ session: string,
204
+ dir: string,
205
+ width: number,
206
+ theme: ThemeConfig | null,
207
+ ): string {
208
+ const widgetCmd = sidebarWidgetCommand(sidebarWidgetScript(), session, dir, theme);
209
+ const paneId = runTmux(sidebarSplitCommand(session, dir, width, widgetCmd), {
210
+ encoding: "utf-8",
211
+ })
212
+ .toString()
213
+ .trim();
214
+ runTmux(["set-option", "-pqt", paneId, SIDEBAR_PANE_OPTION, "1"]);
215
+ runTmux(["select-pane", "-t", paneId, "-T", "sidebar"]);
216
+ return paneId;
217
+ }
218
+
219
+ /** io — close the sidebar column pane. */
220
+ export function closeSidebarPane(paneId: string): void {
221
+ runTmux(["kill-pane", "-t", paneId]);
222
+ }