create-claude-cabinet 0.29.0 → 0.29.2
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/package.json +1 -1
- package/templates/site-audit-runtime/package.json +1 -1
- package/templates/skills/verify/SKILL.md +30 -13
- package/templates/skills/verify/install.sh +1 -1
- package/templates/verify-runtime/package.json +1 -1
- package/templates/verify-runtime/src/human-verdict.ts +2 -0
- package/templates/verify-runtime/src/index.ts +5 -0
- package/templates/verify-runtime/src/launch-options.ts +8 -5
- package/templates/verify-runtime/src/progress.ts +17 -0
- package/templates/verify-runtime/src/world.ts +14 -0
- package/templates/verify-runtime/test/demo-mode.test.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-cabinet/site-audit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Comprehensive deployed-site quality audit engine for Claude Cabinet. Runs checks across performance, accessibility, security, SEO, content, DNS, and privacy against a deployed URL; single-site and comparison modes; standalone HTML report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -286,24 +286,41 @@ the skill handles all `e2e/` directory navigation internally.** No
|
|
|
286
286
|
on macOS — Playwright inherits display access) and human verdicts
|
|
287
287
|
(file-based IPC when stdin is not a TTY).
|
|
288
288
|
|
|
289
|
+
**Progress streaming:** Immediately after starting the background
|
|
290
|
+
command, arm a Monitor on the progress file:
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
Monitor({
|
|
294
|
+
description: "verify progress",
|
|
295
|
+
command: "tail -f e2e/.verify-progress.jsonl | grep -E --line-buffered 'check-fail|scenario-start|scenario-end|run-end|verdict-pending'",
|
|
296
|
+
persistent: true
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
This streams scenario starts, failures, and verdict requests as
|
|
301
|
+
real-time conversation notifications. Check passes are filtered
|
|
302
|
+
out to avoid noise. When a `verdict-pending` event arrives,
|
|
303
|
+
handle it immediately (see below).
|
|
304
|
+
|
|
289
305
|
**Human verdict orchestration:** When the runtime hits a human
|
|
290
306
|
verdict step in non-TTY mode, it writes
|
|
291
|
-
`e2e/.verdict-pending.json` and
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
1.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
4.
|
|
300
|
-
5. Ask the user for their verdict (P/I/S/N + optional notes)
|
|
301
|
-
6. Write `e2e/.verdict-response.json`:
|
|
307
|
+
`e2e/.verdict-pending.json` and emits a `verdict-pending` event
|
|
308
|
+
to the progress file. The Monitor delivers the event as a
|
|
309
|
+
notification. On receiving a `verdict-pending` notification:
|
|
310
|
+
|
|
311
|
+
1. Read `e2e/.verdict-pending.json` — contains `checkId`,
|
|
312
|
+
`description`, `screenshotPath` (absolute path to screenshot)
|
|
313
|
+
2. Read the screenshot image and show it to the user
|
|
314
|
+
3. Ask the user for their verdict (P/I/S/N + optional notes)
|
|
315
|
+
4. Write `e2e/.verdict-response.json`:
|
|
302
316
|
```json
|
|
303
317
|
{ "verdict": "P", "notes": "looks great" }
|
|
304
318
|
```
|
|
305
|
-
|
|
306
|
-
|
|
319
|
+
5. The runtime picks up the response, records it, continues
|
|
320
|
+
|
|
321
|
+
When a `check-fail` notification arrives, surface it immediately:
|
|
322
|
+
show the step text and error message so the user knows what failed
|
|
323
|
+
without waiting for the end summary.
|
|
307
324
|
|
|
308
325
|
The runtime times out after 10 minutes per verdict (auto-skips
|
|
309
326
|
with `human:S` if no response). The pending/response files are
|
|
@@ -452,7 +452,7 @@ fi
|
|
|
452
452
|
|
|
453
453
|
# .gitignore updates at project root.
|
|
454
454
|
GITIGNORE_ROOT=".gitignore"
|
|
455
|
-
GITIGNORE_ENTRIES=("e2e/reports/" "e2e/screenshots/" "e2e/traces/" "e2e/fixtures/articles/" "e2e/.env.local" "e2e/node_modules/" "e2e/.last-verify-run" "e2e/.verdict-pending.json" "e2e/.verdict-response.json")
|
|
455
|
+
GITIGNORE_ENTRIES=("e2e/reports/" "e2e/screenshots/" "e2e/traces/" "e2e/fixtures/articles/" "e2e/.env.local" "e2e/node_modules/" "e2e/.last-verify-run" "e2e/.verdict-pending.json" "e2e/.verdict-response.json" "e2e/.verify-progress.jsonl")
|
|
456
456
|
|
|
457
457
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
458
458
|
for entry in "${GITIGNORE_ENTRIES[@]}"; do
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabinet-verify",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Walkthrough verification harness for Claude Cabinet. Cucumber + Playwright scenarios with human-in-the-loop verdict pause.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -4,6 +4,7 @@ import * as fs from 'fs/promises';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import * as readline from 'readline';
|
|
6
6
|
import { recordVerdict, getCurrentScenarioFile } from './verdict-recorder.js';
|
|
7
|
+
import { emitProgress } from './progress.js';
|
|
7
8
|
import { getFreshPass } from './fresh-pass-cache.js';
|
|
8
9
|
import { computePathHash } from './path-hash.js';
|
|
9
10
|
import { out } from './output.js';
|
|
@@ -155,6 +156,7 @@ export async function askHumanVerdict(
|
|
|
155
156
|
acItemId: options?.acItemId ?? null,
|
|
156
157
|
}), 'utf8');
|
|
157
158
|
|
|
159
|
+
emitProgress({ event: 'verdict-pending', checkId, description, screenshotPath: path.resolve(screenshotPath) });
|
|
158
160
|
process.stderr.write(`\n ${out.c.yellow('⏳')} Waiting for verdict on ${out.c.bold(checkId)} via file IPC...\n`);
|
|
159
161
|
|
|
160
162
|
// Poll for response (500ms intervals, 10 min timeout)
|
|
@@ -20,9 +20,12 @@ export function resolveLaunchOptions(env: Record<string, string | undefined>): L
|
|
|
20
20
|
slowMo = 1000;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const windowSize = env.CABINET_VERIFY_WINDOW_SIZE || (demo ? '1100,750' : '1500,1000');
|
|
24
|
+
const args = [`--window-size=${windowSize}`];
|
|
25
|
+
if (demo) {
|
|
26
|
+
const windowPos = env.CABINET_VERIFY_WINDOW_POSITION || '0,0';
|
|
27
|
+
args.push(`--window-position=${windowPos}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { headless, slowMo, args };
|
|
28
31
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const PROGRESS_FILE = '.verify-progress.jsonl';
|
|
5
|
+
|
|
6
|
+
let progressPath = '';
|
|
7
|
+
|
|
8
|
+
export function initProgress(cwd: string): void {
|
|
9
|
+
progressPath = path.resolve(cwd, PROGRESS_FILE);
|
|
10
|
+
try { fs.writeFileSync(progressPath, '', 'utf8'); } catch { /* tolerate */ }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function emitProgress(event: Record<string, unknown>): void {
|
|
14
|
+
if (!progressPath) return;
|
|
15
|
+
const line = JSON.stringify({ ...event, ts: new Date().toISOString() });
|
|
16
|
+
try { fs.appendFileSync(progressPath, line + '\n', 'utf8'); } catch { /* tolerate */ }
|
|
17
|
+
}
|
|
@@ -53,6 +53,7 @@ import { resolveLaunchOptions, isDemoMode } from './launch-options.js';
|
|
|
53
53
|
import { initDemo, drainDemo } from './demo-recorder.js';
|
|
54
54
|
import { pauseOnFailure } from './pause-on-failure.js';
|
|
55
55
|
import { traceEnabled, traceFilePath } from './trace.js';
|
|
56
|
+
import { initProgress, emitProgress } from './progress.js';
|
|
56
57
|
|
|
57
58
|
// Default 240s — catches real hangs without killing legit long steps.
|
|
58
59
|
// Steps that legitimately take longer (rewrites, manual think-time)
|
|
@@ -64,6 +65,7 @@ let browser: Browser | undefined;
|
|
|
64
65
|
BeforeAll(async () => {
|
|
65
66
|
const opts = resolveLaunchOptions(process.env);
|
|
66
67
|
initDemo(process.env);
|
|
68
|
+
initProgress(process.cwd());
|
|
67
69
|
if (isDemoMode(process.env) && opts.slowMo > 0) {
|
|
68
70
|
out.writeln(` ${out.c.dim('[demo] slowMo: ' + opts.slowMo + 'ms')}`);
|
|
69
71
|
}
|
|
@@ -97,6 +99,7 @@ BeforeAll(async () => {
|
|
|
97
99
|
AfterAll(async () => {
|
|
98
100
|
drainDemo();
|
|
99
101
|
const summary = await endRun();
|
|
102
|
+
emitProgress({ event: 'run-end', passed: summary.passed, failed: summary.failed, total: summary.total });
|
|
100
103
|
out.runSummary({
|
|
101
104
|
runId: summary.runId,
|
|
102
105
|
total: summary.total,
|
|
@@ -183,6 +186,7 @@ Before(async function (this: CabinetVerifyWorld, scenario) {
|
|
|
183
186
|
|
|
184
187
|
out.scenarioStart(scenario.pickle.name, scenario.gherkinDocument.uri || 'unknown');
|
|
185
188
|
setScenarioContext(scenario.gherkinDocument.uri || 'unknown', scenario.pickle.name, this.role);
|
|
189
|
+
emitProgress({ event: 'scenario-start', name: scenario.pickle.name, file: scenario.gherkinDocument.uri || 'unknown' });
|
|
186
190
|
});
|
|
187
191
|
|
|
188
192
|
After(async function (this: CabinetVerifyWorld, scenario) {
|
|
@@ -212,6 +216,7 @@ After(async function (this: CabinetVerifyWorld, scenario) {
|
|
|
212
216
|
this.tracing = false;
|
|
213
217
|
}
|
|
214
218
|
|
|
219
|
+
emitProgress({ event: 'scenario-end', name: scenario.pickle.name, status: scenario.result?.status ?? 'UNKNOWN' });
|
|
215
220
|
await this.context?.close();
|
|
216
221
|
});
|
|
217
222
|
|
|
@@ -225,6 +230,15 @@ AfterStep(async function (this: CabinetVerifyWorld, { result, pickleStep }) {
|
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
232
|
|
|
233
|
+
if (result && pickleStep?.text) {
|
|
234
|
+
const stepStatus = result.status.toString();
|
|
235
|
+
if (stepStatus === 'PASSED') {
|
|
236
|
+
emitProgress({ event: 'check-pass', step: pickleStep.text });
|
|
237
|
+
} else if (stepStatus === 'FAILED') {
|
|
238
|
+
emitProgress({ event: 'check-fail', step: pickleStep.text, error: (result as { message?: string }).message ?? '' });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
228
242
|
if (result) {
|
|
229
243
|
await pauseOnFailure(
|
|
230
244
|
this.page,
|
|
@@ -19,10 +19,12 @@ describe('isDemoMode', () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
describe('resolveLaunchOptions', () => {
|
|
22
|
-
it('DEMO=1 defaults slowMo to 1000
|
|
22
|
+
it('DEMO=1 defaults slowMo to 1000, smaller window, positioned top-left', () => {
|
|
23
23
|
const opts = resolveLaunchOptions({ CABINET_VERIFY_DEMO: '1' });
|
|
24
24
|
assert.equal(opts.slowMo, 1000);
|
|
25
25
|
assert.equal(opts.headless, false);
|
|
26
|
+
assert.ok(opts.args.includes('--window-size=1100,750'));
|
|
27
|
+
assert.ok(opts.args.includes('--window-position=0,0'));
|
|
26
28
|
});
|
|
27
29
|
|
|
28
30
|
it('DEMO=1 respects explicit SLOW_MO=250', () => {
|