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,41 @@
1
+ /**
2
+ * Wire codes for the v2 action dispatcher's error envelope. Codes are
3
+ * stable identifiers — never rename without a coordinated dashboard
4
+ * release. New codes go at the bottom of the union.
5
+ *
6
+ * The runtime class that throws these (`ActionError`, `TerminalCwdError`,
7
+ * etc.) lives in @tmux-ide/daemon's command-center/actions/errors.ts; the
8
+ * type lives here so dashboards and other UI clients can branch on the
9
+ * code without reaching into daemon-internal modules.
10
+ */
11
+ export type ActionErrorCode =
12
+ | "project_not_found"
13
+ | "cwd_not_found"
14
+ | "cwd_not_directory"
15
+ | "cwd_stat_failed"
16
+ | "session_already_running"
17
+ | "session_not_running"
18
+ | "launch_failed"
19
+ | "stop_failed"
20
+ | "terminal_not_found"
21
+ | "validation_failed"
22
+ | "task_not_found"
23
+ | "goal_not_found"
24
+ | "milestone_not_found"
25
+ | "mission_not_set"
26
+ | "task_dependency_unmet"
27
+ | "task_already_assigned"
28
+ | "skill_invalid"
29
+ | "skill_not_found"
30
+ | "ide_yml_missing"
31
+ | "config_path_invalid"
32
+ | "config_validation_failed"
33
+ | "validation_assertion_not_found"
34
+ | "webhook_not_found"
35
+ | "webhook_test_failed"
36
+ | "remote_access_restart_failed"
37
+ | "shutdown_already_in_progress"
38
+ | "bad_request"
39
+ | "thread_not_found"
40
+ | "permission_request_not_found"
41
+ | "internal";
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // PaneInfo — live tmux pane metadata (from src/command-center/discovery.ts)
5
+ // ---------------------------------------------------------------------------
6
+
7
+ export const PaneInfoSchemaZ = z.object({
8
+ id: z.string(),
9
+ index: z.number(),
10
+ title: z.string(),
11
+ currentCommand: z.string(),
12
+ width: z.number(),
13
+ height: z.number(),
14
+ active: z.boolean(),
15
+ role: z
16
+ .enum(["lead", "teammate", "planner", "validator", "researcher", "widget", "shell"])
17
+ .nullable(),
18
+ name: z.string().nullable(),
19
+ type: z.string().nullable(),
20
+ });
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // SessionOverview — minimal session listing entry
24
+ // ---------------------------------------------------------------------------
25
+
26
+ export const SessionOverviewSchemaZ = z.object({
27
+ name: z.string(),
28
+ dir: z.string(),
29
+ });
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Inferred types
33
+ // ---------------------------------------------------------------------------
34
+
35
+ export type PaneInfo = z.infer<typeof PaneInfoSchemaZ>;
36
+ export type SessionOverview = z.infer<typeof SessionOverviewSchemaZ>;
@@ -0,0 +1,170 @@
1
+ import { z } from "zod";
2
+ import { AuthConfigSchema } from "./lib-internal/auth.ts";
3
+ import { HQConfigSchema } from "./lib-internal/hq.ts";
4
+
5
+ const sizeField = z
6
+ .string()
7
+ .regex(/^[1-9]\d*%$/)
8
+ .refine((v) => parseInt(v) <= 100);
9
+
10
+ export const ThemeConfigSchema = z.object({
11
+ accent: z.string().optional(),
12
+ border: z.string().optional(),
13
+ bg: z.string().optional(),
14
+ fg: z.string().optional(),
15
+ });
16
+
17
+ export const PaneSchema = z.object({
18
+ title: z.string().optional(),
19
+ command: z.string().optional(),
20
+ type: z
21
+ .enum([
22
+ "explorer",
23
+ "changes",
24
+ "preview",
25
+ "tasks",
26
+ "costs",
27
+ "config",
28
+ "mission-control",
29
+ "sidebar",
30
+ ])
31
+ .optional(),
32
+ target: z.string().optional(),
33
+ dir: z.string().optional(),
34
+ size: sizeField.optional(),
35
+ focus: z.boolean().optional(),
36
+ env: z.record(z.string(), z.union([z.string(), z.number()])).optional(),
37
+ role: z.enum(["lead", "teammate", "planner", "validator", "researcher"]).optional(),
38
+ task: z.string().optional(),
39
+ specialty: z.string().optional(),
40
+ skill: z.string().optional(),
41
+ });
42
+
43
+ export const RowSchema = z.object({
44
+ size: sizeField.optional(),
45
+ panes: z.array(PaneSchema).min(1),
46
+ });
47
+
48
+ export const WebhookConfigSchema = z.object({
49
+ url: z.string(),
50
+ events: z.array(z.string()).optional(),
51
+ secret: z.string().optional(),
52
+ });
53
+
54
+ export const OrchestratorYamlConfigSchema = z.object({
55
+ enabled: z.boolean().optional(),
56
+ port: z.number().int().positive().optional(),
57
+ auto_dispatch: z.boolean().optional(),
58
+ stall_timeout: z.number().optional(),
59
+ poll_interval: z.number().min(100).optional(),
60
+ master_pane: z.string().optional(),
61
+ before_run: z.string().optional(),
62
+ after_run: z.string().optional(),
63
+ dispatch_mode: z.enum(["tasks", "goals", "missions"]).optional(),
64
+ max_concurrent_agents: z.number().min(1).max(50).optional(),
65
+ widgets: z.boolean().optional(),
66
+ webhooks: z.array(WebhookConfigSchema).optional(),
67
+ services: z
68
+ .record(
69
+ z.string(),
70
+ z.object({
71
+ command: z.string(),
72
+ port: z.number().optional(),
73
+ healthcheck: z.string().optional(),
74
+ }),
75
+ )
76
+ .optional(),
77
+ research: z
78
+ .object({
79
+ enabled: z.boolean().optional(),
80
+ triggers: z
81
+ .object({
82
+ mission_start: z.boolean().optional(),
83
+ milestone_progress: z.number().min(0).optional(),
84
+ milestone_complete: z.boolean().optional(),
85
+ periodic_minutes: z.number().min(0).optional(),
86
+ retry_cluster: z.boolean().optional(),
87
+ stall_detected: z.boolean().optional(),
88
+ discovered_issue: z.boolean().optional(),
89
+ })
90
+ .optional(),
91
+ })
92
+ .optional(),
93
+ });
94
+
95
+ export const TunnelConfigSchema = z.object({
96
+ provider: z.enum(["tailscale", "ngrok", "cloudflare"]),
97
+ auto_start: z.boolean().optional(),
98
+ port: z.number().int().positive().optional(),
99
+ domain: z.string().optional(),
100
+ authtoken: z.string().optional(),
101
+ });
102
+
103
+ export const CommandCenterConfigSchema = z.object({
104
+ port: z.number().optional(),
105
+ enabled: z.boolean().optional(),
106
+ });
107
+
108
+ export const DashboardConfigSchema = z.object({
109
+ port: z.number().int().positive().optional(),
110
+ });
111
+
112
+ /**
113
+ * The app nav-column sidebar sugar. `sidebar: true` injects a full-height left
114
+ * column (default width) at launch; `sidebar: { width: "30" }` sets its column
115
+ * width (in tmux cols, as a string to match the CLI/tmux world). `false` /
116
+ * omitted means no sidebar.
117
+ */
118
+ export const SidebarConfigSchema = z.union([
119
+ z.boolean(),
120
+ z.object({ width: z.string().optional() }),
121
+ ]);
122
+
123
+ export const IdeConfigSchema = z.object({
124
+ name: z.string().optional(),
125
+ before: z.string().optional(),
126
+ team: z
127
+ .object({
128
+ name: z.string(),
129
+ model: z.string().optional(),
130
+ permissions: z.array(z.string()).optional(),
131
+ })
132
+ .optional(),
133
+ rows: z.array(RowSchema).min(1),
134
+ sidebar: SidebarConfigSchema.optional(),
135
+ theme: ThemeConfigSchema.optional(),
136
+ orchestrator: OrchestratorYamlConfigSchema.optional(),
137
+ command_center: CommandCenterConfigSchema.optional(),
138
+ dashboard: DashboardConfigSchema.optional(),
139
+ auth: AuthConfigSchema.optional(),
140
+ tunnel: TunnelConfigSchema.optional(),
141
+ hq: HQConfigSchema.optional(),
142
+ });
143
+
144
+ export const PaneActionSchema = z.object({
145
+ targetPane: z.string(),
146
+ title: z.string().nullable(),
147
+ chdir: z.string().nullable(),
148
+ exports: z.array(z.string()),
149
+ command: z.string().nullable(),
150
+ widgetType: z.string().nullable(),
151
+ widgetTarget: z.string().nullable(),
152
+ paneRole: z.string().nullable(),
153
+ paneType: z.string().nullable(),
154
+ });
155
+
156
+ export const SessionStateSchema = z.object({
157
+ running: z.boolean(),
158
+ reason: z.string().nullable(),
159
+ });
160
+
161
+ export type ThemeConfig = z.infer<typeof ThemeConfigSchema>;
162
+ export type Pane = z.infer<typeof PaneSchema>;
163
+ export type Row = z.infer<typeof RowSchema>;
164
+ export type OrchestratorYamlConfig = z.infer<typeof OrchestratorYamlConfigSchema>;
165
+ export type SidebarConfig = z.infer<typeof SidebarConfigSchema>;
166
+ export type CommandCenterConfig = z.infer<typeof CommandCenterConfigSchema>;
167
+ export type DashboardConfig = z.infer<typeof DashboardConfigSchema>;
168
+ export type IdeConfig = z.infer<typeof IdeConfigSchema>;
169
+ export type PaneAction = z.infer<typeof PaneActionSchema>;
170
+ export type SessionState = z.infer<typeof SessionStateSchema>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @tmux-ide/contracts — shared Zod schemas for the daemon ↔ dashboard
3
+ * boundary. Dashboard, packages/daemon, and any future v2 surfaces all
4
+ * import from this single source of truth.
5
+ *
6
+ * Adding a schema? Put it in either `domain.ts` (runtime data: tasks,
7
+ * missions, sessions, …) or `ide-config.ts` (the on-disk ide.yml
8
+ * shape). Both files are re-exported wholesale below — no manual
9
+ * registration needed.
10
+ *
11
+ * The `lib-internal/` directory holds schema-only helpers (auth, hq)
12
+ * that ide-config depends on — they are exported transitively here
13
+ * so consumers don't have to deep-import.
14
+ */
15
+
16
+ export * from "./lib-internal/auth.ts";
17
+ export * from "./lib-internal/hq.ts";
18
+ export * from "./ide-config.ts";
19
+ export * from "./domain.ts";
20
+ export * from "./tmux.ts";
21
+ export * from "./workspace.ts";
22
+ export * from "./actions-contract.ts";
23
+ export * from "./actions-errors.ts";
24
+ export * from "./terminals.ts";
@@ -0,0 +1,13 @@
1
+ // Based on VibeTunnel (MIT) — github.com/amantus-ai/vibetunnel
2
+ import { z } from "zod";
3
+
4
+ export const AuthConfigSchema = z.object({
5
+ /** Auth method: "none" disables auth (default), "ssh" enables SSH key challenge-response. */
6
+ method: z.enum(["none", "ssh"]).default("none"),
7
+ /** JWT secret — auto-generated if omitted. */
8
+ secret: z.string().optional(),
9
+ /** Token expiry in seconds (default 86400 = 24h). */
10
+ token_expiry: z.number().min(60).default(86400),
11
+ });
12
+
13
+ export type AuthConfig = z.infer<typeof AuthConfigSchema>;
@@ -0,0 +1,38 @@
1
+ // Based on VibeTunnel (MIT) — github.com/amantus-ai/vibetunnel
2
+ //
3
+ // Schemas + serializable interfaces only. The daemon-only `RemoteMachine`
4
+ // runtime type (Date / Set fields) lives in @tmux-ide/daemon's lib/hq —
5
+ // not here.
6
+ import { z } from "zod";
7
+
8
+ export interface HQConfig {
9
+ enabled: boolean;
10
+ role: "hq" | "remote";
11
+ hq_url?: string;
12
+ secret?: string;
13
+ heartbeat_interval?: number;
14
+ machine_name?: string;
15
+ }
16
+
17
+ export interface RegistrationPayload {
18
+ id: string;
19
+ name: string;
20
+ url: string;
21
+ token: string;
22
+ }
23
+
24
+ export const RegistrationPayloadSchema = z.object({
25
+ id: z.string().min(1),
26
+ name: z.string().min(1),
27
+ url: z.string().url(),
28
+ token: z.string().min(1),
29
+ });
30
+
31
+ export const HQConfigSchema = z.object({
32
+ enabled: z.boolean().default(false),
33
+ role: z.enum(["hq", "remote"]),
34
+ hq_url: z.string().url().optional(),
35
+ secret: z.string().optional(),
36
+ heartbeat_interval: z.number().min(1000).default(15000),
37
+ machine_name: z.string().optional(),
38
+ });
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Multi-terminal registry contracts (G20-P1).
3
+ *
4
+ * A `Terminal` is a *named pool entry* — metadata about a PTY the
5
+ * daemon may or may not have spawned yet. The daemon's existing
6
+ * `/ws/pty/:id` endpoint owns the actual process lifecycle; this
7
+ * registry exists so the UI can persist tab labels + restore them
8
+ * across reloads, and so deterministic scope/script tabs collapse to
9
+ * the same id (re-opens land on the running scrollback).
10
+ */
11
+
12
+ import { z } from "zod";
13
+
14
+ // ---------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------
17
+
18
+ export type TerminalKind = "shell" | "setup" | "run" | "teardown";
19
+
20
+ export interface Terminal {
21
+ /** Deterministic id for scope/script tabs; UUID for ad-hoc tabs. */
22
+ id: string;
23
+ /** Session name the terminal belongs to. */
24
+ projectId: string;
25
+ /** Sub-grouping inside a session (mission id, dir hash, …). Free-form. */
26
+ scopeId: string;
27
+ /** Free-text display name. The tab strip renames this without changing id. */
28
+ name: string;
29
+ kind: TerminalKind;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ /** Set when the id was derived via `createScriptTerminalId` so the
33
+ * client can show a "shared script" badge in the tab strip. */
34
+ scripted?: boolean;
35
+ }
36
+
37
+ /** Live runtime info — derived from the daemon's bridge registry, not
38
+ * the JSON store. Combined with `Terminal` for the list endpoint. */
39
+ export interface TerminalRuntime {
40
+ running: boolean;
41
+ cols?: number;
42
+ rows?: number;
43
+ /** Bytes available in the replay ring buffer when a client subscribes. */
44
+ replayBytes?: number;
45
+ }
46
+
47
+ export interface TerminalWithRuntime extends Terminal {
48
+ runtime: TerminalRuntime;
49
+ }
50
+
51
+ export interface TerminalListResponse {
52
+ terminals: TerminalWithRuntime[];
53
+ }
54
+
55
+ // ---------------------------------------------------------------------
56
+ // Deterministic id
57
+ // ---------------------------------------------------------------------
58
+
59
+ /** Build a deterministic 32-char id from `(projectId, scopeId, kind,
60
+ * script)`. Two different callers asking for "the run script terminal
61
+ * for project X, scope Y, script Z" get the same id and land on the
62
+ * same bridge — which means scrollback survives a browser reload. */
63
+ export async function createScriptTerminalId(args: {
64
+ projectId: string;
65
+ scopeId?: string;
66
+ /** Backward-compat alias for `scopeId`. */
67
+ taskId?: string;
68
+ kind: TerminalKind;
69
+ script: string;
70
+ }): Promise<string> {
71
+ const scope = args.scopeId ?? args.taskId;
72
+ if (!scope) {
73
+ throw new Error("createScriptTerminalId: scopeId (or taskId) is required");
74
+ }
75
+ const key = `${args.projectId}::${scope}::${args.kind}::${args.script}`;
76
+ const data = new TextEncoder().encode(key);
77
+ const digest = await crypto.subtle.digest("SHA-256", data);
78
+ return Array.from(new Uint8Array(digest))
79
+ .map((b) => b.toString(16).padStart(2, "0"))
80
+ .join("")
81
+ .slice(0, 32);
82
+ }
83
+
84
+ // ---------------------------------------------------------------------
85
+ // Zod request schemas
86
+ // ---------------------------------------------------------------------
87
+
88
+ export const terminalKindSchema = z.enum(["shell", "setup", "run", "teardown"]);
89
+
90
+ export const terminalCreateRequestSchema = z
91
+ .object({
92
+ scopeId: z.string().trim().min(1).max(256),
93
+ name: z.string().trim().min(1).max(120),
94
+ kind: terminalKindSchema.optional(),
95
+ /** Provide for script tabs to opt into deterministic id collapse. */
96
+ script: z.string().max(2048).optional(),
97
+ /** Explicit id wins. Used by the dashboard to reserve a known id
98
+ * (e.g. the default shell tab derived from session.dir). */
99
+ id: z
100
+ .string()
101
+ .trim()
102
+ .min(8)
103
+ .max(64)
104
+ .regex(/^[A-Za-z0-9_-]+$/u, "id may only contain alphanumerics, '-', '_'")
105
+ .optional(),
106
+ })
107
+ .refine((v) => v.kind !== undefined || v.script === undefined, {
108
+ message: "script requires kind",
109
+ path: ["script"],
110
+ });
111
+ export type TerminalCreateRequest = z.infer<typeof terminalCreateRequestSchema>;
112
+
113
+ export const terminalRenameRequestSchema = z.object({
114
+ name: z.string().trim().min(1).max(120),
115
+ });
116
+ export type TerminalRenameRequest = z.infer<typeof terminalRenameRequestSchema>;
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Tmux state schemas — the canonical typed surface for tmux sessions,
5
+ * windows, panes, and pane addressing.
6
+ *
7
+ * Used by:
8
+ * - packages/tmux-bridge — implementation that shells out to tmux.
9
+ * - packages/daemon (session-monitor, discovery, command-center) —
10
+ * materializes these shapes into the live runtime state.
11
+ * - dashboard — consumes them via the typed REST/SSE clients.
12
+ *
13
+ * Adding a new addressing mode (e.g. byRegex) is intentionally a one-line
14
+ * discriminant change here; bridge resolvers must update in lockstep.
15
+ */
16
+
17
+ export const TmuxPaneSchemaZ = z.object({
18
+ /** Stable tmux pane id (e.g. `%23`). */
19
+ id: z.string(),
20
+ /** Pane index within its window (zero-based). */
21
+ paneIndex: z.number().int().nonnegative(),
22
+ /** Window index within the session (zero-based). */
23
+ windowIndex: z.number().int().nonnegative(),
24
+ title: z.string().nullable(),
25
+ command: z.string().nullable(),
26
+ active: z.boolean(),
27
+ });
28
+ export type TmuxPane = z.infer<typeof TmuxPaneSchemaZ>;
29
+
30
+ export const TmuxWindowSchemaZ = z.object({
31
+ index: z.number().int().nonnegative(),
32
+ name: z.string(),
33
+ panes: z.array(TmuxPaneSchemaZ),
34
+ });
35
+ export type TmuxWindow = z.infer<typeof TmuxWindowSchemaZ>;
36
+
37
+ export const TmuxSessionSchemaZ = z.object({
38
+ name: z.string(),
39
+ windows: z.array(TmuxWindowSchemaZ),
40
+ /** Session creation time (epoch milliseconds). */
41
+ created: z.number().int().nonnegative(),
42
+ attached: z.boolean(),
43
+ /** Project directory the session was launched from, when known. */
44
+ projectDir: z.string().nullable(),
45
+ });
46
+ export type TmuxSession = z.infer<typeof TmuxSessionSchemaZ>;
47
+
48
+ /**
49
+ * Discriminated union for addressing a pane. The bridge's resolver
50
+ * implements byId/byIndex/byTitle directly; byRole is resolved at the
51
+ * daemon layer (it requires reading the ide.yml-driven role mapping)
52
+ * and translated into a concrete address before reaching the bridge.
53
+ */
54
+ export const TmuxPaneTargetSchemaZ = z.discriminatedUnion("kind", [
55
+ z.object({ kind: z.literal("byId"), id: z.string() }),
56
+ z.object({ kind: z.literal("byIndex"), index: z.number().int().nonnegative() }),
57
+ z.object({ kind: z.literal("byTitle"), title: z.string() }),
58
+ z.object({ kind: z.literal("byRole"), role: z.string() }),
59
+ ]);
60
+ export type TmuxPaneTarget = z.infer<typeof TmuxPaneTargetSchemaZ>;
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * A workspace is a project the daemon is actively serving — identified by
5
+ * its tmux session name and bound to a project directory.
6
+ *
7
+ * The workspace registry replaces the historical single-session
8
+ * TMUX_IDE_SESSION env coupling, letting the daemon serve N projects in
9
+ * one process. Goal 12 of the t3-style consolidation; see T065.
10
+ */
11
+ export const WorkspaceSchemaZ = z.object({
12
+ /** Stable workspace name (typically equal to tmux session name). */
13
+ name: z.string().min(1),
14
+ /** Tmux session this workspace maps to. */
15
+ sessionName: z.string().min(1),
16
+ /** Absolute project directory. */
17
+ projectDir: z.string().min(1),
18
+ /** Absolute path to the ide.yml driving this workspace. */
19
+ ideConfigPath: z.string().nullable(),
20
+ /** ISO timestamp of when the workspace was added. */
21
+ addedAt: z.string(),
22
+ });
23
+ export type Workspace = z.infer<typeof WorkspaceSchemaZ>;
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // HTTP request / response shapes for /api/workspaces
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export const WorkspaceListResponseSchemaZ = z.object({
30
+ workspaces: z.array(WorkspaceSchemaZ),
31
+ });
32
+ export type WorkspaceListResponse = z.infer<typeof WorkspaceListResponseSchemaZ>;
33
+
34
+ export const AddWorkspaceRequestSchemaZ = z.object({
35
+ /** Absolute path to the project directory. */
36
+ projectDir: z.string().min(1),
37
+ /** Optional explicit workspace name. Auto-derived from basename when absent. */
38
+ name: z.string().min(1).optional(),
39
+ /** Optional override for the tmux session name (defaults to `name`). */
40
+ sessionName: z.string().min(1).optional(),
41
+ /** Optional ide.yml path the workspace was launched with. */
42
+ ideConfigPath: z.string().min(1).optional(),
43
+ });
44
+ export type AddWorkspaceRequest = z.infer<typeof AddWorkspaceRequestSchemaZ>;
45
+
46
+ export const AddWorkspaceResponseSchemaZ = z.object({
47
+ workspace: WorkspaceSchemaZ,
48
+ });
49
+ export type AddWorkspaceResponse = z.infer<typeof AddWorkspaceResponseSchemaZ>;
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // WebSocket event frames — fired on registry add/remove (including the
53
+ // auto-reconcile loop that drops dead sessions). Kept here so dashboard /
54
+ // daemon consumers share the exact discriminator strings.
55
+ // ---------------------------------------------------------------------------
56
+
57
+ export const WorkspaceAddedFrameSchemaZ = z.object({
58
+ type: z.literal("workspace.added"),
59
+ workspace: WorkspaceSchemaZ,
60
+ });
61
+ export type WorkspaceAddedFrame = z.infer<typeof WorkspaceAddedFrameSchemaZ>;
62
+
63
+ export const WorkspaceRemovedFrameSchemaZ = z.object({
64
+ type: z.literal("workspace.removed"),
65
+ name: z.string(),
66
+ });
67
+ export type WorkspaceRemovedFrame = z.infer<typeof WorkspaceRemovedFrameSchemaZ>;
@@ -1,4 +1,4 @@
1
- import { type DetectedState } from "./tui/detect/manifest.ts";
1
+ import { type DetectedState, type ManifestConfidence } from "./tui/detect/manifest.ts";
2
2
  import { type AgentStatus, type InstantState } from "./tui/detect/classify.ts";
