@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.
- package/CHANGELOG.md +654 -0
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/dist/adapters/claude-code.d.ts +52 -0
- package/dist/adapters/claude-code.js +315 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +39 -0
- package/dist/adapters/codex.js +233 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/jsonl.d.ts +40 -0
- package/dist/adapters/jsonl.js +92 -0
- package/dist/adapters/jsonl.js.map +1 -0
- package/dist/adapters/registry.d.ts +11 -0
- package/dist/adapters/registry.js +21 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +43 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/border-client.d.ts +8 -0
- package/dist/cli/border-client.js +11 -0
- package/dist/cli/border-client.js.map +1 -0
- package/dist/cli/commands/__ask-cancel.d.ts +35 -0
- package/dist/cli/commands/__ask-cancel.js +91 -0
- package/dist/cli/commands/__ask-cancel.js.map +1 -0
- package/dist/cli/commands/__ask-refresh.d.ts +40 -0
- package/dist/cli/commands/__ask-refresh.js +54 -0
- package/dist/cli/commands/__ask-refresh.js.map +1 -0
- package/dist/cli/commands/__ask-run.d.ts +43 -0
- package/dist/cli/commands/__ask-run.js +126 -0
- package/dist/cli/commands/__ask-run.js.map +1 -0
- package/dist/cli/commands/__ask-send.d.ts +50 -0
- package/dist/cli/commands/__ask-send.js +138 -0
- package/dist/cli/commands/__ask-send.js.map +1 -0
- package/dist/cli/commands/__preview-card.d.ts +31 -0
- package/dist/cli/commands/__preview-card.js +72 -0
- package/dist/cli/commands/__preview-card.js.map +1 -0
- package/dist/cli/commands/ask.d.ts +50 -0
- package/dist/cli/commands/ask.js +240 -0
- package/dist/cli/commands/ask.js.map +1 -0
- package/dist/cli/commands/auto.d.ts +12 -0
- package/dist/cli/commands/auto.js +28 -0
- package/dist/cli/commands/auto.js.map +1 -0
- package/dist/cli/commands/cockpit.d.ts +14 -0
- package/dist/cli/commands/cockpit.js +52 -0
- package/dist/cli/commands/cockpit.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +75 -0
- package/dist/cli/commands/daemon.js +258 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +6 -0
- package/dist/cli/commands/doctor.js +159 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/grep.d.ts +2 -0
- package/dist/cli/commands/grep.js +52 -0
- package/dist/cli/commands/grep.js.map +1 -0
- package/dist/cli/commands/index-cmd.d.ts +2 -0
- package/dist/cli/commands/index-cmd.js +29 -0
- package/dist/cli/commands/index-cmd.js.map +1 -0
- package/dist/cli/commands/ls.d.ts +30 -0
- package/dist/cli/commands/ls.js +95 -0
- package/dist/cli/commands/ls.js.map +1 -0
- package/dist/cli/commands/overlay.d.ts +26 -0
- package/dist/cli/commands/overlay.js +216 -0
- package/dist/cli/commands/overlay.js.map +1 -0
- package/dist/cli/commands/pick.d.ts +59 -0
- package/dist/cli/commands/pick.js +254 -0
- package/dist/cli/commands/pick.js.map +1 -0
- package/dist/cli/commands/picker-rows.d.ts +17 -0
- package/dist/cli/commands/picker-rows.js +47 -0
- package/dist/cli/commands/picker-rows.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +25 -0
- package/dist/cli/commands/resume.js +88 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/run.d.ts +13 -0
- package/dist/cli/commands/run.js +78 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +37 -0
- package/dist/cli/commands/setup.js +200 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/show.d.ts +2 -0
- package/dist/cli/commands/show.js +24 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/summarize.d.ts +2 -0
- package/dist/cli/commands/summarize.js +59 -0
- package/dist/cli/commands/summarize.js.map +1 -0
- package/dist/cli/commands/tmux.d.ts +11 -0
- package/dist/cli/commands/tmux.js +90 -0
- package/dist/cli/commands/tmux.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +2 -0
- package/dist/cli/commands/watch.js +76 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/format.d.ts +74 -0
- package/dist/cli/format.js +242 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/gmux-render.d.ts +11 -0
- package/dist/cli/gmux-render.js +60 -0
- package/dist/cli/gmux-render.js.map +1 -0
- package/dist/cli/main.d.ts +8 -0
- package/dist/cli/main.js +145 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/overlay-ask.d.ts +13 -0
- package/dist/cli/overlay-ask.js +32 -0
- package/dist/cli/overlay-ask.js.map +1 -0
- package/dist/cli/overlay.d.ts +26 -0
- package/dist/cli/overlay.js +149 -0
- package/dist/cli/overlay.js.map +1 -0
- package/dist/cli/picker.d.ts +308 -0
- package/dist/cli/picker.js +642 -0
- package/dist/cli/picker.js.map +1 -0
- package/dist/cli/preview.d.ts +87 -0
- package/dist/cli/preview.js +292 -0
- package/dist/cli/preview.js.map +1 -0
- package/dist/cli/tmux-label.d.ts +50 -0
- package/dist/cli/tmux-label.js +146 -0
- package/dist/cli/tmux-label.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.js +91 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/fingerprint.d.ts +21 -0
- package/dist/core/fingerprint.js +59 -0
- package/dist/core/fingerprint.js.map +1 -0
- package/dist/core/gmux-types.d.ts +102 -0
- package/dist/core/gmux-types.js +13 -0
- package/dist/core/gmux-types.js.map +1 -0
- package/dist/core/pane-state.d.ts +5 -0
- package/dist/core/pane-state.js +15 -0
- package/dist/core/pane-state.js.map +1 -0
- package/dist/core/paths.d.ts +68 -0
- package/dist/core/paths.js +106 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/proc-tree.d.ts +9 -0
- package/dist/core/proc-tree.js +33 -0
- package/dist/core/proc-tree.js.map +1 -0
- package/dist/core/text.d.ts +34 -0
- package/dist/core/text.js +126 -0
- package/dist/core/text.js.map +1 -0
- package/dist/core/types.d.ts +322 -0
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -0
- package/dist/services/ask-transcript.d.ts +256 -0
- package/dist/services/ask-transcript.js +544 -0
- package/dist/services/ask-transcript.js.map +1 -0
- package/dist/services/ask.d.ts +54 -0
- package/dist/services/ask.js +161 -0
- package/dist/services/ask.js.map +1 -0
- package/dist/services/auto-summarize.d.ts +188 -0
- package/dist/services/auto-summarize.js +415 -0
- package/dist/services/auto-summarize.js.map +1 -0
- package/dist/services/concurrency.d.ts +2 -0
- package/dist/services/concurrency.js +16 -0
- package/dist/services/concurrency.js.map +1 -0
- package/dist/services/config.d.ts +131 -0
- package/dist/services/config.js +325 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/daemon-client.d.ts +9 -0
- package/dist/services/daemon-client.js +34 -0
- package/dist/services/daemon-client.js.map +1 -0
- package/dist/services/daemon-socket.d.ts +30 -0
- package/dist/services/daemon-socket.js +76 -0
- package/dist/services/daemon-socket.js.map +1 -0
- package/dist/services/daemon.d.ts +43 -0
- package/dist/services/daemon.js +106 -0
- package/dist/services/daemon.js.map +1 -0
- package/dist/services/distill.d.ts +39 -0
- package/dist/services/distill.js +93 -0
- package/dist/services/distill.js.map +1 -0
- package/dist/services/guardian.d.ts +26 -0
- package/dist/services/guardian.js +71 -0
- package/dist/services/guardian.js.map +1 -0
- package/dist/services/index-store.d.ts +36 -0
- package/dist/services/index-store.js +125 -0
- package/dist/services/index-store.js.map +1 -0
- package/dist/services/log-rotation.d.ts +3 -0
- package/dist/services/log-rotation.js +28 -0
- package/dist/services/log-rotation.js.map +1 -0
- package/dist/services/pane-links.d.ts +10 -0
- package/dist/services/pane-links.js +47 -0
- package/dist/services/pane-links.js.map +1 -0
- package/dist/services/pane-process.d.ts +95 -0
- package/dist/services/pane-process.js +183 -0
- package/dist/services/pane-process.js.map +1 -0
- package/dist/services/pane-registry.d.ts +24 -0
- package/dist/services/pane-registry.js +40 -0
- package/dist/services/pane-registry.js.map +1 -0
- package/dist/services/provider-process.d.ts +37 -0
- package/dist/services/provider-process.js +105 -0
- package/dist/services/provider-process.js.map +1 -0
- package/dist/services/providers.d.ts +76 -0
- package/dist/services/providers.js +104 -0
- package/dist/services/providers.js.map +1 -0
- package/dist/services/resolve.d.ts +3 -0
- package/dist/services/resolve.js +18 -0
- package/dist/services/resolve.js.map +1 -0
- package/dist/services/resources.d.ts +17 -0
- package/dist/services/resources.js +113 -0
- package/dist/services/resources.js.map +1 -0
- package/dist/services/search.d.ts +29 -0
- package/dist/services/search.js +135 -0
- package/dist/services/search.js.map +1 -0
- package/dist/services/semantic-gate.d.ts +13 -0
- package/dist/services/semantic-gate.js +23 -0
- package/dist/services/semantic-gate.js.map +1 -0
- package/dist/services/semantic.d.ts +113 -0
- package/dist/services/semantic.js +216 -0
- package/dist/services/semantic.js.map +1 -0
- package/dist/services/sensors.d.ts +92 -0
- package/dist/services/sensors.js +246 -0
- package/dist/services/sensors.js.map +1 -0
- package/dist/services/summarize.d.ts +84 -0
- package/dist/services/summarize.js +230 -0
- package/dist/services/summarize.js.map +1 -0
- package/dist/services/tmux-gateway.d.ts +17 -0
- package/dist/services/tmux-gateway.js +10 -0
- package/dist/services/tmux-gateway.js.map +1 -0
- package/dist/services/tmux-resolve.d.ts +54 -0
- package/dist/services/tmux-resolve.js +0 -0
- package/dist/services/tmux-resolve.js.map +1 -0
- package/dist/services/tmux.d.ts +32 -0
- package/dist/services/tmux.js +107 -0
- package/dist/services/tmux.js.map +1 -0
- package/dist/services/views.d.ts +7 -0
- package/dist/services/views.js +18 -0
- package/dist/services/views.js.map +1 -0
- package/dist/services/watch.d.ts +36 -0
- package/dist/services/watch.js +98 -0
- package/dist/services/watch.js.map +1 -0
- package/dist/services/workspace.d.ts +19 -0
- package/dist/services/workspace.js +88 -0
- package/dist/services/workspace.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,642 @@
|
|
|
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 { spawn, spawnSync } from "node:child_process";
|
|
15
|
+
import { randomBytes } from "node:crypto";
|
|
16
|
+
import { createInterface } from "node:readline/promises";
|
|
17
|
+
import { shellQuote } from "../core/text.js";
|
|
18
|
+
import { formatMarkerKey, formatRow, formatRowLines, terminalWidth, } from "./format.js";
|
|
19
|
+
/** Fraction of the terminal the list occupies; the rest is the preview pane. */
|
|
20
|
+
const LIST_FRACTION = 0.45;
|
|
21
|
+
/** fzf's own chrome: pointer, marker, border, padding. */
|
|
22
|
+
const FZF_CHROME = 6;
|
|
23
|
+
/** First fzf release with multi-line item display. */
|
|
24
|
+
const MULTILINE_FZF = [0, 46, 0];
|
|
25
|
+
/**
|
|
26
|
+
* First fzf release exporting `$FZF_INPUT_STATE`, the split chat's mode oracle.
|
|
27
|
+
*
|
|
28
|
+
* Its own constant rather than `MULTILINE_FZF`: every other action the chat
|
|
29
|
+
* bindings use is at or below 0.46, so the oracle alone sets this floor, and
|
|
30
|
+
* riding on the multi-line gate would be accidental. `MULTILINE_FZF` is wrong
|
|
31
|
+
* anyway — multi-line landed in 0.53 — and the day someone corrects it the chat
|
|
32
|
+
* must not silently change tiers with it.
|
|
33
|
+
*/
|
|
34
|
+
const SPLIT_CHAT_FZF = [0, 59, 0];
|
|
35
|
+
export function hasFzf() {
|
|
36
|
+
return spawnSync("which", ["fzf"], { stdio: "ignore" }).status === 0;
|
|
37
|
+
}
|
|
38
|
+
/** fzf's version as [major, minor, patch], or null if it cannot be determined. */
|
|
39
|
+
export function fzfVersion() {
|
|
40
|
+
const probe = spawnSync("fzf", ["--version"], { encoding: "utf8" });
|
|
41
|
+
if (probe.status !== 0 || typeof probe.stdout !== "string")
|
|
42
|
+
return null;
|
|
43
|
+
const match = /(\d+)\.(\d+)\.(\d+)/.exec(probe.stdout);
|
|
44
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Is `version` at least `want`, comparing component by component?
|
|
48
|
+
*
|
|
49
|
+
* A null version — `fzf --version` unreadable — is NOT enough. Every caller
|
|
50
|
+
* gates a feature on this, and assuming a feature we cannot see makes fzf exit
|
|
51
|
+
* non-zero at startup for exactly the people we could not probe.
|
|
52
|
+
*
|
|
53
|
+
* Missing components read as 0, so a want longer than the version still
|
|
54
|
+
* decides: [0, 46] is not [0, 46, 1].
|
|
55
|
+
*/
|
|
56
|
+
export function atLeast(version, want) {
|
|
57
|
+
if (!version)
|
|
58
|
+
return false;
|
|
59
|
+
for (let i = 0; i < want.length; i++) {
|
|
60
|
+
const part = version[i] ?? 0;
|
|
61
|
+
const need = want[i] ?? 0;
|
|
62
|
+
if (part > need)
|
|
63
|
+
return true;
|
|
64
|
+
if (part < need)
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/** True when this fzf can display one item across several lines. */
|
|
70
|
+
export function supportsMultiline(version) {
|
|
71
|
+
return atLeast(version, MULTILINE_FZF);
|
|
72
|
+
}
|
|
73
|
+
/** True when this fzf can tell a binding which input mode it is in. */
|
|
74
|
+
export function supportsSplitChat(version) {
|
|
75
|
+
return atLeast(version, SPLIT_CHAT_FZF);
|
|
76
|
+
}
|
|
77
|
+
export function askTier(input) {
|
|
78
|
+
// Nothing to ask with. A key that opens a chat which dies instantly is worse
|
|
79
|
+
// than a key that isn't there.
|
|
80
|
+
if (!input.askAvailable)
|
|
81
|
+
return "none";
|
|
82
|
+
// The numbered fallback calls back into this process, so it needs no way to
|
|
83
|
+
// address this build — which is the whole reason it can be offered when
|
|
84
|
+
// `selfCommand` is null.
|
|
85
|
+
if (!input.hasFzf)
|
|
86
|
+
return "prompt";
|
|
87
|
+
// Every fzf tier runs `gmux` through a shell, so an unaddressable build has no
|
|
88
|
+
// command to bind.
|
|
89
|
+
if (!input.selfCommand)
|
|
90
|
+
return "none";
|
|
91
|
+
// An unreadable version lands here too, because `atLeast(null, …)` is false:
|
|
92
|
+
// degrading to the older UI is a worse UI, degrading to `split` is a broken
|
|
93
|
+
// one.
|
|
94
|
+
return supportsSplitChat(input.fzfVersion) ? "split" : "execute";
|
|
95
|
+
}
|
|
96
|
+
/** How wide the list column is inside fzf, once the preview pane is taken out. */
|
|
97
|
+
export function listWidth(width = terminalWidth()) {
|
|
98
|
+
return Math.max(32, Math.floor(width * LIST_FRACTION) - FZF_CHROME);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The records fed to fzf, NUL-delimited.
|
|
102
|
+
*
|
|
103
|
+
* Each record is `<session-id>\t<display>`, where `<display>` may contain
|
|
104
|
+
* newlines. fzf shows fields 2.. and hands field 1 to the preview command and
|
|
105
|
+
* back to us on selection, so the id never appears on screen twice and the
|
|
106
|
+
* mapping back to a session survives wrapping.
|
|
107
|
+
*/
|
|
108
|
+
export function buildFzfRecords(views, multiline, width = listWidth(), now = new Date(), inProgress = new Set()) {
|
|
109
|
+
return views
|
|
110
|
+
.map((view) => {
|
|
111
|
+
const display = multiline
|
|
112
|
+
? formatRowLines(view, now, width, inProgress).join("\n")
|
|
113
|
+
: formatRow(view, now, inProgress);
|
|
114
|
+
return `${view.record.sessionId}\t${display}`;
|
|
115
|
+
})
|
|
116
|
+
.join("\0");
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The session behind the id fzf handed back.
|
|
120
|
+
*
|
|
121
|
+
* Checks the list we opened with first, then falls back to a fresh lookup. That
|
|
122
|
+
* fallback is what makes ctrl-r work: selecting a session that appeared *during*
|
|
123
|
+
* the refresh would otherwise miss the map built at open and report "Nothing
|
|
124
|
+
* selected" — refusing to resume the very session you refreshed to find.
|
|
125
|
+
*/
|
|
126
|
+
export async function resolvePicked(id, views, resolve) {
|
|
127
|
+
const known = views.find((v) => v.record.sessionId === id);
|
|
128
|
+
if (known)
|
|
129
|
+
return known;
|
|
130
|
+
return resolve ? await resolve(id) : null;
|
|
131
|
+
}
|
|
132
|
+
/** Returns the chosen session, or null if the user cancelled. */
|
|
133
|
+
export async function pickSession(views, options = {}) {
|
|
134
|
+
if (views.length === 0)
|
|
135
|
+
return null;
|
|
136
|
+
return hasFzf() ? pickWithFzf(views, options) : pickWithPrompt(views, options);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* How to re-invoke *this* build, as a shell command string.
|
|
140
|
+
*
|
|
141
|
+
* fzf runs the preview and reload commands through a shell, and they must hit
|
|
142
|
+
* this build — not whatever `gmux` happens to be on PATH. During development
|
|
143
|
+
* there may be no `gmux` on PATH at all, and both would silently render nothing.
|
|
144
|
+
*
|
|
145
|
+
* `execArgv` MUST be forwarded, for the same reason `spawnWorker` forwards it:
|
|
146
|
+
* under `npm run dev` the entry point is `src/cli/main.ts` and execArgv carries
|
|
147
|
+
* tsx's loader flags. Drop them and the command becomes `node src/cli/main.ts`,
|
|
148
|
+
* which Node 20 cannot run — so the preview pane and ctrl-r both die in
|
|
149
|
+
* development while working perfectly from `dist/`. (Node 22 strips types
|
|
150
|
+
* natively and hides this, which is exactly what makes it worth a comment.)
|
|
151
|
+
*
|
|
152
|
+
* Returns null when the entry point is unavailable, leaving callers to decide
|
|
153
|
+
* on a fallback.
|
|
154
|
+
*/
|
|
155
|
+
export function selfCommand(execPath, execArgv, entry) {
|
|
156
|
+
if (!entry)
|
|
157
|
+
return null;
|
|
158
|
+
return [execPath, ...execArgv, entry].map(shellQuote).join(" ");
|
|
159
|
+
}
|
|
160
|
+
/** `selfCommand` for the running process. */
|
|
161
|
+
export function selfCommandHere() {
|
|
162
|
+
return selfCommand(process.execPath, process.execArgv, process.argv[1]);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The command fzf runs to fill its preview pane.
|
|
166
|
+
*
|
|
167
|
+
* **Constant for the whole picker run.** Every mutable thing lives in the
|
|
168
|
+
* transcript file, which is what makes `refresh-preview` sufficient and
|
|
169
|
+
* `change-preview` unnecessary — and why there is no reflow and therefore no
|
|
170
|
+
* flicker.
|
|
171
|
+
*
|
|
172
|
+
* `${FZF_PREVIEW_LINES:-0}` — the `:-0` is load-bearing, and not because the
|
|
173
|
+
* variable is plausibly unset (fzf has exported it to the preview process since
|
|
174
|
+
* 0.18.0). The value is environment-controlled text and the parse must not
|
|
175
|
+
* silently shift arguments: a bare `$FZF_PREVIEW_LINES` expanding to nothing
|
|
176
|
+
* makes commander read the NEXT FLAG NAME as the pane height. `0` then means
|
|
177
|
+
* "guess" to `splitPreview`, which is the honest answer.
|
|
178
|
+
*
|
|
179
|
+
* `__preview-card` is spelled literally for the same reason `show` is: picker.ts
|
|
180
|
+
* imports no command module, and that is what keeps it knowing nothing about
|
|
181
|
+
* providers (picker.ts:126-132 — the invariant holds by the import list, not by
|
|
182
|
+
* the layer checker, which would pass `import { defaultAskProvider }` in
|
|
183
|
+
* silence). tests/picker.test.ts pins the spelling against the real constant.
|
|
184
|
+
*/
|
|
185
|
+
function previewCommand(transcript) {
|
|
186
|
+
const self = selfCommandHere() ?? "gmux";
|
|
187
|
+
if (!transcript)
|
|
188
|
+
return `${self} show {1} --no-color`;
|
|
189
|
+
return `${self} __preview-card {1} --chat ${shellQuote(transcript)} --pane-lines \${FZF_PREVIEW_LINES:-0}`;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The shell command behind ctrl-r, or null when we cannot address this build.
|
|
193
|
+
*
|
|
194
|
+
* Without one the key is simply not bound and the header does not advertise it —
|
|
195
|
+
* a key that does nothing is worse than a key that isn't there.
|
|
196
|
+
*/
|
|
197
|
+
function reloadCommand(reloadArgs) {
|
|
198
|
+
const self = selfCommandHere();
|
|
199
|
+
if (!self || !reloadArgs || reloadArgs.length === 0)
|
|
200
|
+
return null;
|
|
201
|
+
return `${self} ${reloadArgs.map(shellQuote).join(" ")}`;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The command behind ctrl-o: open `gmux ask` about the highlighted session.
|
|
205
|
+
*
|
|
206
|
+
* `askArgs` MUST carry the picker's filters and limit, for the same reason
|
|
207
|
+
* `reloadArgs` does. Without them the child re-derives its own window from
|
|
208
|
+
* defaults — the 20 most recent sessions across every project — and the session
|
|
209
|
+
* you are highlighting is often not in it. `--focus` then silently resolves to
|
|
210
|
+
* null and the chat answers about a list you never asked about, looking normal
|
|
211
|
+
* the whole time.
|
|
212
|
+
*
|
|
213
|
+
* `{1}` is appended unquoted because fzf substitutes it: it is the session id
|
|
214
|
+
* field, and quoting it would hand the child the literal string `{1}`.
|
|
215
|
+
*
|
|
216
|
+
* Null when this build cannot address itself, or when the caller says ask is
|
|
217
|
+
* unavailable — and then the key is not bound and not advertised.
|
|
218
|
+
*/
|
|
219
|
+
function askCommand(askArgs) {
|
|
220
|
+
const self = selfCommandHere();
|
|
221
|
+
if (!self || !askArgs || askArgs.length === 0)
|
|
222
|
+
return null;
|
|
223
|
+
return `${self} ${askArgs.map(shellQuote).join(" ")} --focus {1}`;
|
|
224
|
+
}
|
|
225
|
+
/** The browse prompt. Swapping it is not cosmetic — see `sendActions`. */
|
|
226
|
+
const BROWSE_PROMPT = "session > ";
|
|
227
|
+
const ASK_PROMPT = "ask > ";
|
|
228
|
+
/**
|
|
229
|
+
* The ask header lists exactly the two keys ask mode rebinds, and `ctrl-r` is
|
|
230
|
+
* absent because ask mode `unbind`s it rather than leaving it live and
|
|
231
|
+
* unadvertised. Both headers keep the marker legend on line 2 — it must survive
|
|
232
|
+
* both modes.
|
|
233
|
+
*/
|
|
234
|
+
const ASK_KEYS = ["enter: send", "esc: back"];
|
|
235
|
+
/** Two lines: what the keys do, then what the row markers mean. fzf renders an
|
|
236
|
+
* embedded newline as a second header line, and processes the key's colours
|
|
237
|
+
* regardless of --ansi (see `man fzf` on --header). */
|
|
238
|
+
function headerFor(keys) {
|
|
239
|
+
return `${keys.filter((k) => k !== "").join(" ")}\n${formatMarkerKey()}`;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Where the browse query is parked while ask mode owns the query line.
|
|
243
|
+
*
|
|
244
|
+
* A sibling of the transcript, not a state directory: it inherits the
|
|
245
|
+
* transcript's `<pid>-<rand8>` uniqueness for free, so two concurrent pickers
|
|
246
|
+
* cannot collide, and it lands in the cleanup and sweep paths that already exist
|
|
247
|
+
* with no new concept.
|
|
248
|
+
*
|
|
249
|
+
* The suffix is spelled here AND in `askBrowseQueryPath` (services/ask-transcript.ts),
|
|
250
|
+
* which is what deletes and sweeps it — the picker may not import a service. A
|
|
251
|
+
* drift between the two is silent (esc restores nothing), so tests/picker.test.ts
|
|
252
|
+
* pins them equal.
|
|
253
|
+
*/
|
|
254
|
+
function browseQueryPath(transcript) {
|
|
255
|
+
return `${transcript}.browseq`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The ctrl-o transform body: browse → ask.
|
|
259
|
+
*
|
|
260
|
+
* **POSIX `[ … ]` and `if … fi`, no `[[ ]]` and no braces.** fzf runs child
|
|
261
|
+
* commands with `$SHELL -c`, not `sh -c` (`man fzf` says so twice, lines
|
|
262
|
+
* 1400-1402 and 2133-2135). Reproduced: `/bin/dash -c '[[ x = x ]]'` is
|
|
263
|
+
* `[[: not found`, which makes ctrl-o a DEAD KEY with no error anywhere. The
|
|
264
|
+
* `--with-shell 'sh -c'` on the arg list and these bodies are one fix, not two —
|
|
265
|
+
* csh cannot parse `if …; then` either, so the bodies alone are not sufficient
|
|
266
|
+
* and `--with-shell` alone leaves a bash-ism that is simply wrong.
|
|
267
|
+
*
|
|
268
|
+
* **`!= disabled`, never `= enabled`.** The oracle is TERNARY — `man fzf`:1462,
|
|
269
|
+
* *"Current input state (enabled, disabled, hidden)"* — and `--no-input` yields
|
|
270
|
+
* `hidden`. A ctrl-o guarded on `= enabled` is silently inert under `--no-input`.
|
|
271
|
+
* Every spelling here treats `hidden` as browse, which is the fail-safe
|
|
272
|
+
* direction.
|
|
273
|
+
*
|
|
274
|
+
* **No `else`, because a transform that emits nothing is a no-op** — verified:
|
|
275
|
+
* ctrl-o twice does not double-enter or crash.
|
|
276
|
+
*/
|
|
277
|
+
export function enterAskActions(spec) {
|
|
278
|
+
const actions = [
|
|
279
|
+
"disable-search",
|
|
280
|
+
"clear-query",
|
|
281
|
+
// ctrl-r `reload`s the FULL list while `disable-search` is active, so the
|
|
282
|
+
// frozen browse filter — the thing we keep on purpose, so the rows under the
|
|
283
|
+
// cursor do not move when you press ctrl-o — evaporates mid-thread and cannot
|
|
284
|
+
// be restored until esc. Unadvertised AND destructive is exactly what
|
|
285
|
+
// `unbind` is for.
|
|
286
|
+
"unbind(ctrl-r)",
|
|
287
|
+
`change-prompt(${ASK_PROMPT})`,
|
|
288
|
+
`change-header(${spec.askHeader})`,
|
|
289
|
+
].join("+");
|
|
290
|
+
return [
|
|
291
|
+
`if [ "$FZF_INPUT_STATE" != disabled ]; then`,
|
|
292
|
+
// The browse query is parked in a file because ask mode needs the query line
|
|
293
|
+
// back. `disable-search` freezes the list where the browse query left it, and
|
|
294
|
+
// it must stay there.
|
|
295
|
+
`printf '%s' "$FZF_QUERY" > ${shellQuote(browseQueryPath(spec.transcript))}`,
|
|
296
|
+
`echo "${actions}"`,
|
|
297
|
+
"fi",
|
|
298
|
+
].join("\n");
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The esc transform body: ask → browse, and abort in browse mode.
|
|
302
|
+
*
|
|
303
|
+
* The exact inverse of `enterAskActions`, which is why the two are split: for
|
|
304
|
+
* every stateful action there, the undo is here. Two independent
|
|
305
|
+
* `askModeBindings`/`browseModeBindings` could not be tested for that, and the
|
|
306
|
+
* bug they invite is asymmetry — ask fires `disable-search`, browse forgets
|
|
307
|
+
* `enable-search`, and the filter is dead with no error anywhere.
|
|
308
|
+
*/
|
|
309
|
+
export function exitAskActions(spec) {
|
|
310
|
+
const actions = [
|
|
311
|
+
// BEFORE `transform-query`, or the restored query never re-triggers the
|
|
312
|
+
// search and the list stays frozen behind a filter you can see.
|
|
313
|
+
"enable-search",
|
|
314
|
+
"rebind(ctrl-r)",
|
|
315
|
+
// `transform-query(cat file)`, not `change-query($(cat …))`: the browse query
|
|
316
|
+
// is text a human typed and sidestepping the quoting is free here.
|
|
317
|
+
`transform-query(cat ${shellQuote(browseQueryPath(spec.transcript))})`,
|
|
318
|
+
`change-prompt(${BROWSE_PROMPT})`,
|
|
319
|
+
`change-header(${spec.browseHeader})`,
|
|
320
|
+
].join("+");
|
|
321
|
+
return [
|
|
322
|
+
`if [ "$FZF_INPUT_STATE" = disabled ]; then`,
|
|
323
|
+
// `>/dev/null 2>&1` is not tidiness. Verified: a child that inherits fzf's
|
|
324
|
+
// stdout blocks fzf until EOF EVEN WHEN BACKGROUNDED with `&` —
|
|
325
|
+
// `transform(sleep 3 & echo …)` froze fzf for 3s, the redirected form was
|
|
326
|
+
// instantly responsive.
|
|
327
|
+
`${spec.cancelCmd} --port "$FZF_PORT" >/dev/null 2>&1`,
|
|
328
|
+
`echo "${actions}"`,
|
|
329
|
+
"else",
|
|
330
|
+
"echo abort",
|
|
331
|
+
"fi",
|
|
332
|
+
].join("\n");
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* The enter transform body.
|
|
336
|
+
*
|
|
337
|
+
* **Enter means two things, and that is the sharpest edge in this design.** In
|
|
338
|
+
* browse mode it resumes a session, replacing your terminal. In ask mode it
|
|
339
|
+
* sends. A misfire is not cosmetic: enter that resumes when you meant send drops
|
|
340
|
+
* you into someone else's harness and the picker is gone. That is why the prompt
|
|
341
|
+
* changes, why the header changes, and why the oracle is fzf's own state rather
|
|
342
|
+
* than a file that can go stale.
|
|
343
|
+
*
|
|
344
|
+
* **`rebind` cannot give enter a new meaning** — it only restores a binding after
|
|
345
|
+
* `unbind` — so enter is bound ONCE, to a transform that branches.
|
|
346
|
+
*
|
|
347
|
+
* `--port "$FZF_PORT"`, `--focus {1}` and `--question "$FZF_QUERY"` are appended
|
|
348
|
+
* HERE rather than built into `sendCmd`, and never routed through `shellQuote`:
|
|
349
|
+
* its allowed class is `/^[A-Za-z0-9_./:@-]+$/`, so `$FZF_PORT` would be
|
|
350
|
+
* single-quoted and the child would receive the literal string — every
|
|
351
|
+
* `refresh-preview` would then silently miss.
|
|
352
|
+
*
|
|
353
|
+
* `{1}`, not `$FZF_CURRENT_ITEM`: the latter is 0.73.0, and on that exact version
|
|
354
|
+
* a NUL-containing item does not degrade gracefully — it breaks the preview and
|
|
355
|
+
* every other child command outright (fixed in 0.73.1). We use `--read0`.
|
|
356
|
+
* `{1}` is also what makes the focus model free: fzf re-substitutes it every time
|
|
357
|
+
* the binding fires, so this is already "the focus at send time", with no state
|
|
358
|
+
* and no race.
|
|
359
|
+
*/
|
|
360
|
+
export function sendActions(spec) {
|
|
361
|
+
return [
|
|
362
|
+
`if [ "$FZF_INPUT_STATE" = disabled ]; then`,
|
|
363
|
+
// Otherwise enter on an empty ask line sends a blank question.
|
|
364
|
+
`if [ -n "$FZF_QUERY" ]; then`,
|
|
365
|
+
`${spec.sendCmd} --port "$FZF_PORT" --focus {1} --question "$FZF_QUERY" >/dev/null 2>&1`,
|
|
366
|
+
"fi",
|
|
367
|
+
`echo "clear-query"`,
|
|
368
|
+
"else",
|
|
369
|
+
"echo accept",
|
|
370
|
+
"fi",
|
|
371
|
+
].join("\n");
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* The three bindings the mode toggle is made of.
|
|
375
|
+
*
|
|
376
|
+
* `transform:` and not `transform(…)`: the colon form takes the rest of the
|
|
377
|
+
* argument, and every body below contains parentheses.
|
|
378
|
+
*
|
|
379
|
+
* **Mode is not stored anywhere.** fzf already tracks it, and a mode file would
|
|
380
|
+
* be a second source of truth that can disagree with the first.
|
|
381
|
+
*/
|
|
382
|
+
export function chatBindings(spec) {
|
|
383
|
+
return [
|
|
384
|
+
`--bind=ctrl-o:transform:${enterAskActions(spec)}`,
|
|
385
|
+
`--bind=enter:transform:${sendActions(spec)}`,
|
|
386
|
+
`--bind=esc:transform:${exitAskActions(spec)}`,
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* fzf's environment.
|
|
391
|
+
*
|
|
392
|
+
* **`FZF_DEFAULT_OPTS` is stripped, and that is a correctness fix rather than
|
|
393
|
+
* hygiene.** fzf reads it from the environment, and picker.ts used to spawn with
|
|
394
|
+
* no `env` at all — so a user with `FZF_DEFAULT_OPTS=--disabled` handed us a
|
|
395
|
+
* picker whose `$FZF_INPUT_STATE` is `disabled` at the very first frame: the
|
|
396
|
+
* bindings believe they are already in ask mode, enter never resumes, and ctrl-o
|
|
397
|
+
* cannot get you back. gmux builds its full arg set anyway, and a user `--bind`
|
|
398
|
+
* colliding with ours is the same class of problem.
|
|
399
|
+
*
|
|
400
|
+
* **The api key rides here and NOWHERE else.** `--listen` is an
|
|
401
|
+
* arbitrary-command-execution surface — verified against a plain localhost
|
|
402
|
+
* `--listen` on 0.74, `POST execute-silent(touch pwned)` returned HTTP 200 and
|
|
403
|
+
* created the file — and `FZF_API_KEY` turns an unauthenticated POST into a 401.
|
|
404
|
+
* It must never reach an argv: measured, `ps -ww -o args=` prints another user's
|
|
405
|
+
* argv, while `ps e` on their environment needs their uid. So argv is strictly
|
|
406
|
+
* worse than the env here, which is the opposite of the usual instinct. Honestly:
|
|
407
|
+
* this raises the bar against a different-user or non-local attacker and does
|
|
408
|
+
* NOT stop a same-uid one, who can read fzf's environment and POST as us. The
|
|
409
|
+
* only real boundary there is a unix socket, whose 0.66.0 floor is out of reach
|
|
410
|
+
* under a 0.59 gate. Known and accepted.
|
|
411
|
+
*/
|
|
412
|
+
export function fzfSpawnEnv(env = process.env, apiKey = null) {
|
|
413
|
+
const clean = { ...env };
|
|
414
|
+
delete clean["FZF_DEFAULT_OPTS"];
|
|
415
|
+
delete clean["FZF_DEFAULT_OPTS_FILE"];
|
|
416
|
+
if (apiKey)
|
|
417
|
+
clean["FZF_API_KEY"] = apiKey;
|
|
418
|
+
return clean;
|
|
419
|
+
}
|
|
420
|
+
/** 32 random bytes, minted per picker run. Never on an argv — see `fzfSpawnEnv`. */
|
|
421
|
+
function newApiKey() {
|
|
422
|
+
return randomBytes(32).toString("base64");
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Everything we hand fzf on the command line.
|
|
426
|
+
*
|
|
427
|
+
* Split out from the spawn so it is testable: pressing a key needs a terminal,
|
|
428
|
+
* but the args that decide what the key *does* are just data.
|
|
429
|
+
*/
|
|
430
|
+
export function fzfArgs(spec) {
|
|
431
|
+
const { multiline, preview, reloadCmd, tier } = spec;
|
|
432
|
+
// **A half-bound ask mode must never ship.** `split` with no `chat` has no
|
|
433
|
+
// transcript to write and no commands to run, so enter would send into the void
|
|
434
|
+
// and esc could not get you out — strictly worse than the REPL it replaced.
|
|
435
|
+
// Fall back to the whole browse-only arg set instead, the same null-guard
|
|
436
|
+
// `reloadCommand` takes.
|
|
437
|
+
const chat = tier === "split" ? spec.chat : undefined;
|
|
438
|
+
// The tiers with no ctrl-o inside fzf must not get one however `askCmd` was
|
|
439
|
+
// built. Half a binding is not a degraded picker, it is a broken one.
|
|
440
|
+
const askCmd = tier === "execute" ? spec.askCmd : null;
|
|
441
|
+
// A key that does nothing is worse than a key that isn't there, so the header
|
|
442
|
+
// advertises exactly what got bound.
|
|
443
|
+
const keys = [
|
|
444
|
+
"enter: resume",
|
|
445
|
+
reloadCmd ? "ctrl-r: refresh" : "",
|
|
446
|
+
chat || askCmd ? "ctrl-o: ask" : "",
|
|
447
|
+
"ctrl-c: cancel",
|
|
448
|
+
]
|
|
449
|
+
.filter((k) => k !== "")
|
|
450
|
+
.join(" ");
|
|
451
|
+
const browseHeader = headerFor([keys]);
|
|
452
|
+
const args = [
|
|
453
|
+
"--ansi",
|
|
454
|
+
"--delimiter=\t",
|
|
455
|
+
"--with-nth=2..",
|
|
456
|
+
"--height=90%",
|
|
457
|
+
"--layout=reverse",
|
|
458
|
+
"--border",
|
|
459
|
+
"--prompt=session > ",
|
|
460
|
+
"--preview",
|
|
461
|
+
preview,
|
|
462
|
+
"--preview-window=right,55%,wrap",
|
|
463
|
+
"--header",
|
|
464
|
+
browseHeader,
|
|
465
|
+
];
|
|
466
|
+
// ctrl-r replaces the list with a fresh one, and kicks off summaries for
|
|
467
|
+
// whatever needs them. `reload` feeds on the command's stdout, so the binding
|
|
468
|
+
// is just "print the records again" — see commands/picker-rows.ts.
|
|
469
|
+
if (reloadCmd)
|
|
470
|
+
args.push(`--bind=ctrl-r:reload(${reloadCmd})`);
|
|
471
|
+
// ctrl-o, not shift-f: fzf's query line eats plain letters, so `F` types an F
|
|
472
|
+
// rather than firing a binding. alt-a — the obvious "ask" mnemonic — is worse
|
|
473
|
+
// than useless on macOS, where Terminal and iTerm2 both send an accented
|
|
474
|
+
// character on Option by default. ctrl-s/ctrl-q are flow control. ctrl-o is
|
|
475
|
+
// unbound in fzf and safe everywhere.
|
|
476
|
+
//
|
|
477
|
+
// `execute` suspends fzf and hands the child the terminal, restoring the list
|
|
478
|
+
// when it exits — so you can ask, read, and be back in the picker with your
|
|
479
|
+
// query and position intact.
|
|
480
|
+
if (askCmd)
|
|
481
|
+
args.push(`--bind=ctrl-o:execute(${askCmd})`);
|
|
482
|
+
// The split tier: ctrl-o becomes a MODE, not a launch. The list stays, and the
|
|
483
|
+
// answer arrives under the card while you keep arrowing around.
|
|
484
|
+
if (chat) {
|
|
485
|
+
args.push(
|
|
486
|
+
// fzf runs children with `$SHELL -c`. Under `SHELL=/bin/dash` a bash-only
|
|
487
|
+
// body makes ctrl-o a dead key; under `SHELL=/bin/tcsh` the enter transform
|
|
488
|
+
// emits nothing, a transform that emits nothing is a no-op, and enter stops
|
|
489
|
+
// resuming sessions AT ALL — the picker is bricked for csh users, by a
|
|
490
|
+
// regression we would have introduced. `--with-shell` is the only fix that
|
|
491
|
+
// covers every login shell, and at 0.51.0 it is free under a 0.59 floor.
|
|
492
|
+
"--with-shell", "sh -c",
|
|
493
|
+
// The answer arrives with no keypress, and `refresh-preview` over the listen
|
|
494
|
+
// port is the only thing that can deliver it. No argument: fzf picks the
|
|
495
|
+
// port and exports `$FZF_PORT` to its children, so nothing in gmux needs to
|
|
496
|
+
// know it. It is also an ACE surface — see `fzfSpawnEnv` for the key.
|
|
497
|
+
"--listen", ...chatBindings({ ...chat, browseHeader, askHeader: headerFor(ASK_KEYS) }));
|
|
498
|
+
}
|
|
499
|
+
if (multiline) {
|
|
500
|
+
// Items are NUL-delimited, so a record may contain newlines; --print0 keeps
|
|
501
|
+
// the selection unambiguous on the way back out.
|
|
502
|
+
args.push("--read0", "--print0", "--highlight-line");
|
|
503
|
+
}
|
|
504
|
+
return args;
|
|
505
|
+
}
|
|
506
|
+
async function pickWithFzf(views, options = {}) {
|
|
507
|
+
const version = fzfVersion();
|
|
508
|
+
const multiline = supportsMultiline(version);
|
|
509
|
+
const records = buildFzfRecords(views, multiline, listWidth(), new Date(), options.inProgress);
|
|
510
|
+
// `askArgs` is the picker's only word on whether ask would answer — it is set
|
|
511
|
+
// exactly when the caller found a provider — which is what keeps this file
|
|
512
|
+
// knowing nothing about providers.
|
|
513
|
+
const tier = askTier({
|
|
514
|
+
hasFzf: true,
|
|
515
|
+
fzfVersion: version,
|
|
516
|
+
askAvailable: (options.askArgs?.length ?? 0) > 0,
|
|
517
|
+
selfCommand: selfCommandHere(),
|
|
518
|
+
});
|
|
519
|
+
// The caller may hand us a chat at any version; only this tier can render one.
|
|
520
|
+
const chat = tier === "split" ? options.chat : undefined;
|
|
521
|
+
const args = fzfArgs({
|
|
522
|
+
multiline,
|
|
523
|
+
preview: previewCommand(chat?.transcript),
|
|
524
|
+
reloadCmd: reloadCommand(options.reloadArgs),
|
|
525
|
+
askCmd: askCommand(options.askArgs),
|
|
526
|
+
tier,
|
|
527
|
+
...(chat ? { chat } : {}),
|
|
528
|
+
});
|
|
529
|
+
// Only the chat tier opens a port, so only it mints a key.
|
|
530
|
+
const apiKey = chat ? newApiKey() : null;
|
|
531
|
+
// `finally` covers every way the picker ends on its own, INCLUDING ctrl-c
|
|
532
|
+
// inside fzf: fzf exits, the promise resolves, we clean up. The signal handlers
|
|
533
|
+
// cover the other case — the shell killing `gmux` — and are registered only when
|
|
534
|
+
// there is something to close, so a picker with no chat keeps today's exact
|
|
535
|
+
// signal behaviour (no handler, no survival, no change).
|
|
536
|
+
let closed = false;
|
|
537
|
+
const cleanup = async () => {
|
|
538
|
+
if (closed)
|
|
539
|
+
return; // `finally` and a signal can both get here.
|
|
540
|
+
closed = true;
|
|
541
|
+
await options.onClose?.();
|
|
542
|
+
};
|
|
543
|
+
const onSignal = () => {
|
|
544
|
+
void cleanup().finally(() => process.exit(130));
|
|
545
|
+
};
|
|
546
|
+
const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
547
|
+
if (options.onClose)
|
|
548
|
+
for (const signal of signals)
|
|
549
|
+
process.on(signal, onSignal);
|
|
550
|
+
try {
|
|
551
|
+
const selected = await new Promise((resolve) => {
|
|
552
|
+
const child = spawn("fzf", args, {
|
|
553
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
554
|
+
// NOT the inherited environment: a user's `FZF_DEFAULT_OPTS=--disabled`
|
|
555
|
+
// would brick the mode oracle before the first frame. See `fzfSpawnEnv`.
|
|
556
|
+
env: fzfSpawnEnv(process.env, apiKey),
|
|
557
|
+
});
|
|
558
|
+
let stdout = "";
|
|
559
|
+
child.stdout.on("data", (chunk) => (stdout += String(chunk)));
|
|
560
|
+
child.on("error", () => resolve(null));
|
|
561
|
+
child.on("close", () => {
|
|
562
|
+
const picked = stdout.replace(/\0+$/, "").trim();
|
|
563
|
+
resolve(picked === "" ? null : picked);
|
|
564
|
+
});
|
|
565
|
+
child.stdin.write(records);
|
|
566
|
+
child.stdin.end();
|
|
567
|
+
});
|
|
568
|
+
if (!selected)
|
|
569
|
+
return null;
|
|
570
|
+
const id = selected.split("\t")[0].trim();
|
|
571
|
+
// NOT a map built at open: ctrl-r may have introduced this session since.
|
|
572
|
+
return await resolvePicked(id, views, options.resolve);
|
|
573
|
+
}
|
|
574
|
+
finally {
|
|
575
|
+
if (options.onClose)
|
|
576
|
+
for (const signal of signals)
|
|
577
|
+
process.off(signal, onSignal);
|
|
578
|
+
await cleanup();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* The no-fzf fallback: a numbered list at a readline prompt.
|
|
583
|
+
*
|
|
584
|
+
* Control keys are not interceptable here, so the bindings are spelled as
|
|
585
|
+
* letters — `r` for refresh, `a` for ask — the way a numbered prompt can
|
|
586
|
+
* express them.
|
|
587
|
+
*/
|
|
588
|
+
async function pickWithPrompt(views, options = {}) {
|
|
589
|
+
// Wrap here too: the numbered fallback is a list you have to read, so chopping
|
|
590
|
+
// the description defeats the point just as badly as it does in fzf.
|
|
591
|
+
const width = Math.max(40, terminalWidth() - 5);
|
|
592
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
593
|
+
const { reload, ask } = options;
|
|
594
|
+
let current = views;
|
|
595
|
+
let inProgress = options.inProgress;
|
|
596
|
+
try {
|
|
597
|
+
for (;;) {
|
|
598
|
+
const shown = current.slice(0, 30);
|
|
599
|
+
for (const [i, view] of shown.entries()) {
|
|
600
|
+
const [first = "", ...rest] = formatRowLines(view, new Date(), width, inProgress);
|
|
601
|
+
process.stdout.write(`${String(i + 1).padStart(3)}. ${first}\n`);
|
|
602
|
+
for (const line of rest)
|
|
603
|
+
process.stdout.write(` ${line}\n`);
|
|
604
|
+
}
|
|
605
|
+
// The same static key fzf gets. This path re-renders on `r` and could
|
|
606
|
+
// afford `formatLegend`'s counts, but "the picker's key" should not read
|
|
607
|
+
// differently depending on what happens to be on your PATH.
|
|
608
|
+
process.stdout.write(`\n${formatMarkerKey()}\n`);
|
|
609
|
+
process.stdout.write("\n(install fzf for fuzzy search and previews: brew install fzf)\n");
|
|
610
|
+
const hint = [
|
|
611
|
+
"number",
|
|
612
|
+
reload ? "r to refresh" : "",
|
|
613
|
+
ask ? "a to ask" : "",
|
|
614
|
+
"or blank to cancel",
|
|
615
|
+
]
|
|
616
|
+
.filter((part) => part !== "")
|
|
617
|
+
.join(", ");
|
|
618
|
+
const answer = (await rl.question(`\nresume which? [${hint}] `)).trim();
|
|
619
|
+
if (reload && answer.toLowerCase() === "r") {
|
|
620
|
+
({ views: current, inProgress } = await reload());
|
|
621
|
+
process.stdout.write("\n");
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
if (ask && answer.toLowerCase() === "a") {
|
|
625
|
+
// The readline interface is ours and holds stdin; the chat layer opens
|
|
626
|
+
// its own. Close first or the two race for every keystroke and the
|
|
627
|
+
// conversation eats the picker's input.
|
|
628
|
+
rl.close();
|
|
629
|
+
await ask();
|
|
630
|
+
return pickWithPrompt(current, { ...options, ...(inProgress ? { inProgress } : {}) });
|
|
631
|
+
}
|
|
632
|
+
const choice = Number.parseInt(answer, 10);
|
|
633
|
+
if (!Number.isFinite(choice) || choice < 1 || choice > shown.length)
|
|
634
|
+
return null;
|
|
635
|
+
return shown[choice - 1] ?? null;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
finally {
|
|
639
|
+
rl.close();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
//# sourceMappingURL=picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picker.js","sourceRoot":"","sources":["../../src/cli/picker.ts"],"names":[],"mappings":"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"}
|