@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,50 @@
1
+ /**
2
+ * `gmux __ask-send` — what enter does in the picker's ask mode.
3
+ *
4
+ * ~170ms of work and then it is gone: take the lock, append the question,
5
+ * fork the worker, tell the pane to repaint. It never calls a model. The model
6
+ * call is `__ask-run`'s, in a detached grandchild, because a send that waited
7
+ * for an answer would hold fzf's binding for twenty seconds.
8
+ *
9
+ * **Why the sender writes the question and not the worker.** The worker costs
10
+ * ~170ms of Node startup before it could echo anything, and 170ms between your
11
+ * enter and your own words appearing reads as dropped input. Two writers is the
12
+ * price; `O_APPEND` plus the lock plus a seq-keyed fold is what makes it safe.
13
+ *
14
+ * **Read-your-writes.** The question is written and the fd closed BEFORE the
15
+ * spawn. That ordering is a happens-before rather than a hope: spawn first and
16
+ * the worker races the question it is supposed to answer.
17
+ *
18
+ * Hidden, and `__`-prefixed for three separate reasons — `shouldRunSetupWizard`
19
+ * bails on `__` so this cannot block on a human prompt while fzf owns the
20
+ * terminal, main.ts's `postAction` bails on `__` so typing a question does not
21
+ * fork a summarize pass, and it is not a thing a person runs.
22
+ */
23
+ import type { Command } from "commander";
24
+ import type { LsOptions } from "./ls.js";
25
+ /** The hidden command fzf's `enter` binding runs in ask mode. */
26
+ export declare const ASK_SEND_COMMAND = "__ask-send";
27
+ export interface AskSendOptions extends LsOptions {
28
+ transcript: string;
29
+ question: string;
30
+ /**
31
+ * The row highlighted at the instant enter fired — fzf re-substitutes `{1}`
32
+ * per keypress, so this is "the focus at send time" with no state and no race.
33
+ */
34
+ focus?: string;
35
+ /** fzf's `$FZF_PORT`. On the argv, not in the env: it is not a secret. */
36
+ port?: string;
37
+ now?: Date;
38
+ /** Injected in tests, so no test forks a detached child. Returns the pid. */
39
+ spawnWorker?: (args: readonly string[]) => number | undefined;
40
+ }
41
+ export type AskSendStatus = "sent" | "locked" | "empty" | "no-provider" | "spawn-failed";
42
+ /**
43
+ * Append one question and hand it to a worker.
44
+ *
45
+ * Never throws: fzf owns the terminal, so there is nowhere for an error to go
46
+ * but the transcript. Every "no" still repaints the pane — a key that appears
47
+ * to do nothing is the bug this repo already refuses to ship.
48
+ */
49
+ export declare function sendAskQuestion(options: AskSendOptions): Promise<AskSendStatus>;
50
+ export declare function registerAskSend(program: Command): void;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `gmux __ask-send` — what enter does in the picker's ask mode.
3
+ *
4
+ * ~170ms of work and then it is gone: take the lock, append the question,
5
+ * fork the worker, tell the pane to repaint. It never calls a model. The model
6
+ * call is `__ask-run`'s, in a detached grandchild, because a send that waited
7
+ * for an answer would hold fzf's binding for twenty seconds.
8
+ *
9
+ * **Why the sender writes the question and not the worker.** The worker costs
10
+ * ~170ms of Node startup before it could echo anything, and 170ms between your
11
+ * enter and your own words appearing reads as dropped input. Two writers is the
12
+ * price; `O_APPEND` plus the lock plus a seq-keyed fold is what makes it safe.
13
+ *
14
+ * **Read-your-writes.** The question is written and the fd closed BEFORE the
15
+ * spawn. That ordering is a happens-before rather than a hope: spawn first and
16
+ * the worker races the question it is supposed to answer.
17
+ *
18
+ * Hidden, and `__`-prefixed for three separate reasons — `shouldRunSetupWizard`
19
+ * bails on `__` so this cannot block on a human prompt while fzf owns the
20
+ * terminal, main.ts's `postAction` bails on `__` so typing a question does not
21
+ * fork a summarize pass, and it is not a thing a person runs.
22
+ */
23
+ import { NoProviderError } from "../../core/errors.js";
24
+ import { acquireAskLock, appendAskEvent, askRunIdOf, closeAskTranscript, nextSeq, openAskTranscript, readAskTranscript, releaseAskLock, spawnAskWorker, writeAskLockPid, } from "../../services/ask-transcript.js";
25
+ import { readConfig, resolveAskCommand } from "../../services/config.js";
26
+ import { refreshNotifier } from "./__ask-refresh.js";
27
+ import { ASK_RUN_COMMAND } from "./__ask-run.js";
28
+ import { filterArgs } from "./pick.js";
29
+ /** The hidden command fzf's `enter` binding runs in ask mode. */
30
+ export const ASK_SEND_COMMAND = "__ask-send";
31
+ /**
32
+ * Append one question and hand it to a worker.
33
+ *
34
+ * Never throws: fzf owns the terminal, so there is nowhere for an error to go
35
+ * but the transcript. Every "no" still repaints the pane — a key that appears
36
+ * to do nothing is the bug this repo already refuses to ship.
37
+ */
38
+ export async function sendAskQuestion(options) {
39
+ const notify = refreshNotifier(options.port);
40
+ const now = options.now ?? new Date();
41
+ const question = options.question.trim();
42
+ if (question === "")
43
+ return "empty"; // Enter on an empty query. Nothing was asked.
44
+ // The second enter appends nothing and refreshes a pane that already says
45
+ // "still answering". Two workers interleaving chunks is the one thing that
46
+ // makes the fold ambiguous, and it is the one thing this stops.
47
+ if (!(await acquireAskLock(options.transcript, now))) {
48
+ notify();
49
+ return "locked";
50
+ }
51
+ const argv = resolveAskCommand(await readConfig());
52
+ const existing = await readAskTranscript(options.transcript);
53
+ const seq = nextSeq(existing.events);
54
+ const fd = openAskTranscript(options.transcript);
55
+ try {
56
+ // `meta` has one writer and one reader: whoever `cat`s a transcript whose
57
+ // answers look wrong. Written inside the open that creates the file.
58
+ if (existing.events.length === 0) {
59
+ appendAskEvent(fd, {
60
+ t: "meta",
61
+ runId: askRunIdOf(options.transcript),
62
+ startedAt: now.toISOString(),
63
+ // The argv, never the environment — that is where the api key lives.
64
+ provider: argv ? argv.join(" ") : "(none)",
65
+ });
66
+ }
67
+ appendAskEvent(fd, {
68
+ t: "question",
69
+ seq,
70
+ at: now.toISOString(),
71
+ // Focus is captured per question and lives in the record, not on the
72
+ // worker's argv: the transcript is then self-describing, and there is one
73
+ // source of truth for what the model was told.
74
+ focus: options.focus?.trim() ? options.focus.trim() : null,
75
+ text: question,
76
+ });
77
+ // Configured "none" means no model calls. The picker does not bind ctrl-o
78
+ // without a provider, so reaching here means config changed under a live
79
+ // picker — the question is still echoed, and the answer slot says why.
80
+ if (!argv) {
81
+ const failed = new NoProviderError("`gmux ask`");
82
+ appendAskEvent(fd, {
83
+ t: "error",
84
+ seq,
85
+ at: now.toISOString(),
86
+ message: failed.fix ? `${failed.message}\n${failed.fix}` : failed.message,
87
+ });
88
+ await releaseAskLock(options.transcript);
89
+ notify();
90
+ return "no-provider";
91
+ }
92
+ }
93
+ finally {
94
+ closeAskTranscript(fd);
95
+ }
96
+ const workerArgs = [
97
+ ASK_RUN_COMMAND,
98
+ "--transcript",
99
+ options.transcript,
100
+ "--seq",
101
+ String(seq),
102
+ ...(options.port ? ["--port", options.port] : []),
103
+ // The same filters this picker was opened with. Without them the worker
104
+ // re-derives its own window from defaults and `--focus` resolves to a
105
+ // session that is not in it — the answer is then about a list nobody was
106
+ // looking at, with no sign anything went wrong.
107
+ ...filterArgs(options),
108
+ ];
109
+ const pid = (options.spawnWorker ?? spawnAskWorker)(workerArgs);
110
+ if (pid === undefined) {
111
+ await releaseAskLock(options.transcript);
112
+ notify();
113
+ return "spawn-failed";
114
+ }
115
+ await writeAskLockPid(options.transcript, pid, now);
116
+ notify();
117
+ return "sent";
118
+ }
119
+ export function registerAskSend(program) {
120
+ program
121
+ .command(ASK_SEND_COMMAND, { hidden: true })
122
+ .description("internal: append a question and fork a worker (run by the picker's enter binding)")
123
+ .requiredOption("--transcript <path>", "the picker's chat transcript")
124
+ .requiredOption("--question <text>", "what was typed")
125
+ .option("--focus <id>", "the session highlighted when enter fired")
126
+ .option("--port <port>", "fzf's listen port, for the repaint")
127
+ .option("--harness <id>", "only this harness")
128
+ .option("-p, --project <name>", "only sessions whose project matches")
129
+ .option("-b, --branch <name>", "only sessions whose git branch matches")
130
+ .option("-s, --since <when>", "only sessions newer than this")
131
+ .option("-n, --limit <count>", "how many recent sessions to consider")
132
+ .option("--include-sidechains", "include subagent transcripts")
133
+ .option("--include-automated", "include non-interactive runs")
134
+ .action(async (options) => {
135
+ await sendAskQuestion(options);
136
+ });
137
+ }
138
+ //# sourceMappingURL=__ask-send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__ask-send.js","sourceRoot":"","sources":["../../../src/cli/commands/__ask-send.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `gmux __preview-card` — what fills the picker's preview pane.
3
+ *
4
+ * **Not `gmux show --chat <path>`.** `gmux show` is a public command and `--chat` is
5
+ * a private IPC channel: it is meaningless to a human at a terminal and exists
6
+ * only because fzf's preview command must be a shell string. So the preview gets
7
+ * a hidden command that calls the same renderer, `__picker-rows`' precedent, and
8
+ * **`gmux show`'s flags, output and `--json` schema are exactly as they were.**
9
+ *
10
+ * **It re-runs on EVERY cursor move, and it must never call a model.** That is
11
+ * the hard correctness constraint the whole chat design hangs off: the chat half
12
+ * renders from the transcript file or it does not render. A model call here is a
13
+ * model call per keystroke.
14
+ *
15
+ * Geometry is passed in, never measured. `--pane-lines` carries the pane's
16
+ * height because our stdout is a pipe — `process.stdout.columns` is undefined and
17
+ * `terminalWidth()` would return its 100 default — and because `$LINES` is the
18
+ * TERMINAL, not the pane: verified on a 40x120 terminal, the preview saw
19
+ * `LINES=40` but `FZF_PREVIEW_LINES=32`. Using `$LINES` overestimates by up to 2x.
20
+ * Same rule, same reason as `__picker-rows --width`.
21
+ */
22
+ import type { Command } from "commander";
23
+ /** The hidden command fzf's preview binding runs. Not a thing a person runs. */
24
+ export declare const PREVIEW_CARD_COMMAND = "__preview-card";
25
+ export interface PreviewCardOptions {
26
+ /** The picker's transcript. Absent — or missing on disk — is the empty state. */
27
+ chat?: string;
28
+ paneLines?: string;
29
+ }
30
+ export declare function renderPreviewCard(id: string, options: PreviewCardOptions, env?: NodeJS.ProcessEnv, now?: Date): Promise<string>;
31
+ export declare function registerPreviewCard(program: Command): void;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * `gmux __preview-card` — what fills the picker's preview pane.
3
+ *
4
+ * **Not `gmux show --chat <path>`.** `gmux show` is a public command and `--chat` is
5
+ * a private IPC channel: it is meaningless to a human at a terminal and exists
6
+ * only because fzf's preview command must be a shell string. So the preview gets
7
+ * a hidden command that calls the same renderer, `__picker-rows`' precedent, and
8
+ * **`gmux show`'s flags, output and `--json` schema are exactly as they were.**
9
+ *
10
+ * **It re-runs on EVERY cursor move, and it must never call a model.** That is
11
+ * the hard correctness constraint the whole chat design hangs off: the chat half
12
+ * renders from the transcript file or it does not render. A model call here is a
13
+ * model call per keystroke.
14
+ *
15
+ * Geometry is passed in, never measured. `--pane-lines` carries the pane's
16
+ * height because our stdout is a pipe — `process.stdout.columns` is undefined and
17
+ * `terminalWidth()` would return its 100 default — and because `$LINES` is the
18
+ * TERMINAL, not the pane: verified on a 40x120 terminal, the preview saw
19
+ * `LINES=40` but `FZF_PREVIEW_LINES=32`. Using `$LINES` overestimates by up to 2x.
20
+ * Same rule, same reason as `__picker-rows --width`.
21
+ */
22
+ import { readAskTranscript } from "../../services/ask-transcript.js";
23
+ import { resolveSession } from "../../services/resolve.js";
24
+ import { readSummary } from "../../services/summarize.js";
25
+ import { loadRecords } from "../../services/views.js";
26
+ import { formatPreview, hasChatContent, splitPreview } from "../preview.js";
27
+ /** The hidden command fzf's preview binding runs. Not a thing a person runs. */
28
+ export const PREVIEW_CARD_COMMAND = "__preview-card";
29
+ /**
30
+ * The pane width when fzf did not say. Not `terminalWidth()`: our stdout is a
31
+ * pipe, so it would answer with its default for the whole TERMINAL — roughly
32
+ * twice the pane, since the preview window is 55% of it.
33
+ */
34
+ const ASSUMED_PANE_COLUMNS = 80;
35
+ /**
36
+ * A positive integer out of environment-controlled text, or null.
37
+ *
38
+ * Hostile input, and the `${FZF_PREVIEW_LINES:-0}` in the preview command is the
39
+ * other half of the same rule: a bare `$FZF_PREVIEW_LINES` that expands to
40
+ * nothing makes commander read the NEXT FLAG NAME as the pane height.
41
+ */
42
+ function positiveInt(value) {
43
+ const parsed = Number.parseInt(value ?? "", 10);
44
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
45
+ }
46
+ export async function renderPreviewCard(id, options, env = process.env, now = new Date()) {
47
+ // Naming a session explicitly means you want it, whatever kind it is — the
48
+ // same rule `gmux show` follows, because this is the same card.
49
+ const records = await loadRecords({ includeSidechains: true, includeAutomated: true });
50
+ const record = resolveSession(records, id);
51
+ const summary = await readSummary(record);
52
+ // ENOENT is a STATE, not an error, and so is a malformed file:
53
+ // `readAskTranscript` returns zero events either way and the card takes the
54
+ // whole pane. That is how the empty state costs no flag and no branch.
55
+ const transcript = options.chat ? await readAskTranscript(options.chat) : null;
56
+ // Read from our own environment rather than passed as a flag: fzf exports it to
57
+ // the preview process, and unlike the height nothing else needs it.
58
+ const width = positiveInt(env["FZF_PREVIEW_COLUMNS"]) ?? ASSUMED_PANE_COLUMNS;
59
+ const split = splitPreview(positiveInt(options.paneLines) ?? 0, hasChatContent(transcript));
60
+ return formatPreview({ record, summary }, transcript, split, width, now);
61
+ }
62
+ export function registerPreviewCard(program) {
63
+ program
64
+ .command(`${PREVIEW_CARD_COMMAND} <id>`, { hidden: true })
65
+ .description("internal: render the picker's preview pane (run by fzf on every cursor move)")
66
+ .option("--chat <path>", "the picker's chat transcript, rendered under the card")
67
+ .option("--pane-lines <n>", "the preview pane's height, which we cannot measure")
68
+ .action(async (id, options) => {
69
+ process.stdout.write(`${await renderPreviewCard(id, options)}\n`);
70
+ });
71
+ }
72
+ //# sourceMappingURL=__preview-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__preview-card.js","sourceRoot":"","sources":["../../../src/cli/commands/__preview-card.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE5E,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAErD;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAQhC;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,KAAyB;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAU,EACV,OAA2B,EAC3B,MAAyB,OAAO,CAAC,GAAG,EACpC,MAAY,IAAI,IAAI,EAAE;IAEtB,2EAA2E;IAC3E,gEAAgE;IAChE,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;IACvF,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAE1C,+DAA+D;IAC/D,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/E,gFAAgF;IAChF,oEAAoE;IACpE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,IAAI,oBAAoB,CAAC;IAC9E,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAE5F,OAAO,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,GAAG,oBAAoB,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SACzD,WAAW,CAAC,8EAA8E,CAAC;SAC3F,MAAM,CAAC,eAAe,EAAE,uDAAuD,CAAC;SAChF,MAAM,CAAC,kBAAkB,EAAE,oDAAoD,CAAC;SAChF,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,OAA2B,EAAE,EAAE;QACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `gmux ask` — ask questions about your sessions.
3
+ *
4
+ * Two shapes, one implementation:
5
+ *
6
+ * gmux ask a REPL. What ctrl-o in the picker opens.
7
+ * gmux ask "<question>" one-shot, for a shell or an agent.
8
+ * gmux ask "<q>" --json the same, parseable.
9
+ *
10
+ * The one-shot is not a convenience — non-negotiable #4 says every read command
11
+ * supports `--json`, and an agent can only use what it can parse.
12
+ */
13
+ import type { Command } from "commander";
14
+ import { type AskContext } from "../../core/types.js";
15
+ import { type LsOptions } from "./ls.js";
16
+ export interface AskOptions extends LsOptions {
17
+ /** Session the picker was highlighting when ctrl-o was pressed. */
18
+ focus?: string;
19
+ /** Ask across the agent sessions in this tmux window (the ctrl-g overlay passes it). */
20
+ window?: string;
21
+ json?: boolean;
22
+ }
23
+ /** How many sessions had a summary — the honest measure of how much gmux knows. */
24
+ export declare function summarizedCount(context: AskContext): number;
25
+ /**
26
+ * Warn when the answer is going to be thin.
27
+ *
28
+ * Asking "what should I focus on?" against twenty un-summarized sessions gets
29
+ * you a shrug, and a user who doesn't know why will read that as the feature
30
+ * being useless rather than as the summaries not being written yet.
31
+ */
32
+ export declare function thinContextNotice(context: AskContext): string | null;
33
+ /**
34
+ * Open the chat layer from inside the picker's numbered fallback.
35
+ *
36
+ * Never throws. The picker is mid-flow and the user is going straight back to
37
+ * the list — a missing provider is a line of advice, not a crash that loses the
38
+ * list they were reading.
39
+ */
40
+ export declare function askAboutSessions(options: AskOptions): Promise<void>;
41
+ /**
42
+ * Whether `gmux ask` would work right now.
43
+ *
44
+ * The picker asks before advertising ctrl-o. A key that opens a chat which
45
+ * immediately dies — and, under fzf, gets repainted over before you can read
46
+ * why — is exactly the "key that does nothing" this codebase already refuses to
47
+ * offer for ctrl-r.
48
+ */
49
+ export declare function askIsAvailable(): Promise<boolean>;
50
+ export declare function registerAsk(program: Command): void;
@@ -0,0 +1,240 @@
1
+ /**
2
+ * `gmux ask` — ask questions about your sessions.
3
+ *
4
+ * Two shapes, one implementation:
5
+ *
6
+ * gmux ask a REPL. What ctrl-o in the picker opens.
7
+ * gmux ask "<question>" one-shot, for a shell or an agent.
8
+ * gmux ask "<q>" --json the same, parseable.
9
+ *
10
+ * The one-shot is not a convenience — non-negotiable #4 says every read command
11
+ * supports `--json`, and an agent can only use what it can parse.
12
+ */
13
+ import { createInterface } from "node:readline/promises";
14
+ import { GmuxError, NoProviderError } from "../../core/errors.js";
15
+ import { SCHEMA_VERSION, } from "../../core/types.js";
16
+ import { ASK_SESSION_LIMIT, buildAskContext, buildAskPrompt, defaultAskProvider, } from "../../services/ask.js";
17
+ import { prunePaneLinks } from "../../services/pane-links.js";
18
+ import { listAllPanes, listPanes } from "../../services/tmux.js";
19
+ import { resolvePanesLive } from "../../services/tmux-resolve.js";
20
+ import { attachSummaries, loadRecords, loadViews } from "../../services/views.js";
21
+ import { bold, cyan, dim, jsonEnvelope, yellow } from "../format.js";
22
+ import { toFilters } from "./ls.js";
23
+ /** How many sessions had a summary — the honest measure of how much gmux knows. */
24
+ export function summarizedCount(context) {
25
+ return context.sessions.filter((v) => v.summary !== null).length;
26
+ }
27
+ /**
28
+ * Warn when the answer is going to be thin.
29
+ *
30
+ * Asking "what should I focus on?" against twenty un-summarized sessions gets
31
+ * you a shrug, and a user who doesn't know why will read that as the feature
32
+ * being useless rather than as the summaries not being written yet.
33
+ */
34
+ export function thinContextNotice(context) {
35
+ const total = context.sessions.length;
36
+ if (total === 0)
37
+ return "No sessions indexed. `gmux doctor` will say why.";
38
+ const summarized = summarizedCount(context);
39
+ if (summarized === 0) {
40
+ return "None of these sessions is summarized yet — answers will be thin. `gmux summarize` writes them.";
41
+ }
42
+ if (summarized < total / 2) {
43
+ return `Only ${summarized} of ${total} sessions are summarized — answers may be thin.`;
44
+ }
45
+ return null;
46
+ }
47
+ async function loadContext(options) {
48
+ if (options.window) {
49
+ const views = await windowViews(options.window);
50
+ return buildAskContext(views, options.focus ?? null, Math.max(views.length, 1));
51
+ }
52
+ const views = await loadViews(toFilters(options, ASK_SESSION_LIMIT));
53
+ return buildAskContext(views, options.focus ?? null, Number(options.limit) || ASK_SESSION_LIMIT);
54
+ }
55
+ /**
56
+ * The distinct agent sessions running in a tmux window, with their summaries.
57
+ * Resolves every pane in the server (so a fresh pane can't claim a session
58
+ * another window's pane owns), then keeps this window's.
59
+ */
60
+ async function windowViews(windowId) {
61
+ const all = await listAllPanes();
62
+ const links = await prunePaneLinks(all.map((p) => p.paneId));
63
+ const records = await loadRecords();
64
+ const windowPaneIds = new Set((await listPanes(windowId)).map((p) => p.paneId));
65
+ const resolved = (await resolvePanesLive(all, records, links)).filter((r) => windowPaneIds.has(r.pane.paneId));
66
+ const seen = new Set();
67
+ const unique = resolved
68
+ .map((r) => r.record)
69
+ .filter((r) => r !== null)
70
+ .filter((r) => (seen.has(r.sessionId) ? false : (seen.add(r.sessionId), true)));
71
+ return attachSummaries(unique);
72
+ }
73
+ /**
74
+ * The provider for this run, or a typed error saying which kind of "no" it is.
75
+ *
76
+ * Three different answers, and they must stay three:
77
+ *
78
+ * - configured "none" -> a decision to revisit (`gmux setup`)
79
+ * - configured, missing -> a binary to install, named by its own name
80
+ * - present -> go
81
+ *
82
+ * The availability check mirrors what `gmux summarize` already does. Without it a
83
+ * user who picked Codex and never installed it gets a raw `spawn codex ENOENT`
84
+ * from deep inside the spawn, which is not an error that carries its fix.
85
+ */
86
+ async function resolveProvider() {
87
+ const provider = await defaultAskProvider();
88
+ if (!provider)
89
+ throw new NoProviderError("`gmux ask`");
90
+ if (!(await provider.isAvailable())) {
91
+ throw new GmuxError(`Ask provider "${provider.name}" is not on your PATH.`, {
92
+ fix: "Run `gmux setup` to choose a provider that is installed.",
93
+ exitCode: 6,
94
+ });
95
+ }
96
+ return provider;
97
+ }
98
+ /**
99
+ * Read one question, holding readline open for no longer than it takes to ask.
100
+ *
101
+ * The interface is created and closed per question, and that is NOT ceremony.
102
+ * `readline/promises` drops any line that arrives while no `question()` is
103
+ * pending: the model call between turns takes tens of seconds, and anything the
104
+ * user typed during it — echoed to the terminal, so it looks accepted — would be
105
+ * silently thrown away.
106
+ *
107
+ * With no interface open, those keystrokes stay in the tty buffer and are
108
+ * delivered to the next `question()` instead of being lost. Same reason the
109
+ * picker closes its readline before opening the chat, and the same trap that
110
+ * made `gmux setup` require a TTY.
111
+ *
112
+ * Returns null on ctrl-d, which rejects the promise rather than resolving it.
113
+ */
114
+ async function readQuestion() {
115
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
116
+ try {
117
+ return (await rl.question(`\n${cyan("?")} `)).trim();
118
+ }
119
+ catch {
120
+ return null; // ctrl-d. An exit, not an error.
121
+ }
122
+ finally {
123
+ rl.close();
124
+ }
125
+ }
126
+ /**
127
+ * The REPL.
128
+ *
129
+ * Turns accumulate in memory and are replayed into each prompt, because the
130
+ * providers we call are one-shot — see services/ask.ts. Blank line or ctrl-d
131
+ * exits, which is what returns you to the picker when fzf spawned us.
132
+ */
133
+ async function repl(context, provider) {
134
+ const turns = [];
135
+ const notice = thinContextNotice(context);
136
+ process.stdout.write(`\n${bold("gmux ask")} ${dim(`— ${context.sessions.length} recent session${context.sessions.length === 1 ? "" : "s"} loaded, ${summarizedCount(context)} summarized · ${provider.name}`)}\n`);
137
+ if (notice)
138
+ process.stdout.write(`${yellow(notice)}\n`);
139
+ process.stdout.write(`${dim("Ask anything. Blank line or ctrl-d to go back.")}\n`);
140
+ for (;;) {
141
+ const question = await readQuestion();
142
+ if (question === null || question === "")
143
+ return;
144
+ process.stdout.write(`${dim(" thinking…")}`);
145
+ let answer;
146
+ try {
147
+ answer = await provider.ask(buildAskPrompt(context, turns, question));
148
+ }
149
+ catch (error) {
150
+ // A failed turn must not end the conversation: the next question may be
151
+ // cheaper, or the provider may just have been slow once.
152
+ process.stdout.write(`\r\x1b[K${yellow(error.message)}\n`);
153
+ continue;
154
+ }
155
+ process.stdout.write("\r\x1b[K");
156
+ process.stdout.write(`${answer}\n`);
157
+ turns.push({ question, answer });
158
+ }
159
+ }
160
+ /**
161
+ * Open the chat layer from inside the picker's numbered fallback.
162
+ *
163
+ * Never throws. The picker is mid-flow and the user is going straight back to
164
+ * the list — a missing provider is a line of advice, not a crash that loses the
165
+ * list they were reading.
166
+ */
167
+ export async function askAboutSessions(options) {
168
+ try {
169
+ const provider = await resolveProvider();
170
+ await repl(await loadContext(options), provider);
171
+ }
172
+ catch (error) {
173
+ const fix = error instanceof GmuxError ? error.fix : undefined;
174
+ process.stdout.write(`\n${yellow(error.message)}\n`);
175
+ if (fix)
176
+ process.stdout.write(`${dim(fix)}\n`);
177
+ }
178
+ }
179
+ /**
180
+ * Whether `gmux ask` would work right now.
181
+ *
182
+ * The picker asks before advertising ctrl-o. A key that opens a chat which
183
+ * immediately dies — and, under fzf, gets repainted over before you can read
184
+ * why — is exactly the "key that does nothing" this codebase already refuses to
185
+ * offer for ctrl-r.
186
+ */
187
+ export async function askIsAvailable() {
188
+ const provider = await defaultAskProvider();
189
+ return provider !== null && (await provider.isAvailable());
190
+ }
191
+ export function registerAsk(program) {
192
+ program
193
+ .command("ask [question]")
194
+ .description("ask about your recent sessions — what landed, what to pick up next")
195
+ .option("--focus <id>", "the session you're looking at (the picker passes this)")
196
+ .option("--window <id>", "ask across the agent sessions in this tmux window (the ctrl-g overlay passes it)")
197
+ .option("--harness <id>", "only this harness")
198
+ .option("-p, --project <name>", "only sessions whose project matches")
199
+ .option("-b, --branch <name>", "only sessions whose git branch matches")
200
+ .option("-s, --since <when>", "only sessions newer than this (3d, 12h, 2w)")
201
+ .option("-n, --limit <count>", "how many recent sessions to consider", String(ASK_SESSION_LIMIT))
202
+ // These two exist because the picker forwards them: ctrl-o reproduces the
203
+ // filter set the list was opened with, and an option the child does not
204
+ // declare is one commander rejects outright — so `gmux pick --include-automated`
205
+ // would bind a ctrl-o that dies on "unknown option".
206
+ .option("--include-sidechains", "include subagent transcripts")
207
+ .option("--include-automated", "include non-interactive runs (claude -p, codex exec)")
208
+ .option("--json", "emit JSON for scripts and agents")
209
+ .action(async (question, options) => {
210
+ const context = await loadContext(options);
211
+ const provider = await resolveProvider();
212
+ if (!question) {
213
+ if (options.json) {
214
+ // A REPL cannot emit an envelope. Failing loudly beats handing an
215
+ // agent an interactive process that looks like a hang.
216
+ throw new GmuxError("`gmux ask --json` needs a question.", {
217
+ fix: 'Pass one: gmux ask "what should I pick up?" --json',
218
+ exitCode: 2,
219
+ });
220
+ }
221
+ await repl(context, provider);
222
+ return;
223
+ }
224
+ const answer = await provider.ask(buildAskPrompt(context, [], question));
225
+ if (options.json) {
226
+ process.stdout.write(`${jsonEnvelope(SCHEMA_VERSION, {
227
+ answer,
228
+ provider: provider.name,
229
+ sessionCount: context.sessions.length,
230
+ summarizedCount: summarizedCount(context),
231
+ })}\n`);
232
+ return;
233
+ }
234
+ const notice = thinContextNotice(context);
235
+ if (notice)
236
+ process.stderr.write(`${yellow(notice)}\n`);
237
+ process.stdout.write(`${answer}\n`);
238
+ });
239
+ }
240
+ //# sourceMappingURL=ask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask.js","sourceRoot":"","sources":["../../../src/cli/commands/ask.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * The background worker, re-entered as `gmux __auto-summarize`.
4
+ *
5
+ * Hidden: it is not a thing a person runs. It exists so the foreground command
6
+ * can hand the slow model calls to a detached copy of itself and exit.
7
+ *
8
+ * It writes nothing to stdout or stderr — its stdio is `ignore`d anyway — and it
9
+ * exits 0 whatever happens. A failed background summary is not the user's
10
+ * problem; the row simply stays marked "no summary yet".
11
+ */
12
+ export declare function registerAutoSummarizeWorker(program: Command): void;
@@ -0,0 +1,28 @@
1
+ import { AUTO_SUMMARIZE_COMMAND, releaseLock, runAutoSummarize, } from "../../services/auto-summarize.js";
2
+ import { defaultSummaryProvider } from "../../services/summarize.js";
3
+ /**
4
+ * The background worker, re-entered as `gmux __auto-summarize`.
5
+ *
6
+ * Hidden: it is not a thing a person runs. It exists so the foreground command
7
+ * can hand the slow model calls to a detached copy of itself and exit.
8
+ *
9
+ * It writes nothing to stdout or stderr — its stdio is `ignore`d anyway — and it
10
+ * exits 0 whatever happens. A failed background summary is not the user's
11
+ * problem; the row simply stays marked "no summary yet".
12
+ */
13
+ export function registerAutoSummarizeWorker(program) {
14
+ program
15
+ .command(AUTO_SUMMARIZE_COMMAND, { hidden: true })
16
+ .description("internal: write summaries for recent sessions (run detached by gmux itself)")
17
+ .action(async () => {
18
+ // A null provider means the user configured "no model calls". Same
19
+ // outcome as a missing binary: drop the lock and do nothing.
20
+ const provider = await defaultSummaryProvider();
21
+ if (!provider || !(await provider.isAvailable())) {
22
+ await releaseLock();
23
+ return;
24
+ }
25
+ await runAutoSummarize(provider);
26
+ });
27
+ }
28
+ //# sourceMappingURL=auto.js.map