muuuuse 0.1.0 → 0.2.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/src/cli.js ADDED
@@ -0,0 +1,193 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const readline = require("node:readline/promises");
4
+
5
+ const { PRESETS } = require("./agents");
6
+ const { Controller, SeatDaemon, armSeat, configureScript, enableLiveMode } = require("./runtime");
7
+ const { getPaneInfo, insideTmux } = require("./tmux");
8
+ const {
9
+ BRAND,
10
+ commandExists,
11
+ findFirstExisting,
12
+ parseFlags,
13
+ readCommandVersion,
14
+ toInt,
15
+ usage,
16
+ } = require("./util");
17
+
18
+ async function main(argv = process.argv.slice(2)) {
19
+ if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
20
+ process.stdout.write(`${usage()}\n`);
21
+ return;
22
+ }
23
+
24
+ const command = argv[0];
25
+
26
+ if (command === "doctor") {
27
+ runDoctor();
28
+ return;
29
+ }
30
+
31
+ if (command === "daemon") {
32
+ const sessionName = argv[1];
33
+ const seatId = toInt(argv[2], 0);
34
+ if (!sessionName || ![1, 2].includes(seatId)) {
35
+ throw new Error("daemon requires <session-name> <1|2>.");
36
+ }
37
+
38
+ const daemon = new SeatDaemon(sessionName, seatId);
39
+ const code = await daemon.run();
40
+ process.exit(code);
41
+ }
42
+
43
+ if (command === "1" || command === "2") {
44
+ armCurrentPane(Number(command));
45
+ return;
46
+ }
47
+
48
+ if (command === "3") {
49
+ const { positionals, flags } = parseFlags(argv.slice(1));
50
+ const paneInfo = requireTmuxPane();
51
+ const seedText = positionals.join(" ").trim();
52
+ const controller = new Controller(paneInfo.sessionName, {
53
+ seedSeat: flags.seedSeat,
54
+ seedText,
55
+ maxRelays: flags.maxRelays,
56
+ });
57
+ const code = await controller.run();
58
+ process.exit(code);
59
+ }
60
+
61
+ if (command === "script") {
62
+ await setScriptMode(argv.slice(1));
63
+ return;
64
+ }
65
+
66
+ if (command === "live") {
67
+ const paneInfo = requireTmuxPane();
68
+ const seat = enableLiveMode({
69
+ sessionName: paneInfo.sessionName,
70
+ paneId: paneInfo.paneId,
71
+ });
72
+ process.stdout.write(`${BRAND} seat ${seat.seatId} is back in live-listen mode.\n`);
73
+ return;
74
+ }
75
+
76
+ throw new Error(`Unknown command '${command}'.`);
77
+ }
78
+
79
+ function requireTmuxPane() {
80
+ if (!insideTmux()) {
81
+ throw new Error("muuuuse must run inside tmux so it can arm and inject the current pane.");
82
+ }
83
+
84
+ const paneInfo = getPaneInfo();
85
+ if (!paneInfo) {
86
+ throw new Error("tmux is active, but the current pane could not be resolved.");
87
+ }
88
+ return paneInfo;
89
+ }
90
+
91
+ function armCurrentPane(seatId) {
92
+ const paneInfo = requireTmuxPane();
93
+ const binPath = path.resolve(__dirname, "..", "bin", "muuse.js");
94
+ const meta = armSeat({
95
+ seatId,
96
+ paneInfo,
97
+ binPath,
98
+ });
99
+
100
+ process.stdout.write(`${BRAND} armed seat ${seatId} in tmux session ${meta.sessionName}.\n`);
101
+ process.stdout.write(`Pane: ${meta.paneId}\n`);
102
+ process.stdout.write(`Path: ${meta.cwd}\n`);
103
+ process.stdout.write("\nLaunch one of these in this same terminal whenever you're ready:\n");
104
+ for (const preset of Object.values(PRESETS)) {
105
+ process.stdout.write(`- ${preset.command.join(" ")}\n`);
106
+ }
107
+ process.stdout.write("\nOr switch this seat to deterministic mode with `muuuuse script`.\n");
108
+ }
109
+
110
+ async function setScriptMode(argv) {
111
+ const paneInfo = requireTmuxPane();
112
+ const { positionals, flags } = parseFlags(argv);
113
+ const count = Math.max(1, toInt(positionals[0] || 1, 1));
114
+ const steps = [...flags.step];
115
+
116
+ if (steps.length < count) {
117
+ const rl = readline.createInterface({
118
+ input: process.stdin,
119
+ output: process.stdout,
120
+ });
121
+
122
+ try {
123
+ while (steps.length < count) {
124
+ const answer = (await rl.question(`Script step ${steps.length + 1}/${count}: `)).trim();
125
+ if (!answer) {
126
+ process.stdout.write("Step cannot be empty.\n");
127
+ continue;
128
+ }
129
+ steps.push(answer);
130
+ }
131
+ } finally {
132
+ rl.close();
133
+ }
134
+ }
135
+
136
+ const result = configureScript({
137
+ sessionName: paneInfo.sessionName,
138
+ paneId: paneInfo.paneId,
139
+ steps: steps.slice(0, count),
140
+ });
141
+
142
+ process.stdout.write(`${BRAND} seat ${result.seatId} is now in script mode with ${result.steps.length} step`);
143
+ process.stdout.write(result.steps.length === 1 ? ".\n" : "s.\n");
144
+ }
145
+
146
+ function runDoctor() {
147
+ const checks = [
148
+ checkBinary("git", ["--version"], true),
149
+ checkBinary("npm", ["--version"], true),
150
+ checkBinary("tmux", ["-V"], true),
151
+ checkBinary("codex", ["--version"], false),
152
+ checkBinary("claude", ["--version"], false),
153
+ checkBinary("gemini", ["--version"], false),
154
+ checkPath("npm token file", findFirstExisting(["/root/npm.txt", "/root/_ops-bank/credentials/npm.txt"]), true),
155
+ ];
156
+
157
+ process.stdout.write(`${BRAND} doctor\n\n`);
158
+ for (const item of checks) {
159
+ process.stdout.write(`${item.ok ? "OK " : "MISS"} ${item.label}${item.detail ? `: ${item.detail}` : ""}\n`);
160
+ }
161
+
162
+ process.stdout.write("\nLaunch presets\n");
163
+ for (const [name, preset] of Object.entries(PRESETS)) {
164
+ process.stdout.write(`- ${name}: ${preset.command.join(" ")}\n`);
165
+ }
166
+
167
+ process.stdout.write("\nRemote routing lives in Codeman / codemansbot. This package stays local-only.\n");
168
+
169
+ const missingRequired = checks.some((item) => item.required && !item.ok);
170
+ if (missingRequired) {
171
+ process.exitCode = 1;
172
+ }
173
+ }
174
+
175
+ function checkBinary(command, versionArgs, required) {
176
+ if (!commandExists(command)) {
177
+ return { label: command, ok: false, detail: "not installed", required };
178
+ }
179
+ const version = readCommandVersion(command, versionArgs) || "installed";
180
+ return { label: command, ok: true, detail: version, required };
181
+ }
182
+
183
+ function checkPath(label, filePath, required) {
184
+ if (!filePath || !fs.existsSync(filePath)) {
185
+ return { label, ok: false, detail: "not found", required };
186
+ }
187
+ return { label, ok: true, detail: filePath, required };
188
+ }
189
+
190
+ module.exports = {
191
+ main,
192
+ runDoctor,
193
+ };