tmux-ide 2.6.0 → 2.6.1

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 (209) hide show
  1. package/bin/cli.js +13 -5
  2. package/bin/cli.ts +1 -1
  3. package/bunfig.toml +4 -0
  4. package/package.json +11 -3
  5. package/packages/contracts/package.json +22 -0
  6. package/packages/contracts/src/__tests__/ide-config.test.ts +46 -0
  7. package/packages/contracts/src/__tests__/terminals.test.ts +87 -0
  8. package/packages/contracts/src/actions-contract.ts +310 -0
  9. package/packages/contracts/src/actions-errors.ts +41 -0
  10. package/packages/contracts/src/domain.ts +36 -0
  11. package/packages/contracts/src/ide-config.ts +170 -0
  12. package/packages/contracts/src/index.ts +24 -0
  13. package/packages/contracts/src/lib-internal/auth.ts +13 -0
  14. package/packages/contracts/src/lib-internal/hq.ts +38 -0
  15. package/packages/contracts/src/terminals.ts +116 -0
  16. package/packages/contracts/src/tmux.ts +60 -0
  17. package/packages/contracts/src/workspace.ts +67 -0
  18. package/packages/daemon/src/agent-explain.ts +270 -0
  19. package/packages/daemon/src/attach.ts +20 -0
  20. package/packages/daemon/src/bin.ts +4 -0
  21. package/packages/daemon/src/canonical.ts +7 -0
  22. package/packages/daemon/src/cli.ts +499 -0
  23. package/packages/daemon/src/command-center/actions/contract.ts +2 -0
  24. package/packages/daemon/src/command-center/actions/dispatcher.ts +137 -0
  25. package/packages/daemon/src/command-center/actions/errors.ts +105 -0
  26. package/packages/daemon/src/command-center/actions/handlers/_project-context.ts +30 -0
  27. package/packages/daemon/src/command-center/actions/handlers/_resolve-project.ts +78 -0
  28. package/packages/daemon/src/command-center/actions/handlers/app-set-remote-access.ts +118 -0
  29. package/packages/daemon/src/command-center/actions/handlers/config-actions.ts +113 -0
  30. package/packages/daemon/src/command-center/actions/handlers/daemon-shutdown.ts +38 -0
  31. package/packages/daemon/src/command-center/actions/handlers/project-activate.ts +30 -0
  32. package/packages/daemon/src/command-center/actions/handlers/project-launch.ts +70 -0
  33. package/packages/daemon/src/command-center/actions/handlers/project-open-terminal.ts +87 -0
  34. package/packages/daemon/src/command-center/actions/handlers/project-restart.ts +38 -0
  35. package/packages/daemon/src/command-center/actions/handlers/project-stop.ts +62 -0
  36. package/packages/daemon/src/command-center/actions/handlers/terminal-respawn.ts +119 -0
  37. package/packages/daemon/src/command-center/actions/handlers/terminal-stop.ts +35 -0
  38. package/packages/daemon/src/command-center/actions/registry.ts +149 -0
  39. package/packages/daemon/src/command-center/discovery.ts +96 -0
  40. package/packages/daemon/src/command-center/index.ts +31 -0
  41. package/packages/daemon/src/command-center/schemas.ts +85 -0
  42. package/packages/daemon/src/command-center/server.ts +1260 -0
  43. package/packages/daemon/src/command-center/ws-events.ts +316 -0
  44. package/packages/daemon/src/config.ts +549 -0
  45. package/packages/daemon/src/detect.ts +248 -0
  46. package/packages/daemon/src/doctor.ts +242 -0
  47. package/packages/daemon/src/embed.ts +5 -0
  48. package/packages/daemon/src/index.ts +12 -0
  49. package/packages/daemon/src/init.ts +211 -0
  50. package/packages/daemon/src/inspect.ts +178 -0
  51. package/packages/daemon/src/js-yaml.d.ts +10 -0
  52. package/packages/daemon/src/launch.ts +349 -0
  53. package/packages/daemon/src/lib/active-projects.ts +49 -0
  54. package/packages/daemon/src/lib/agent-discovery.ts +121 -0
  55. package/packages/daemon/src/lib/app-config.ts +336 -0
  56. package/packages/daemon/src/lib/app-settings.ts +53 -0
  57. package/packages/daemon/src/lib/auth/auth-service.ts +227 -0
  58. package/packages/daemon/src/lib/auth/middleware.ts +56 -0
  59. package/packages/daemon/src/lib/auth/types.ts +2 -0
  60. package/packages/daemon/src/lib/auth-token.ts +5 -0
  61. package/packages/daemon/src/lib/authorship.ts +280 -0
  62. package/packages/daemon/src/lib/canonical-daemon.ts +122 -0
  63. package/packages/daemon/src/lib/cli-action-bridge.ts +216 -0
  64. package/packages/daemon/src/lib/daemon-embed.ts +782 -0
  65. package/packages/daemon/src/lib/daemon-watchdog.ts +111 -0
  66. package/packages/daemon/src/lib/daemon.ts +79 -0
  67. package/packages/daemon/src/lib/dot-path.ts +17 -0
  68. package/packages/daemon/src/lib/errors.ts +67 -0
  69. package/packages/daemon/src/lib/filesystem-browser.ts +292 -0
  70. package/packages/daemon/src/lib/launch-plan.ts +90 -0
  71. package/packages/daemon/src/lib/log.ts +134 -0
  72. package/packages/daemon/src/lib/output.ts +76 -0
  73. package/packages/daemon/src/lib/project-init-runner.ts +150 -0
  74. package/packages/daemon/src/lib/project-inspect.ts +80 -0
  75. package/packages/daemon/src/lib/project-onboard.ts +149 -0
  76. package/packages/daemon/src/lib/project-probe.ts +92 -0
  77. package/packages/daemon/src/lib/project-registry.ts +296 -0
  78. package/packages/daemon/src/lib/session-monitor.ts +122 -0
  79. package/packages/daemon/src/lib/session-options.ts +100 -0
  80. package/packages/daemon/src/lib/shell.ts +8 -0
  81. package/packages/daemon/src/lib/sizes.ts +36 -0
  82. package/packages/daemon/src/lib/skill-sync.ts +155 -0
  83. package/packages/daemon/src/lib/slugify.ts +10 -0
  84. package/packages/daemon/src/lib/terminals-store.ts +125 -0
  85. package/packages/daemon/src/lib/update-check.ts +298 -0
  86. package/packages/daemon/src/lib/update.ts +158 -0
  87. package/packages/daemon/src/lib/workspace-registry.ts +229 -0
  88. package/packages/daemon/src/lib/worktree.ts +289 -0
  89. package/packages/daemon/src/lib/yaml-io.ts +27 -0
  90. package/packages/daemon/src/ls.ts +40 -0
  91. package/packages/daemon/src/restart.ts +24 -0
  92. package/packages/daemon/src/restore.ts +514 -0
  93. package/packages/daemon/src/schemas/domain.ts +2 -0
  94. package/packages/daemon/src/schemas/filesystem.ts +34 -0
  95. package/packages/daemon/src/schemas/ide-config.ts +2 -0
  96. package/packages/daemon/src/schemas/index.ts +59 -0
  97. package/packages/daemon/src/schemas/inspect.ts +67 -0
  98. package/packages/daemon/src/schemas/registry.ts +55 -0
  99. package/packages/daemon/src/schemas/ws-events.ts +135 -0
  100. package/packages/daemon/src/send.ts +171 -0
  101. package/packages/daemon/src/server/README.md +15 -0
  102. package/packages/daemon/src/server/index.ts +74 -0
  103. package/packages/daemon/src/server/pty-bridge.ts +532 -0
  104. package/packages/daemon/src/server/standalone.ts +18 -0
  105. package/packages/daemon/src/server/ws-route.ts +483 -0
  106. package/packages/daemon/src/status.ts +59 -0
  107. package/packages/daemon/src/stop.ts +28 -0
  108. package/packages/daemon/src/terminal/NodePtyAdapter.ts +271 -0
  109. package/packages/daemon/src/terminal/PtyAdapter.ts +140 -0
  110. package/packages/daemon/src/terminal/README.md +92 -0
  111. package/packages/daemon/src/tui/chrome/cheatsheet.ts +260 -0
  112. package/packages/daemon/src/tui/chrome/chip.ts +30 -0
  113. package/packages/daemon/src/tui/chrome/events.ts +119 -0
  114. package/packages/daemon/src/tui/chrome/kitty-keys.ts +55 -0
  115. package/packages/daemon/src/tui/chrome/menu.ts +289 -0
  116. package/packages/daemon/src/tui/chrome/notify.ts +195 -0
  117. package/packages/daemon/src/tui/chrome/panels.ts +111 -0
  118. package/packages/daemon/src/tui/chrome/sidebar.ts +222 -0
  119. package/packages/daemon/src/tui/chrome/snapshot.ts +425 -0
  120. package/packages/daemon/src/tui/chrome/statusline.ts +595 -0
  121. package/packages/daemon/src/tui/chrome/updater.ts +428 -0
  122. package/packages/daemon/src/tui/chrome/welcome.ts +124 -0
  123. package/packages/daemon/src/tui/compiled.ts +113 -0
  124. package/packages/daemon/src/tui/detect/classify.ts +193 -0
  125. package/packages/daemon/src/tui/detect/manifest-loader.ts +192 -0
  126. package/packages/daemon/src/tui/detect/manifest.ts +184 -0
  127. package/packages/daemon/src/tui/detect/manifests.ts +226 -0
  128. package/packages/daemon/src/tui/detect/process-tree.ts +197 -0
  129. package/packages/daemon/src/tui/detect/snapshot.ts +70 -0
  130. package/packages/daemon/src/tui/integrations/claude.ts +176 -0
  131. package/packages/daemon/src/tui/integrations/offer.ts +145 -0
  132. package/packages/daemon/src/tui/main.ts +70 -0
  133. package/packages/daemon/src/tui/mirror/control-client.ts +143 -0
  134. package/packages/daemon/src/tui/mirror/control.ts +97 -0
  135. package/packages/daemon/src/tui/mirror/pane-mirror.ts +81 -0
  136. package/packages/daemon/src/tui/mirror/viewer.tsx +166 -0
  137. package/packages/daemon/src/tui/team/CONTROL.md +50 -0
  138. package/packages/daemon/src/tui/team/entry.ts +11 -0
  139. package/packages/daemon/src/tui/team/fuzzy.ts +133 -0
  140. package/packages/daemon/src/tui/team/home.ts +170 -0
  141. package/packages/daemon/src/tui/team/index.tsx +1521 -0
  142. package/packages/daemon/src/tui/team/input.ts +34 -0
  143. package/packages/daemon/src/tui/team/keymap.ts +127 -0
  144. package/packages/daemon/src/tui/team/mouse.ts +29 -0
  145. package/packages/daemon/src/tui/team/nav.ts +31 -0
  146. package/packages/daemon/src/tui/team/preview.ts +34 -0
  147. package/packages/daemon/src/tui/team/projects.ts +191 -0
  148. package/packages/daemon/src/tui/team/report.ts +73 -0
  149. package/packages/daemon/src/tui/team/sessions.ts +344 -0
  150. package/packages/daemon/src/tui/team/tree.ts +62 -0
  151. package/packages/daemon/src/types.ts +13 -0
  152. package/packages/daemon/src/ui/index.ts +32 -0
  153. package/packages/daemon/src/ui/terminal/index.ts +9 -0
  154. package/packages/daemon/src/ui/types.ts +91 -0
  155. package/packages/daemon/src/ui/web/base.css +80 -0
  156. package/packages/daemon/src/ui/web/components/Box.tsx +59 -0
  157. package/packages/daemon/src/ui/web/components/Input.tsx +32 -0
  158. package/packages/daemon/src/ui/web/components/ScrollBox.tsx +60 -0
  159. package/packages/daemon/src/ui/web/components/Text.tsx +28 -0
  160. package/packages/daemon/src/ui/web/hooks.ts +106 -0
  161. package/packages/daemon/src/ui/web/index.ts +27 -0
  162. package/packages/daemon/src/ui/web/render.ts +77 -0
  163. package/packages/daemon/src/ui/web/utils/color.ts +27 -0
  164. package/packages/daemon/src/validate.ts +217 -0
  165. package/packages/daemon/src/widgets/changes/README.md +3 -0
  166. package/packages/daemon/src/widgets/changes/index.tsx +691 -0
  167. package/packages/daemon/src/widgets/config/README.md +3 -0
  168. package/packages/daemon/src/widgets/config/index.tsx +481 -0
  169. package/packages/daemon/src/widgets/explorer/README.md +3 -0
  170. package/packages/daemon/src/widgets/explorer/breadcrumbs.tsx +77 -0
  171. package/packages/daemon/src/widgets/explorer/footer.tsx +20 -0
  172. package/packages/daemon/src/widgets/explorer/header.tsx +23 -0
  173. package/packages/daemon/src/widgets/explorer/index.tsx +456 -0
  174. package/packages/daemon/src/widgets/explorer/tree-model.ts +103 -0
  175. package/packages/daemon/src/widgets/explorer/tree.tsx +165 -0
  176. package/packages/daemon/src/widgets/lib/config-model.ts +116 -0
  177. package/packages/daemon/src/widgets/lib/files.ts +88 -0
  178. package/packages/daemon/src/widgets/lib/git.ts +88 -0
  179. package/packages/daemon/src/widgets/lib/grammar.ts +126 -0
  180. package/packages/daemon/src/widgets/lib/help-overlay.tsx +101 -0
  181. package/packages/daemon/src/widgets/lib/pane-comms.ts +209 -0
  182. package/packages/daemon/src/widgets/lib/theme.ts +194 -0
  183. package/packages/daemon/src/widgets/lib/watcher.ts +132 -0
  184. package/packages/daemon/src/widgets/preview/README.md +3 -0
  185. package/packages/daemon/src/widgets/preview/index.tsx +416 -0
  186. package/packages/daemon/src/widgets/resolve.ts +121 -0
  187. package/packages/daemon/src/widgets/setup/README.md +3 -0
  188. package/packages/daemon/src/widgets/setup/agent-naming.tsx +112 -0
  189. package/packages/daemon/src/widgets/setup/config-tree.tsx +246 -0
  190. package/packages/daemon/src/widgets/setup/detect-panel.tsx +72 -0
  191. package/packages/daemon/src/widgets/setup/field-editor.tsx +265 -0
  192. package/packages/daemon/src/widgets/setup/footer.tsx +107 -0
  193. package/packages/daemon/src/widgets/setup/index.tsx +341 -0
  194. package/packages/daemon/src/widgets/setup/layout-picker.tsx +96 -0
  195. package/packages/daemon/src/widgets/setup/orchestrator-panel.tsx +200 -0
  196. package/packages/daemon/src/widgets/setup/review-panel.tsx +140 -0
  197. package/packages/daemon/src/widgets/setup/setup-model.ts +188 -0
  198. package/packages/daemon/src/widgets/sidebar/index.tsx +527 -0
  199. package/packages/tmux-bridge/package.json +22 -0
  200. package/packages/tmux-bridge/src/errors.ts +28 -0
  201. package/packages/tmux-bridge/src/index.ts +31 -0
  202. package/packages/tmux-bridge/src/monitor.ts +77 -0
  203. package/packages/tmux-bridge/src/panes.ts +136 -0
  204. package/packages/tmux-bridge/src/runner.test.ts +501 -0
  205. package/packages/tmux-bridge/src/runner.ts +91 -0
  206. package/packages/tmux-bridge/src/sessions.ts +126 -0
  207. package/packages/tmux-bridge/src/targeting.test.ts +107 -0
  208. package/packages/tmux-bridge/src/targeting.ts +90 -0
  209. package/scripts/postinstall.js +26 -2
