@stevecastaneda/paseo-progress 1.14.3 → 1.14.5
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 +8 -0
- package/dist/server/cli.js +51 -2
- package/package.json +2 -2
- package/server/cli.ts +1 -1
- package/server/tool.ts +50 -32
- package/skills/paseo-progress/SKILL.md +15 -3
package/README.md
CHANGED
|
@@ -178,6 +178,14 @@ The progress file, `progress.jsonl`, holds one JSON event per line, each with a
|
|
|
178
178
|
|
|
179
179
|
## Changelog
|
|
180
180
|
|
|
181
|
+
### 1.14.5
|
|
182
|
+
|
|
183
|
+
- [Fixed] **Agents record your answer on the first try.** In Claude Code, an agent that hadn't loaded the answer tool guessed its field names, and the call failed once before the agent retried. The skill now has agents load every Progress tool at once, and again after Claude Code compacts a long chat. Asking a question also tells the agent which fields the answer takes.
|
|
184
|
+
|
|
185
|
+
### 1.14.4
|
|
186
|
+
|
|
187
|
+
- [Improved] **Agents learn what Paseo's preview blocks.** Paseo opens an HTML deliverable in a sandboxed preview, where some common page code fails without a visible error. Saving to localStorage, sessionStorage, cookies or IndexedDB throws, so a page that loads its saved state first stops before it draws anything. `alert`, `confirm`, `prompt`, `window.open` and form submits do nothing, `navigator.clipboard` throws, and one syntax error stops a whole script. The skill and the command help now list these, with what to do instead.
|
|
188
|
+
|
|
181
189
|
### 1.14.3
|
|
182
190
|
|
|
183
191
|
- [Fixed] **Open hands only pictures, video, audio, PDFs and text to their default app.** This fixes a security issue in opening deliverables. Progress used to open any file an agent recorded with its default app, and for a script or an app that means running it. Now Open works only for images, video, audio, PDFs and text files. Pressing any other file, an SVG file or a folder copies its path, and an HTML page always opens in Paseo.
|
package/dist/server/cli.js
CHANGED
|
@@ -661,6 +661,7 @@ __export(util_exports, {
|
|
|
661
661
|
required: () => required,
|
|
662
662
|
safeExtend: () => safeExtend,
|
|
663
663
|
shallowClone: () => shallowClone,
|
|
664
|
+
sizeCaps: () => sizeCaps,
|
|
664
665
|
slugify: () => slugify,
|
|
665
666
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
666
667
|
toZod: () => toZod,
|
|
@@ -1180,6 +1181,18 @@ function prefixIssues(path, issues) {
|
|
|
1180
1181
|
function unwrapMessage(message) {
|
|
1181
1182
|
return typeof message === "string" ? message : message?.message;
|
|
1182
1183
|
}
|
|
1184
|
+
function sizeCaps(defChecks) {
|
|
1185
|
+
const caps = [];
|
|
1186
|
+
for (const ch of defChecks ?? []) {
|
|
1187
|
+
const d = ch._zod.def;
|
|
1188
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
1189
|
+
break;
|
|
1190
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
1191
|
+
if (max >= 0)
|
|
1192
|
+
caps.push([max, ch]);
|
|
1193
|
+
}
|
|
1194
|
+
return caps;
|
|
1195
|
+
}
|
|
1183
1196
|
function attachSchema(issues, start, inst) {
|
|
1184
1197
|
var _a3;
|
|
1185
1198
|
for (let i = start; i < issues.length; i++) {
|
|
@@ -2600,7 +2613,7 @@ ${content.join("\n")}
|
|
|
2600
2613
|
var version = {
|
|
2601
2614
|
major: 4,
|
|
2602
2615
|
minor: 6,
|
|
2603
|
-
patch:
|
|
2616
|
+
patch: 7
|
|
2604
2617
|
};
|
|
2605
2618
|
|
|
2606
2619
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -3356,6 +3369,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
3356
3369
|
return payload;
|
|
3357
3370
|
};
|
|
3358
3371
|
});
|
|
3372
|
+
function capSize(inst, size) {
|
|
3373
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
3374
|
+
if (!caps.length)
|
|
3375
|
+
return;
|
|
3376
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
3377
|
+
const parse3 = inst._zod.parse;
|
|
3378
|
+
inst._zod.parse = (payload, ctx) => {
|
|
3379
|
+
const n = size(payload.value);
|
|
3380
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
3381
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
3382
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
3383
|
+
const start = payload.issues.length;
|
|
3384
|
+
ch._zod.check(payload);
|
|
3385
|
+
payload.issues[start].continue = false;
|
|
3386
|
+
attachSchema(payload.issues, start, inst);
|
|
3387
|
+
return payload;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
return parse3(payload, ctx);
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
3395
|
+
var mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
|
|
3396
|
+
var setSize = (input2) => input2 instanceof Set ? input2.size : -1;
|
|
3359
3397
|
function handleArrayResult(result, final, index) {
|
|
3360
3398
|
if (result.issues.length) {
|
|
3361
3399
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -3399,6 +3437,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
3399
3437
|
}
|
|
3400
3438
|
return payload;
|
|
3401
3439
|
};
|
|
3440
|
+
capSize(inst, arraySize);
|
|
3402
3441
|
});
|
|
3403
3442
|
function handlePropertyResult(result, final, key, input2, optin, optout) {
|
|
3404
3443
|
const isPresent = key in input2;
|
|
@@ -4355,6 +4394,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
|
|
|
4355
4394
|
return Promise.all(proms).then(() => payload);
|
|
4356
4395
|
return payload;
|
|
4357
4396
|
};
|
|
4397
|
+
capSize(inst, mapSize);
|
|
4358
4398
|
});
|
|
4359
4399
|
function handleMapResult(keyResult, valueResult, final, key, input2, inst, ctx) {
|
|
4360
4400
|
if (keyResult.issues.length) {
|
|
@@ -4421,6 +4461,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
|
|
|
4421
4461
|
return Promise.all(proms).then(() => payload);
|
|
4422
4462
|
return payload;
|
|
4423
4463
|
};
|
|
4464
|
+
capSize(inst, setSize);
|
|
4424
4465
|
});
|
|
4425
4466
|
function handleSetResult(result, final) {
|
|
4426
4467
|
if (result.issues.length) {
|
|
@@ -13951,9 +13992,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
13951
13992
|
doc.write(`}`);
|
|
13952
13993
|
return outputVar;
|
|
13953
13994
|
}
|
|
13995
|
+
function generateSizeGate(doc, schema, size) {
|
|
13996
|
+
for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
|
|
13997
|
+
doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
|
|
13998
|
+
}
|
|
13999
|
+
}
|
|
13954
14000
|
function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
13955
14001
|
const def = schema._zod.def;
|
|
13956
14002
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
14003
|
+
generateSizeGate(doc, schema, `${accessor}.length`);
|
|
13957
14004
|
const outputVar = buildsValue ? newVar(ctx) : null;
|
|
13958
14005
|
const iVar = newVar(ctx);
|
|
13959
14006
|
const elemVar = newVar(ctx);
|
|
@@ -14353,6 +14400,7 @@ function literalPropertyKey(ctx, key) {
|
|
|
14353
14400
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
14354
14401
|
const def = schema._zod.def;
|
|
14355
14402
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
14403
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
14356
14404
|
const outputVar = newVar(ctx);
|
|
14357
14405
|
const kVar = newVar(ctx);
|
|
14358
14406
|
const valVar = newVar(ctx);
|
|
@@ -14369,6 +14417,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
|
|
|
14369
14417
|
function generateSetCheck(doc, ctx, schema, accessor) {
|
|
14370
14418
|
const def = schema._zod.def;
|
|
14371
14419
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
14420
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
14372
14421
|
const outputVar = newVar(ctx);
|
|
14373
14422
|
const valVar = newVar(ctx);
|
|
14374
14423
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -21004,7 +21053,7 @@ ${modeLine(mode)}`
|
|
|
21004
21053
|
},
|
|
21005
21054
|
"deliverable add": {
|
|
21006
21055
|
usage: `deliverable add "<title>" <path or http(s) URL> [--ticket <id>] [--kind ${DELIVERABLE_KINDS.join("|")}]`,
|
|
21007
|
-
summary: `Add something the user can review. Paths are relative to where you run the command. Prints its id (D1, D2, ...). An HTML deliverable
|
|
21056
|
+
summary: `Add something the user can review. Paths are relative to where you run the command. Prints its id (D1, D2, ...). An HTML deliverable opens in Paseo's sandboxed preview, which blocks everything from the web, so put its CSS in the page and embed images as data URIs. In the preview, storage APIs and navigator.clipboard throw an error. The alert, confirm, prompt and window.open calls and form submits do nothing. One syntax error stops a whole script.`,
|
|
21008
21057
|
options: { ticket: { type: "string" }, kind: { type: "string" } },
|
|
21009
21058
|
run({ positionals, values, state, cwd, root }) {
|
|
21010
21059
|
const title = required2(positionals[0], "deliverable title");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.5",
|
|
4
4
|
"description": "A live progress dashboard per worktree for Paseo, drawn from a file agents write with the Progress tools or the paseo-progress command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"coding-agents",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"react": "19.1.0",
|
|
58
58
|
"react-native": "0.81.5",
|
|
59
59
|
"typescript": "^5.9.3",
|
|
60
|
-
"zod": "^4.
|
|
60
|
+
"zod": "^4.6.7"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": "^22.18.0 || >=23.6.0"
|
package/server/cli.ts
CHANGED
|
@@ -397,7 +397,7 @@ const commands: Record<string, Command> = {
|
|
|
397
397
|
},
|
|
398
398
|
"deliverable add": {
|
|
399
399
|
usage: `deliverable add "<title>" <path or http(s) URL> [--ticket <id>] [--kind ${DELIVERABLE_KINDS.join("|")}]`,
|
|
400
|
-
summary: `Add something the user can review. Paths are relative to where you run the command. Prints its id (D1, D2, ...). An HTML deliverable
|
|
400
|
+
summary: `Add something the user can review. Paths are relative to where you run the command. Prints its id (D1, D2, ...). An HTML deliverable opens in Paseo's sandboxed preview, which blocks everything from the web, so put its CSS in the page and embed images as data URIs. In the preview, storage APIs and navigator.clipboard throw an error. The alert, confirm, prompt and window.open calls and form submits do nothing. One syntax error stops a whole script.`,
|
|
401
401
|
options: { ticket: { type: "string" }, kind: { type: "string" } },
|
|
402
402
|
run({ positionals, values, state, cwd, root }) {
|
|
403
403
|
const title = required(positionals[0], "deliverable title");
|
package/server/tool.ts
CHANGED
|
@@ -56,6 +56,21 @@ function commandTool<Input extends z.ZodType>(title: string, command: string, in
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// Claude Code hides a tool's inputs until the agent loads it, so an agent
|
|
60
|
+
// that skipped loading the answer tool guesses its field names.
|
|
61
|
+
export const ANSWER_NOTE =
|
|
62
|
+
"When the user replies, record it with progress_question_answer: id is this question's reference, choice is the option letter or their own short answer, and words is what they said.";
|
|
63
|
+
|
|
64
|
+
function withNote(tool: Tool, note: string): Tool {
|
|
65
|
+
return {
|
|
66
|
+
...tool,
|
|
67
|
+
async execute(input, context) {
|
|
68
|
+
const result = await tool.execute(input, context);
|
|
69
|
+
return result.isError ? result : { ...result, text: `${result.text}\n${note}` };
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
const ticketId = (what: string) => z.string().describe(`${what}, like T03`);
|
|
60
75
|
const estimate = z.number().int().describe("Whole minutes, like 90");
|
|
61
76
|
const ticketSource = z.string().describe("Spec file path or http(s) link where the ticket is written up");
|
|
@@ -102,41 +117,44 @@ const tools: Record<ProgressToolName, Tool> = {
|
|
|
102
117
|
({ id, status, stage, title, estimateMinutes, note, waitsFor, source }) =>
|
|
103
118
|
commandArgs("ticket update", [id], { status, stage, title, estimate: estimateMinutes, note, "waits-for": waitsFor, source }),
|
|
104
119
|
),
|
|
105
|
-
progress_question_ask:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
z.
|
|
113
|
-
letter: z.string().describe("A, B, C, ..."),
|
|
114
|
-
label: z.string(),
|
|
115
|
-
consequence: z.string().describe("What picking it means for the user"),
|
|
116
|
-
}),
|
|
117
|
-
),
|
|
118
|
-
recommend: z.string().describe("The option letter you would pick"),
|
|
119
|
-
big: z.string().optional().describe("Only for a big question: which test it meets, in a few plain words"),
|
|
120
|
-
background: z.string().optional(),
|
|
121
|
-
files: z
|
|
122
|
-
.array(
|
|
120
|
+
progress_question_ask: withNote(
|
|
121
|
+
commandTool(
|
|
122
|
+
"Ask question",
|
|
123
|
+
"question ask",
|
|
124
|
+
z.object({
|
|
125
|
+
title: z.string().describe("A short title"),
|
|
126
|
+
question: z.string(),
|
|
127
|
+
options: z.array(
|
|
123
128
|
z.object({
|
|
124
|
-
|
|
125
|
-
label: z.string()
|
|
129
|
+
letter: z.string().describe("A, B, C, ..."),
|
|
130
|
+
label: z.string(),
|
|
131
|
+
consequence: z.string().describe("What picking it means for the user"),
|
|
126
132
|
}),
|
|
127
|
-
)
|
|
128
|
-
.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
),
|
|
134
|
+
recommend: z.string().describe("The option letter you would pick"),
|
|
135
|
+
big: z.string().optional().describe("Only for a big question: which test it meets, in a few plain words"),
|
|
136
|
+
background: z.string().optional(),
|
|
137
|
+
files: z
|
|
138
|
+
.array(
|
|
139
|
+
z.object({
|
|
140
|
+
target: z.string().describe("A file path or http(s) link, like a screenshot"),
|
|
141
|
+
label: z.string().optional(),
|
|
142
|
+
}),
|
|
143
|
+
)
|
|
144
|
+
.optional(),
|
|
145
|
+
ticket: ticketId("The ticket it's about").optional(),
|
|
139
146
|
}),
|
|
147
|
+
({ title, question, options, recommend, big, background, files, ticket }) =>
|
|
148
|
+
commandArgs("question ask", [title, question], {
|
|
149
|
+
option: options.map(({ letter, label, consequence }) => `${letter}=${label} | ${consequence}`),
|
|
150
|
+
recommend,
|
|
151
|
+
big,
|
|
152
|
+
background,
|
|
153
|
+
file: files?.map(({ target, label }) => (label ? `${target}=${label}` : target)),
|
|
154
|
+
ticket,
|
|
155
|
+
}),
|
|
156
|
+
),
|
|
157
|
+
ANSWER_NOTE,
|
|
140
158
|
),
|
|
141
159
|
progress_question_answer: commandTool(
|
|
142
160
|
"Record answer",
|
|
@@ -7,9 +7,15 @@ description: Keep the Paseo Progress dashboard live with the Progress tools or t
|
|
|
7
7
|
|
|
8
8
|
The user watches a live dashboard in Paseo for this worktree. Keep it live: record each change the moment it happens, so the dashboard is a side effect of the work. Write every line for a product manager, in plain words.
|
|
9
9
|
|
|
10
|
-
Use the Progress tools when you have them
|
|
10
|
+
Use the Progress tools when you have them. Paseo hands them to agents when the user turns on Paseo tools. Claude Code hides their inputs until you load them, and a compacted conversation loses what you loaded. So before your first update, and again after each compaction, load them all in one `ToolSearch` call with `max_results` 11 and this query:
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
```text
|
|
13
|
+
select:mcp__paseo__progress_show,mcp__paseo__progress_start,mcp__paseo__progress_ticket_add,mcp__paseo__progress_ticket_update,mcp__paseo__progress_question_ask,mcp__paseo__progress_question_answer,mcp__paseo__progress_deliverable_add,mcp__paseo__progress_activity_add,mcp__paseo__progress_ticker,mcp__paseo__progress_finish,mcp__paseo__paseo_progress
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Without the tools, run the `paseo-progress` command from inside the worktree. When a tool call says the tool isn't found, use the command for the rest of that reply.
|
|
17
|
+
|
|
18
|
+
The tools and the command take the same values and print the same answers. The steps below use the command's words: `--estimate` is `estimateMinutes` in the tools, `--waits-for` is `waitsFor`, and `question answer Q7 A` is `id` and `choice`. `paseo-progress --help` is the reference for every command and flag.
|
|
13
19
|
|
|
14
20
|
## 1. Set up
|
|
15
21
|
|
|
@@ -25,7 +31,13 @@ Done when `show` lists every ticket with an estimate.
|
|
|
25
31
|
|
|
26
32
|
Move each ticket through `--status working --stage Build`, then the stages `Review`, `Fixes` and `Verify`, then `--status done`. Along the way, record what the user would want to see: `deliverable add` for anything reviewable, `activity add` for milestones, `ticker set` for what you are doing right now. Tag each with `--ticket` unless it covers the whole run.
|
|
27
33
|
|
|
28
|
-
- An HTML deliverable
|
|
34
|
+
- An HTML deliverable opens in Paseo's preview, a sandboxed frame. Write the page for it:
|
|
35
|
+
- Keep it self-contained, with its CSS in the page and images embedded as data URIs. The preview blocks every script, style and image from the web.
|
|
36
|
+
- Keep the page's state in variables. localStorage, sessionStorage, cookies and IndexedDB throw an error.
|
|
37
|
+
- Show messages in the page. `alert`, `confirm`, `prompt` and `window.open` do nothing. `confirm` returns false and `prompt` returns null.
|
|
38
|
+
- Handle form input in a script. A form never submits.
|
|
39
|
+
- To copy text, select a hidden textarea and call `document.execCommand("copy")`. `navigator.clipboard` throws an error.
|
|
40
|
+
- Check that each script parses before you add the page. One syntax error stops that whole script, and the page can open blank.
|
|
29
41
|
- A ticket past its estimate with no update turns stuck. Keep its stage current, or raise its `--estimate`.
|
|
30
42
|
- A ticket another session adds is part of the plan. Work it in order.
|
|
31
43
|
- `--status blocked` and `stuck set` are for things outside the run, like a service that's down or missing access.
|