mockpit 1.0.0 → 1.2.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 +12 -3
- package/bin/mockpit.js +47 -21
- package/dist/mcp/server.js +43 -4
- package/dist/server/apiViews.d.ts +11 -8
- package/dist/server/apiViews.js +21 -16
- package/dist/server/app.d.ts +3 -1
- package/dist/server/app.js +374 -67
- package/dist/server/appIcon.d.ts +22 -0
- package/dist/server/appIcon.js +43 -0
- package/dist/server/appIconPng.d.ts +1 -0
- package/dist/server/appIconPng.js +6 -0
- package/dist/server/askNudges.d.ts +3 -0
- package/dist/server/askNudges.js +40 -0
- package/dist/server/base64.d.ts +1 -0
- package/dist/server/base64.js +8 -0
- package/dist/server/designGuide.js +15 -13
- package/dist/server/eventLog.d.ts +27 -0
- package/dist/server/eventLog.js +187 -0
- package/dist/server/feedbackBatch.d.ts +16 -4
- package/dist/server/feedbackBatch.js +59 -9
- package/dist/server/handoff.d.ts +6 -0
- package/dist/server/handoff.js +38 -0
- package/dist/server/index.js +12 -0
- package/dist/server/knobs.js +2 -3
- package/dist/server/logFile.d.ts +2 -0
- package/dist/server/logFile.js +27 -0
- package/dist/server/mcpHttp.d.ts +2 -2
- package/dist/server/mcpHttp.js +11 -5
- package/dist/server/mcpSpec.d.ts +8 -0
- package/dist/server/mcpSpec.js +26 -1
- package/dist/server/run.js +2 -2
- package/dist/server/runApi.d.ts +1 -1
- package/dist/server/runApi.js +1 -1
- package/dist/server/sqlStore.d.ts +3 -1
- package/dist/server/sqlStore.js +40 -8
- package/dist/server/types.d.ts +25 -2
- package/dist/server/types.js +47 -4
- package/extensions/mockpit.js +24 -9
- package/guide/topics/asks.md +19 -7
- package/guide/topics/feedback.md +17 -7
- package/guide/topics/knobs.md +3 -3
- package/guide/topics/surfaces.md +8 -8
- package/package.json +2 -2
- package/skills/mockpit/SKILL.md +4 -2
- package/viewer/dist/index.html +9 -9
package/extensions/mockpit.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { basename, resolve } from "node:path";
|
|
4
5
|
|
|
@@ -127,8 +128,19 @@ function resolveProjectName(cwd) {
|
|
|
127
128
|
);
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
// Names this client in the server's event log, with the version that would
|
|
132
|
+
// show it running behind the server.
|
|
133
|
+
const CLIENT = `pi/${(() => {
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
136
|
+
} catch {
|
|
137
|
+
return "unknown";
|
|
138
|
+
}
|
|
139
|
+
})()}`;
|
|
140
|
+
|
|
130
141
|
function authHeaders(extra = {}) {
|
|
131
142
|
return {
|
|
143
|
+
"x-mockpit-client": CLIENT,
|
|
132
144
|
...(process.env.MOCKPIT_TOKEN ? { authorization: `Bearer ${process.env.MOCKPIT_TOKEN}` } : {}),
|
|
133
145
|
...extra,
|
|
134
146
|
};
|
|
@@ -208,6 +220,7 @@ function feedbackLines(feedback) {
|
|
|
208
220
|
const lines = [];
|
|
209
221
|
for (const batch of feedback ?? []) {
|
|
210
222
|
const prefix = batch.mock ? `${batch.mock}: ` : "";
|
|
223
|
+
if (batch.handoff) lines.push(`- ${prefix}${batch.handoff}`);
|
|
211
224
|
const reply = batch.reply;
|
|
212
225
|
if (reply) {
|
|
213
226
|
for (const a of reply.asks ?? []) {
|
|
@@ -234,17 +247,19 @@ function feedbackLines(feedback) {
|
|
|
234
247
|
return lines;
|
|
235
248
|
}
|
|
236
249
|
|
|
237
|
-
// What the user is doing right now, so the agent can say "take your time".
|
|
238
|
-
// viewerOpen is workspace-wide, so it is said once rather than per mock.
|
|
250
|
+
// What the user is doing right now, per mock, so the agent can say "take your time".
|
|
239
251
|
function pendingLines(pending) {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
252
|
+
const lines = [pending ?? []]
|
|
253
|
+
.flat()
|
|
254
|
+
.flatMap((p) =>
|
|
255
|
+
p.draft
|
|
256
|
+
? [
|
|
257
|
+
`- ${p.mock}: the user is answering (${p.draft.answered} of ${p.draft.of} answered, ${p.draft.comments} comments)`,
|
|
258
|
+
]
|
|
259
|
+
: p.viewerOpen
|
|
260
|
+
? [`- ${p.mock}: open in the viewer`]
|
|
261
|
+
: [],
|
|
246
262
|
);
|
|
247
|
-
if (lines.length === 0 && rows.some((p) => p.viewerOpen)) lines.push("- the viewer is open");
|
|
248
263
|
return lines.length ? ["Pending:", ...lines] : [];
|
|
249
264
|
}
|
|
250
265
|
|
package/guide/topics/asks.md
CHANGED
|
@@ -7,9 +7,12 @@ where to look and end your turn.
|
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
9
|
mockpit ask --mock writer "Which look?" --option Quiet=quiet --option Dark=dark --id look
|
|
10
|
+
mockpit ask --mock writer "Trim?" --option Above=knob:trim.position=top --option Below=knob:trim.position=bottom
|
|
10
11
|
mockpit ask --mock writer --asks asks.json
|
|
11
12
|
```
|
|
12
13
|
|
|
14
|
+
`=knob:` sets one knob; for an option that sets several, use `--asks` JSON.
|
|
15
|
+
|
|
13
16
|
## Shape
|
|
14
17
|
|
|
15
18
|
The same JSON goes to `--asks`, MCP `ask` and `POST /api/mocks/:id/asks`:
|
|
@@ -60,10 +63,14 @@ An option binds to one of:
|
|
|
60
63
|
- a knob `set`: picking it applies those values live.
|
|
61
64
|
- nothing: a plain pill.
|
|
62
65
|
|
|
66
|
+
Options that change how something looks carry a `variant` or a knob `set`,
|
|
67
|
+
so the user sees them live. Plain pills only for what can't be seen.
|
|
68
|
+
|
|
63
69
|
The viewer shows a picture of each option it can render. `multi: true` allows
|
|
64
70
|
several answers. Option ids default to the slugged label. Reusing an ask `id`
|
|
65
71
|
replaces that ask. The viewer adds an "Other…" write-in and a note to every
|
|
66
|
-
ask, so don't add your own; the option id `other`
|
|
72
|
+
ask (both take attached images), so don't add your own; the option id `other`
|
|
73
|
+
is reserved.
|
|
67
74
|
|
|
68
75
|
## The Look ask
|
|
69
76
|
|
|
@@ -72,16 +79,21 @@ ask it first. Questions appear in the order you ask them. This one is titled
|
|
|
72
79
|
"Look" and unlocks Mix, which lets the user borrow a part from another look.
|
|
73
80
|
Variants without a Look ask get a plain switcher in the frame header.
|
|
74
81
|
|
|
82
|
+
A variant decides everything it differs on; don't re-ask it. Ask other axes as
|
|
83
|
+
knob `set`s.
|
|
84
|
+
|
|
75
85
|
## When you forget to ask
|
|
76
86
|
|
|
77
87
|
A publish that leaves a state with two or more open variants and no ask
|
|
78
88
|
binding them returns a nudge and a ready `suggestedAsk` (mock-wide when the
|
|
79
|
-
variant names line up across states, else for that state)
|
|
80
|
-
|
|
81
|
-
prints the `mockpit ask` command
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
variant names line up across states, else for that state), or your existing
|
|
90
|
+
ask plus the new variants. Send it as is with `ask`, or write your own question
|
|
91
|
+
with options bound to the variants. The CLI prints the `mockpit ask` command
|
|
92
|
+
for you. Sending it stops the nudge and keeps the answered Look ask on the
|
|
93
|
+
mock; the built-in one is gone once answered.
|
|
94
|
+
|
|
95
|
+
Until an ask binds them, the viewer shows a built-in Look ask with a picture
|
|
96
|
+
of each variant, so the user can still choose in one Send. Its answer
|
|
85
97
|
reads like any other ask under the reserved id `variant` (per state:
|
|
86
98
|
`variant:<state>`), and accepts and archives per state. Your own ask over
|
|
87
99
|
those variants replaces it.
|
package/guide/topics/feedback.md
CHANGED
|
@@ -24,7 +24,8 @@ It returns at once:
|
|
|
24
24
|
"ask": "trim",
|
|
25
25
|
"text": "Trim above or below?",
|
|
26
26
|
"chosen": [{ "id": "below", "label": "Below", "set": { "trim.position": "bottom" } }],
|
|
27
|
-
"note": "below on mobile only"
|
|
27
|
+
"note": "below on mobile only",
|
|
28
|
+
"noteImages": [{ "id": "k3Jf9xQ2aLw", "url": "http://localhost:8228/a/k3Jf9xQ2aLw" }]
|
|
28
29
|
},
|
|
29
30
|
{
|
|
30
31
|
"ask": "lang",
|
|
@@ -35,6 +36,7 @@ It returns at once:
|
|
|
35
36
|
"mix": { "body": "quiet" },
|
|
36
37
|
"tuned": { "body.size": 19 },
|
|
37
38
|
"comments": [{ "part": "title", "state": "Writing", "text": "bigger" }],
|
|
39
|
+
"reactions": ["👍 part \"title\" (variant dark, state Writing)"],
|
|
38
40
|
"text": "close, go dark"
|
|
39
41
|
},
|
|
40
42
|
"comments": [],
|
|
@@ -53,8 +55,8 @@ It returns at once:
|
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
`feedback` holds one batch per mock, in delivery order. `pending` is what the
|
|
56
|
-
user is doing right now, per mock: `viewerOpen` says a browser has
|
|
57
|
-
|
|
58
|
+
user is doing right now, per mock: `viewerOpen` says a browser has that
|
|
59
|
+
mock on screen, `draft` is their unsent progress (`null` when there is none).
|
|
58
60
|
An empty `feedback` with a draft means "still answering": tell the user to take
|
|
59
61
|
their time, don't ask again. `mockpit read` returns `pending` too, without
|
|
60
62
|
taking any feedback.
|
|
@@ -64,18 +66,26 @@ taking any feedback.
|
|
|
64
66
|
1. `answers` and `asks` decide structure. A variant-bound answer has already
|
|
65
67
|
accepted that variant and archived its siblings (`accepted`, `archived`).
|
|
66
68
|
Stop iterating on the archived ones. The ask `variant` (per state
|
|
67
|
-
`variant:<state>`) is the viewer's built-in
|
|
69
|
+
`variant:<state>`) is the viewer's built-in Look ask, read like any
|
|
68
70
|
other. A `chosen` entry with `other: true` is
|
|
69
71
|
the user's own answer (its `label`), not one of your options; it flips no
|
|
70
72
|
variant and sets no knob. `note` qualifies the answer and may come with
|
|
71
|
-
nothing chosen.
|
|
73
|
+
nothing chosen. The user can attach images (screenshots, references) to
|
|
74
|
+
either: `images` on the `other` choice, `noteImages` on the ask, each
|
|
75
|
+
`{id, url}`; the text may then be `""`. Look at every one. Over MCP they
|
|
76
|
+
also arrive as image content in the same result; elsewhere fetch the `url`
|
|
77
|
+
(with your token, if the server has one) and open the file.
|
|
72
78
|
2. `tuned` holds knob values. Write them back into the source as the new
|
|
73
79
|
defaults. tunekit's skill (`skills/tunekit` in the tunekit package) covers
|
|
74
80
|
applying copied values.
|
|
75
81
|
3. `mix` maps a part to the variant it should come from. Compose that part from
|
|
76
|
-
the other look into the chosen one.
|
|
82
|
+
the other look into the chosen one. If `mix` conflicts with an answer, the
|
|
83
|
+
answer wins.
|
|
77
84
|
4. `comments` are anchored on a part in a state (`part: null` means no part).
|
|
78
|
-
Address each one.
|
|
85
|
+
Address each one. `reactions` are one-tap verdicts, one line each:
|
|
86
|
+
👍 keep it, 👎 rework it, "more like this" go further that way. A reaction
|
|
87
|
+
without a part is about the whole variant; one may end with the user's
|
|
88
|
+
note in quotes. Reactions never also appear in `comments`.
|
|
79
89
|
5. `decision` (`{kind: accept|revise|drop, state, variant}`) comes instead of
|
|
80
90
|
answers when the mock has no asks and the user pressed Accept, Revise or
|
|
81
91
|
Drop.
|
package/guide/topics/knobs.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A knob is a value the user tunes live on one render. Use one when one render
|
|
4
4
|
plus a control shows the choice. A choice is several variants plus one ask
|
|
5
|
-
that binds them; for that, publish variants and ask instead (topic `asks`).
|
|
6
|
-
|
|
7
|
-
one
|
|
5
|
+
that binds them; for that, publish variants and ask instead (topic `asks`).
|
|
6
|
+
A 2–3 option knob is a choice: ask with options that `set` it. Publishing
|
|
7
|
+
one returns that nudge.
|
|
8
8
|
|
|
9
9
|
## Declaring
|
|
10
10
|
|
package/guide/topics/surfaces.md
CHANGED
|
@@ -90,11 +90,11 @@ bytes dedupe. An asset lives as long as something references it. The limit is
|
|
|
90
90
|
|
|
91
91
|
Each thing you add lights up its part of the viewer.
|
|
92
92
|
|
|
93
|
-
| you publish | the user gets
|
|
94
|
-
| ---------------------- |
|
|
95
|
-
| a plain mock, any kind | the stage and Thread, with Accept / Revise / Drop
|
|
96
|
-
| several variants | a switcher, plus a built-in
|
|
97
|
-
| asks | Questions: picture options on the stage, then Send
|
|
98
|
-
| `data-part` | Tune's component list and comments anchored on parts
|
|
99
|
-
| `knobs` | Tune's controls, live on the stage
|
|
100
|
-
| several states | the state strip under the stage
|
|
93
|
+
| you publish | the user gets |
|
|
94
|
+
| ---------------------- | ---------------------------------------------------- |
|
|
95
|
+
| a plain mock, any kind | the stage and Thread, with Accept / Revise / Drop |
|
|
96
|
+
| several variants | a switcher, plus a built-in Look ask until you ask |
|
|
97
|
+
| asks | Questions: picture options on the stage, then Send |
|
|
98
|
+
| `data-part` | Tune's component list and comments anchored on parts |
|
|
99
|
+
| `knobs` | Tune's controls, live on the stage |
|
|
100
|
+
| several states | the state strip under the stage |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mockpit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A design-decision loop for terminal coding agents — agents publish mocks with states and variants, ask questions and expose knobs; you answer, tune and comment, then send one reply.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-tools",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"lint:fix": "oxlint . --fix",
|
|
83
83
|
"changeset": "changeset",
|
|
84
84
|
"changeset:status": "changeset status",
|
|
85
|
-
"release:version": "changeset version",
|
|
85
|
+
"release:version": "changeset version && npm install --package-lock-only --ignore-scripts",
|
|
86
86
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.workers.json --noEmit && tsc -p tsconfig.viewer.json --noEmit",
|
|
87
87
|
"clean:dist": "node scripts/clean-dist.ts",
|
|
88
88
|
"build": "npm run clean:dist && tsc -p tsconfig.build.json && npm run build:viewer",
|
package/skills/mockpit/SKILL.md
CHANGED
|
@@ -19,8 +19,10 @@ The loop never waits for the user:
|
|
|
19
19
|
2. `mockpit publish --mock <slug> --state "<user's words>" --variant <v> --html f.html`,
|
|
20
20
|
once per variant; mark parts with `data-part`.
|
|
21
21
|
3. A choice is several variants plus one ask that binds them: `mockpit ask`
|
|
22
|
-
with options bound to variants.
|
|
23
|
-
|
|
22
|
+
with options bound to variants. A variant decides everything it differs on;
|
|
23
|
+
don't re-ask it. Visual options carry a `variant` or knob `set`. One render
|
|
24
|
+
plus a control: `--knobs`. When a publish result nudges, send its
|
|
25
|
+
`suggestedAsk`.
|
|
24
26
|
4. Tell the user in one line where to look, then end your turn. The question
|
|
25
27
|
itself lives in the mock (`ask`), never in chat. The user answers in the
|
|
26
28
|
browser at their own pace. Never poll.
|