@gigaflow/gmux 0.15.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 (229) hide show
  1. package/CHANGELOG.md +654 -0
  2. package/LICENSE +21 -0
  3. package/README.md +351 -0
  4. package/dist/adapters/claude-code.d.ts +52 -0
  5. package/dist/adapters/claude-code.js +315 -0
  6. package/dist/adapters/claude-code.js.map +1 -0
  7. package/dist/adapters/codex.d.ts +39 -0
  8. package/dist/adapters/codex.js +233 -0
  9. package/dist/adapters/codex.js.map +1 -0
  10. package/dist/adapters/jsonl.d.ts +40 -0
  11. package/dist/adapters/jsonl.js +92 -0
  12. package/dist/adapters/jsonl.js.map +1 -0
  13. package/dist/adapters/registry.d.ts +11 -0
  14. package/dist/adapters/registry.js +21 -0
  15. package/dist/adapters/registry.js.map +1 -0
  16. package/dist/adapters/types.d.ts +43 -0
  17. package/dist/adapters/types.js +2 -0
  18. package/dist/adapters/types.js.map +1 -0
  19. package/dist/cli/border-client.d.ts +8 -0
  20. package/dist/cli/border-client.js +11 -0
  21. package/dist/cli/border-client.js.map +1 -0
  22. package/dist/cli/commands/__ask-cancel.d.ts +35 -0
  23. package/dist/cli/commands/__ask-cancel.js +91 -0
  24. package/dist/cli/commands/__ask-cancel.js.map +1 -0
  25. package/dist/cli/commands/__ask-refresh.d.ts +40 -0
  26. package/dist/cli/commands/__ask-refresh.js +54 -0
  27. package/dist/cli/commands/__ask-refresh.js.map +1 -0
  28. package/dist/cli/commands/__ask-run.d.ts +43 -0
  29. package/dist/cli/commands/__ask-run.js +126 -0
  30. package/dist/cli/commands/__ask-run.js.map +1 -0
  31. package/dist/cli/commands/__ask-send.d.ts +50 -0
  32. package/dist/cli/commands/__ask-send.js +138 -0
  33. package/dist/cli/commands/__ask-send.js.map +1 -0
  34. package/dist/cli/commands/__preview-card.d.ts +31 -0
  35. package/dist/cli/commands/__preview-card.js +72 -0
  36. package/dist/cli/commands/__preview-card.js.map +1 -0
  37. package/dist/cli/commands/ask.d.ts +50 -0
  38. package/dist/cli/commands/ask.js +240 -0
  39. package/dist/cli/commands/ask.js.map +1 -0
  40. package/dist/cli/commands/auto.d.ts +12 -0
  41. package/dist/cli/commands/auto.js +28 -0
  42. package/dist/cli/commands/auto.js.map +1 -0
  43. package/dist/cli/commands/cockpit.d.ts +14 -0
  44. package/dist/cli/commands/cockpit.js +52 -0
  45. package/dist/cli/commands/cockpit.js.map +1 -0
  46. package/dist/cli/commands/daemon.d.ts +75 -0
  47. package/dist/cli/commands/daemon.js +258 -0
  48. package/dist/cli/commands/daemon.js.map +1 -0
  49. package/dist/cli/commands/doctor.d.ts +6 -0
  50. package/dist/cli/commands/doctor.js +159 -0
  51. package/dist/cli/commands/doctor.js.map +1 -0
  52. package/dist/cli/commands/grep.d.ts +2 -0
  53. package/dist/cli/commands/grep.js +52 -0
  54. package/dist/cli/commands/grep.js.map +1 -0
  55. package/dist/cli/commands/index-cmd.d.ts +2 -0
  56. package/dist/cli/commands/index-cmd.js +29 -0
  57. package/dist/cli/commands/index-cmd.js.map +1 -0
  58. package/dist/cli/commands/ls.d.ts +30 -0
  59. package/dist/cli/commands/ls.js +95 -0
  60. package/dist/cli/commands/ls.js.map +1 -0
  61. package/dist/cli/commands/overlay.d.ts +26 -0
  62. package/dist/cli/commands/overlay.js +216 -0
  63. package/dist/cli/commands/overlay.js.map +1 -0
  64. package/dist/cli/commands/pick.d.ts +59 -0
  65. package/dist/cli/commands/pick.js +254 -0
  66. package/dist/cli/commands/pick.js.map +1 -0
  67. package/dist/cli/commands/picker-rows.d.ts +17 -0
  68. package/dist/cli/commands/picker-rows.js +47 -0
  69. package/dist/cli/commands/picker-rows.js.map +1 -0
  70. package/dist/cli/commands/resume.d.ts +25 -0
  71. package/dist/cli/commands/resume.js +88 -0
  72. package/dist/cli/commands/resume.js.map +1 -0
  73. package/dist/cli/commands/run.d.ts +13 -0
  74. package/dist/cli/commands/run.js +78 -0
  75. package/dist/cli/commands/run.js.map +1 -0
  76. package/dist/cli/commands/setup.d.ts +37 -0
  77. package/dist/cli/commands/setup.js +200 -0
  78. package/dist/cli/commands/setup.js.map +1 -0
  79. package/dist/cli/commands/show.d.ts +2 -0
  80. package/dist/cli/commands/show.js +24 -0
  81. package/dist/cli/commands/show.js.map +1 -0
  82. package/dist/cli/commands/summarize.d.ts +2 -0
  83. package/dist/cli/commands/summarize.js +59 -0
  84. package/dist/cli/commands/summarize.js.map +1 -0
  85. package/dist/cli/commands/tmux.d.ts +11 -0
  86. package/dist/cli/commands/tmux.js +90 -0
  87. package/dist/cli/commands/tmux.js.map +1 -0
  88. package/dist/cli/commands/watch.d.ts +2 -0
  89. package/dist/cli/commands/watch.js +76 -0
  90. package/dist/cli/commands/watch.js.map +1 -0
  91. package/dist/cli/format.d.ts +74 -0
  92. package/dist/cli/format.js +242 -0
  93. package/dist/cli/format.js.map +1 -0
  94. package/dist/cli/gmux-render.d.ts +11 -0
  95. package/dist/cli/gmux-render.js +60 -0
  96. package/dist/cli/gmux-render.js.map +1 -0
  97. package/dist/cli/main.d.ts +8 -0
  98. package/dist/cli/main.js +145 -0
  99. package/dist/cli/main.js.map +1 -0
  100. package/dist/cli/overlay-ask.d.ts +13 -0
  101. package/dist/cli/overlay-ask.js +32 -0
  102. package/dist/cli/overlay-ask.js.map +1 -0
  103. package/dist/cli/overlay.d.ts +26 -0
  104. package/dist/cli/overlay.js +149 -0
  105. package/dist/cli/overlay.js.map +1 -0
  106. package/dist/cli/picker.d.ts +308 -0
  107. package/dist/cli/picker.js +642 -0
  108. package/dist/cli/picker.js.map +1 -0
  109. package/dist/cli/preview.d.ts +87 -0
  110. package/dist/cli/preview.js +292 -0
  111. package/dist/cli/preview.js.map +1 -0
  112. package/dist/cli/tmux-label.d.ts +50 -0
  113. package/dist/cli/tmux-label.js +146 -0
  114. package/dist/cli/tmux-label.js.map +1 -0
  115. package/dist/core/errors.d.ts +54 -0
  116. package/dist/core/errors.js +91 -0
  117. package/dist/core/errors.js.map +1 -0
  118. package/dist/core/fingerprint.d.ts +21 -0
  119. package/dist/core/fingerprint.js +59 -0
  120. package/dist/core/fingerprint.js.map +1 -0
  121. package/dist/core/gmux-types.d.ts +102 -0
  122. package/dist/core/gmux-types.js +13 -0
  123. package/dist/core/gmux-types.js.map +1 -0
  124. package/dist/core/pane-state.d.ts +5 -0
  125. package/dist/core/pane-state.js +15 -0
  126. package/dist/core/pane-state.js.map +1 -0
  127. package/dist/core/paths.d.ts +68 -0
  128. package/dist/core/paths.js +106 -0
  129. package/dist/core/paths.js.map +1 -0
  130. package/dist/core/proc-tree.d.ts +9 -0
  131. package/dist/core/proc-tree.js +33 -0
  132. package/dist/core/proc-tree.js.map +1 -0
  133. package/dist/core/text.d.ts +34 -0
  134. package/dist/core/text.js +126 -0
  135. package/dist/core/text.js.map +1 -0
  136. package/dist/core/types.d.ts +322 -0
  137. package/dist/core/types.js +10 -0
  138. package/dist/core/types.js.map +1 -0
  139. package/dist/services/ask-transcript.d.ts +256 -0
  140. package/dist/services/ask-transcript.js +544 -0
  141. package/dist/services/ask-transcript.js.map +1 -0
  142. package/dist/services/ask.d.ts +54 -0
  143. package/dist/services/ask.js +161 -0
  144. package/dist/services/ask.js.map +1 -0
  145. package/dist/services/auto-summarize.d.ts +188 -0
  146. package/dist/services/auto-summarize.js +415 -0
  147. package/dist/services/auto-summarize.js.map +1 -0
  148. package/dist/services/concurrency.d.ts +2 -0
  149. package/dist/services/concurrency.js +16 -0
  150. package/dist/services/concurrency.js.map +1 -0
  151. package/dist/services/config.d.ts +131 -0
  152. package/dist/services/config.js +325 -0
  153. package/dist/services/config.js.map +1 -0
  154. package/dist/services/daemon-client.d.ts +9 -0
  155. package/dist/services/daemon-client.js +34 -0
  156. package/dist/services/daemon-client.js.map +1 -0
  157. package/dist/services/daemon-socket.d.ts +30 -0
  158. package/dist/services/daemon-socket.js +76 -0
  159. package/dist/services/daemon-socket.js.map +1 -0
  160. package/dist/services/daemon.d.ts +43 -0
  161. package/dist/services/daemon.js +106 -0
  162. package/dist/services/daemon.js.map +1 -0
  163. package/dist/services/distill.d.ts +39 -0
  164. package/dist/services/distill.js +93 -0
  165. package/dist/services/distill.js.map +1 -0
  166. package/dist/services/guardian.d.ts +26 -0
  167. package/dist/services/guardian.js +71 -0
  168. package/dist/services/guardian.js.map +1 -0
  169. package/dist/services/index-store.d.ts +36 -0
  170. package/dist/services/index-store.js +125 -0
  171. package/dist/services/index-store.js.map +1 -0
  172. package/dist/services/log-rotation.d.ts +3 -0
  173. package/dist/services/log-rotation.js +28 -0
  174. package/dist/services/log-rotation.js.map +1 -0
  175. package/dist/services/pane-links.d.ts +10 -0
  176. package/dist/services/pane-links.js +47 -0
  177. package/dist/services/pane-links.js.map +1 -0
  178. package/dist/services/pane-process.d.ts +95 -0
  179. package/dist/services/pane-process.js +183 -0
  180. package/dist/services/pane-process.js.map +1 -0
  181. package/dist/services/pane-registry.d.ts +24 -0
  182. package/dist/services/pane-registry.js +40 -0
  183. package/dist/services/pane-registry.js.map +1 -0
  184. package/dist/services/provider-process.d.ts +37 -0
  185. package/dist/services/provider-process.js +105 -0
  186. package/dist/services/provider-process.js.map +1 -0
  187. package/dist/services/providers.d.ts +76 -0
  188. package/dist/services/providers.js +104 -0
  189. package/dist/services/providers.js.map +1 -0
  190. package/dist/services/resolve.d.ts +3 -0
  191. package/dist/services/resolve.js +18 -0
  192. package/dist/services/resolve.js.map +1 -0
  193. package/dist/services/resources.d.ts +17 -0
  194. package/dist/services/resources.js +113 -0
  195. package/dist/services/resources.js.map +1 -0
  196. package/dist/services/search.d.ts +29 -0
  197. package/dist/services/search.js +135 -0
  198. package/dist/services/search.js.map +1 -0
  199. package/dist/services/semantic-gate.d.ts +13 -0
  200. package/dist/services/semantic-gate.js +23 -0
  201. package/dist/services/semantic-gate.js.map +1 -0
  202. package/dist/services/semantic.d.ts +113 -0
  203. package/dist/services/semantic.js +216 -0
  204. package/dist/services/semantic.js.map +1 -0
  205. package/dist/services/sensors.d.ts +92 -0
  206. package/dist/services/sensors.js +246 -0
  207. package/dist/services/sensors.js.map +1 -0
  208. package/dist/services/summarize.d.ts +84 -0
  209. package/dist/services/summarize.js +230 -0
  210. package/dist/services/summarize.js.map +1 -0
  211. package/dist/services/tmux-gateway.d.ts +17 -0
  212. package/dist/services/tmux-gateway.js +10 -0
  213. package/dist/services/tmux-gateway.js.map +1 -0
  214. package/dist/services/tmux-resolve.d.ts +54 -0
  215. package/dist/services/tmux-resolve.js +0 -0
  216. package/dist/services/tmux-resolve.js.map +1 -0
  217. package/dist/services/tmux.d.ts +32 -0
  218. package/dist/services/tmux.js +107 -0
  219. package/dist/services/tmux.js.map +1 -0
  220. package/dist/services/views.d.ts +7 -0
  221. package/dist/services/views.js +18 -0
  222. package/dist/services/views.js.map +1 -0
  223. package/dist/services/watch.d.ts +36 -0
  224. package/dist/services/watch.js +98 -0
  225. package/dist/services/watch.js.map +1 -0
  226. package/dist/services/workspace.d.ts +19 -0
  227. package/dist/services/workspace.js +88 -0
  228. package/dist/services/workspace.js.map +1 -0
  229. package/package.json +64 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Draw every pane's summary card in place. Pure: given resolved cells and the
