passalong 0.18.0 → 0.19.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/README.md +3 -3
- package/bin/passalong +19 -12
- package/package.json +1 -1
- package/skill/SKILL.md +1 -2
- package/src/api.js +2 -0
- package/src/guide.js +7 -6
- package/src/mcp.js +19 -22
- package/src/passalong.js +11 -6
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ codex mcp add passalong -- passalong mcp # Codex: the same MCP server
|
|
|
28
28
|
## Commands
|
|
29
29
|
|
|
30
30
|
```
|
|
31
|
-
passalong share [file] [--to team[/handle]] publish a guide
|
|
31
|
+
passalong share [file] [--to team[/handle]] publish a guide → id + link, Ready; --to addresses it
|
|
32
32
|
passalong pull <id|link> print a guide; --save also writes ./.passalong/<id>.md (git-ignored)
|
|
33
33
|
passalong inbox guides handed to you that you have not pulled yet
|
|
34
34
|
passalong board waiting on you, in flight, landed, worth keeping
|
|
@@ -73,8 +73,8 @@ passalong inbox # on Ada's side: what was handed to you
|
|
|
73
73
|
passalong activity # back on your side: it landed, and who pulled it
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
Every guide, whatever its kind, moves the same way: it is
|
|
77
|
-
|
|
76
|
+
Every guide, whatever its kind, moves the same way: it is Ready when shared (or a draft, with
|
|
77
|
+
`status: draft`, until a person runs `passalong ready <id>`), then whoever takes it (`passalong take <id>`) holds it, hands it in with
|
|
78
78
|
the evidence, and a person approves it or sends it back with the reason (`passalong approve` /
|
|
79
79
|
`passalong reject`).
|
|
80
80
|
|
package/bin/passalong
CHANGED
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
// handed to whoever pulls or starts that guide
|
|
7
7
|
// TARGET is a team, team/@handle for one person, or "team/#group" for the people who do a
|
|
8
8
|
// thing — quote that one, since a shell may read the # as a comment
|
|
9
|
-
// passalong task "<what needs doing>" write a task for an agent;
|
|
9
|
+
// passalong task "<what needs doing>" write a task for an agent; Ready at once
|
|
10
10
|
// passalong ready <id...|--all> guides you have read are ready — whoever they are for may take them now
|
|
11
11
|
// passalong tasks every task and where it is: draft, ready, claimed, stalled, review
|
|
12
12
|
// passalong work [--once] [--agent CMD] run an agent on each ready task here, one after another
|
|
13
|
-
// passalong
|
|
14
|
-
// passalong reject <id> <why> not done: back to Ready, and whoever takes it next reads why
|
|
13
|
+
// passalong reopen <id> <why> finished work isn't right: back to To do, and whoever takes it next reads why
|
|
15
14
|
// passalong release <id> take work back from whoever holds it, of any kind
|
|
16
15
|
// passalong attach <file> upload a picture or a file and print the markdown that points at it
|
|
17
16
|
// passalong reply <id> <text> write to whoever holds it (--file PATH to attach); or leave a note
|
|
@@ -337,9 +336,8 @@ const commands = {
|
|
|
337
336
|
const id = guide.meta.id;
|
|
338
337
|
console.log(id);
|
|
339
338
|
const target = guide.meta.target_context;
|
|
340
|
-
console.error(`task "${guide.meta.title}" is
|
|
339
|
+
console.error(`task "${guide.meta.title}" is Ready${target ? ` for ${target}` : " (no repo)"}`);
|
|
341
340
|
if (guide.meta.team) console.error(`handed to ${guide.meta.team} (team inbox)`);
|
|
342
|
-
console.error(`when an agent may take it: passalong ready ${id}`);
|
|
343
341
|
},
|
|
344
342
|
|
|
345
343
|
async tasks() {
|
|
@@ -384,16 +382,25 @@ const commands = {
|
|
|
384
382
|
if (n) console.error("review them with: passalong tasks");
|
|
385
383
|
},
|
|
386
384
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
console.error(
|
|
385
|
+
// Nothing waits for approval now (docs/REVIEW.md): finishing is final. Kept so a script that
|
|
386
|
+
// calls it is told, not refused.
|
|
387
|
+
async approve() {
|
|
388
|
+
console.error(
|
|
389
|
+
"nothing to approve: finished work is already Done. If it isn't right: passalong reopen <id> <why>",
|
|
390
|
+
);
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
/** Finished work back to To do, with why at its top; whoever finished it is told. */
|
|
394
|
+
async reopen(f) {
|
|
395
|
+
const id = f._[0] || die("usage: passalong reopen <id> <why>");
|
|
396
|
+
const why = f._.slice(1).join(" ");
|
|
397
|
+
if (!why) die("say why: passalong reopen <id> <what is wrong>");
|
|
398
|
+
await passalong.reopen(id, why).catch((e) => die(e.message));
|
|
399
|
+
console.error(`${id} is back in To do — whoever takes it next reads why`);
|
|
391
400
|
},
|
|
392
401
|
|
|
393
402
|
async reject(f) {
|
|
394
|
-
|
|
395
|
-
await passalong.rejectTask(id, f._.slice(1).join(" ")).catch((e) => die(e.message));
|
|
396
|
-
console.error(`${id} is back in Ready — the next agent reads why`);
|
|
403
|
+
return commands.reopen(f);
|
|
397
404
|
},
|
|
398
405
|
|
|
399
406
|
async release(f) {
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -67,8 +67,7 @@ machine, another agent session, or a teammate without your branch.
|
|
|
67
67
|
follow-up gets the guide it came out of, and where that guide has got to — so write it as a
|
|
68
68
|
note on that work rather than as a standalone brief. What you did or found while working on a
|
|
69
69
|
guide belongs in its `hand_in`, not a follow-up.
|
|
70
|
-
6. **Report** the id (and link) and
|
|
71
|
-
it and marks it Ready (`passalong ready <id>`, or Ready in the hub). Then one line on how to use
|
|
70
|
+
6. **Report** the id (and link). It is Ready and has reached whoever it is for. Then one line on how to use
|
|
72
71
|
it on the other side: `passalong take <id>` in the target repo, or "take passalong <id>" to an
|
|
73
72
|
agent with the Passalong MCP server. `take` rather than `pull` on purpose: it holds the work as
|
|
74
73
|
well as fetching it, and until somebody takes it the sender cannot tell a guide nobody noticed
|
package/src/api.js
CHANGED
|
@@ -429,6 +429,8 @@ export const approveTask = (id) =>
|
|
|
429
429
|
call(`/v1/tasks/${encodeURIComponent(id)}/approve`, { method: "POST", body: {} });
|
|
430
430
|
export const rejectTask = (id, why) =>
|
|
431
431
|
call(`/v1/tasks/${encodeURIComponent(id)}/reject`, { method: "POST", body: { why } });
|
|
432
|
+
export const reopen = (id, why) =>
|
|
433
|
+
call(`/v1/guides/${encodeURIComponent(id)}/reopen`, { method: "POST", body: { why } });
|
|
432
434
|
/** A handoff's or bug's approve and send-back. No `place` names the only hand-in there is. */
|
|
433
435
|
export const closeGuide = (id) =>
|
|
434
436
|
call(`/v1/guides/${encodeURIComponent(id)}/close`, { method: "POST", body: {} });
|
package/src/guide.js
CHANGED
|
@@ -497,7 +497,9 @@ export function stamp(guide, defaults = {}) {
|
|
|
497
497
|
const meta = { ...guide.meta };
|
|
498
498
|
if (!meta.id) meta.id = newId();
|
|
499
499
|
if (!meta.created) meta.created = new Date().toISOString();
|
|
500
|
-
|
|
500
|
+
// Ready unless whoever wrote it said draft: a guide sent to someone is meant to reach them, and
|
|
501
|
+
// drafts were where addressed work sat forgotten (every draft in production on 2026-10-15).
|
|
502
|
+
if (!meta.status) meta.status = "published";
|
|
501
503
|
for (const [k, v] of Object.entries(defaults))
|
|
502
504
|
if (meta[k] === undefined || meta[k] === "") meta[k] = v;
|
|
503
505
|
return { meta, body: guide.body };
|
|
@@ -525,8 +527,7 @@ export function bugGuide({
|
|
|
525
527
|
area = "",
|
|
526
528
|
report = "",
|
|
527
529
|
environment = "",
|
|
528
|
-
|
|
529
|
-
status = "draft",
|
|
530
|
+
status = "published",
|
|
530
531
|
evidence = [],
|
|
531
532
|
} = {}) {
|
|
532
533
|
/**
|
|
@@ -617,7 +618,7 @@ export function template(meta = {}) {
|
|
|
617
618
|
summary: "",
|
|
618
619
|
author: "",
|
|
619
620
|
source_context: "",
|
|
620
|
-
status: "
|
|
621
|
+
status: "published",
|
|
621
622
|
stack_assumptions: [],
|
|
622
623
|
tags: [],
|
|
623
624
|
...meta,
|
|
@@ -655,7 +656,7 @@ function bugTemplate(meta = {}) {
|
|
|
655
656
|
kind: "bug",
|
|
656
657
|
author: "",
|
|
657
658
|
source_context: "",
|
|
658
|
-
status: "
|
|
659
|
+
status: "published",
|
|
659
660
|
severity: "s3",
|
|
660
661
|
stack_assumptions: [],
|
|
661
662
|
tags: ["bug"],
|
|
@@ -697,7 +698,7 @@ function taskTemplate(meta = {}) {
|
|
|
697
698
|
author: "",
|
|
698
699
|
source_context: "",
|
|
699
700
|
target_context: "",
|
|
700
|
-
status: "
|
|
701
|
+
status: "published",
|
|
701
702
|
stack_assumptions: [],
|
|
702
703
|
tags: [],
|
|
703
704
|
...meta,
|
package/src/mcp.js
CHANGED
|
@@ -297,15 +297,16 @@ export function buildServer() {
|
|
|
297
297
|
"to act. It is what the person sees first in their hub; the document is behind it. A guide " +
|
|
298
298
|
"with no summary is refused.\n" +
|
|
299
299
|
"SAY WHICH KIND IT IS. There is no default: a guide with no `kind:` line is refused, and so " +
|
|
300
|
-
"is a spelling that is not one of the three.
|
|
301
|
-
"
|
|
302
|
-
"
|
|
300
|
+
"is a spelling that is not one of the three. A NEW GUIDE IS READY: it reaches whoever it is " +
|
|
301
|
+
"for at once. Say `status: draft` only when the person should read it first — something you " +
|
|
302
|
+
"are unsure of — and tell them; plan_tasks drafts for you. Every kind moves the same way — " +
|
|
303
|
+
"Ready, taken, handed in, approved. " +
|
|
303
304
|
`kind: task is work nobody has done yet. Sections: ${TASK_SECTIONS.join(", ")}. ` +
|
|
304
305
|
"It has no Steps: work out how to reach Goal within Constraints, leave Out of scope " +
|
|
305
306
|
"alone, and treat Acceptance as the definition of done. A proposal awaiting a team decision " +
|
|
306
|
-
"stays Draft until a person makes it Ready. hand_in
|
|
307
|
-
"
|
|
308
|
-
"
|
|
307
|
+
"stays Draft until a person makes it Ready. hand_in when every Acceptance line holds: it is " +
|
|
308
|
+
"final — the task goes to Done with what you showed, and nobody reviews it after you — so " +
|
|
309
|
+
"show it, and say anything you had to adapt in `writeup` (500 characters).\n" +
|
|
309
310
|
`kind: transfer (or no kind: line) is a finished implementation to repeat here. Sections: ${SECTIONS.join(", ")}. ` +
|
|
310
311
|
"Follow its Steps, adapting anything marked ASSUMES to this codebase; run its " +
|
|
311
312
|
"Verification, then hand_in with ok and, when it failed, a note saying what went wrong.\n" +
|
|
@@ -1084,7 +1085,7 @@ export function buildServer() {
|
|
|
1084
1085
|
const ids = await passalong.planTasks(steps, { cwd: cwd || process.cwd() });
|
|
1085
1086
|
return json({
|
|
1086
1087
|
drafts: ids.map((id, i) => ({ id, title: steps[i].title, after: steps[i].after || [] })),
|
|
1087
|
-
next:
|
|
1088
|
+
next: `all in Draft: ask the user to read them and mark them Ready on the board at ${api.baseUrl()}/hub/work, or run \`passalong ready --all\``,
|
|
1088
1089
|
});
|
|
1089
1090
|
} catch (err) {
|
|
1090
1091
|
return fail(err);
|
|
@@ -1157,7 +1158,7 @@ export function buildServer() {
|
|
|
1157
1158
|
outputSchema: handInOut,
|
|
1158
1159
|
annotations: ADDS,
|
|
1159
1160
|
description:
|
|
1160
|
-
"
|
|
1161
|
+
"Finished here, with proof. IT IS FINAL: the work goes to Done with what you showed, and nobody reviews it after you (anyone can reopen it with a reason). So SHOW IT: a screenshot for anything with a screen — before and after, for a bug — or, for backend work, the one command and the line of its output that shows it works. Send `checks`: one entry per " +
|
|
1161
1162
|
"line the guide asks for — `## Acceptance` on a task, `## Verification` on a handoff or a " +
|
|
1162
1163
|
"bug — each with that line and the evidence for it. That is what its author reads, line " +
|
|
1163
1164
|
"against line, instead of hunting through a wall of output for the part that answers each " +
|
|
@@ -1165,8 +1166,7 @@ export function buildServer() {
|
|
|
1165
1166
|
"command proves it: the command is run here before the hand-in lands, a non-zero exit " +
|
|
1166
1167
|
"refuses it, and what it printed is recorded instead of your account of it. Use `ran` for " +
|
|
1167
1168
|
"a check no command can settle. `evidence` as one block is the older shape and still " +
|
|
1168
|
-
"works. A task
|
|
1169
|
-
"this publishes it and attaches it. A handoff or a bug takes `ok`, whether its " +
|
|
1169
|
+
"works. A task needs no write-up guide any more. A handoff or a bug takes `ok`, whether its " +
|
|
1170
1170
|
"Verification held here, and `note` saying what went wrong when it did not. DO NOT " +
|
|
1171
1171
|
"publish a guide to carry your evidence: it belongs on this call, and a second document " +
|
|
1172
1172
|
'titled "Hand-in evidence" is the thing `checks` exists to replace. If it worked but you ' +
|
|
@@ -1242,10 +1242,10 @@ export function buildServer() {
|
|
|
1242
1242
|
.string()
|
|
1243
1243
|
.optional()
|
|
1244
1244
|
.describe(
|
|
1245
|
-
"
|
|
1246
|
-
"
|
|
1247
|
-
"it out when it worked as written. The next
|
|
1248
|
-
"
|
|
1245
|
+
"what you had to adapt to make it work here — a version, a name, a step that needed " +
|
|
1246
|
+
"something the guide does not mention. 500 characters at most; anything longer " +
|
|
1247
|
+
"belongs in the change. Optional: leave it out when it worked as written. The next " +
|
|
1248
|
+
"person to open the guide is shown it",
|
|
1249
1249
|
),
|
|
1250
1250
|
risk: z
|
|
1251
1251
|
.string()
|
|
@@ -1259,11 +1259,11 @@ export function buildServer() {
|
|
|
1259
1259
|
markdown: z
|
|
1260
1260
|
.string()
|
|
1261
1261
|
.optional()
|
|
1262
|
-
.describe("
|
|
1262
|
+
.describe("older shape, not needed: say what you adapted in `writeup` instead"),
|
|
1263
1263
|
report: z
|
|
1264
1264
|
.string()
|
|
1265
1265
|
.optional()
|
|
1266
|
-
.describe("
|
|
1266
|
+
.describe("older shape, not needed: say what you adapted in `writeup` instead"),
|
|
1267
1267
|
pr: z.string().optional().describe("task: PR or branch link, when there is one"),
|
|
1268
1268
|
cwd: z
|
|
1269
1269
|
.string()
|
|
@@ -1579,7 +1579,8 @@ export function buildServer() {
|
|
|
1579
1579
|
...(guide.meta.status === "draft"
|
|
1580
1580
|
? {
|
|
1581
1581
|
status: "draft",
|
|
1582
|
-
|
|
1582
|
+
// The hub page, never the share link: the share page has no Ready button.
|
|
1583
|
+
next: `in Draft: ask the user to read it and mark it Ready at ${api.baseUrl()}/hub/g/${guide.meta.id}, or run \`passalong ready ${guide.meta.id}\``,
|
|
1583
1584
|
}
|
|
1584
1585
|
: {}),
|
|
1585
1586
|
});
|
|
@@ -1677,11 +1678,7 @@ export function buildServer() {
|
|
|
1677
1678
|
{ title: title || "", environment: environment || "", issues },
|
|
1678
1679
|
{ cwd: cwd || process.cwd(), to },
|
|
1679
1680
|
);
|
|
1680
|
-
return json(
|
|
1681
|
-
...filed,
|
|
1682
|
-
// Filed is not sent: every bug is a draft until a person reads it.
|
|
1683
|
-
next: "all in Draft: ask the user to read them, then run `passalong ready --all`",
|
|
1684
|
-
});
|
|
1681
|
+
return json(filed);
|
|
1685
1682
|
} catch (err) {
|
|
1686
1683
|
return fail(err);
|
|
1687
1684
|
}
|
package/src/passalong.js
CHANGED
|
@@ -78,12 +78,6 @@ export async function share(markdown, { cwd = process.cwd(), to, follows } = {})
|
|
|
78
78
|
];
|
|
79
79
|
if (errors.length)
|
|
80
80
|
throw new PassalongError(`guide is not ready to share:\n - ${errors.join("\n - ")}`);
|
|
81
|
-
// Every guide is a draft when it is first shared, whatever its kind: Draft is where it waits
|
|
82
|
-
// until a person says it is ready — `ready()` — and an agent that writes one over MCP comes
|
|
83
|
-
// through here too, so publishing on share would let it reach people with nobody reading it.
|
|
84
|
-
// Sharing *is* putting it in Draft, so a new guide that names no status says so here; one already
|
|
85
|
-
// published keeps what its document says.
|
|
86
|
-
if (!guide.meta.status) guide = { ...guide, meta: { ...guide.meta, status: "draft" } };
|
|
87
81
|
|
|
88
82
|
// The server is where a guide lives; nothing is kept on this machine.
|
|
89
83
|
needsSync("sharing a guide");
|
|
@@ -583,6 +577,14 @@ export async function now({ cwd = process.cwd() } = {}) {
|
|
|
583
577
|
return { held, waiting, needs: review + handed + waiting.inbox, agent: who.agent };
|
|
584
578
|
}
|
|
585
579
|
|
|
580
|
+
/** Finished work back to To do, with why (docs/REVIEW.md §4). */
|
|
581
|
+
export async function reopen(id, why) {
|
|
582
|
+
needsSync("reopening work");
|
|
583
|
+
if (!String(why || "").trim())
|
|
584
|
+
throw new PassalongError("say why: whoever takes it next reads it");
|
|
585
|
+
return api.reopen(await resolveId(id), why);
|
|
586
|
+
}
|
|
587
|
+
|
|
586
588
|
/**
|
|
587
589
|
* The gate, a person's three answers to a task. Approve: the work in review is done. Reject: it
|
|
588
590
|
* goes back to Ready with `why` on it for the next agent. Release: a task an agent holds, live or
|
|
@@ -1046,10 +1048,13 @@ export async function planTasks(steps, { cwd = process.cwd() } = {}) {
|
|
|
1046
1048
|
].join("\n");
|
|
1047
1049
|
// The summary is the step's own, said by whoever planned it: it is what a person reads about the
|
|
1048
1050
|
// task, and this builds the document without guessing one (the server refuses a task without).
|
|
1051
|
+
// A plan is the case Draft is for: several tasks at once, which a person reads before any of
|
|
1052
|
+
// them runs. Stated here, by the code that knows it is building one.
|
|
1049
1053
|
const meta = {
|
|
1050
1054
|
title: step.title,
|
|
1051
1055
|
summary: step.summary,
|
|
1052
1056
|
kind: "task",
|
|
1057
|
+
status: "draft",
|
|
1053
1058
|
target_context: step.target_context ?? repo,
|
|
1054
1059
|
};
|
|
1055
1060
|
const after = (step.after || []).map((j) => ids[j]);
|