@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,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":"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"}
|
|
@@ -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[];
|