3
3
  export interface ExplainReport {
4
4
  pane: {
@@ -23,6 +23,13 @@ export interface ExplainReport {
23
23
  manifestId: string | null;
24
24
  matchedCommand: string;
25
25
  source: "hint" | "fast" | "tree" | "none";
26
+ /** The matched manifest's evidence confidence (null when nothing resolved). */
27
+ confidence: ManifestConfidence | null;
28
+ /**
29
+ * Executable-ish tokens seen in the pane's process subtree. Populated for
30
+ * the "no manifest matched" case so the report can say what it DID find.
31
+ */
32
+ subtree: string[];
26
33
  };
27
34
  states: Array<{
28
35
  state: DetectedState;
@@ -17,7 +17,7 @@ import { execFileSync } from "node:child_process";
17
17
  import { explain } from "./tui/detect/manifest.js";
18
18
  import { classifyInstant, parseAuthority, } from "./tui/detect/classify.js";
19
19
  import { getManifests } from "./tui/detect/manifest-loader.js";
20
- import { readProcessTable, resolveAgentCommand } from "./tui/detect/process-tree.js";
20
+ import { describeSubtree, readProcessTable, resolveAgentCommand, } from "./tui/detect/process-tree.js";
21
21
  import { readPaneSnapshot } from "./tui/detect/snapshot.js";
22
22
  import { IdeError } from "./lib/errors.js";
23
23
  /** Working/blocked authority reports older than this are stale (mirrors classify.ts). */
@@ -91,6 +91,9 @@ export function buildReport(target) {
91
91
  hint: info.hintRaw,
92
92
  });
93
93
  const manifest = resolved.manifest;
94
+ // When nothing resolved, record what the process-tree DID see so `explain`
95
+ // can point the user at an `@agent_hint` target instead of a dead end.
96
+ const subtree = manifest ? [] : describeSubtree(table, info.pid);
94
97
  // Snapshot + per-state explanation.
95
98
  const snapshot = { ...readPaneSnapshot(info.id), title: info.title };
96
99
  const explained = manifest
@@ -119,6 +122,8 @@ export function buildReport(target) {
119
122
  manifestId: manifest?.id ?? null,
120
123
  matchedCommand: resolved.matchedCommand,
121
124
  source: resolved.source,
125
+ confidence: manifest ? (manifest.confidence ?? "conservative") : null,
126
+ subtree,
122
127
  },
123
128
  states: explained.checked,
124
129
  winner: explained.state,
@@ -168,8 +173,19 @@ export function renderReport(r, opts = {}) {
168
173
  out.push(` ${label("hint")} ${dim("(unset)")}`);
169
174
  }
170
175
  // Resolution
171
- const mid = r.resolution.manifestId ?? dim("none");
172
- out.push(` ${label("manifest")} ${mid} ${dim(`via ${r.resolution.source}` + (r.resolution.matchedCommand ? ` "${r.resolution.matchedCommand}"` : ""))}`);
176
+ if (r.resolution.manifestId) {
177
+ const conf = r.resolution.confidence === "tuned"
178
+ ? c("\x1b[32m", "tuned")
179
+ : dim(r.resolution.confidence ?? "conservative");
180
+ out.push(` ${label("manifest")} ${r.resolution.manifestId} ${dim(`via ${r.resolution.source}` + (r.resolution.matchedCommand ? ` "${r.resolution.matchedCommand}"` : ""))} [${conf}]`);
181
+ }
182
+ else {
183
+ // No manifest matched — say so, and report what the process-tree DID see so
184
+ // the user can set an `@agent_hint` or add a manifest.
185
+ const saw = r.resolution.subtree.length > 0 ? r.resolution.subtree.join(", ") : r.pane.cmd || "(nothing)";
186
+ out.push(` ${label("manifest")} ${dim("none matched")} — ${dim(`process-tree saw: ${saw}`)}`);
187
+ out.push(` ${dim("set `tmux set-option -p @agent_hint <agent>` to force one")}`);
188
+ }
173
189
  // Per-state rule results
174
190
  out.push("");
175
191
  out.push(bold(" state rules"));