passalong 0.2.2 → 0.3.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 +2 -2
- package/bin/passalong +114 -9
- package/package.json +1 -1
- package/skill/SKILL.md +57 -1
- package/src/api.js +41 -0
- package/src/guide.js +262 -5
- package/src/mcp.js +328 -23
- package/src/passalong.js +215 -8
package/src/mcp.js
CHANGED
|
@@ -3,31 +3,93 @@
|
|
|
3
3
|
// team, the same tools see the team's guides and the user's inbox.
|
|
4
4
|
//
|
|
5
5
|
// claude mcp add passalong -- passalong mcp
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
6
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
9
|
import { z } from "zod";
|
|
9
|
-
import
|
|
10
|
+
import * as api from "./api.js";
|
|
11
|
+
import { AREAS, BUG_SECTIONS, parse, SECTIONS, template } from "./guide.js";
|
|
10
12
|
import * as passalong from "./passalong.js";
|
|
11
13
|
|
|
14
|
+
/**
|
|
15
|
+
* What the server calls itself in the MCP handshake — read from package.json rather than written
|
|
16
|
+
* here, because a literal drifts. This one said 0.2.0 for two releases while the package was
|
|
17
|
+
* 0.2.2, and the one thing a version string in a handshake is for is telling a client which build
|
|
18
|
+
* it is talking to. `pnpm release` bumps package.json, so there is only one number to bump.
|
|
19
|
+
*/
|
|
20
|
+
const VERSION = JSON.parse(
|
|
21
|
+
readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
|
22
|
+
).version;
|
|
23
|
+
|
|
12
24
|
const text = (s) => ({ content: [{ type: "text", text: s }] });
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The rest of the report this issue came from.
|
|
28
|
+
*
|
|
29
|
+
* A QA pass files six bugs at once, and they arrive in an inbox as six unrelated guides — which
|
|
30
|
+
* is how the same root cause gets fixed three times, or how someone fixes one of a pair and calls
|
|
31
|
+
* the sweep done. Naming the siblings costs one request and only happens for a guide that has a
|
|
32
|
+
* parent. A failure here is not worth failing the pull over: the issue itself is what was asked
|
|
33
|
+
* for, and it is already in hand.
|
|
34
|
+
*/
|
|
35
|
+
async function related(meta) {
|
|
36
|
+
if (!meta.report) return "";
|
|
37
|
+
try {
|
|
38
|
+
const { report } = await api.report(meta.report);
|
|
39
|
+
const others = (report.areas || [])
|
|
40
|
+
.flatMap((a) => a.issues.map((i) => ({ ...i, area: a.area })))
|
|
41
|
+
.filter((i) => i.id !== meta.id);
|
|
42
|
+
if (!others.length) return "";
|
|
43
|
+
const lines = others.map(
|
|
44
|
+
(i) => ` ${i.id} [${i.severity || "--"}] ${i.area || "no area"} — ${i.title}`,
|
|
45
|
+
);
|
|
46
|
+
return (
|
|
47
|
+
`\n\n<!-- passalong: filed with ${others.length} other issue${others.length === 1 ? "" : "s"}` +
|
|
48
|
+
`${report.title ? ` in "${report.title}"` : ""}. Fix only this one unless asked; the others are\n` +
|
|
49
|
+
`${lines.join("\n")}\n-->`
|
|
50
|
+
);
|
|
51
|
+
} catch {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
13
55
|
const json = (data) => text(JSON.stringify(data, null, 2));
|
|
14
56
|
const fail = (err) => ({ content: [{ type: "text", text: err.message }], isError: true });
|
|
15
57
|
|
|
16
58
|
export async function serve() {
|
|
17
59
|
const server = new McpServer(
|
|
18
|
-
{ name: "passalong", version:
|
|
60
|
+
{ name: "passalong", version: VERSION },
|
|
19
61
|
{
|
|
20
62
|
instructions:
|
|
21
|
-
"Passalong hands
|
|
22
|
-
`
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"
|
|
63
|
+
"Passalong hands work between contexts as guides: markdown with frontmatter, in two " +
|
|
64
|
+
"kinds, and `kind:` in the frontmatter says which. READ IT BEFORE ACTING — the two ask " +
|
|
65
|
+
"for opposite behaviour.\n" +
|
|
66
|
+
`kind: transfer (or absent) is a finished implementation to repeat here. Sections: ${SECTIONS.join(", ")}. ` +
|
|
67
|
+
"Follow its Steps, adapting anything marked ASSUMES to this codebase; run its " +
|
|
68
|
+
"Verification before declaring done, then verify_guide with the result.\n" +
|
|
69
|
+
"OPEN A GUIDE YOU MEAN TO ACT ON WITH start_guide, NOT get_guide. Both return the same " +
|
|
70
|
+
"markdown; start_guide also says you are on it, and that is the only signal the sender " +
|
|
71
|
+
"gets between handing work over and hearing it worked. get_guide is for reading one you " +
|
|
72
|
+
"have not committed to. Taking your own guide is a no-op, so there is no case where " +
|
|
73
|
+
"start_guide is the wrong call on work you are about to do. If it turns out not to be " +
|
|
74
|
+
"yours, hand it back with ack_guide taken=false and a reason — say why, or the sender is " +
|
|
75
|
+
"left exactly where silence left them.\n" +
|
|
76
|
+
`kind: bug is a defect to FIX here. Sections: ${BUG_SECTIONS.join(", ")}. ` +
|
|
77
|
+
"It has no Steps and nothing in it is a procedure to apply: Reproduce is how to see the " +
|
|
78
|
+
"bug and running it produces the bug, Verification is the behaviour that should have " +
|
|
79
|
+
"happened. Fix the defect, then check Verification and verify_guide with the result — " +
|
|
80
|
+
"ok true once the behaviour it describes actually holds. A bug report is not broken " +
|
|
81
|
+
"because you reproduced it. " +
|
|
82
|
+
"When you find defects you are not fixing — a test run, a QA pass, a review — call " +
|
|
83
|
+
"file_bugs with all of them at once; each becomes a guide someone can take on its own. " +
|
|
26
84
|
"When the user asks to pass along, hand off, or " +
|
|
27
85
|
"share what was just done, distill the session into a guide (guide_template shows the " +
|
|
28
|
-
"shape) and call publish_guide, with `to` as team
|
|
29
|
-
"
|
|
30
|
-
"
|
|
86
|
+
"shape) and call publish_guide, with `to` as team, team/@handle for one teammate, or " +
|
|
87
|
+
"team/#group for the people who do a thing. " +
|
|
88
|
+
"At the start of work, inbox shows guides teammates have handed to this user — open one " +
|
|
89
|
+
"with start_guide — and activity " +
|
|
90
|
+
"shows whether the guides they handed off have landed. When the user asks what they have " +
|
|
91
|
+
"been working on, or wants a standup or a summary of a period, call log — but say that it " +
|
|
92
|
+
"holds what they passed along and not everything they did. Gotchas are " +
|
|
31
93
|
"the highest-value section: record what failed and why.",
|
|
32
94
|
},
|
|
33
95
|
);
|
|
@@ -79,9 +141,8 @@ export async function serve() {
|
|
|
79
141
|
description:
|
|
80
142
|
"The state of this user's transfers as queues: waiting on you (handed to you, not " +
|
|
81
143
|
"pulled), not working (someone gave it a failing verdict — the most urgent), in flight " +
|
|
82
|
-
"(handed over, nobody has taken it — `stale` means it has sat over a week), landed " +
|
|
83
|
-
"(someone
|
|
84
|
-
"promote into a reference). Use it to answer 'what is outstanding?'.",
|
|
144
|
+
"(handed over, nobody has taken it — `stale` means it has sat over a week), and landed " +
|
|
145
|
+
"(someone else has it). Use it to answer 'what is outstanding?'.",
|
|
85
146
|
inputSchema: {},
|
|
86
147
|
},
|
|
87
148
|
async () => {
|
|
@@ -93,6 +154,63 @@ export async function serve() {
|
|
|
93
154
|
},
|
|
94
155
|
);
|
|
95
156
|
|
|
157
|
+
server.registerTool(
|
|
158
|
+
"log",
|
|
159
|
+
{
|
|
160
|
+
title: "What this user did",
|
|
161
|
+
description:
|
|
162
|
+
"This user's own acts on guides, newest first: what they published, what they took " +
|
|
163
|
+
"delivery of, and every verdict and ack they gave. Each item has a rendered `text` line " +
|
|
164
|
+
"and the guide's repo. Use it to answer 'what have I been working on', to write a " +
|
|
165
|
+
"standup or a weekly summary, or to find work from a repo by when it happened rather " +
|
|
166
|
+
"than by what it was called. This is the opposite of `activity`, which is what other " +
|
|
167
|
+
"people did. IMPORTANT: it records what was passed along, not what was worked on — work " +
|
|
168
|
+
"that never became a guide has no entry, so never present it as a complete record of " +
|
|
169
|
+
"this user's work, and never infer that a quiet period was an idle one.",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
repo: z.string().default("").describe("narrow to guides whose source repo matches this"),
|
|
172
|
+
since: z
|
|
173
|
+
.string()
|
|
174
|
+
.default("")
|
|
175
|
+
.describe("only what happened on or after this date: 2026, 2026-09, or 2026-09-11"),
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
async ({ repo, since }) => {
|
|
179
|
+
try {
|
|
180
|
+
return json({ log: await passalong.log({ repo: repo || "", since: since || "" }) });
|
|
181
|
+
} catch (err) {
|
|
182
|
+
return fail(err);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
server.registerTool(
|
|
188
|
+
"ack_guide",
|
|
189
|
+
{
|
|
190
|
+
title: "Say whether you are taking it",
|
|
191
|
+
description:
|
|
192
|
+
"The first word back on a guide handed to you, before any work: take it, or pass it " +
|
|
193
|
+
"back. Passing must say why — an unanswered handoff is indistinguishable from one nobody " +
|
|
194
|
+
"has noticed, and the sender finds out in a week instead of a minute. Answer this when " +
|
|
195
|
+
"you pick up an inbox; verify_guide comes later, once you have actually run it.",
|
|
196
|
+
inputSchema: {
|
|
197
|
+
id: z.string().describe("passalong id"),
|
|
198
|
+
taken: z.boolean().describe("true if you are doing it; false hands it back"),
|
|
199
|
+
note: z
|
|
200
|
+
.string()
|
|
201
|
+
.default("")
|
|
202
|
+
.describe("why it is not yours (required when taken is false); one line, max 280 chars"),
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
async ({ id, taken, note }) => {
|
|
206
|
+
try {
|
|
207
|
+
return json(await passalong.ack(id, taken, note || ""));
|
|
208
|
+
} catch (err) {
|
|
209
|
+
return fail(err);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
);
|
|
213
|
+
|
|
96
214
|
server.registerTool(
|
|
97
215
|
"verify_guide",
|
|
98
216
|
{
|
|
@@ -143,14 +261,66 @@ export async function serve() {
|
|
|
143
261
|
},
|
|
144
262
|
);
|
|
145
263
|
|
|
264
|
+
server.registerTool(
|
|
265
|
+
"start_guide",
|
|
266
|
+
{
|
|
267
|
+
title: "Start work on a guide",
|
|
268
|
+
description:
|
|
269
|
+
"Take a guide handed to you AND fetch it, in one call — use this the moment you are going " +
|
|
270
|
+
"to do the work. Same markdown as get_guide, plus the handoff is answered: until somebody " +
|
|
271
|
+
"says they are on it, a guide nobody has noticed and a guide somebody is deep in look " +
|
|
272
|
+
"identical to the sender, who finds out in a week instead of a minute. Taking your own " +
|
|
273
|
+
"guide is a no-op, not an error, so this is always safe to call. If the work turns out " +
|
|
274
|
+
"not to be yours after reading, hand it back with ack_guide taken=false and a reason.",
|
|
275
|
+
inputSchema: {
|
|
276
|
+
ref: z.string().describe("passalong id (e.g. k3mq2xa7) or share URL"),
|
|
277
|
+
cwd: z
|
|
278
|
+
.string()
|
|
279
|
+
.optional()
|
|
280
|
+
.describe("directory to write .passalong/<id>.md into; default is the server's cwd"),
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
async ({ ref, cwd }) => {
|
|
284
|
+
try {
|
|
285
|
+
const r = await passalong.start(ref, { cwd: cwd || process.cwd() });
|
|
286
|
+
const meta = parse(r.markdown).meta;
|
|
287
|
+
const lead =
|
|
288
|
+
meta.kind === "bug"
|
|
289
|
+
? "THIS IS A BUG REPORT, NOT WORK TO REPEAT. Do not follow Reproduce as instructions " +
|
|
290
|
+
"— those steps produce the defect. Fix what Problem describes, then check " +
|
|
291
|
+
"Verification and answer with verify_guide.\n\n"
|
|
292
|
+
: "";
|
|
293
|
+
// What actually happened to the ack, said plainly. An agent that reports "took it" when
|
|
294
|
+
// nothing was sent is the failure this tool exists to prevent, one step further along.
|
|
295
|
+
const took = r.took
|
|
296
|
+
? "handoff taken; the sender has been told"
|
|
297
|
+
: r.own
|
|
298
|
+
? "your own guide — nothing to take, and nobody to tell"
|
|
299
|
+
: r.ack_error
|
|
300
|
+
? `NOT taken (${r.ack_error}) — retry with ack_guide before reporting that you have it`
|
|
301
|
+
: "not logged in, so no handoff was taken";
|
|
302
|
+
const siblings = await related(meta);
|
|
303
|
+
return text(
|
|
304
|
+
`${lead}${r.markdown}${siblings}` +
|
|
305
|
+
`\n\n<!-- passalong: ${r.from}; written to ${r.path}; ${took} -->`,
|
|
306
|
+
);
|
|
307
|
+
} catch (err) {
|
|
308
|
+
return fail(err);
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
);
|
|
312
|
+
|
|
146
313
|
server.registerTool(
|
|
147
314
|
"get_guide",
|
|
148
315
|
{
|
|
149
316
|
title: "Get guide",
|
|
150
317
|
description:
|
|
151
|
-
"
|
|
318
|
+
"READ a transfer guide by passalong id or share link and return its full markdown. Also " +
|
|
152
319
|
"writes it to .passalong/<id>.md in the working directory so it survives the session. " +
|
|
153
|
-
"Pulling a teammate's guide tells them the transfer landed."
|
|
320
|
+
"Pulling a teammate's guide tells them the transfer landed. Use this to look at a guide " +
|
|
321
|
+
"you have not committed to. If you are about to DO the work, call start_guide instead: " +
|
|
322
|
+
"it does this and takes the handoff in one call, which is the only way the sender learns " +
|
|
323
|
+
"somebody picked it up.",
|
|
154
324
|
inputSchema: {
|
|
155
325
|
ref: z.string().describe("passalong id (e.g. k3mq2xa7) or share URL"),
|
|
156
326
|
cwd: z
|
|
@@ -162,7 +332,55 @@ export async function serve() {
|
|
|
162
332
|
async ({ ref, cwd }) => {
|
|
163
333
|
try {
|
|
164
334
|
const { markdown, path, from } = await passalong.pull(ref, { cwd: cwd || process.cwd() });
|
|
165
|
-
|
|
335
|
+
// The heading text is what an agent keys on, and a bug's headings look enough like a
|
|
336
|
+
// transfer guide's to be followed by one that never opened the frontmatter. So the kind
|
|
337
|
+
// is stated in front of the document, in the imperative, every time.
|
|
338
|
+
const meta = parse(markdown).meta;
|
|
339
|
+
const lead =
|
|
340
|
+
meta.kind === "bug"
|
|
341
|
+
? "THIS IS A BUG REPORT, NOT WORK TO REPEAT. Do not follow Reproduce as instructions " +
|
|
342
|
+
"— those steps produce the defect. Fix what Problem describes, then check " +
|
|
343
|
+
"Verification and answer with verify_guide.\n\n"
|
|
344
|
+
: "";
|
|
345
|
+
const siblings = await related(meta);
|
|
346
|
+
// After the document, with the other trailing comments, not in front of it. An
|
|
347
|
+
// instruction that arrives with the payload is what gets read — but anything before the
|
|
348
|
+
// opening `---` stops the frontmatter being frontmatter, and this fires on every guide
|
|
349
|
+
// anyone was handed rather than only on bugs. The bug lead stays where it is: it is a
|
|
350
|
+
// warning against executing the document, so being read first is its whole job.
|
|
351
|
+
return text(
|
|
352
|
+
`${lead}${markdown}${siblings}` +
|
|
353
|
+
`\n\n<!-- passalong: ${from}; written to ${path} -->` +
|
|
354
|
+
`\n${passalong.handoffNudge(meta)}`,
|
|
355
|
+
);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
return fail(err);
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
server.registerTool(
|
|
363
|
+
"attach_screenshot",
|
|
364
|
+
{
|
|
365
|
+
title: "Attach a screenshot",
|
|
366
|
+
description:
|
|
367
|
+
"Upload an image from this machine as evidence, and get back the markdown line that " +
|
|
368
|
+
"points at it. Put that line in the guide body — a guide travels as markdown to whoever " +
|
|
369
|
+
"holds its link, so evidence beside the document does not travel at all. Publishing " +
|
|
370
|
+
"claims whatever the markdown names, so attach first and publish after. png, jpg, webp " +
|
|
371
|
+
"or gif.",
|
|
372
|
+
inputSchema: {
|
|
373
|
+
file: z.string().describe("path to the image on this machine"),
|
|
374
|
+
name: z.string().default("").describe("label for the image; defaults to its filename"),
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
async ({ file, name }) => {
|
|
378
|
+
try {
|
|
379
|
+
const shot = await passalong.attach(file, { name: name || "" });
|
|
380
|
+
return text(
|
|
381
|
+
`${JSON.stringify({ id: shot.id, url: shot.url, bytes: shot.bytes }, null, 2)}\n\n` +
|
|
382
|
+
`Put this in the guide body:\n${shot.markdown}`,
|
|
383
|
+
);
|
|
166
384
|
} catch (err) {
|
|
167
385
|
return fail(err);
|
|
168
386
|
}
|
|
@@ -174,12 +392,16 @@ export async function serve() {
|
|
|
174
392
|
{
|
|
175
393
|
title: "Publish guide",
|
|
176
394
|
description:
|
|
177
|
-
"Publish a
|
|
395
|
+
"Publish a guide from markdown (frontmatter + sections) — a transfer guide by default, or " +
|
|
396
|
+
"a single bug with `kind: bug`; use file_bugs for more than one. Missing id, created, " +
|
|
178
397
|
'author, and source_context are filled in. `to` addresses it to a team ("khaime") or a ' +
|
|
179
398
|
'teammate ("khaime/lukman"), who is notified. Returns the id and share link.',
|
|
180
399
|
inputSchema: {
|
|
181
400
|
markdown: z.string().describe("full guide markdown; start from guide_template"),
|
|
182
|
-
to: z
|
|
401
|
+
to: z
|
|
402
|
+
.string()
|
|
403
|
+
.optional()
|
|
404
|
+
.describe("team slug, team/@handle for one teammate, or team/#group for a set of them"),
|
|
183
405
|
cwd: z
|
|
184
406
|
.string()
|
|
185
407
|
.optional()
|
|
@@ -208,14 +430,96 @@ export async function serve() {
|
|
|
208
430
|
},
|
|
209
431
|
);
|
|
210
432
|
|
|
433
|
+
server.registerTool(
|
|
434
|
+
"file_bugs",
|
|
435
|
+
{
|
|
436
|
+
title: "File bugs",
|
|
437
|
+
description:
|
|
438
|
+
"File one or more bugs you found but are not fixing, as a set. Opens a report and " +
|
|
439
|
+
"publishes each issue as its own guide — its own id, share link, and verdict — so a " +
|
|
440
|
+
"reviewer can hand any one of them to whoever fixes it. Use this after a test run, a QA " +
|
|
441
|
+
"pass, or a review that turned up defects; use publish_guide instead for work you " +
|
|
442
|
+
"finished and want repeated elsewhere. Needs sync (`passalong login`).",
|
|
443
|
+
inputSchema: {
|
|
444
|
+
title: z
|
|
445
|
+
.string()
|
|
446
|
+
.optional()
|
|
447
|
+
.describe('what the sweep was, e.g. "Checkout regression pass, 8 Sep build"'),
|
|
448
|
+
environment: z
|
|
449
|
+
.string()
|
|
450
|
+
.optional()
|
|
451
|
+
.describe("production, staging or development — where you saw these"),
|
|
452
|
+
to: z
|
|
453
|
+
.string()
|
|
454
|
+
.optional()
|
|
455
|
+
.describe("team slug, team/@handle for one teammate, or team/#group for a set of them"),
|
|
456
|
+
issues: z
|
|
457
|
+
.array(
|
|
458
|
+
z.object({
|
|
459
|
+
title: z.string().describe("what is broken, in one line"),
|
|
460
|
+
problem: z
|
|
461
|
+
.string()
|
|
462
|
+
.describe(
|
|
463
|
+
"what is broken and what it stops someone doing; the error if there is one",
|
|
464
|
+
),
|
|
465
|
+
reproduce: z
|
|
466
|
+
.string()
|
|
467
|
+
.describe(
|
|
468
|
+
"numbered steps that show the bug — these produce it, they are not a fix",
|
|
469
|
+
),
|
|
470
|
+
verification: z
|
|
471
|
+
.string()
|
|
472
|
+
.optional()
|
|
473
|
+
.describe(
|
|
474
|
+
"the behaviour that should have happened, as something a fixer can check",
|
|
475
|
+
),
|
|
476
|
+
gotchas: z
|
|
477
|
+
.string()
|
|
478
|
+
.optional()
|
|
479
|
+
.describe("anything already ruled out, or that made it hard to pin down"),
|
|
480
|
+
area: z
|
|
481
|
+
.string()
|
|
482
|
+
.optional()
|
|
483
|
+
.describe(`which surface it is on — known: ${AREAS.map((a) => a.slug).join(", ")}`),
|
|
484
|
+
severity: z
|
|
485
|
+
.string()
|
|
486
|
+
.optional()
|
|
487
|
+
.describe("s1 blocker, s2 major, s3 minor, s4 cosmetic (default s3)"),
|
|
488
|
+
}),
|
|
489
|
+
)
|
|
490
|
+
.min(1)
|
|
491
|
+
.describe("one entry per defect; file them together rather than one call each"),
|
|
492
|
+
cwd: z.string().optional().describe("directory the bugs were found in"),
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
async ({ title, environment, to, issues, cwd }) => {
|
|
496
|
+
try {
|
|
497
|
+
const filed = await passalong.fileBugs(
|
|
498
|
+
{ title: title || "", environment: environment || "", issues },
|
|
499
|
+
{ cwd: cwd || process.cwd(), to },
|
|
500
|
+
);
|
|
501
|
+
return json(filed);
|
|
502
|
+
} catch (err) {
|
|
503
|
+
return fail(err);
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
);
|
|
507
|
+
|
|
211
508
|
server.registerTool(
|
|
212
509
|
"guide_template",
|
|
213
510
|
{
|
|
214
511
|
title: "Guide template",
|
|
215
|
-
description:
|
|
216
|
-
|
|
512
|
+
description:
|
|
513
|
+
"The empty guide skeleton with guidance comments for each section. `kind: bug` gives the " +
|
|
514
|
+
"bug report skeleton instead, which has a Reproduce section and no Steps.",
|
|
515
|
+
inputSchema: {
|
|
516
|
+
kind: z
|
|
517
|
+
.enum(["transfer", "bug"])
|
|
518
|
+
.optional()
|
|
519
|
+
.describe("transfer (default) for finished work to repeat; bug for a defect to fix"),
|
|
520
|
+
},
|
|
217
521
|
},
|
|
218
|
-
async () => text(template()),
|
|
522
|
+
async ({ kind }) => text(template(kind === "bug" ? { kind: "bug" } : {})),
|
|
219
523
|
);
|
|
220
524
|
|
|
221
525
|
server.registerTool(
|
|
@@ -223,8 +527,9 @@ export async function serve() {
|
|
|
223
527
|
{
|
|
224
528
|
title: "Set guide status",
|
|
225
529
|
description:
|
|
226
|
-
"
|
|
227
|
-
"
|
|
530
|
+
"Deprecated — prefer verify_guide. Marks a guide consumed or promoted, an author lifecycle " +
|
|
531
|
+
"nothing reads any more: the board reports where a transfer is and the verdict reports " +
|
|
532
|
+
"whether it worked. Kept so existing callers do not break. Only the author can promote.",
|
|
228
533
|
inputSchema: { id: z.string(), status: z.enum(["published", "consumed", "promoted"]) },
|
|
229
534
|
},
|
|
230
535
|
async ({ id, status }) => {
|