@maka/maka-cli 5.144.0 → 5.146.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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.146.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|
|
@@ -2,6 +2,89 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { Command } from '../../command.js';
|
|
4
4
|
import { UsageError } from '../../error.js';
|
|
5
|
+
/**
|
|
6
|
+
* `maka play:backlog:mcp` -- an MCP server exposing the game-backlog
|
|
7
|
+
* admin actions as tools, so an AI agent can drive backlog resolution
|
|
8
|
+
* directly (list items, read one, attach evidence, advance status,
|
|
9
|
+
* request an AI-drafted bench) with a human approving the consequential
|
|
10
|
+
* calls through the MCP client's own tool-permission UI.
|
|
11
|
+
*
|
|
12
|
+
* SCOPED HERE, NOT AT THE CLI'S TOP LEVEL (user ruling 2026-09-08). A
|
|
13
|
+
* command whose only job is game-backlog administration does not belong
|
|
14
|
+
* as a generic top-level command just because an unrelated, unbuilt stub
|
|
15
|
+
* (`maka ai:mcp`) already claimed that shape in .claude/mcp.json -- that
|
|
16
|
+
* stub is left alone. This lives under the feature it actually serves,
|
|
17
|
+
* registered as a CHILD of `play:backlog` (see registerBacklogMcp below
|
|
18
|
+
* and its call site in sideQuest-backlog.sub.cmd.ts).
|
|
19
|
+
*
|
|
20
|
+
* RUNS AS THE M2M SERVICE ACCOUNT, NEVER A HUMAN'S SESSION. Every tool
|
|
21
|
+
* below is a thin wrapper over the exact same REST routes
|
|
22
|
+
* game-backlog-v1-rest-api.ts already exposes to the web board and the
|
|
23
|
+
* human CLI -- so every role check, evidence gate and audit log line
|
|
24
|
+
* those routes already enforce applies here unchanged. Nothing is
|
|
25
|
+
* bypassed or reimplemented; this command only adds a way to call them.
|
|
26
|
+
*
|
|
27
|
+
* STDOUT IS THE PROTOCOL, NOT A LOG (the one hard constraint worth
|
|
28
|
+
* repeating). StdioServerTransport owns process.stdout for the MCP
|
|
29
|
+
* JSON-RPC stream once connected -- anything else written there
|
|
30
|
+
* (including Log.* from this codebase's own logger, which writes via
|
|
31
|
+
* console.log) corrupts every message after it. Diagnostics in this
|
|
32
|
+
* file go to console.error (stderr) only, and Log is not imported here
|
|
33
|
+
* at all, on purpose.
|
|
34
|
+
*
|
|
35
|
+
* SECRETS COME FROM THE MAKA-CLI.COM PROJECT'S OWN CONFIG.
|
|
36
|
+
*
|
|
37
|
+
* `--site <path>` IS THE REAL ANSWER, NOT `cwd` (corrected 2026-09-08,
|
|
38
|
+
* after `cwd` alone shipped broken). The first cut relied entirely on
|
|
39
|
+
* `this.cfg.getAppConfigPath(env)` against `process.cwd()` -- the same
|
|
40
|
+
* mechanism `maka env:get` uses -- on the theory that an MCP client's
|
|
41
|
+
* server config setting `cwd` to a maka-cli.com checkout would make
|
|
42
|
+
* that resolve correctly. It does not, at least not for a server
|
|
43
|
+
* registered in Claude Code's GLOBAL config (~/.claude.json): the
|
|
44
|
+
* process spawns with the session's own working directory regardless
|
|
45
|
+
* of a `cwd` field there, `mustBeInMakaProject` fails immediately
|
|
46
|
+
* ("No maka project config file found"), and the process exits before
|
|
47
|
+
* the MCP handshake completes -- which surfaces to the client as a
|
|
48
|
+
* bare "Connection closed", not a readable error. `--site` sidesteps
|
|
49
|
+
* the whole question of what a given MCP client's `cwd` support
|
|
50
|
+
* actually is: the checkout path is a literal argument, not inferred
|
|
51
|
+
* from where the process happened to start.
|
|
52
|
+
*
|
|
53
|
+
* `cwd` STILL WORKS AS A CONVENIENCE, for a terminal or a client that
|
|
54
|
+
* does set it correctly (a project-scoped `.claude/mcp.json`, say):
|
|
55
|
+
* omitting `--site` falls back to the original cwd-based resolution.
|
|
56
|
+
* `mustBeInMakaProject` is OFF at the framework level either way --
|
|
57
|
+
* this command does its own check so both paths get the same clear
|
|
58
|
+
* error instead of the framework's for one and a hand-rolled one for
|
|
59
|
+
* the other.
|
|
60
|
+
*
|
|
61
|
+
* AUTHZERO_SECRET never leaves this lookup either way: it is used
|
|
62
|
+
* once, in-process, to mint an Auth0 token, and is never logged or
|
|
63
|
+
* forwarded anywhere.
|
|
64
|
+
*/
|
|
65
|
+
/**
|
|
66
|
+
* WHAT A CONNECTING SESSION IS TOLD, surfaced at MCP connection time.
|
|
67
|
+
* This is the loop as it actually runs, corrected in-session against
|
|
68
|
+
* real friction on the first item worked through it (2026-09-08/09) --
|
|
69
|
+
* not a policy written in advance of ever using the tool.
|
|
70
|
+
*/
|
|
71
|
+
const MCP_SERVER_INSTRUCTIONS = `This server exposes the game-backlog admin actions as tools. Every tool is a thin wrapper over the same REST routes the human web board and CLI already use -- role checks, evidence gates, and audit logging all apply exactly as they do for a human admin. Nothing here bypasses them.
|
|
72
|
+
|
|
73
|
+
THE LOOP IS NEARLY AUTOMATED BY DESIGN -- that is the whole reason this server exists instead of a human doing this work by hand. For each item: read it in full (backlog_get), find the root cause in the relevant repo (maka-cli-src for the CLI/game engine, maka-cli.com for the site/backend -- see sources-1/CLAUDE.md for which repo owns what), fix it, write/run tests, build, lint, typecheck -- then commit, version-bump (maka-cli-src releases go through the full chain: push -> build -> publish -> GitHub release + site pin -> the pin auto-deploys the site; maka-cli.com-only changes need either the next pin or a manual "gh workflow run deploy.yml"), and once the fix is committed AND actually deployed live: backlog_advance(status: "ready-for-bench", commits: [sha]) -> backlog_draft_bench -> backlog_dry_run_bench (read the transcript it returns -- this is automated QA, not the reporter's confirmation, and needs no human) -> if the transcript demonstrates the fix, backlog_advance(status: "in-review", repro: <the same spec that just dry-ran clean>), which promotes the draft to a real bench AND notifies the reporter in one call. That is as far as automation goes: only the reporter can sign off or reject from here, in-game -- see "WHAT THIS SERVER DELIBERATELY DOES NOT EXPOSE" below for why that boundary is real and the bench-verification step is not.
|
|
74
|
+
|
|
75
|
+
DO NOT PAUSE TO ASK PERMISSION for a routine step in that loop -- committing a clean, tested, single-purpose change, bumping a version, pushing, letting the release chain run, or dry-running a drafted bench. That is exactly the friction this tooling was built to remove. Reserve a real stop for a production-only action with no easy rollback, or a failure that genuinely needs human judgment to interpret.
|
|
76
|
+
|
|
77
|
+
DESIGN DECISIONS THAT COME UP WHILE FIXING ARE NOT A STOP EITHER. Make the call, then record the decision and its reasoning as a backlog_comment (kind: "note") on the item itself -- that comment is the durable record a later reviewer or the reporter can read; a live question to the human is the thing this loop exists to avoid. Only escalate a design question directly to the human when the choice changes externally-visible behavior in a way nobody could reconstruct from the code and the note.
|
|
78
|
+
|
|
79
|
+
SR5 CANON FIDELITY IS NOT OPTIONAL for anything in the side-quest game (see maka-cli-src/CLAUDE.md, "ALWAYS FOLLOW THE SHADOWRUN RULES"): a deviation from the rules is a defect, full stop -- check the shadowrun-rag MCP tool (ask_shadowrun_rules) rather than reasoning from memory, including when this item's own vetting text claims to summarize the rules; it has been wrong before. "The excerpt didn't mention X" is not "X doesn't exist."
|
|
80
|
+
|
|
81
|
+
SWEEP THE FAMILY. If a root cause affects more than one open item (the same clamp, the same missing check, the same divergent code path), fix all of them in one pass and add a backlog_comment cross-referencing the shared cause on every affected item -- fixing one twin and leaving the other broken reproduces the same report one layer down.
|
|
82
|
+
|
|
83
|
+
"ready-for-bench" MEANS COMMITTED *AND* DEPLOYED, not just committed -- a bench built against undeployed code would replay the old bug and report the fix as failed. Don't advance to ready-for-bench before confirming the deploy actually succeeded.
|
|
84
|
+
|
|
85
|
+
WHAT THIS SERVER DELIBERATELY DOES NOT EXPOSE, and why: signoff/reject/close are the reporter's own verbs -- only the person who hit the bug can confirm it is actually gone in play, and an AI closing its own fix would be exactly the self-certification this project's design already rejects for human admins. grant/revoke/delete/top-priority are rarer, higher-blast-radius actions left to a human directly. NOTE THE DIFFERENCE FROM backlog_dry_run_bench: proving a bench SCRIPT replays correctly is a technical fact with no subjective side, and it needs no human -- it is not the same gate as the reporter deciding their own bug is gone, and must never be treated as though it were (that confusion cost a full stop the first time this loop ran for real).
|
|
86
|
+
|
|
87
|
+
A "failed" vetting state means the automated vetting pipeline itself errored (no lore-search hits, or the model call failed) -- it is NOT a judgment that the item lacks merit. Treat those items as needing your own diagnosis from scratch, same as any other open item.`;
|
|
5
88
|
export function registerBacklogMcp(parent) {
|
|
6
89
|
return Command.create({
|
|
7
90
|
name: 'mcp',
|
|
@@ -94,6 +177,7 @@ export function registerBacklogMcp(parent) {
|
|
|
94
177
|
const { z } = await import('zod');
|
|
95
178
|
const { ensureServiceSession } = await import('./sideQuest/utilities/service-account-auth.js');
|
|
96
179
|
const { fetchBacklog, fetchBacklogItemById, patchBacklogItem, draftBacklogBench, postBacklogComment, BACKLOG_STATUSES, } = await import('./sideQuest/utilities/backlog.js');
|
|
180
|
+
const { dryRunRepro } = await import('./sideQuest/utilities/repro-launch.js');
|
|
97
181
|
const statusEnum = BACKLOG_STATUSES;
|
|
98
182
|
/** Every tool call goes through this: reuse the cached session, and
|
|
99
183
|
* on the first `expired` outcome, mint a fresh one and retry
|
|
@@ -117,7 +201,16 @@ export function registerBacklogMcp(parent) {
|
|
|
117
201
|
isError: value.outcome !== 'ok',
|
|
118
202
|
};
|
|
119
203
|
}
|
|
120
|
-
|
|
204
|
+
// SERVER INSTRUCTIONS -- surfaced by MCP clients at connection time
|
|
205
|
+
// as priority context, which is the point: this ships the workflow
|
|
206
|
+
// WITH the tool, for whichever Claude session connects, rather than
|
|
207
|
+
// depending on one session's own accumulated memory of how this is
|
|
208
|
+
// supposed to run. Written and revised in-session (2026-09-08/09)
|
|
209
|
+
// after the first real item exposed exactly the friction it warns
|
|
210
|
+
// against -- see MCP_SERVER_INSTRUCTIONS's own history if this ever
|
|
211
|
+
// needs updating again: keep it a record of what actually went
|
|
212
|
+
// wrong, not a wishlist.
|
|
213
|
+
const server = new McpServer({ name: 'maka-play-backlog', version: '1.0.0' }, { instructions: MCP_SERVER_INSTRUCTIONS });
|
|
121
214
|
server.registerTool('backlog_list', {
|
|
122
215
|
title: 'List backlog items',
|
|
123
216
|
description: 'List game-backlog items, optionally filtered by status. Closed (complete) items are excluded unless a status is given explicitly.',
|
|
@@ -144,29 +237,75 @@ export function registerBacklogMcp(parent) {
|
|
|
144
237
|
}, async ({ itemId, text, kind, awaitingReporter }) => toolResult(await withSession(async (token) => ({
|
|
145
238
|
outcome: await postBacklogComment(itemId, text, { kind, awaitingReporter }, token),
|
|
146
239
|
}))));
|
|
240
|
+
// MIRRORS IRepro (types/repro.ts), LOOSELY. `steps` is the one field
|
|
241
|
+
// that actually matters to this tool and to `dryRunRepro` -- the
|
|
242
|
+
// server's own validateRepro is the real authority on the rest, so
|
|
243
|
+
// `fixture`'s rich nested shape (barrier/npcs/items/level/host) is
|
|
244
|
+
// passed through as an opaque object rather than re-declared here.
|
|
245
|
+
// Re-mirroring a shape this deep in a second place is exactly the
|
|
246
|
+
// kind of drift this codebase's own comments repeatedly warn about;
|
|
247
|
+
// letting the server validate it is the one copy that matters.
|
|
248
|
+
const reproSchema = z.object({
|
|
249
|
+
fixture: z.record(z.string(), z.unknown()).optional(),
|
|
250
|
+
steps: z.array(z.string()).min(1),
|
|
251
|
+
premise: z.string().optional(),
|
|
252
|
+
expect: z.string().optional(),
|
|
253
|
+
minVersion: z.string().optional(),
|
|
254
|
+
live: z.string().optional(),
|
|
255
|
+
});
|
|
147
256
|
server.registerTool('backlog_advance', {
|
|
148
257
|
title: 'Advance a backlog item (attach evidence / change status)',
|
|
149
|
-
description: `Move a backlog item's status
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
258
|
+
description: `Move a backlog item's status, attach fix-commit evidence, and/or promote a
|
|
259
|
+
dry-run-verified bench (see backlog_dry_run_bench) from reproDraft to a real repro. This
|
|
260
|
+
is the actual resolution action -- it will be refused by the server (with a message
|
|
261
|
+
explaining why) if you try to enter "in-review" or "complete" without at least one commit
|
|
262
|
+
attached, or "complete" without the reporter's sign-off already on the item. This tool
|
|
263
|
+
never signs off or rejects a fix on the reporter's behalf -- only the person who filed the
|
|
264
|
+
item can do that, in-game.`,
|
|
155
265
|
inputSchema: {
|
|
156
266
|
itemId: z.string(),
|
|
157
267
|
status: z.enum(statusEnum),
|
|
158
268
|
commits: z.array(z.string()).optional()
|
|
159
269
|
.describe('Fix-commit SHAs to attach (merged with whatever is already there).'),
|
|
270
|
+
repro: reproSchema.optional()
|
|
271
|
+
.describe('Promote a bench to real: pass the exact spec that just dry-ran clean via backlog_dry_run_bench. Clears any pending reproDraft.'),
|
|
160
272
|
},
|
|
161
|
-
}, async ({ itemId, status, commits }) => toolResult(await withSession((token) => patchBacklogItem(itemId, { status, commits }, token))));
|
|
273
|
+
}, async ({ itemId, status, commits, repro }) => toolResult(await withSession((token) => patchBacklogItem(itemId, { status, commits, repro }, token))));
|
|
162
274
|
server.registerTool('backlog_draft_bench', {
|
|
163
275
|
title: 'Draft an AI bench for a backlog item',
|
|
164
|
-
description: 'Ask the server to AI-draft a two-room repro bench for one item (lands in reproDraft, not repro --
|
|
276
|
+
description: 'Ask the server to AI-draft a two-room repro bench for one item (lands in reproDraft, not repro -- dry-run it with backlog_dry_run_bench, then promote it with backlog_advance\'s repro param). Costs a model call server-side.',
|
|
165
277
|
inputSchema: {
|
|
166
278
|
itemId: z.string(),
|
|
167
279
|
verbs: z.array(z.string()).optional().describe('The verbs available to reference in the bench steps.'),
|
|
168
280
|
},
|
|
169
281
|
}, async ({ itemId, verbs }) => toolResult(await withSession((token) => draftBacklogBench(itemId, token, verbs))));
|
|
282
|
+
server.registerTool('backlog_dry_run_bench', {
|
|
283
|
+
title: 'Dry-run a backlog item\'s bench, headlessly',
|
|
284
|
+
description: `Replay a backlog item's bench steps against a real boot of the game engine
|
|
285
|
+
-- no terminal, no login, no human -- and return the transcript (including dice/mechanics
|
|
286
|
+
lines). Proves the drafted bench (or an already-attached one) reproduces the reported
|
|
287
|
+
behavior BEFORE it is promoted or shown to the reporter. Prefers the item's pending
|
|
288
|
+
reproDraft; falls back to its attached repro if there is no draft. This is technical
|
|
289
|
+
verification, not the reporter's in-play confirmation -- reading the transcript and
|
|
290
|
+
judging whether it demonstrates the fix is exactly the kind of judgment call this tool
|
|
291
|
+
exists to let you make yourself.`,
|
|
292
|
+
inputSchema: { itemId: z.string() },
|
|
293
|
+
}, async ({ itemId }) => {
|
|
294
|
+
const result = await withSession((token) => fetchBacklogItemById(itemId, token));
|
|
295
|
+
if (result.outcome !== 'ok' || !result.item)
|
|
296
|
+
return toolResult(result);
|
|
297
|
+
const spec = result.item.reproDraft?.spec ?? result.item.repro;
|
|
298
|
+
if (!spec || !Array.isArray(spec.steps) || spec.steps.length === 0) {
|
|
299
|
+
return toolResult({ outcome: 'rejected', message: 'No reproDraft or repro on this item yet -- call backlog_draft_bench first.' });
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const { lines, warnings } = await dryRunRepro(spec);
|
|
303
|
+
return toolResult({ outcome: 'ok', transcript: lines.join('\n'), warnings });
|
|
304
|
+
}
|
|
305
|
+
catch (e) {
|
|
306
|
+
return toolResult({ outcome: 'rejected', message: `Dry run failed to boot: ${e.message}` });
|
|
307
|
+
}
|
|
308
|
+
});
|
|
170
309
|
const transport = new StdioServerTransport();
|
|
171
310
|
await server.connect(transport);
|
|
172
311
|
console.error('[play:backlog:mcp] ready');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.146.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|