@stevecastaneda/paseo-progress 1.14.4 → 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 +4 -0
- package/dist/server/cli.js +50 -1
- package/package.json +2 -2
- package/server/tool.ts +50 -32
- package/skills/paseo-progress/SKILL.md +8 -2
package/README.md
CHANGED
|
@@ -178,6 +178,10 @@ 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
|
+
|
|
181
185
|
### 1.14.4
|
|
182
186
|
|
|
183
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.
|
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();`);
|
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/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
|
|