@@ -0,0 +1,270 @@
1
+ /**
2
+ * `tmux-ide agent explain <pane>` — the detection debugger.
3
+ *
4
+ * For a single pane it prints EVERYTHING the fleet detector reasons over:
5
+ * - the pane's command + pid,
6
+ * - the `@agent_state` authority option (raw + parsed verdict + staleness),
7
+ * - the `@agent_hint` override option,
8
+ * - which manifest resolved and via which path (hint/fast/tree),
9
+ * - each state's rule → matched or not (via `explain()`),
10
+ * - the winning instantaneous classification, and
11
+ * - the bottom 5 snapshot lines it judged.
12
+ *
13
+ * This is READ-ONLY: it captures and inspects, it never sends keys or mutates
14
+ * the target pane.
15
+ */
16
+ import { execFileSync } from "node:child_process";
17
+ import { explain, type DetectedState } from "./tui/detect/manifest.ts";
18
+ import {
19
+ classifyInstant,
20
+ parseAuthority,
21
+ type AgentStatus,
22
+ type InstantState,
23
+ } from "./tui/detect/classify.ts";
24
+ import { getManifests } from "./tui/detect/manifest-loader.ts";
25
+ import { readProcessTable, resolveAgentCommand } from "./tui/detect/process-tree.ts";
26
+ import { readPaneSnapshot } from "./tui/detect/snapshot.ts";
27
+ import { IdeError } from "./lib/errors.ts";
28
+
29
+ /** Working/blocked authority reports older than this are stale (mirrors classify.ts). */
30
+ const AUTHORITY_STALE_SECONDS = 600;
31
+
32
+ interface PaneInfo {
33
+ id: string;
34
+ pid: number;
35
+ cmd: string;
36
+ title: string;
37
+ authorityRaw: string;
38
+ hintRaw: string;
39
+ }
40
+
41
+ export interface ExplainReport {
42
+ pane: { id: string; cmd: string; pid: number; title: string };
43
+ authority: {
44
+ raw: string | null;
45
+ state: string | null;
46
+ epoch: number | null;
47
+ ageSeconds: number | null;
48
+ stale: boolean;
49
+ verdict: AgentStatus | null;
50
+ };
51
+ hint: { raw: string | null; applied: boolean };
52
+ resolution: {
53
+ manifestId: string | null;
54
+ matchedCommand: string;
55
+ source: "hint" | "fast" | "tree" | "none";
56
+ };
57
+ states: Array<{ state: DetectedState; matched: boolean }>;
58
+ winner: DetectedState | null;
59
+ instant: InstantState;
60
+ classification: AgentStatus;
61
+ bottomLines: string[];
62
+ }
63
+
64
+ function tmux(args: string[]): string {
65
+ try {
66
+ return execFileSync("tmux", args, {
67
+ encoding: "utf8",
68
+ stdio: ["ignore", "pipe", "ignore"],
69
+ }).trim();
70
+ } catch {
71
+ return "";
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Resolve a target to a concrete pane's fields. `%N` is a pane id; anything
77
+ * else is treated as a session (or any tmux target) whose ACTIVE pane is used.
78
+ */
79
+ function readPaneInfo(target: string): PaneInfo | null {
80
+ const fmt =
81
+ "#{pane_id}\t#{pane_pid}\t#{pane_current_command}\t#{@agent_state}\t#{@agent_hint}\t#{pane_title}";
82
+ const raw = tmux(["display-message", "-p", "-t", target, "-F", fmt]);
83
+ if (!raw) return null;
84
+ const [id = "", pid = "", cmd = "", authorityRaw = "", hintRaw = "", ...titleParts] =
85
+ raw.split("\t");
86
+ if (!id) return null;
87
+ return {
88
+ id,
89
+ pid: Number(pid) || 0,
90
+ cmd,
91
+ authorityRaw,
92
+ hintRaw,
93
+ title: titleParts.join("\t"),
94
+ };
95
+ }
96
+
97
+ /** Build the full report for a pane. Does all the io (tmux + ps + capture). */
98
+ export function buildReport(target: string): ExplainReport {
99
+ const info = readPaneInfo(target);
100
+ if (!info) {
101
+ throw new IdeError(
102
+ `No pane found for "${target}". Pass a pane id (%N) or a live session name.`,
103
+ { code: "USAGE", exitCode: 1 },
104
+ );
105
+ }
106
+
107
+ const nowSec = Math.floor(Date.now() / 1000);
108
+
109
+ // Authority: parse richly for the report (parseAuthority collapses to a
110
+ // verdict; here we also expose the raw state/epoch/staleness).
111
+ const authRaw = info.authorityRaw || null;
112
+ let authState: string | null = null;
113
+ let authEpoch: number | null = null;
114
+ let ageSeconds: number | null = null;
115
+ let stale = false;
116
+ if (authRaw) {
117
+ const sep = authRaw.lastIndexOf(":");
118
+ if (sep !== -1) {
119
+ authState = authRaw.slice(0, sep);
120
+ const epoch = Number(authRaw.slice(sep + 1));
121
+ if (Number.isFinite(epoch)) {
122
+ authEpoch = epoch;
123
+ ageSeconds = nowSec - epoch;
124
+ stale =
125
+ (authState === "working" || authState === "blocked") &&
126
+ ageSeconds > AUTHORITY_STALE_SECONDS;
127
+ }
128
+ }
129
+ }
130
+ const verdict = parseAuthority(info.authorityRaw, nowSec);
131
+
132
+ // Resolution: hint > fast > tree, over the loaded (bundled + override) set.
133
+ const manifests = getManifests();
134
+ const table = readProcessTable();
135
+ const resolved = resolveAgentCommand(info.cmd, info.pid, table, {
136
+ manifests,
137
+ hint: info.hintRaw,
138
+ });
139
+ const manifest = resolved.manifest;
140
+
141
+ // Snapshot + per-state explanation.
142
+ const snapshot = { ...readPaneSnapshot(info.id), title: info.title };
143
+ const explained = manifest
144
+ ? explain(snapshot, manifest)
145
+ : {
146
+ state: null as DetectedState | null,
147
+ checked: [] as Array<{ state: DetectedState; matched: boolean }>,
148
+ };
149
+ const instant = classifyInstant(snapshot, manifest);
150
+
151
+ // Final classification mirrors sessions.ts: a fresh authority verdict wins;
152
+ // otherwise the instantaneous manifest classification. (The cross-tick `done`
153
+ // is not derivable from a single snapshot, so this is instant-only.)
154
+ const classification: AgentStatus = verdict ?? instant;
155
+
156
+ return {
157
+ pane: { id: info.id, cmd: info.cmd, pid: info.pid, title: info.title },
158
+ authority: {
159
+ raw: authRaw,
160
+ state: authState,
161
+ epoch: authEpoch,
162
+ ageSeconds,
163
+ stale,
164
+ verdict,
165
+ },
166
+ hint: { raw: info.hintRaw || null, applied: resolved.source === "hint" },
167
+ resolution: {
168
+ manifestId: manifest?.id ?? null,
169
+ matchedCommand: resolved.matchedCommand,
170
+ source: resolved.source,
171
+ },
172
+ states: explained.checked,
173
+ winner: explained.state,
174
+ instant,
175
+ classification,
176
+ bottomLines: snapshot.bottomNonEmpty.slice(-5),
177
+ };
178
+ }
179
+
180
+ const STATUS_COLOR: Record<AgentStatus, string> = {
181
+ blocked: "\x1b[31m", // red
182
+ working: "\x1b[33m", // yellow
183
+ done: "\x1b[32m", // green
184
+ idle: "\x1b[36m", // cyan
185
+ unknown: "\x1b[90m", // grey
186
+ };
187
+
188
+ /** Render the report as colored human-readable text. */
189
+ export function renderReport(r: ExplainReport, opts: { color?: boolean } = {}): string {
190
+ const color = opts.color ?? !("NO_COLOR" in process.env);
191
+ const c = (code: string, s: string) => (color ? `${code}${s}\x1b[0m` : s);
192
+ const bold = (s: string) => c("\x1b[1m", s);
193
+ const dim = (s: string) => c("\x1b[2m", s);
194
+ const label = (s: string) => c("\x1b[36m", s);
195
+ const status = (s: AgentStatus) => c(STATUS_COLOR[s] ?? "", s);
196
+ const yesno = (v: boolean) => (v ? c("\x1b[32m", "yes") : dim("no"));
197
+
198
+ const out: string[] = [];
199
+ out.push(bold(`agent explain — ${r.pane.id}`));
200
+ out.push(` ${label("command")} ${r.pane.cmd} ${dim(`(pid ${r.pane.pid})`)}`);
201
+ if (r.pane.title) out.push(` ${label("title")} ${r.pane.title}`);
202
+
203
+ // Authority
204
+ if (r.authority.raw) {
205
+ const age =
206
+ r.authority.ageSeconds !== null ? ` ${dim(`(${r.authority.ageSeconds}s ago)`)}` : "";
207
+ const staleTag = r.authority.stale ? " " + c("\x1b[31m", "[STALE → ignored]") : "";
208
+ const verdict = r.authority.verdict
209
+ ? status(r.authority.verdict)
210
+ : dim("none (stale/malformed)");
211
+ out.push(` ${label("authority")} ${r.authority.raw}${age}${staleTag} → ${verdict}`);
212
+ } else {
213
+ out.push(` ${label("authority")} ${dim("(unset — falling back to scraping)")}`);
214
+ }
215
+
216
+ // Hint
217
+ if (r.hint.raw) {
218
+ out.push(
219
+ ` ${label("hint")} @agent_hint=${r.hint.raw} → ${yesno(r.hint.applied)} applied`,
220
+ );
221
+ } else {
222
+ out.push(` ${label("hint")} ${dim("(unset)")}`);
223
+ }
224
+
225
+ // Resolution
226
+ const mid = r.resolution.manifestId ?? dim("none");
227
+ out.push(
228
+ ` ${label("manifest")} ${mid} ${dim(`via ${r.resolution.source}` + (r.resolution.matchedCommand ? ` "${r.resolution.matchedCommand}"` : ""))}`,
229
+ );
230
+
231
+ // Per-state rule results
232
+ out.push("");
233
+ out.push(bold(" state rules"));
234
+ if (r.states.length === 0) {
235
+ out.push(` ${dim("(no manifest resolved — nothing to evaluate)")}`);
236
+ } else {
237
+ for (const s of r.states) {
238
+ const mark = s.matched ? c("\x1b[32m", "✓ matched") : dim("· no match");
239
+ const win = r.winner === s.state ? " " + c("\x1b[1m", "← winner") : "";
240
+ out.push(` ${s.state.padEnd(8)} ${mark}${win}`);
241
+ }
242
+ }
243
+
244
+ // Final classification
245
+ out.push("");
246
+ out.push(
247
+ ` ${bold("classification")} ${status(r.classification)} ${dim(`(instant: ${r.instant})`)}`,
248
+ );
249
+
250
+ // Snapshot tail
251
+ out.push("");
252
+ out.push(bold(" bottom 5 lines judged"));
253
+ if (r.bottomLines.length === 0) {
254
+ out.push(` ${dim("(empty capture)")}`);
255
+ } else {
256
+ for (const line of r.bottomLines) out.push(` ${dim("│")} ${line}`);
257
+ }
258
+
259
+ return out.join("\n");
260
+ }
261
+
262
+ /** CLI entry: build + print the report (human or JSON). */
263
+ export function agentExplain(target: string, opts: { json?: boolean } = {}): void {
264
+ const report = buildReport(target);
265
+ if (opts.json) {
266
+ console.log(JSON.stringify(report, null, 2));
267
+ } else {
268
+ console.log(renderReport(report));
269
+ }
270
+ }
@@ -0,0 +1,20 @@
1
+ import { resolve } from "node:path";
2
+ import { getSessionName } from "./lib/yaml-io.ts";
3
+ import { outputError } from "./lib/output.ts";
4
+ import { attachSession, getSessionState } from "@tmux-ide/tmux-bridge";
5
+
6
+ export async function attach(
7
+ targetDir: string | undefined,
8
+ { json: _json }: { json?: boolean } = {},
9
+ ): Promise<void> {
10
+ const dir = resolve(targetDir ?? ".");
11
+ const { name: session } = getSessionName(dir);
12
+ const state = getSessionState(session);
13
+
14
+ if (!state.running) {
15
+ outputError(`Session "${session}" is not running. Start it with: tmux-ide`, "NOT_RUNNING");
16
+ return;
17
+ }
18
+
19
+ attachSession(session);
20
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bun
2
+ import { main } from "./cli.ts";
3
+
4
+ main();
@@ -0,0 +1,7 @@
1
+ export {
2
+ clearCanonicalDaemonInfo,
3
+ isCanonicalDaemonAlive,
4
+ readCanonicalDaemonInfo,
5
+ writeCanonicalDaemonInfo,
6
+ } from "./lib/canonical-daemon.ts";
7
+ export type { CanonicalDaemonInfo } from "./lib/canonical-daemon.ts";