parawatch 0.3.6 → 0.3.7
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/README.md +2 -0
- package/dist/bin/cli.js +6 -1
- package/dist/core/format.js +2 -0
- package/dist/core/ops.js +22 -0
- package/dist/core/state.js +2 -0
- package/package.json +1 -1
- package/skills/parawatch/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -167,6 +167,8 @@ An agent claims the project the work is for, not the folder it runs in, and clai
|
|
|
167
167
|
|
|
168
168
|
An approval (`ask --action`) shows the exact command and the text it sends, with Do it and Don't, so the human can say yes without opening the session.
|
|
169
169
|
|
|
170
|
+
The human can dismiss a card that no longer matters, with the x on the card in the app or `parawatch dismiss <card> -n "why"`. The card closes without an answer, and the agent that asked gets "dismissed by the human" at its next `wait` or `inbox`.
|
|
171
|
+
|
|
170
172
|
`parawatch edit <card>` changes a card after the fact: its kind, wording, task, who asked or `--since`. `parawatch goto <card>` opens the session that asked in the Claude desktop app (`--copy` also puts a line about the card on the clipboard, to paste there); sessions outside the desktop app get the `claude --resume` command instead.
|
|
171
173
|
|
|
172
174
|
`wait` prints the answers, and news on linked pull requests, and exits with code 0. Run from an agent session, it reads that session's own cards and pull requests, and the ones no session owns in the project it claimed, with a cursor of its own: four sessions in one project each wake only for their own news. `wait -p <project>` reads the whole project, for a lead. With `--timeout <seconds>` it exits with code 2 when nothing came. An answer given before `wait` starts is not lost: each consumer has its own cursor, and `wait` returns everything after it. While a `wait` runs, cards say an agent is waiting; when none does, an answered card says the answer is kept until the agent next checks.
|
package/dist/bin/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { UserError, addProject, answerCard, beatRun, claimSession, closeCard, commentCard, inboxScope, editCard, endRun, hideRun, unhideRun, linkPr, listeners, listenersFor, noteRun, openCard, prEvent, findCard, readInbox, readState, removeProject, startListening, startRun, unlinkPr, waitInbox, } from "../core/ops.js";
|
|
5
|
+
import { UserError, addProject, answerCard, beatRun, claimSession, closeCard, dismissCard, commentCard, inboxScope, editCard, endRun, hideRun, unhideRun, linkPr, listeners, listenersFor, noteRun, openCard, prEvent, findCard, readInbox, readState, removeProject, startListening, startRun, unlinkPr, waitInbox, } from "../core/ops.js";
|
|
6
6
|
import { githubConfig, readCache, resolvePrRef, syncGithub, prViews } from "../core/github.js";
|
|
7
7
|
import { digest, digestText, localDate } from "../core/digest.js";
|
|
8
8
|
import { everywhere, readEvents } from "../core/store.js";
|
|
@@ -183,6 +183,11 @@ program
|
|
|
183
183
|
.description("close a card once it has been acted on")
|
|
184
184
|
.option("-n, --note <text>", "what was done")
|
|
185
185
|
.action((id, o) => closeCard(id, o.note));
|
|
186
|
+
program
|
|
187
|
+
.command("dismiss <card>")
|
|
188
|
+
.description("close a card that no longer matters, without answering it; the agent that asked is told")
|
|
189
|
+
.option("-n, --note <text>", "why, for the agent")
|
|
190
|
+
.action((id, o) => dismissCard(id, o.note));
|
|
186
191
|
program
|
|
187
192
|
.command("show <card>")
|
|
188
193
|
.description("show one card with its answers")
|
package/dist/core/format.js
CHANGED
|
@@ -187,6 +187,8 @@ export function inboxLine(i) {
|
|
|
187
187
|
const head = `[${card.project}] ${card.id} "${card.title}"`;
|
|
188
188
|
if (i.kind === "comment")
|
|
189
189
|
return `${head} comment: ${i.comment}`;
|
|
190
|
+
if (i.kind === "dismissed")
|
|
191
|
+
return `${head} dismissed by the human: no longer needed, do not act on it${i.comment ? `; said: ${i.comment}` : ""}`;
|
|
190
192
|
const parts = [];
|
|
191
193
|
if (i.choice !== undefined)
|
|
192
194
|
parts.push(`chose ${i.choice}${i.choiceText ? ` (${i.choiceText})` : ""}`);
|
package/dist/core/ops.js
CHANGED
|
@@ -224,6 +224,16 @@ export function closeCard(id, note) {
|
|
|
224
224
|
return;
|
|
225
225
|
appendEvent(() => ({ type: "card.close", card: card.id, note: note?.trim() || undefined }));
|
|
226
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* The human drops a card that no longer matters, without answering it. It closes, and the agent
|
|
229
|
+
* that asked reads that it was dismissed (a wait wakes for it), so nothing waits on it forever.
|
|
230
|
+
*/
|
|
231
|
+
export function dismissCard(id, note) {
|
|
232
|
+
const card = requireCard(id);
|
|
233
|
+
if (card.status === "closed")
|
|
234
|
+
throw new UserError(`card ${card.id} is already closed`);
|
|
235
|
+
appendEvent(() => ({ type: "card.close", card: card.id, note: note?.trim() || undefined, dismissed: true }));
|
|
236
|
+
}
|
|
227
237
|
export function startRun(r) {
|
|
228
238
|
if (r.heartbeat !== undefined && (!Number.isFinite(r.heartbeat) || r.heartbeat <= 0))
|
|
229
239
|
throw new UserError("--heartbeat must be a number of seconds above 0");
|
|
@@ -385,6 +395,18 @@ export function inboxFrom(events, after, filter, state = readState(events)) {
|
|
|
385
395
|
out.push({ seq: ev.seq, dev: ev.dev, at: ev.ts, kind: "pr", by: ev.by ?? ev.source, pr: { ref: ev.pr, change: ev.change, title: ev.title, text: ev.text, project: link.project } });
|
|
386
396
|
continue;
|
|
387
397
|
}
|
|
398
|
+
if (ev.type === "card.close") {
|
|
399
|
+
// a card the human dismissed reaches the agent that asked, once
|
|
400
|
+
if (!ev.dismissed || !isAfter(ev, pos))
|
|
401
|
+
continue;
|
|
402
|
+
const card = state.cards.get(ev.card);
|
|
403
|
+
if (!card || !inScope(card.session, card.project, filter, ended))
|
|
404
|
+
continue;
|
|
405
|
+
if (filter.card && card.id !== filter.card)
|
|
406
|
+
continue;
|
|
407
|
+
out.push({ seq: ev.seq, dev: ev.dev, at: ev.ts, kind: "dismissed", card, by: HUMAN, comment: ev.note });
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
388
410
|
if (ev.type !== "card.answer" && ev.type !== "card.comment")
|
|
389
411
|
continue;
|
|
390
412
|
let late = false;
|
package/dist/core/state.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parawatch",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "A control tower for people running many AI coding agents in parallel: see what is running, what needs you, and wake the right agent when you answer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -93,6 +93,8 @@ The output looks like `[project] c4 "Ship the top-box ending?" chose 2 (Top box)
|
|
|
93
93
|
3. Close the card with what you did: `parawatch close c4 -n "Top box shipped in #212"`.
|
|
94
94
|
4. If more cards are open, or a linked pull request is not merged yet, arm the wait again.
|
|
95
95
|
|
|
96
|
+
A card can also come back as `[project] c4 "…" dismissed by the human: no longer needed, do not act on it`. The human closed it without answering. Drop that step, do not open the card again, and go on with the rest of the work.
|
|
97
|
+
|
|
96
98
|
To add information to a card without waking anyone, use `parawatch comment <card> "<text>"`. Never answer a card yourself: `answer` is the human's.
|
|
97
99
|
|
|
98
100
|
## 6. Long jobs that leave no agent log
|