roger-roger 0.1.13 → 0.1.14
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roger-roger",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Bring yourself back to the conversation: your coding agents reach you by Slack, a sound or a spoken line when they finish, get stuck, or need a decision, and you answer from your phone.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"node": ">=18"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"test": "node --test",
|
|
18
|
+
"test": "node --test --import ./test/setup.mjs",
|
|
19
19
|
"make-sounds": "node tools/make-sounds.mjs",
|
|
20
20
|
"version": "node tools/sync-version.mjs && git add skills/roger-roger/herdr-plugin.toml"
|
|
21
21
|
},
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
id = "roger-roger"
|
|
8
8
|
name = "roger-roger"
|
|
9
|
-
version = "0.1.
|
|
9
|
+
version = "0.1.14"
|
|
10
10
|
min_herdr_version = "0.9.0"
|
|
11
11
|
description = "Slack, a sound or a spoken line from the agents in your panes, and questions you answer from your phone"
|
|
12
12
|
platforms = ["linux", "macos", "windows"]
|
|
@@ -151,25 +151,28 @@ export function workFromTitle(title, agent = "") {
|
|
|
151
151
|
return t;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
// Herdr is asked
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
154
|
+
// Herdr is asked about the pane at most this often per session; a label follows the topic, not keystrokes.
|
|
155
|
+
const ASK_EVERY_MS = 60_000;
|
|
156
|
+
const asked = new Map();
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* What Herdr says is in the pane: whether an agent is there at all, and the work its terminal title
|
|
160
|
+
* names. A pane with no agent in it, a shell where someone ran `roger-roger install` say, is never
|
|
161
|
+
* labelled: its tab belongs to whatever else is in it, and nothing there is an agent to name.
|
|
162
|
+
*/
|
|
163
|
+
export async function paneAgent(session, run = runHerdr, now = Date.now()) {
|
|
164
|
+
const last = asked.get(session.id);
|
|
165
|
+
if (last && now - last.at < ASK_EVERY_MS) return last.found;
|
|
166
|
+
let found = { agent: false, work: "" };
|
|
164
167
|
try {
|
|
165
168
|
const out = await run(["agent", "get", session.herdr.pane], { bin: session.herdr.bin, socket: session.herdr.socket });
|
|
166
|
-
const agent = JSON.parse(out)?.result?.agent
|
|
167
|
-
|
|
169
|
+
const agent = JSON.parse(out)?.result?.agent;
|
|
170
|
+
if (agent) found = { agent: true, work: workFromTitle(agent.terminal_title_stripped ?? agent.terminal_title, agent.agent ?? session.agent) };
|
|
168
171
|
} catch {
|
|
169
|
-
//
|
|
172
|
+
// Herdr says there is no agent there (agent_not_found), or can't be asked: either way, no labels.
|
|
170
173
|
}
|
|
171
|
-
|
|
172
|
-
return
|
|
174
|
+
asked.set(session.id, { at: now, found });
|
|
175
|
+
return found;
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
const inFlight = new Set();
|
|
@@ -178,14 +181,17 @@ const inFlight = new Set();
|
|
|
178
181
|
* Put the session's labels on its Herdr pane, if it has one and they are not there yet. Never
|
|
179
182
|
* throws: a pane that can't be labelled is a line in the log, not a failed command. `touch` records
|
|
180
183
|
* what was put on, so it isn't done again; `run` is the herdr invocation (replaced in tests).
|
|
181
|
-
* Without a `--session` name, the work is
|
|
184
|
+
* Only a pane Herdr says has an agent in it is labelled. Without a `--session` name, the work is
|
|
185
|
+
* taken from the pane's terminal title.
|
|
182
186
|
*/
|
|
183
187
|
export async function labelPane(session, { log = () => {}, touch = () => {}, run = runHerdr } = {}) {
|
|
184
188
|
if (!session?.herdr?.tab || !session?.herdr?.pane || inFlight.has(session.id)) return { labelled: false };
|
|
185
189
|
inFlight.add(session.id);
|
|
186
|
-
let commands;
|
|
190
|
+
let commands = [];
|
|
187
191
|
try {
|
|
188
|
-
|
|
192
|
+
const pane = await paneAgent(session, run);
|
|
193
|
+
// The --session name wins; the terminal title stands in when there is none.
|
|
194
|
+
if (pane.agent) commands = labelCommands({ ...session, name: str(session.name) || pane.work });
|
|
189
195
|
} catch {
|
|
190
196
|
commands = [];
|
|
191
197
|
}
|
|
@@ -882,7 +882,7 @@ export async function runInstall(args, { script, hooks, input = process.stdin, o
|
|
|
882
882
|
await ui.run("Starting the daemon", () => daemon("ping"), { done: (r) => ({ text: "Daemon running", detail: `pid ${r.data?.pid ?? "?"}, it stays up for every agent on this machine` }) }).catch(() => {});
|
|
883
883
|
const touchedDelivery = ["reach", "sound", "voice"].some(asking);
|
|
884
884
|
if (first || touchedDelivery) {
|
|
885
|
-
const session = { identity: identity(env, process.cwd(), { session: "roger-roger install" }), about: { name: "roger-roger install", agent: "install" } };
|
|
885
|
+
const session = { identity: identity({ ...env, HERDR_ENV: "" }, process.cwd(), { session: "roger-roger install" }), about: { name: "roger-roger install", agent: "install" } };
|
|
886
886
|
const test = await ui.confirm({ message: "Send a test notification now?", initial: first });
|
|
887
887
|
if (test) {
|
|
888
888
|
await attempt(ui, "Sending the test", () =>
|
|
@@ -211,16 +211,24 @@ export function createUI({ input = process.stdin, output = process.stdout, env =
|
|
|
211
211
|
write("\x1b[?25l");
|
|
212
212
|
rawOn();
|
|
213
213
|
const keys = keyReader(input);
|
|
214
|
+
// Something started by a key can finish after the question is answered (a voice preview still
|
|
215
|
+
// speaking when Enter was pressed). Its redraw must not bring the answered question back, drawn
|
|
216
|
+
// under the next one.
|
|
217
|
+
let open = true;
|
|
218
|
+
const redraw = () => {
|
|
219
|
+
if (open) paint.draw(draw());
|
|
220
|
+
};
|
|
214
221
|
try {
|
|
215
222
|
paint.draw(draw());
|
|
216
223
|
for (;;) {
|
|
217
224
|
const key = await keys.next();
|
|
218
225
|
if (cancelKey(key)) throw new Cancelled();
|
|
219
|
-
const result = await onKey(key,
|
|
226
|
+
const result = await onKey(key, redraw);
|
|
220
227
|
if (result === true) break;
|
|
221
228
|
paint.draw(draw());
|
|
222
229
|
}
|
|
223
230
|
} finally {
|
|
231
|
+
open = false;
|
|
224
232
|
keys.close();
|
|
225
233
|
rawOff();
|
|
226
234
|
paint.clear();
|