3
+ * clock, produce one string of ANSI cursor moves that paints a full-screen
4
+ * overlay. The command layer supplies the cells and the terminal; nothing here
5
+ * reads tmux, stdin, or the clock on its own.
6
+ *
7
+ * A compact sibling of `formatCard`: same summary fields and section names,
8
+ * sized to a rectangle with a degradation ladder — full card, then title +
9
+ * what-landed, then just the title, then a muted placeholder for a pane with no
10
+ * agent. Monochrome: the structure is carried by layout, so it survives a pane
11
+ * that cannot render colour.
12
+ */
13
+ import { relativeAge, truncate, wrapText } from "../core/text.js";
14
+ import { indent, sessionLabel } from "./format.js";
15
+ const CLEAR = "\x1b[2J\x1b[H";
16
+ /**
17
+ * When the summary last landed, and whether one is regenerating now — shown
18
+ * together, so a card that says `refreshing…` still tells you how old the summary
19
+ * you're reading is.
20
+ */
21
+ function freshnessLine(cell, now) {
22
+ const summary = cell.view?.summary;
23
+ const age = summary ? `updated ${relativeAge(summary.generatedAt, now)} ago` : "no summary yet";
24
+ return cell.refreshing ? `refreshing… · ${age}` : age;
25
+ }
26
+ function section(label, body, width) {
27
+ if (!body)
28
+ return [];
29
+ const wrapped = wrapText(body, Math.max(1, width - 2)).map((l) => indent(l));
30
+ return [label, ...wrapped, ""];
31
+ }
32
+ function placeholder(cell, width, height) {
33
+ const lines = ["· no agent here ·"];
34
+ if (height > 1)
35
+ lines.push(truncate(cell.pane.command, width));
36
+ return lines.slice(0, Math.max(1, height));
37
+ }
38
+ /** The normal summary card (title, freshness, the widening-zoom sections). */
39
+ function summaryCard(cell, w, h, now) {
40
+ const { record, summary } = cell.view;
41
+ const title = truncate(sessionLabel(record), w);
42
+ if (h <= 1)
43
+ return [title];
44
+ const fresh = freshnessLine(cell, now);
45
+ const landed = summary?.landed || summary?.headline || record.lastUserPrompt || "";
46
+ if (h <= 4) {
47
+ const lines = [title];
48
+ if (h >= 3)
49
+ lines.push(...wrapText(landed, w).slice(0, h - 2));
50
+ lines.push(fresh);
51
+ return lines.slice(0, h);
52
+ }
53
+ const body = [title];
54
+ if (record.endedMidTask)
55
+ body.push("⚠ ended mid-task");
56
+ body.push(fresh, "");
57
+ if (summary) {
58
+ body.push(...section("HEADLINE", summary.headline, w));
59
+ body.push(...section("OVERVIEW", summary.overview, w));
60
+ body.push(...section("SUMMARY", summary.summary ?? "", w));
61
+ body.push(...section("RECENT WORK", summary.landed, w));
62
+ body.push(...section("STILL OPEN", summary.open, w));
63
+ body.push(...section("NEXT STEP", summary.nextStep, w));
64
+ }
65
+ else {
66
+ body.push("no summary yet — gmux summarize " + record.sessionId.slice(0, 8));
67
+ }
68
+ return body.slice(0, h);
69
+ }
70
+ /** The broadcast-answer block appended under the summary: `asking…`, or the answer. */
71
+ function askBlock(cell, w) {
72
+ return cell.asking ? ["▸ asking…"] : ["▸ answer", ...wrapText(cell.answer ?? "", w).map(indent)];
73
+ }
74
+ export function cellLines(cell, width, height, now) {
75
+ const w = Math.max(1, Math.floor(width));
76
+ const h = Math.max(1, Math.floor(height));
77
+ if (!cell.view)
78
+ return placeholder(cell, w, h);
79
+ if (h <= 1)
80
+ return [truncate(sessionLabel(cell.view.record), w)];
81
+ // No question in flight: the whole card is the summary.
82
+ if (!cell.asking && cell.answer == null)
83
+ return summaryCard(cell, w, h, now);
84
+ // A broadcast question: keep the summary and APPEND the answer beneath it,
85
+ // reserving room so both show (the summary is clipped, not swallowed).
86
+ const ask = askBlock(cell, w);
87
+ const askRows = Math.min(ask.length, Math.max(1, h - 2)); // always leave the card ≥2 rows
88
+ const cardRows = Math.max(1, h - askRows - 1); // −1 for the blank separator
89
+ return [...summaryCard(cell, w, cardRows, now), "", ...ask.slice(0, askRows)].slice(0, h);
90
+ }
91
+ /**
92
+ * Strip C0 control bytes and DEL — including ESC (0x1b) — from untrusted card
93
+ * text. Summary fields, prompts and pane commands all originate outside this
94
+ * process; a stray control byte among them could otherwise move the cursor out
95
+ * of the pane's rectangle (absolute positioning) or emit an OSC sequence to the
96
+ * terminal. Replaced with a space rather than dropped, so columns still line up.
97
+ */
98
+ function sanitize(line) {
99
+ return line.replace(/[\x00-\x1f\x7f]/g, " ");
100
+ }
101
+ /** Clip one line to `width` display columns (no wrapping — the card already wrapped). */
102
+ function clip(line, width) {
103
+ const clean = sanitize(line);
104
+ return clean.length > width ? clean.slice(0, width) : clean;
105
+ }
106
+ /**
107
+ * A box outline around a pane's rectangle, so adjacent cards read as separate
108
+ * panes rather than one blur of text. Drawn with box-drawing glyphs at absolute
109
+ * coordinates; the card content is inset one cell inside it. `left`/`top` are
110
+ * 0-based (tmux geometry); terminal coordinates are 1-based.
111
+ */
112
+ function borderBox(left, top, width, height) {
113
+ const out = [];
114
+ const inner = "─".repeat(Math.max(0, width - 2));
115
+ const colLeft = left + 1;
116
+ const colRight = left + width;
117
+ const rowTop = top + 1;
118
+ const rowBottom = top + height;
119
+ out.push(`\x1b[${rowTop};${colLeft}H┌${inner}┐`);
120
+ for (let row = rowTop + 1; row <= rowBottom - 1; row++) {
121
+ out.push(`\x1b[${row};${colLeft}H│`);
122
+ out.push(`\x1b[${row};${colRight}H│`);
123
+ }
124
+ out.push(`\x1b[${rowBottom};${colLeft}H└${inner}┘`);
125
+ return out;
126
+ }
127
+ export function renderOverlay(cells, now = new Date()) {
128
+ const out = [CLEAR];
129
+ for (const cell of cells) {
130
+ const { left, top, width, height } = cell.pane;
131
+ // Big enough for a frame with at least a 1×1 interior: draw the border and
132
+ // inset the card by one cell on every side.
133
+ if (width >= 3 && height >= 3) {
134
+ out.push(...borderBox(left, top, width, height));
135
+ const lines = cellLines(cell, width - 2, height - 2, now);
136
+ lines.forEach((line, i) => {
137
+ out.push(`\x1b[${top + 2 + i};${left + 2}H${clip(line, width - 2)}`);
138
+ });
139
+ continue;
140
+ }
141
+ // Too small to frame — just paint the content at the pane origin.
142
+ const lines = cellLines(cell, width, height, now);
143
+ lines.forEach((line, i) => {
144
+ out.push(`\x1b[${top + i + 1};${left + 1}H${clip(line, width)}`);
145
+ });
146
+ }
147
+ return out.join("");
148
+ }
149
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/cli/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAcnD,MAAM,KAAK,GAAG,eAAe,CAAC;AAE9B;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAiB,EAAE,GAAS;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAChG,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AACxD,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,WAAW,CAAC,IAAiB,EAAE,KAAa,EAAE,MAAc;IACnE,MAAM,KAAK,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACpC,IAAI,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/D,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,8EAA8E;AAC9E,SAAS,WAAW,CAAC,IAAiB,EAAE,CAAS,EAAE,CAAS,EAAE,GAAS;IACrE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,IAAK,CAAC;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,QAAQ,IAAI,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IAEnF,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,IAAI,GAAa,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,YAAY;QAAE,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAErB,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,IAAI,CAAC,kCAAkC,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,uFAAuF;AACvF,SAAS,QAAQ,CAAC,IAAiB,EAAE,CAAS;IAC5C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAiB,EAAE,KAAa,EAAE,MAAc,EAAE,GAAS;IACnF,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1C,IAAI,CAAC,IAAI,CAAC,IAAI;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEjE,wDAAwD;IACxD,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAE7E,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC;IAC1F,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,6BAA6B;IAC5E,OAAO,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED;;;;;;GAMG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,yFAAyF;AACzF,SAAS,IAAI,CAAC,IAAY,EAAE,KAAa;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,SAAS,SAAS,CAAC,IAAY,EAAE,GAAW,EAAE,KAAa,EAAE,MAAc;IACzE,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC;IACzB,MAAM,QAAQ,GAAG,IAAI,GAAG,KAAK,CAAC;IAC9B,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC;IACvB,MAAM,SAAS,GAAG,GAAG,GAAG,MAAM,CAAC;IAC/B,GAAG,CAAC,IAAI,CAAC,QAAQ,MAAM,IAAI,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC;IACjD,KAAK,IAAI,GAAG,GAAG,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI,SAAS,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;QACvD,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,IAAI,CAAC,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,QAAQ,SAAS,IAAI,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAA6B,EAAE,MAAY,IAAI,IAAI,EAAE;IACjF,MAAM,GAAG,GAAa,CAAC,KAAK,CAAC,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;QAE/C,2EAA2E;QAC3E,4CAA4C;QAC5C,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;YACjD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;YAC1D,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACxB,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,kEAAkE;QAClE,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACxB,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC"}
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Session picker.
3
+ *
4
+ * fzf is used when installed — it gives fuzzy matching and a live preview pane
5
+ * for free. When it is absent we fall back to a numbered prompt rather than
6
+ * failing: gmux must work on a machine with nothing but Node.
7
+ *
8
+ * Rows WRAP rather than being chopped at the right edge, in both modes. In fzf
9
+ * that needs multi-line items: records are NUL-delimited (`--read0`) so a single
10
+ * session can span several display lines and still be selected as one thing.
11
+ * fzf gained multi-line display in 0.46, so older versions fall back to
12
+ * single-line rows rather than rendering one session as several bogus entries.
13
+ */
14
+ import type { SessionView } from "../core/types.js";
15
+ import { type InProgress } from "./format.js";
16
+ export declare function hasFzf(): boolean;
17
+ /** fzf's version as [major, minor, patch], or null if it cannot be determined. */
18
+ export declare function fzfVersion(): number[] | null;
19
+ /**
20
+ * Is `version` at least `want`, comparing component by component?
21
+ *
22
+ * A null version — `fzf --version` unreadable — is NOT enough. Every caller
23
+ * gates a feature on this, and assuming a feature we cannot see makes fzf exit
24
+ * non-zero at startup for exactly the people we could not probe.
25
+ *
26
+ * Missing components read as 0, so a want longer than the version still
27
+ * decides: [0, 46] is not [0, 46, 1].
28
+ */
29
+ export declare function atLeast(version: number[] | null, want: readonly number[]): boolean;
30
+ /** True when this fzf can display one item across several lines. */
31
+ export declare function supportsMultiline(version: number[] | null): boolean;
32
+ /** True when this fzf can tell a binding which input mode it is in. */
33
+ export declare function supportsSplitChat(version: number[] | null): boolean;
34
+ /**
35
+ * Which ask experience this environment gets.
36
+ *
37
+ * The tiers below `split` are what shipped before it, and they are load-bearing
38
+ * rather than politeness: an fzf flag leaked into a tier that does not
39
+ * understand it does not degrade the picker, it deletes it — fzf exits non-zero
40
+ * at startup, for exactly the people the fallback exists to protect and for
41
+ * nobody else. Hence a named ladder with a truth table over it.
42
+ */
43
+ export type AskTier =
44
+ /** The chat under the card, in the preview pane. */
45
+ "split"
46
+ /** Today's full-screen `execute` REPL: fzf suspends, the list comes back after. */
47
+ | "execute"
48
+ /** The numbered fallback's `a` key. No fzf to bind anything in. */
49
+ | "prompt"
50
+ /** No ask at all — the key is not bound and not advertised. */
51
+ | "none";
52
+ export declare function askTier(input: {
53
+ hasFzf: boolean;
54
+ fzfVersion: number[] | null;
55
+ askAvailable: boolean;
56
+ selfCommand: string | null;
57
+ }): AskTier;
58
+ /** How wide the list column is inside fzf, once the preview pane is taken out. */
59
+ export declare function listWidth(width?: number): number;
60
+ /**
61
+ * The records fed to fzf, NUL-delimited.
62
+ *
63
+ * Each record is `<session-id>\t<display>`, where `<display>` may contain
64
+ * newlines. fzf shows fields 2.. and hands field 1 to the preview command and
65
+ * back to us on selection, so the id never appears on screen twice and the
66
+ * mapping back to a session survives wrapping.
67
+ */
68
+ export declare function buildFzfRecords(views: readonly SessionView[], multiline: boolean, width?: number, now?: Date, inProgress?: InProgress): string;
69
+ /** A freshly loaded list, and what the worker is writing as of that moment. */
70
+ export interface PickRefresh {
71
+ views: readonly SessionView[];
72
+ inProgress: InProgress;
73
+ }
74
+ export interface PickOptions {
75
+ /** argv reproducing this filter set, for fzf's reload binding. */
76
+ reloadArgs?: readonly string[];
77
+ /**
78
+ * Re-load for the no-fzf path, which has no subprocess to shell out to.
79
+ *
80
+ * Returns the markers as well as the rows: carrying the `inProgress` set from
81
+ * open would re-render it stale, showing `○` for a row the worker has since
82
+ * picked up.
83
+ */
84
+ reload?: () => Promise<PickRefresh>;
85
+ /**
86
+ * Look up a session id the picker did not open with.
87
+ *
88
+ * REQUIRED for ctrl-r to be useful. After a reload, fzf's list contains
89
+ * sessions that did not exist when we built our id map, and those are exactly
90
+ * the ones you refreshed in order to find.
91
+ */
92
+ resolve?: (id: string) => Promise<SessionView | null>;
93
+ /** Rows the worker is writing right now, rendered `◐`. */
94
+ inProgress?: InProgress;
95
+ /**
96
+ * argv reproducing this filter set for fzf's ctrl-o binding, or undefined
97
+ * when ask is unavailable — which leaves the key unbound and unadvertised.
98
+ *
99
+ * Carries the filters for the same reason `reloadArgs` does: the child builds
100
+ * its own window, and a window built from defaults may not contain the
101
+ * session you are pointing at.
102
+ */
103
+ askArgs?: readonly string[];
104
+ /**
105
+ * Open the chat layer. `a` in the numbered fallback; ctrl-o in fzf does NOT
106
+ * come through here — fzf runs its own `execute` binding against this build.
107
+ *
108
+ * A callback rather than a direct import, so the picker keeps knowing nothing
109
+ * about providers.
110
+ */
111
+ ask?: () => Promise<void>;
112
+ /**
113
+ * The chat pane's opaque strings, or undefined when it cannot be offered.
114
+ *
115
+ * Used only on the `split` tier — every other tier ignores it — so a caller can
116
+ * build it unconditionally and let the fzf version decide.
117
+ *
118
+ * Strings in, shell commands out, and that is the whole contract: the picker
119
+ * never opens the transcript, never learns it is a file, never learns where it
120
+ * lives, and never learns a provider exists. Exactly as `askArgs` already
121
+ * works, and for the same reason.
122
+ */
123
+ chat?: ChatSpec;
124
+ /**
125
+ * Run when the picker is done with the terminal, however it ended.
126
+ *
127
+ * The thread's files, its lock and the worker's process group all have to go,
128
+ * and NONE of that is the picker's to know — `rm` needs `node:fs`, the lock
129
+ * name is a convention in a service, and the group kill needs the lock's shape.
130
+ * `pick.ts` owns the transcript, so `pick.ts` owns the cleanup and the picker's
131
+ * import list stays free of every one of them.
132
+ */
133
+ onClose?: () => Promise<void>;
134
+ }
135
+ /**
136
+ * The session behind the id fzf handed back.
137
+ *
138
+ * Checks the list we opened with first, then falls back to a fresh lookup. That
139
+ * fallback is what makes ctrl-r work: selecting a session that appeared *during*
140
+ * the refresh would otherwise miss the map built at open and report "Nothing
141
+ * selected" — refusing to resume the very session you refreshed to find.
142
+ */
143
+ export declare function resolvePicked(id: string, views: readonly SessionView[], resolve?: (id: string) => Promise<SessionView | null>): Promise<SessionView | null>;
144
+ /** Returns the chosen session, or null if the user cancelled. */
145
+ export declare function pickSession(views: readonly SessionView[], options?: PickOptions): Promise<SessionView | null>;
146
+ /**
147
+ * How to re-invoke *this* build, as a shell command string.
148
+ *
149
+ * fzf runs the preview and reload commands through a shell, and they must hit
150
+ * this build — not whatever `gmux` happens to be on PATH. During development
151
+ * there may be no `gmux` on PATH at all, and both would silently render nothing.
152
+ *
153
+ * `execArgv` MUST be forwarded, for the same reason `spawnWorker` forwards it:
154
+ * under `npm run dev` the entry point is `src/cli/main.ts` and execArgv carries
155
+ * tsx's loader flags. Drop them and the command becomes `node src/cli/main.ts`,
156
+ * which Node 20 cannot run — so the preview pane and ctrl-r both die in
157
+ * development while working perfectly from `dist/`. (Node 22 strips types
158
+ * natively and hides this, which is exactly what makes it worth a comment.)
159
+ *
160
+ * Returns null when the entry point is unavailable, leaving callers to decide
161
+ * on a fallback.
162
+ */
163
+ export declare function selfCommand(execPath: string, execArgv: readonly string[], entry: string | undefined): string | null;
164
+ /** `selfCommand` for the running process. */
165
+ export declare function selfCommandHere(): string | null;
166
+ /**
167
+ * Everything the chat tier needs, as opaque strings the picker never interprets.
168
+ *
169
+ * Built by `pick.ts`, which owns the transcript and the provider question. The
170
+ * picker interpolates these into shell strings and does nothing else with them.
171
+ */
172
+ export interface ChatSpec {
173
+ /** Baked into the preview command and the send/cancel commands. Never opened here. */
174
+ transcript: string;
175
+ /** Full shell command for `enter` in ask mode, minus the appended `--port`. */
176
+ sendCmd: string;
177
+ /** Full shell command for `esc` in ask mode, minus the appended `--port`. */
178
+ cancelCmd: string;
179
+ }
180
+ /**
181
+ * `ChatSpec` plus the two headers the mode toggle swaps between.
182
+ *
183
+ * The headers are not in `ChatSpec` because `pick.ts` cannot know them: the
184
+ * browse header depends on whether `ctrl-r` got bound at all, which is `fzfArgs`'
185
+ * decision and nobody else's. esc must restore the EXACT header the picker
186
+ * started with, so it has to be handed the one that was actually set.
187
+ */
188
+ export interface AskModeSpec extends ChatSpec {
189
+ browseHeader: string;
190
+ askHeader: string;
191
+ }
192
+ /**
193
+ * The ctrl-o transform body: browse → ask.
194
+ *
195
+ * **POSIX `[ … ]` and `if … fi`, no `[[ ]]` and no braces.** fzf runs child
196
+ * commands with `$SHELL -c`, not `sh -c` (`man fzf` says so twice, lines
197
+ * 1400-1402 and 2133-2135). Reproduced: `/bin/dash -c '[[ x = x ]]'` is
198
+ * `[[: not found`, which makes ctrl-o a DEAD KEY with no error anywhere. The
199
+ * `--with-shell 'sh -c'` on the arg list and these bodies are one fix, not two —
200
+ * csh cannot parse `if …; then` either, so the bodies alone are not sufficient
201
+ * and `--with-shell` alone leaves a bash-ism that is simply wrong.
202
+ *
203
+ * **`!= disabled`, never `= enabled`.** The oracle is TERNARY — `man fzf`:1462,
204
+ * *"Current input state (enabled, disabled, hidden)"* — and `--no-input` yields
205
+ * `hidden`. A ctrl-o guarded on `= enabled` is silently inert under `--no-input`.
206
+ * Every spelling here treats `hidden` as browse, which is the fail-safe
207
+ * direction.
208
+ *
209
+ * **No `else`, because a transform that emits nothing is a no-op** — verified:
210
+ * ctrl-o twice does not double-enter or crash.
211
+ */
212
+ export declare function enterAskActions(spec: AskModeSpec): string;
213
+ /**
214
+ * The esc transform body: ask → browse, and abort in browse mode.
215
+ *
216
+ * The exact inverse of `enterAskActions`, which is why the two are split: for
217
+ * every stateful action there, the undo is here. Two independent
218
+ * `askModeBindings`/`browseModeBindings` could not be tested for that, and the
219
+ * bug they invite is asymmetry — ask fires `disable-search`, browse forgets
220
+ * `enable-search`, and the filter is dead with no error anywhere.
221
+ */
222
+ export declare function exitAskActions(spec: AskModeSpec): string;
223
+ /**
224
+ * The enter transform body.
225
+ *
226
+ * **Enter means two things, and that is the sharpest edge in this design.** In
227
+ * browse mode it resumes a session, replacing your terminal. In ask mode it
228
+ * sends. A misfire is not cosmetic: enter that resumes when you meant send drops
229
+ * you into someone else's harness and the picker is gone. That is why the prompt
230
+ * changes, why the header changes, and why the oracle is fzf's own state rather
231
+ * than a file that can go stale.
232
+ *
233
+ * **`rebind` cannot give enter a new meaning** — it only restores a binding after
234
+ * `unbind` — so enter is bound ONCE, to a transform that branches.
235
+ *
236
+ * `--port "$FZF_PORT"`, `--focus {1}` and `--question "$FZF_QUERY"` are appended
237
+ * HERE rather than built into `sendCmd`, and never routed through `shellQuote`:
238
+ * its allowed class is `/^[A-Za-z0-9_./:@-]+$/`, so `$FZF_PORT` would be
239
+ * single-quoted and the child would receive the literal string — every
240
+ * `refresh-preview` would then silently miss.
241
+ *
242
+ * `{1}`, not `$FZF_CURRENT_ITEM`: the latter is 0.73.0, and on that exact version
243
+ * a NUL-containing item does not degrade gracefully — it breaks the preview and
244
+ * every other child command outright (fixed in 0.73.1). We use `--read0`.
245
+ * `{1}` is also what makes the focus model free: fzf re-substitutes it every time
246
+ * the binding fires, so this is already "the focus at send time", with no state
247
+ * and no race.
248
+ */
249
+ export declare function sendActions(spec: AskModeSpec): string;
250
+ /**
251
+ * The three bindings the mode toggle is made of.
252
+ *
253
+ * `transform:` and not `transform(…)`: the colon form takes the rest of the
254
+ * argument, and every body below contains parentheses.
255
+ *
256
+ * **Mode is not stored anywhere.** fzf already tracks it, and a mode file would
257
+ * be a second source of truth that can disagree with the first.
258
+ */
259
+ export declare function chatBindings(spec: AskModeSpec): string[];
260
+ /**
261
+ * fzf's environment.
262
+ *
263
+ * **`FZF_DEFAULT_OPTS` is stripped, and that is a correctness fix rather than
264
+ * hygiene.** fzf reads it from the environment, and picker.ts used to spawn with
265
+ * no `env` at all — so a user with `FZF_DEFAULT_OPTS=--disabled` handed us a
266
+ * picker whose `$FZF_INPUT_STATE` is `disabled` at the very first frame: the
267
+ * bindings believe they are already in ask mode, enter never resumes, and ctrl-o
268
+ * cannot get you back. gmux builds its full arg set anyway, and a user `--bind`
269
+ * colliding with ours is the same class of problem.
270
+ *
271
+ * **The api key rides here and NOWHERE else.** `--listen` is an
272
+ * arbitrary-command-execution surface — verified against a plain localhost
273
+ * `--listen` on 0.74, `POST execute-silent(touch pwned)` returned HTTP 200 and
274
+ * created the file — and `FZF_API_KEY` turns an unauthenticated POST into a 401.
275
+ * It must never reach an argv: measured, `ps -ww -o args=` prints another user's
276
+ * argv, while `ps e` on their environment needs their uid. So argv is strictly
277
+ * worse than the env here, which is the opposite of the usual instinct. Honestly:
278
+ * this raises the bar against a different-user or non-local attacker and does
279
+ * NOT stop a same-uid one, who can read fzf's environment and POST as us. The
280
+ * only real boundary there is a unix socket, whose 0.66.0 floor is out of reach
281
+ * under a 0.59 gate. Known and accepted.
282
+ */
283
+ export declare function fzfSpawnEnv(env?: NodeJS.ProcessEnv, apiKey?: string | null): NodeJS.ProcessEnv;
284
+ /**
285
+ * Everything the fzf arg set is built from.
286
+ *
287
+ * An options object rather than positionals because the chat tier adds a
288
+ * transcript and two more commands to this list, and eight positionals read as
289
+ * `fzfArgs(true, "p", null, null, "split", …)` at every call site.
290
+ */
291
+ export interface FzfSpec {
292
+ multiline: boolean;
293
+ preview: string;
294
+ /** The already-built shell command, or null when ctrl-r cannot be offered. */
295
+ reloadCmd: string | null;
296
+ /** The `execute()` REPL behind ctrl-o, or null when ask cannot be offered. */
297
+ askCmd: string | null;
298
+ tier: AskTier;
299
+ /** Required iff `tier === "split"`. Absent, the split tier degrades to browse. */
300
+ chat?: ChatSpec;
301
+ }
302
+ /**
303
+ * Everything we hand fzf on the command line.
304
+ *
305
+ * Split out from the spawn so it is testable: pressing a key needs a terminal,
306
+ * but the args that decide what the key *does* are just data.
307
+ */
308
+ export declare function fzfArgs(spec: FzfSpec): string[];