@towles/tool 0.0.94 → 0.0.96
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/package.json +1 -1
- package/src/commands/agentboard.ts +219 -272
package/package.json
CHANGED
|
@@ -1,333 +1,280 @@
|
|
|
1
|
-
import { Args
|
|
2
|
-
import { execSync
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import { resolve
|
|
5
|
-
import { networkInterfaces } from "node:os";
|
|
1
|
+
import { Args } from "@oclif/core";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
6
5
|
import consola from "consola";
|
|
7
6
|
import { colors } from "consola/utils";
|
|
8
|
-
import prompts from "prompts";
|
|
9
7
|
import { BaseCommand } from "./base.js";
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
const PLUGIN_DIR = resolve(import.meta.dirname, "../../plugins/tt-agentboard");
|
|
10
|
+
|
|
11
|
+
// Keybinding defaults
|
|
12
|
+
const DEFAULT_KEY = "a";
|
|
13
|
+
const TMUX_BINDINGS = { toggle: "t", focus: "s" } as const;
|
|
14
|
+
const RUN_SHELL_LINE = `run-shell '${PLUGIN_DIR}/agentboard.tmux'`;
|
|
15
|
+
const MARKER = "# agentboard";
|
|
16
|
+
|
|
17
|
+
function findTmuxConf(): string | null {
|
|
18
|
+
const candidates = [
|
|
19
|
+
resolve(process.env.HOME ?? "~", ".tmux.conf"),
|
|
20
|
+
resolve(process.env.HOME ?? "~", ".config/tmux/tmux.conf"),
|
|
21
|
+
];
|
|
22
|
+
for (const path of candidates) {
|
|
23
|
+
try {
|
|
24
|
+
const real = existsSync(path) ? path : null;
|
|
25
|
+
if (real) return real;
|
|
26
|
+
} catch {
|
|
27
|
+
continue;
|
|
19
28
|
}
|
|
20
29
|
}
|
|
21
|
-
return
|
|
30
|
+
return null;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
export default class
|
|
25
|
-
static override aliases = ["
|
|
26
|
-
static override description = "
|
|
33
|
+
export default class Agentboard2 extends BaseCommand {
|
|
34
|
+
static override aliases = ["ag2"];
|
|
35
|
+
static override description = "AgentBoard2 — opensessions-style tmux TUI sidebar";
|
|
27
36
|
|
|
28
37
|
static override examples = [
|
|
29
38
|
{
|
|
30
|
-
description: "
|
|
31
|
-
command: "<%= config.bin %> agentboard",
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
description: "Start on a custom port",
|
|
35
|
-
command: "<%= config.bin %> ag --port 3000",
|
|
39
|
+
description: "Install agentboard into tmux",
|
|
40
|
+
command: "<%= config.bin %> agentboard setup",
|
|
36
41
|
},
|
|
37
42
|
{
|
|
38
|
-
description: "
|
|
39
|
-
command: "<%= config.bin %>
|
|
43
|
+
description: "Uninstall from tmux",
|
|
44
|
+
command: "<%= config.bin %> agentboard uninstall",
|
|
40
45
|
},
|
|
41
46
|
{
|
|
42
|
-
description: "
|
|
43
|
-
command: "<%= config.bin %>
|
|
47
|
+
description: "Launch the server",
|
|
48
|
+
command: "<%= config.bin %> agentboard server",
|
|
44
49
|
},
|
|
45
50
|
{
|
|
46
|
-
description: "
|
|
47
|
-
command: "<%= config.bin %>
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
description: "Delete entire database without prompting",
|
|
51
|
-
command: "<%= config.bin %> ag reset --all",
|
|
51
|
+
description: "Launch the TUI directly",
|
|
52
|
+
command: "<%= config.bin %> agentboard tui",
|
|
52
53
|
},
|
|
53
54
|
];
|
|
54
55
|
|
|
55
|
-
static override flags = {
|
|
56
|
-
port: Flags.string({
|
|
57
|
-
char: "p",
|
|
58
|
-
description: "Port to serve on",
|
|
59
|
-
default: "4200",
|
|
60
|
-
}),
|
|
61
|
-
open: Flags.boolean({
|
|
62
|
-
description: "Open browser after starting",
|
|
63
|
-
default: true,
|
|
64
|
-
allowNo: true,
|
|
65
|
-
}),
|
|
66
|
-
"data-dir": Flags.string({
|
|
67
|
-
char: "d",
|
|
68
|
-
description: "Directory for AgentBoard data (SQLite DB, artifacts)",
|
|
69
|
-
env: "AGENTBOARD_DATA_DIR",
|
|
70
|
-
}),
|
|
71
|
-
lan: Flags.boolean({
|
|
72
|
-
description: "Listen on all interfaces (0.0.0.0) for LAN access. Default: localhost only.",
|
|
73
|
-
default: false,
|
|
74
|
-
}),
|
|
75
|
-
all: Flags.boolean({
|
|
76
|
-
description: "Reset entire database without prompting (for tt ag reset --all)",
|
|
77
|
-
default: false,
|
|
78
|
-
}),
|
|
79
|
-
};
|
|
80
|
-
|
|
81
56
|
static override args = {
|
|
82
57
|
subcommand: Args.string({
|
|
83
|
-
description: "Subcommand
|
|
84
|
-
required: false,
|
|
85
|
-
}),
|
|
86
|
-
cardId: Args.string({
|
|
87
|
-
description: "Card ID for attach subcommand",
|
|
58
|
+
description: "Subcommand: setup, uninstall, server, tui, keys",
|
|
88
59
|
required: false,
|
|
60
|
+
options: ["setup", "uninstall", "server", "tui", "start", "keys"],
|
|
89
61
|
}),
|
|
90
62
|
};
|
|
91
63
|
|
|
92
64
|
async run(): Promise<void> {
|
|
93
|
-
const { args
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
65
|
+
const { args } = await this.parse(Agentboard2);
|
|
66
|
+
|
|
67
|
+
switch (args.subcommand) {
|
|
68
|
+
case "setup":
|
|
69
|
+
this.setup();
|
|
70
|
+
break;
|
|
71
|
+
case "uninstall":
|
|
72
|
+
this.uninstall();
|
|
73
|
+
break;
|
|
74
|
+
case "server":
|
|
75
|
+
this.startServer();
|
|
76
|
+
break;
|
|
77
|
+
case "tui":
|
|
78
|
+
this.startTui();
|
|
79
|
+
break;
|
|
80
|
+
case "start":
|
|
81
|
+
// For backwards compat, start = tui
|
|
82
|
+
this.startTui();
|
|
83
|
+
break;
|
|
84
|
+
case "keys":
|
|
85
|
+
this.showKeys();
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
this.showKeys();
|
|
89
|
+
break;
|
|
103
90
|
}
|
|
91
|
+
}
|
|
104
92
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
const dbPath = join(dataDir, "agentboard.db");
|
|
93
|
+
private ensureDeps(): void {
|
|
94
|
+
// Check bun is installed
|
|
95
|
+
try {
|
|
96
|
+
execSync("bun --version", { stdio: "pipe" });
|
|
97
|
+
} catch {
|
|
98
|
+
this.error("bun is required but not found. Install: https://bun.sh");
|
|
99
|
+
}
|
|
113
100
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
// Install deps if needed for runtime package
|
|
102
|
+
const runtimeNodeModules = resolve(PLUGIN_DIR, "packages/runtime/node_modules");
|
|
103
|
+
if (!existsSync(runtimeNodeModules)) {
|
|
104
|
+
consola.info("Installing agentboard dependencies...");
|
|
105
|
+
execSync("pnpm install", { cwd: PLUGIN_DIR, stdio: "inherit" });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
118
108
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
109
|
+
private setup(): void {
|
|
110
|
+
this.ensureDeps();
|
|
123
111
|
|
|
124
|
-
|
|
112
|
+
// Find tmux.conf
|
|
113
|
+
const confPath = findTmuxConf();
|
|
114
|
+
if (!confPath) {
|
|
115
|
+
consola.warn("No tmux.conf found. Add this line manually:");
|
|
116
|
+
consola.info(colors.cyan(` ${RUN_SHELL_LINE}`));
|
|
125
117
|
return;
|
|
126
118
|
}
|
|
127
119
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
);
|
|
135
|
-
const dataDir = flags["data-dir"] ? resolve(flags["data-dir"]) : defaultDataDir;
|
|
136
|
-
const localIp = getLocalIp();
|
|
137
|
-
const dbPath = join(dataDir, "agentboard.db");
|
|
138
|
-
const isFirstRun = !existsSync(dbPath);
|
|
139
|
-
|
|
140
|
-
const lanMode = flags.lan;
|
|
141
|
-
const host = lanMode ? "0.0.0.0" : "127.0.0.1";
|
|
142
|
-
|
|
143
|
-
const lines = [`AgentBoard\n\n Local: http://localhost:${port}`];
|
|
144
|
-
if (lanMode) {
|
|
145
|
-
lines.push(` Network: http://${localIp}:${port}`);
|
|
146
|
-
} else {
|
|
147
|
-
lines.push(` Network: disabled (use --lan to enable)`);
|
|
120
|
+
// If it's a symlink, resolve to the real file for editing
|
|
121
|
+
let editPath = confPath;
|
|
122
|
+
try {
|
|
123
|
+
editPath = realpathSync(confPath);
|
|
124
|
+
} catch {
|
|
125
|
+
// keep confPath
|
|
148
126
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
consola.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
" 1. Ensure tmux is installed (sudo apt install tmux / brew install tmux)\n" +
|
|
157
|
-
" 2. Set GITHUB_TOKEN for GitHub features (optional)\n" +
|
|
158
|
-
" 3. Open the board → Workspaces → Add a workspace slot\n" +
|
|
159
|
-
" 4. Create your first card and drag it to In Progress",
|
|
160
|
-
);
|
|
127
|
+
|
|
128
|
+
// Check if already installed
|
|
129
|
+
const content = readFileSync(editPath, "utf8");
|
|
130
|
+
if (content.includes("agentboard.tmux")) {
|
|
131
|
+
consola.success("Already installed in tmux.conf");
|
|
132
|
+
this.reloadTmux();
|
|
133
|
+
return;
|
|
161
134
|
}
|
|
162
135
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
...process.env,
|
|
168
|
-
NUXT_DEV_HOST: host,
|
|
169
|
-
AGENTBOARD_DATA_DIR: dataDir,
|
|
170
|
-
AGENTBOARD_LAN: lanMode ? "1" : "0",
|
|
171
|
-
},
|
|
172
|
-
});
|
|
136
|
+
// Add run-shell line before TPM init
|
|
137
|
+
const tpmLine = "run '~/.config/tmux/plugins/tpm/tpm'";
|
|
138
|
+
const altTpmLine = "run-shell '~/.tmux/plugins/tpm/tpm'";
|
|
139
|
+
const insertLines = `\n${MARKER}\n${RUN_SHELL_LINE}\n`;
|
|
173
140
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
141
|
+
let newContent: string;
|
|
142
|
+
if (content.includes(tpmLine)) {
|
|
143
|
+
newContent = content.replace(tpmLine, `${insertLines}\n${tpmLine}`);
|
|
144
|
+
} else if (content.includes(altTpmLine)) {
|
|
145
|
+
newContent = content.replace(altTpmLine, `${insertLines}\n${altTpmLine}`);
|
|
146
|
+
} else {
|
|
147
|
+
// No TPM found, append to end
|
|
148
|
+
newContent = content + insertLines;
|
|
182
149
|
}
|
|
183
150
|
|
|
184
|
-
|
|
185
|
-
|
|
151
|
+
writeFileSync(editPath, newContent);
|
|
152
|
+
consola.success(`Added agentboard to ${editPath}`);
|
|
186
153
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const shmPath = `${dbPath}-shm`;
|
|
190
|
-
consola.warn(`This will delete: ${dbPath}`);
|
|
191
|
-
const { unlinkSync } = await import("node:fs");
|
|
192
|
-
for (const f of [dbPath, walPath, shmPath]) {
|
|
193
|
-
if (existsSync(f)) {
|
|
194
|
-
unlinkSync(f);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
consola.success("Database reset. Start AgentBoard to create a fresh DB.");
|
|
154
|
+
this.reloadTmux();
|
|
155
|
+
this.showKeys();
|
|
198
156
|
}
|
|
199
157
|
|
|
200
|
-
private
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const sqlite = new Database(dbPath) as {
|
|
205
|
-
pragma(stmt: string): void;
|
|
206
|
-
prepare(sql: string): {
|
|
207
|
-
get(): unknown;
|
|
208
|
-
run(): { changes: number };
|
|
209
|
-
};
|
|
210
|
-
close(): void;
|
|
211
|
-
};
|
|
212
|
-
sqlite.pragma("foreign_keys = ON");
|
|
213
|
-
|
|
214
|
-
const counts = {
|
|
215
|
-
doneCards: sqlite.prepare("SELECT COUNT(*) as c FROM cards WHERE column = 'done'").get() as {
|
|
216
|
-
c: number;
|
|
217
|
-
},
|
|
218
|
-
failedCards: sqlite
|
|
219
|
-
.prepare("SELECT COUNT(*) as c FROM cards WHERE status = 'failed'")
|
|
220
|
-
.get() as { c: number },
|
|
221
|
-
allCards: sqlite.prepare("SELECT COUNT(*) as c FROM cards").get() as { c: number },
|
|
222
|
-
workflowRuns: sqlite.prepare("SELECT COUNT(*) as c FROM workflow_runs").get() as {
|
|
223
|
-
c: number;
|
|
224
|
-
},
|
|
225
|
-
cardEvents: sqlite.prepare("SELECT COUNT(*) as c FROM card_events").get() as { c: number },
|
|
226
|
-
agentLogs: sqlite.prepare("SELECT COUNT(*) as c FROM agent_logs").get() as { c: number },
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const choices = [
|
|
230
|
-
{
|
|
231
|
-
title: `Completed cards ${colors.dim(`(${counts.doneCards.c} cards in "done" column + events/runs)`)}`,
|
|
232
|
-
value: "done_cards",
|
|
233
|
-
disabled: counts.doneCards.c === 0,
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
title: `Failed cards ${colors.dim(`(${counts.failedCards.c} cards with "failed" status + events/runs)`)}`,
|
|
237
|
-
value: "failed_cards",
|
|
238
|
-
disabled: counts.failedCards.c === 0,
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
title: `Execution history ${colors.dim(`(${counts.workflowRuns.c} workflow runs, step runs, ${counts.agentLogs.c} agent logs)`)}`,
|
|
242
|
-
value: "execution_history",
|
|
243
|
-
disabled: counts.workflowRuns.c === 0,
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
title: `Event logs ${colors.dim(`(${counts.cardEvents.c} card events)`)}`,
|
|
247
|
-
value: "event_logs",
|
|
248
|
-
disabled: counts.cardEvents.c === 0,
|
|
249
|
-
},
|
|
250
|
-
{
|
|
251
|
-
title: `All cards ${colors.dim(`(${counts.allCards.c} cards — keeps repos, boards, slots)`)}`,
|
|
252
|
-
value: "all_cards",
|
|
253
|
-
disabled: counts.allCards.c === 0,
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
title: colors.red(`Everything (delete entire database)`),
|
|
257
|
-
value: "everything",
|
|
258
|
-
},
|
|
259
|
-
];
|
|
260
|
-
|
|
261
|
-
const result = await prompts(
|
|
262
|
-
{
|
|
263
|
-
name: "selected",
|
|
264
|
-
message: "What would you like to clear?",
|
|
265
|
-
type: "multiselect",
|
|
266
|
-
choices,
|
|
267
|
-
instructions: false,
|
|
268
|
-
hint: "- Space to select, Enter to confirm",
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
onCancel: () => {
|
|
272
|
-
consola.info(colors.dim("Canceled"));
|
|
273
|
-
process.exit(0);
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
const selected: string[] = result.selected;
|
|
279
|
-
if (!selected || selected.length === 0) {
|
|
280
|
-
consola.info("Nothing selected.");
|
|
281
|
-
sqlite.close();
|
|
158
|
+
private uninstall(): void {
|
|
159
|
+
const confPath = findTmuxConf();
|
|
160
|
+
if (!confPath) {
|
|
161
|
+
consola.info("No tmux.conf found.");
|
|
282
162
|
return;
|
|
283
163
|
}
|
|
284
164
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
165
|
+
let editPath = confPath;
|
|
166
|
+
try {
|
|
167
|
+
editPath = realpathSync(confPath);
|
|
168
|
+
} catch {
|
|
169
|
+
// keep confPath
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const content = readFileSync(editPath, "utf8");
|
|
173
|
+
if (!content.includes("agentboard")) {
|
|
174
|
+
consola.info("agentboard not found in tmux.conf");
|
|
288
175
|
return;
|
|
289
176
|
}
|
|
290
177
|
|
|
291
|
-
|
|
178
|
+
// Remove the marker line and run-shell line
|
|
179
|
+
const newContent = content
|
|
180
|
+
.split("\n")
|
|
181
|
+
.filter((line) => !line.includes("agentboard"))
|
|
182
|
+
.join("\n")
|
|
183
|
+
.replace(/\n{3,}/g, "\n\n");
|
|
292
184
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
totalDeleted += deleted.changes;
|
|
298
|
-
}
|
|
185
|
+
writeFileSync(editPath, newContent);
|
|
186
|
+
consola.success("Removed agentboard from tmux.conf");
|
|
187
|
+
this.reloadTmux();
|
|
188
|
+
}
|
|
299
189
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
totalDeleted += deleted.changes;
|
|
304
|
-
}
|
|
190
|
+
// Foreground command — blocks until server exits (Ctrl+C to stop)
|
|
191
|
+
private startServer(): void {
|
|
192
|
+
this.ensureDeps();
|
|
305
193
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const deleted = sqlite.prepare("DELETE FROM workflow_runs").run();
|
|
309
|
-
consola.success(`Cleared ${deleted.changes} workflow run(s) and associated logs`);
|
|
310
|
-
totalDeleted += deleted.changes;
|
|
311
|
-
}
|
|
194
|
+
const serverEntry = resolve(PLUGIN_DIR, "apps/server/src/main.ts");
|
|
195
|
+
consola.info("Starting agentboard server (foreground, Ctrl+C to stop)...");
|
|
312
196
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
197
|
+
execSync(`bun run ${serverEntry}`, {
|
|
198
|
+
stdio: "inherit",
|
|
199
|
+
cwd: PLUGIN_DIR,
|
|
200
|
+
env: {
|
|
201
|
+
...process.env,
|
|
202
|
+
AGENTBOARD2_DIR: PLUGIN_DIR,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Foreground command — blocks until TUI exits
|
|
208
|
+
private startTui(): void {
|
|
209
|
+
this.ensureDeps();
|
|
318
210
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
211
|
+
const tuiEntry = resolve(PLUGIN_DIR, "apps/tui/src/index.tsx");
|
|
212
|
+
|
|
213
|
+
execSync(`bun run ${tuiEntry}`, {
|
|
214
|
+
stdio: "inherit",
|
|
215
|
+
cwd: resolve(PLUGIN_DIR, "apps/tui"),
|
|
216
|
+
env: {
|
|
217
|
+
...process.env,
|
|
218
|
+
AGENTBOARD2_DIR: PLUGIN_DIR,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private showKeys(): void {
|
|
224
|
+
// Get tmux prefix and agentboard key from tmux
|
|
225
|
+
let prefix = "C-a";
|
|
226
|
+
let key = DEFAULT_KEY;
|
|
227
|
+
try {
|
|
228
|
+
prefix = execSync("tmux show-option -gv prefix", {
|
|
229
|
+
encoding: "utf8",
|
|
230
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
231
|
+
}).trim();
|
|
232
|
+
const ab2Key = execSync(
|
|
233
|
+
`tmux show-option -gv @agentboard-key 2>/dev/null || echo ${DEFAULT_KEY}`,
|
|
234
|
+
{
|
|
235
|
+
encoding: "utf8",
|
|
236
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
237
|
+
},
|
|
238
|
+
).trim();
|
|
239
|
+
if (ab2Key) key = ab2Key;
|
|
240
|
+
} catch {
|
|
241
|
+
// use defaults
|
|
323
242
|
}
|
|
324
243
|
|
|
325
|
-
|
|
244
|
+
const { toggle, focus } = TMUX_BINDINGS;
|
|
245
|
+
consola.box(
|
|
246
|
+
[
|
|
247
|
+
`${colors.bold("AgentBoard2 Keybindings")}\n`,
|
|
248
|
+
`${colors.cyan(`tmux (prefix = ${prefix}, C = Ctrl):`)}`,
|
|
249
|
+
` ${prefix} ${key} ${toggle} toggle sidebar`,
|
|
250
|
+
` ${prefix} ${key} ${focus} focus sidebar`,
|
|
251
|
+
` ${prefix} ${key} 1-9 jump to session\n`,
|
|
252
|
+
`${colors.cyan("In sidebar:")}`,
|
|
253
|
+
` Tab cycle sessions`,
|
|
254
|
+
` j / ↓ move down`,
|
|
255
|
+
` k / ↑ move up`,
|
|
256
|
+
` Enter / l switch to selected session`,
|
|
257
|
+
` 1-9 jump to session`,
|
|
258
|
+
` d hide session`,
|
|
259
|
+
` x kill session`,
|
|
260
|
+
` t theme picker`,
|
|
261
|
+
` r refresh`,
|
|
262
|
+
` q quit`,
|
|
263
|
+
].join("\n"),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
326
266
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
267
|
+
private reloadTmux(): void {
|
|
268
|
+
try {
|
|
269
|
+
execSync(
|
|
270
|
+
"tmux source-file ~/.config/tmux/tmux.conf 2>/dev/null || tmux source-file ~/.tmux.conf 2>/dev/null",
|
|
271
|
+
{
|
|
272
|
+
stdio: "pipe",
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
consola.success("tmux config reloaded");
|
|
276
|
+
} catch {
|
|
277
|
+
consola.info("Reload tmux manually: tmux source-file ~/.config/tmux/tmux.conf");
|
|
331
278
|
}
|
|
332
279
|
}
|
|
333
280
|
}
|