@iamu-dev/iamu 0.2.0-beta.12 → 0.2.0-beta.14
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/dist/bin.js +330 -149
- package/dist/commands/evaluate.d.ts +5 -3
- package/dist/commands/match.d.ts +6 -4
- package/dist/commands/new-finding.d.ts +6 -0
- package/dist/commands/summary.d.ts +14 -0
- package/dist/index.js +326 -145
- package/dist/templates/prompts/walk.md +32 -34
- package/dist/templates/skills/iamu-findings.md +13 -28
- package/dist/templates/skills/iamu-run.md +56 -76
- package/package.json +3 -3
package/dist/bin.js
CHANGED
|
@@ -12112,15 +12112,15 @@ function resolveApiUrl(configured) {
|
|
|
12112
12112
|
assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
|
|
12113
12113
|
return url;
|
|
12114
12114
|
}
|
|
12115
|
-
function parseStore(raw,
|
|
12115
|
+
function parseStore(raw, path32) {
|
|
12116
12116
|
let parsed;
|
|
12117
12117
|
try {
|
|
12118
12118
|
parsed = JSON.parse(raw);
|
|
12119
12119
|
} catch (err) {
|
|
12120
|
-
throw new CredentialsFileError(
|
|
12120
|
+
throw new CredentialsFileError(path32, "not valid JSON", err);
|
|
12121
12121
|
}
|
|
12122
12122
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
12123
|
-
throw new CredentialsFileError(
|
|
12123
|
+
throw new CredentialsFileError(path32, "expected a JSON object");
|
|
12124
12124
|
}
|
|
12125
12125
|
return parsed;
|
|
12126
12126
|
}
|
|
@@ -12128,26 +12128,26 @@ function isNotFound(err) {
|
|
|
12128
12128
|
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
12129
12129
|
}
|
|
12130
12130
|
async function readCredentialsStore() {
|
|
12131
|
-
const
|
|
12131
|
+
const path32 = getCredentialsPath();
|
|
12132
12132
|
let raw;
|
|
12133
12133
|
try {
|
|
12134
|
-
raw = await readFile(
|
|
12134
|
+
raw = await readFile(path32, "utf-8");
|
|
12135
12135
|
} catch (err) {
|
|
12136
12136
|
if (isNotFound(err)) return {};
|
|
12137
12137
|
warnUnreadableStoreOnce(err);
|
|
12138
12138
|
throw err;
|
|
12139
12139
|
}
|
|
12140
12140
|
try {
|
|
12141
|
-
return parseStore(raw,
|
|
12141
|
+
return parseStore(raw, path32);
|
|
12142
12142
|
} catch (err) {
|
|
12143
12143
|
warnUnreadableStoreOnce(err);
|
|
12144
12144
|
throw err;
|
|
12145
12145
|
}
|
|
12146
12146
|
}
|
|
12147
12147
|
async function writeCredentialsStore(store) {
|
|
12148
|
-
const
|
|
12149
|
-
await mkdir(dirname(
|
|
12150
|
-
await atomicWriteFile(
|
|
12148
|
+
const path32 = getCredentialsPath();
|
|
12149
|
+
await mkdir(dirname(path32), { recursive: true, mode: 448 });
|
|
12150
|
+
await atomicWriteFile(path32, `${JSON.stringify(store, null, 2)}
|
|
12151
12151
|
`, {
|
|
12152
12152
|
mode: 384
|
|
12153
12153
|
});
|
|
@@ -12167,16 +12167,16 @@ function resolveApiKey(apiUrl) {
|
|
|
12167
12167
|
} catch {
|
|
12168
12168
|
return null;
|
|
12169
12169
|
}
|
|
12170
|
-
const
|
|
12170
|
+
const path32 = getCredentialsPath();
|
|
12171
12171
|
let raw;
|
|
12172
12172
|
try {
|
|
12173
|
-
raw = readFileSync(
|
|
12173
|
+
raw = readFileSync(path32, "utf-8");
|
|
12174
12174
|
} catch (err) {
|
|
12175
12175
|
if (!isNotFound(err)) warnUnreadableStoreOnce(err);
|
|
12176
12176
|
return null;
|
|
12177
12177
|
}
|
|
12178
12178
|
try {
|
|
12179
|
-
const entry = parseStore(raw,
|
|
12179
|
+
const entry = parseStore(raw, path32)[url];
|
|
12180
12180
|
if (typeof entry !== "object" || entry === null) return null;
|
|
12181
12181
|
const token = entry.token;
|
|
12182
12182
|
return typeof token === "string" && token ? token : null;
|
|
@@ -12215,12 +12215,12 @@ var init_credentials = __esm({
|
|
|
12215
12215
|
* @param reason - What is wrong with it.
|
|
12216
12216
|
* @param cause - Underlying parse error, if any.
|
|
12217
12217
|
*/
|
|
12218
|
-
constructor(
|
|
12218
|
+
constructor(path32, reason, cause) {
|
|
12219
12219
|
super(
|
|
12220
|
-
`Credentials file ${
|
|
12220
|
+
`Credentials file ${path32} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
|
|
12221
12221
|
{
|
|
12222
12222
|
cause,
|
|
12223
|
-
suggestedAction: `Delete ${
|
|
12223
|
+
suggestedAction: `Delete ${path32} and run \`iamu auth login\`, or set IAMU_TOKEN.`
|
|
12224
12224
|
}
|
|
12225
12225
|
);
|
|
12226
12226
|
}
|
|
@@ -13087,16 +13087,16 @@ async function needsWrite(filePath, content) {
|
|
|
13087
13087
|
return true;
|
|
13088
13088
|
}
|
|
13089
13089
|
}
|
|
13090
|
-
function isCliWrittenSkill(
|
|
13090
|
+
function isCliWrittenSkill(path32) {
|
|
13091
13091
|
try {
|
|
13092
|
-
return /^generatedBy:/m.test(readFileSync3(
|
|
13092
|
+
return /^generatedBy:/m.test(readFileSync3(path32, "utf-8"));
|
|
13093
13093
|
} catch {
|
|
13094
13094
|
return false;
|
|
13095
13095
|
}
|
|
13096
13096
|
}
|
|
13097
|
-
function isCliWrittenPrompt(
|
|
13097
|
+
function isCliWrittenPrompt(path32) {
|
|
13098
13098
|
try {
|
|
13099
|
-
const content = readFileSync3(
|
|
13099
|
+
const content = readFileSync3(path32, "utf-8");
|
|
13100
13100
|
return content.includes("iamu") || /^generatedBy:/m.test(content);
|
|
13101
13101
|
} catch {
|
|
13102
13102
|
return false;
|
|
@@ -13108,8 +13108,8 @@ function planSkillFiles(hosts, projectRoot, version2) {
|
|
|
13108
13108
|
for (const host of hosts) {
|
|
13109
13109
|
if (pluginProvidesSkills(host, projectRoot)) {
|
|
13110
13110
|
for (const name of skillTemplates.keys()) {
|
|
13111
|
-
const
|
|
13112
|
-
if (isCliWrittenSkill(
|
|
13111
|
+
const path32 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
|
|
13112
|
+
if (isCliWrittenSkill(path32)) files.push({ host, path: path32, content: null });
|
|
13113
13113
|
}
|
|
13114
13114
|
} else if (host.supportsSkills) {
|
|
13115
13115
|
const skillsDir = host.getSkillsDir(projectRoot);
|
|
@@ -19758,7 +19758,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
|
|
|
19758
19758
|
const byKey = /* @__PURE__ */ new Map();
|
|
19759
19759
|
const byPlaceView = /* @__PURE__ */ new Map();
|
|
19760
19760
|
const signatureOf = (s) => exploreSignature(s.headings, s.title);
|
|
19761
|
-
function addSurface(state, place, view,
|
|
19761
|
+
function addSurface(state, place, view, path32, depth) {
|
|
19762
19762
|
const key = exploreSurfaceKey(place, view);
|
|
19763
19763
|
const surface = {
|
|
19764
19764
|
key,
|
|
@@ -19770,7 +19770,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
|
|
|
19770
19770
|
headings: state.headings,
|
|
19771
19771
|
fields: state.fields,
|
|
19772
19772
|
depth,
|
|
19773
|
-
path:
|
|
19773
|
+
path: path32,
|
|
19774
19774
|
actions: []
|
|
19775
19775
|
};
|
|
19776
19776
|
surfaces.push(surface);
|
|
@@ -19788,9 +19788,9 @@ ${surface.signature}`, key);
|
|
|
19788
19788
|
});
|
|
19789
19789
|
await settle(page, settleMs);
|
|
19790
19790
|
}
|
|
19791
|
-
async function replay(
|
|
19792
|
-
if (!("goto" in (
|
|
19793
|
-
for (const step of
|
|
19791
|
+
async function replay(path32) {
|
|
19792
|
+
if (!("goto" in (path32[0] ?? {}))) await goto(opts.startUrl);
|
|
19793
|
+
for (const step of path32) {
|
|
19794
19794
|
if ("goto" in step) {
|
|
19795
19795
|
await goto(step.goto);
|
|
19796
19796
|
} else {
|
|
@@ -19914,8 +19914,8 @@ ${sig}`, key);
|
|
|
19914
19914
|
if (stats.surfaces >= budget.max_surfaces) {
|
|
19915
19915
|
stats.truncated = true;
|
|
19916
19916
|
} else {
|
|
19917
|
-
const
|
|
19918
|
-
addSurface(after, place, view,
|
|
19917
|
+
const path32 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
|
|
19918
|
+
addSurface(after, place, view, path32, surface.depth + 1);
|
|
19919
19919
|
}
|
|
19920
19920
|
}
|
|
19921
19921
|
}
|
|
@@ -20790,15 +20790,15 @@ function composeWalkerPrompt(p) {
|
|
|
20790
20790
|
"",
|
|
20791
20791
|
`Run \`iamu\` as: ${iamu} (from ${p.projectRoot})`,
|
|
20792
20792
|
`Run directory: ${rd}`,
|
|
20793
|
-
`Record steps atomically:`,
|
|
20793
|
+
`Record steps atomically via CLI (do not hand-craft step JSON files or use host Write tools):`,
|
|
20794
20794
|
` \u2022 For iamu browse: ${iamu} browse step --run ${p.runId} --action "<action>" --note "<monologue>" <command> [args...]`,
|
|
20795
20795
|
` \u2022 For other surfaces: ${iamu} step --run ${p.runId} --action "<action>" --note "<monologue>" [--shot <path>]`,
|
|
20796
|
-
|
|
20797
|
-
"Never hand-craft raw JSON step files with custom scripts; the CLI handles step numbering, screenshots, and telemetry automatically.",
|
|
20796
|
+
"The CLI handles step numbering, timestamps, screenshot capture/linking, and telemetry automatically.",
|
|
20798
20797
|
`Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
|
|
20799
|
-
`Record findings with: ${iamu} new-finding --scenario ${p.scenarioSlug} --run ${p.runId} --step <N> --title "..." --type <helps|hinders|missing|irrelevant|incorrect> --severity <critical|major|minor|observation> --ui-fact "..." --consequence "..." --task-impact <blocks|impairs|slows|neutral|accelerates|enables> --confidence <high|medium|low> --
|
|
20798
|
+
`Record findings with: ${iamu} new-finding --scenario ${p.scenarioSlug} --run ${p.runId} --step <N> --title "..." --type <helps|hinders|missing|irrelevant|incorrect> --severity <critical|major|minor|observation> --ui-fact "..." --consequence "..." --task-impact <blocks|impairs|slows|neutral|accelerates|enables> --confidence <high|medium|low> --description "..." --recommendation "..." --json`,
|
|
20800
20799
|
"`--step <N>` is the step whose screenshot shows the problem (or strength): it is the evidence people see next to the finding, so record that step and its screenshot first, and pick the screenshot where the thing is actually visible.",
|
|
20801
|
-
`Before you finish,
|
|
20800
|
+
`Before you finish, record synthesis with: ${iamu} summary ${p.runId} --worked "..." --worked "..." --question "..."`,
|
|
20801
|
+
"Execute everything via CLI commands in the shell. Never invoke host Edit or Write tools on files in the workspace.",
|
|
20802
20802
|
"",
|
|
20803
20803
|
"Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
|
|
20804
20804
|
"",
|
|
@@ -21209,11 +21209,14 @@ async function newFinding(opts) {
|
|
|
21209
21209
|
if (opts.taskImpact) frontmatter.task_impact = opts.taskImpact;
|
|
21210
21210
|
if (opts.confidence) frontmatter.confidence = opts.confidence;
|
|
21211
21211
|
if (stepId) frontmatter.walkthrough_step_id = stepId;
|
|
21212
|
-
const
|
|
21212
|
+
const desc = opts.description?.trim();
|
|
21213
|
+
const rec = opts.recommendation?.trim();
|
|
21214
|
+
const body = opts.body ?? [
|
|
21215
|
+
desc ?? "",
|
|
21213
21216
|
"",
|
|
21214
21217
|
"## Recommendation",
|
|
21215
21218
|
"",
|
|
21216
|
-
"<!-- How should this be fixed? -->",
|
|
21219
|
+
rec ?? "<!-- How should this be fixed? -->",
|
|
21217
21220
|
""
|
|
21218
21221
|
].join("\n");
|
|
21219
21222
|
try {
|
|
@@ -21269,6 +21272,122 @@ var init_new_finding = __esm({
|
|
|
21269
21272
|
}
|
|
21270
21273
|
});
|
|
21271
21274
|
|
|
21275
|
+
// src/commands/summary.ts
|
|
21276
|
+
var summary_exports = {};
|
|
21277
|
+
__export(summary_exports, {
|
|
21278
|
+
summaryCommand: () => summaryCommand
|
|
21279
|
+
});
|
|
21280
|
+
import { promises as fs29 } from "fs";
|
|
21281
|
+
import path28 from "path";
|
|
21282
|
+
import chalk30 from "chalk";
|
|
21283
|
+
async function summaryCommand(runIdArg, opts) {
|
|
21284
|
+
const ctx = await resolveProject(opts);
|
|
21285
|
+
if (!ctx) return;
|
|
21286
|
+
const { projectRoot } = ctx;
|
|
21287
|
+
const runId = runIdArg ?? opts.run;
|
|
21288
|
+
if (!runId) {
|
|
21289
|
+
commandError("Run ID is required. Pass as argument or via --run.", opts);
|
|
21290
|
+
return;
|
|
21291
|
+
}
|
|
21292
|
+
const runDirPath = resolveRunDir(projectRoot, runId, opts);
|
|
21293
|
+
if (!runDirPath) return;
|
|
21294
|
+
let whatWorked = [];
|
|
21295
|
+
let openQuestions = [];
|
|
21296
|
+
let model = opts.model?.trim() || void 0;
|
|
21297
|
+
if (opts.file) {
|
|
21298
|
+
try {
|
|
21299
|
+
let raw;
|
|
21300
|
+
if (opts.file === "-") {
|
|
21301
|
+
const chunks = [];
|
|
21302
|
+
for await (const chunk of process.stdin) {
|
|
21303
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
21304
|
+
}
|
|
21305
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
21306
|
+
} else {
|
|
21307
|
+
raw = await fs29.readFile(path28.resolve(projectRoot, opts.file), "utf8");
|
|
21308
|
+
}
|
|
21309
|
+
const parsed = JSON.parse(raw);
|
|
21310
|
+
if (Array.isArray(parsed.what_worked)) {
|
|
21311
|
+
whatWorked = parsed.what_worked.filter(
|
|
21312
|
+
(x) => typeof x === "string" && x.trim() !== ""
|
|
21313
|
+
);
|
|
21314
|
+
}
|
|
21315
|
+
if (Array.isArray(parsed.open_questions)) {
|
|
21316
|
+
openQuestions = parsed.open_questions.filter(
|
|
21317
|
+
(x) => typeof x === "string" && x.trim() !== ""
|
|
21318
|
+
);
|
|
21319
|
+
}
|
|
21320
|
+
if (typeof parsed.model === "string" && parsed.model.trim()) {
|
|
21321
|
+
model = parsed.model.trim();
|
|
21322
|
+
}
|
|
21323
|
+
} catch (err) {
|
|
21324
|
+
commandError(
|
|
21325
|
+
`Failed to parse summary file: ${err instanceof Error ? err.message : String(err)}`,
|
|
21326
|
+
opts
|
|
21327
|
+
);
|
|
21328
|
+
return;
|
|
21329
|
+
}
|
|
21330
|
+
}
|
|
21331
|
+
const toList = (v) => {
|
|
21332
|
+
if (!v) return [];
|
|
21333
|
+
const arr = Array.isArray(v) ? v : [v];
|
|
21334
|
+
return arr.map((s) => s.trim()).filter(Boolean);
|
|
21335
|
+
};
|
|
21336
|
+
const cliWorked = toList(opts.worked);
|
|
21337
|
+
if (cliWorked.length > 0) {
|
|
21338
|
+
whatWorked = [...whatWorked, ...cliWorked];
|
|
21339
|
+
}
|
|
21340
|
+
const cliQuestions = toList(opts.question);
|
|
21341
|
+
if (cliQuestions.length > 0) {
|
|
21342
|
+
openQuestions = [...openQuestions, ...cliQuestions];
|
|
21343
|
+
}
|
|
21344
|
+
const data = {
|
|
21345
|
+
what_worked: whatWorked,
|
|
21346
|
+
open_questions: openQuestions,
|
|
21347
|
+
...model ? { model } : {}
|
|
21348
|
+
};
|
|
21349
|
+
const summaryPath = path28.join(runDirPath, "summary.json");
|
|
21350
|
+
await atomicWriteFile(summaryPath, `${JSON.stringify(data, null, 2)}
|
|
21351
|
+
`);
|
|
21352
|
+
if (opts.json) {
|
|
21353
|
+
console.log(
|
|
21354
|
+
JSON.stringify(
|
|
21355
|
+
{
|
|
21356
|
+
run_id: runId,
|
|
21357
|
+
path: path28.relative(projectRoot, summaryPath),
|
|
21358
|
+
summary: data
|
|
21359
|
+
},
|
|
21360
|
+
null,
|
|
21361
|
+
2
|
|
21362
|
+
)
|
|
21363
|
+
);
|
|
21364
|
+
return;
|
|
21365
|
+
}
|
|
21366
|
+
console.log();
|
|
21367
|
+
console.log(
|
|
21368
|
+
` ${symbols.success} Summary recorded for run ${chalk30.cyan(runId)}`
|
|
21369
|
+
);
|
|
21370
|
+
if (data.what_worked.length > 0) {
|
|
21371
|
+
console.log(
|
|
21372
|
+
` ${chalk30.dim("what worked:")} ${data.what_worked.length} item(s)`
|
|
21373
|
+
);
|
|
21374
|
+
}
|
|
21375
|
+
if (data.open_questions.length > 0) {
|
|
21376
|
+
console.log(
|
|
21377
|
+
` ${chalk30.dim("open questions:")} ${data.open_questions.length} item(s)`
|
|
21378
|
+
);
|
|
21379
|
+
}
|
|
21380
|
+
console.log();
|
|
21381
|
+
}
|
|
21382
|
+
var init_summary = __esm({
|
|
21383
|
+
"src/commands/summary.ts"() {
|
|
21384
|
+
"use strict";
|
|
21385
|
+
init_dist2();
|
|
21386
|
+
init_symbols();
|
|
21387
|
+
init_context4();
|
|
21388
|
+
}
|
|
21389
|
+
});
|
|
21390
|
+
|
|
21272
21391
|
// src/commands/view-results.ts
|
|
21273
21392
|
var view_results_exports = {};
|
|
21274
21393
|
__export(view_results_exports, {
|
|
@@ -21276,7 +21395,7 @@ __export(view_results_exports, {
|
|
|
21276
21395
|
promptViewResults: () => promptViewResults
|
|
21277
21396
|
});
|
|
21278
21397
|
import { isCancel as isCancel4, select as select4 } from "@clack/prompts";
|
|
21279
|
-
import
|
|
21398
|
+
import chalk31 from "chalk";
|
|
21280
21399
|
function launchCloudDashboard(config, runId) {
|
|
21281
21400
|
const cloudRunId = runId ? toCloudRunUuid(runId) : void 0;
|
|
21282
21401
|
const url = getCloudDashboardUrl(
|
|
@@ -21285,7 +21404,7 @@ function launchCloudDashboard(config, runId) {
|
|
|
21285
21404
|
cloudRunId
|
|
21286
21405
|
);
|
|
21287
21406
|
console.log();
|
|
21288
|
-
console.log(` ${symbols.success} Opening Iamu Cloud: ${
|
|
21407
|
+
console.log(` ${symbols.success} Opening Iamu Cloud: ${chalk31.cyan(url)}
|
|
21289
21408
|
`);
|
|
21290
21409
|
openBrowser(url);
|
|
21291
21410
|
}
|
|
@@ -21353,9 +21472,9 @@ __export(evaluate_exports, {
|
|
|
21353
21472
|
evaluate: () => evaluate,
|
|
21354
21473
|
evaluateRecord: () => evaluateRecord
|
|
21355
21474
|
});
|
|
21356
|
-
import { promises as
|
|
21357
|
-
import
|
|
21358
|
-
import
|
|
21475
|
+
import { promises as fs30 } from "fs";
|
|
21476
|
+
import path29 from "path";
|
|
21477
|
+
import chalk32 from "chalk";
|
|
21359
21478
|
import { z as z25 } from "zod";
|
|
21360
21479
|
function formatIssues(error) {
|
|
21361
21480
|
return error.issues.map(
|
|
@@ -21385,10 +21504,10 @@ async function evaluate(runId, opts) {
|
|
|
21385
21504
|
const resolved = await resolveProjectAndRunDir(runId, opts);
|
|
21386
21505
|
if (!resolved) return;
|
|
21387
21506
|
const { projectRoot, runDirPath } = resolved;
|
|
21388
|
-
const canonicalPath =
|
|
21507
|
+
const canonicalPath = path29.join(runDirPath, "canonical.json");
|
|
21389
21508
|
let canonical;
|
|
21390
21509
|
try {
|
|
21391
|
-
const raw = await
|
|
21510
|
+
const raw = await fs30.readFile(canonicalPath, "utf8");
|
|
21392
21511
|
canonical = JSON.parse(raw);
|
|
21393
21512
|
} catch {
|
|
21394
21513
|
commandError(
|
|
@@ -21444,15 +21563,15 @@ async function evaluate(runId, opts) {
|
|
|
21444
21563
|
)
|
|
21445
21564
|
};
|
|
21446
21565
|
const prompt = composeJudgePrompt(input);
|
|
21447
|
-
const promptFilePath = opts?.promptFile ??
|
|
21448
|
-
await
|
|
21566
|
+
const promptFilePath = opts?.promptFile ?? path29.join(runDirPath, "judge_prompt.md");
|
|
21567
|
+
await fs30.writeFile(promptFilePath, prompt, "utf8");
|
|
21449
21568
|
if (opts?.json) {
|
|
21450
21569
|
console.log(
|
|
21451
21570
|
JSON.stringify(
|
|
21452
21571
|
{
|
|
21453
21572
|
...input,
|
|
21454
|
-
prompt_file:
|
|
21455
|
-
prompt_file_abs:
|
|
21573
|
+
prompt_file: path29.relative(projectRoot, promptFilePath),
|
|
21574
|
+
prompt_file_abs: path29.resolve(promptFilePath),
|
|
21456
21575
|
judge_prompt: prompt
|
|
21457
21576
|
},
|
|
21458
21577
|
null,
|
|
@@ -21469,21 +21588,39 @@ async function evaluateRecord(runId, opts) {
|
|
|
21469
21588
|
const { projectRoot, runDirPath } = resolved;
|
|
21470
21589
|
sendLivePhase(projectRoot, runId, "judging").catch(() => {
|
|
21471
21590
|
});
|
|
21472
|
-
const canonicalPath =
|
|
21591
|
+
const canonicalPath = path29.join(runDirPath, "canonical.json");
|
|
21473
21592
|
let canonical;
|
|
21474
21593
|
try {
|
|
21475
|
-
const raw = await
|
|
21594
|
+
const raw = await fs30.readFile(canonicalPath, "utf8");
|
|
21476
21595
|
canonical = JSON.parse(raw);
|
|
21477
21596
|
} catch {
|
|
21478
21597
|
commandError(`No canonical.json for run: ${runId}`, opts);
|
|
21479
21598
|
return;
|
|
21480
21599
|
}
|
|
21600
|
+
if (!opts.file && !opts.data) {
|
|
21601
|
+
commandError("Either --file <path> or --data <json> is required", opts);
|
|
21602
|
+
return;
|
|
21603
|
+
}
|
|
21481
21604
|
let parsedFile;
|
|
21482
21605
|
try {
|
|
21483
|
-
|
|
21606
|
+
let raw;
|
|
21607
|
+
if (opts.data) {
|
|
21608
|
+
raw = opts.data;
|
|
21609
|
+
} else if (opts.file === "-") {
|
|
21610
|
+
const chunks = [];
|
|
21611
|
+
for await (const chunk of process.stdin) {
|
|
21612
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
21613
|
+
}
|
|
21614
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
21615
|
+
} else {
|
|
21616
|
+
raw = await fs30.readFile(opts.file, "utf8");
|
|
21617
|
+
}
|
|
21484
21618
|
parsedFile = JSON.parse(raw);
|
|
21485
|
-
} catch {
|
|
21486
|
-
commandError(
|
|
21619
|
+
} catch (err) {
|
|
21620
|
+
commandError(
|
|
21621
|
+
`Cannot read verdicts: ${err instanceof Error ? err.message : String(err)}`,
|
|
21622
|
+
opts
|
|
21623
|
+
);
|
|
21487
21624
|
return;
|
|
21488
21625
|
}
|
|
21489
21626
|
const fileResult = verdictsFileSchema.safeParse(parsedFile);
|
|
@@ -21537,8 +21674,8 @@ ${formatIssues(fileResult.error)}`,
|
|
|
21537
21674
|
overall: inputVerdicts.overall
|
|
21538
21675
|
});
|
|
21539
21676
|
const reportContent = renderCanonicalMarkdown(canonicalForReport);
|
|
21540
|
-
const verdictsPath =
|
|
21541
|
-
const reportPath =
|
|
21677
|
+
const verdictsPath = path29.join(runDirPath, "verdicts.json");
|
|
21678
|
+
const reportPath = path29.join(runDirPath, "report.md");
|
|
21542
21679
|
await Promise.all([
|
|
21543
21680
|
atomicWriteFile(
|
|
21544
21681
|
verdictsPath,
|
|
@@ -21558,8 +21695,8 @@ ${formatIssues(fileResult.error)}`,
|
|
|
21558
21695
|
JSON.stringify(
|
|
21559
21696
|
{
|
|
21560
21697
|
runId,
|
|
21561
|
-
verdictsPath:
|
|
21562
|
-
reportPath:
|
|
21698
|
+
verdictsPath: path29.relative(projectRoot, verdictsPath),
|
|
21699
|
+
reportPath: path29.relative(projectRoot, reportPath),
|
|
21563
21700
|
verdictsCount: validatedVerdicts.length,
|
|
21564
21701
|
linkedCount: linked.length,
|
|
21565
21702
|
...cloudInfo ? { cloud: cloudInfo } : {}
|
|
@@ -21572,18 +21709,18 @@ ${formatIssues(fileResult.error)}`,
|
|
|
21572
21709
|
}
|
|
21573
21710
|
console.log();
|
|
21574
21711
|
console.log(
|
|
21575
|
-
` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${
|
|
21712
|
+
` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${chalk32.cyan(runId)}`
|
|
21576
21713
|
);
|
|
21577
21714
|
if (linked.length > 0) {
|
|
21578
21715
|
console.log(
|
|
21579
|
-
` ${
|
|
21716
|
+
` ${chalk32.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
|
|
21580
21717
|
);
|
|
21581
21718
|
}
|
|
21582
21719
|
console.log(
|
|
21583
|
-
` ${
|
|
21720
|
+
` ${chalk32.dim("verdicts:")} ${path29.relative(projectRoot, verdictsPath)}`
|
|
21584
21721
|
);
|
|
21585
21722
|
console.log(
|
|
21586
|
-
` ${
|
|
21723
|
+
` ${chalk32.dim("report:")} ${path29.relative(projectRoot, reportPath)}`
|
|
21587
21724
|
);
|
|
21588
21725
|
printCloudRunInfo(cloudInfo);
|
|
21589
21726
|
await promptViewResults(projectRoot, { ...opts, runId });
|
|
@@ -21648,9 +21785,9 @@ __export(complete_exports, {
|
|
|
21648
21785
|
screenshotSizeWarning: () => screenshotSizeWarning,
|
|
21649
21786
|
validateCanonicalWalkthrough: () => validateCanonicalWalkthrough
|
|
21650
21787
|
});
|
|
21651
|
-
import { promises as
|
|
21652
|
-
import
|
|
21653
|
-
import
|
|
21788
|
+
import { promises as fs31 } from "fs";
|
|
21789
|
+
import path30 from "path";
|
|
21790
|
+
import chalk33 from "chalk";
|
|
21654
21791
|
async function complete(runId, opts) {
|
|
21655
21792
|
const run = await loadRun(runId, opts);
|
|
21656
21793
|
if (!run) return;
|
|
@@ -21662,7 +21799,7 @@ async function complete(runId, opts) {
|
|
|
21662
21799
|
}
|
|
21663
21800
|
if (!opts?.json) {
|
|
21664
21801
|
console.log();
|
|
21665
|
-
console.log(
|
|
21802
|
+
console.log(chalk33.bold(" Assembling canonical JSON\u2026"));
|
|
21666
21803
|
}
|
|
21667
21804
|
const provenanceAudit = auditStepProvenance(
|
|
21668
21805
|
await readStepsForRun(run.projectRoot, runId)
|
|
@@ -21675,8 +21812,8 @@ async function complete(runId, opts) {
|
|
|
21675
21812
|
const walkerSummary = await readWalkerSummary(run.runDirPath);
|
|
21676
21813
|
let judgeReported;
|
|
21677
21814
|
try {
|
|
21678
|
-
const verdictsRaw = await
|
|
21679
|
-
|
|
21815
|
+
const verdictsRaw = await fs31.readFile(
|
|
21816
|
+
path30.join(run.runDirPath, "verdicts.json"),
|
|
21680
21817
|
"utf8"
|
|
21681
21818
|
);
|
|
21682
21819
|
const parsed = JSON.parse(verdictsRaw);
|
|
@@ -21728,16 +21865,16 @@ async function loadRun(runId, opts) {
|
|
|
21728
21865
|
if (!resolved) return null;
|
|
21729
21866
|
const { projectRoot, runDirPath } = resolved;
|
|
21730
21867
|
try {
|
|
21731
|
-
await
|
|
21868
|
+
await fs31.access(runDirPath);
|
|
21732
21869
|
} catch {
|
|
21733
21870
|
commandError(`Run not found: ${runId}`, opts);
|
|
21734
21871
|
return null;
|
|
21735
21872
|
}
|
|
21736
|
-
const manifestPath =
|
|
21873
|
+
const manifestPath = path30.join(runDirPath, "walkthrough.json");
|
|
21737
21874
|
let manifest;
|
|
21738
21875
|
try {
|
|
21739
21876
|
manifest = JSON.parse(
|
|
21740
|
-
await
|
|
21877
|
+
await fs31.readFile(manifestPath, "utf8")
|
|
21741
21878
|
);
|
|
21742
21879
|
} catch {
|
|
21743
21880
|
commandError(`No walkthrough.json found for run: ${runId}`, opts);
|
|
@@ -21749,8 +21886,8 @@ async function reportAlreadyCompleted(run, opts) {
|
|
|
21749
21886
|
const { runId, manifest } = run;
|
|
21750
21887
|
if (opts?.json) {
|
|
21751
21888
|
try {
|
|
21752
|
-
const raw = await
|
|
21753
|
-
|
|
21889
|
+
const raw = await fs31.readFile(
|
|
21890
|
+
path30.join(run.runDirPath, "canonical.json"),
|
|
21754
21891
|
"utf8"
|
|
21755
21892
|
);
|
|
21756
21893
|
console.log(raw.trim());
|
|
@@ -21770,18 +21907,18 @@ async function reportAlreadyCompleted(run, opts) {
|
|
|
21770
21907
|
return;
|
|
21771
21908
|
}
|
|
21772
21909
|
console.log();
|
|
21773
|
-
console.log(
|
|
21774
|
-
console.log(` status ${
|
|
21910
|
+
console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
|
|
21911
|
+
console.log(` status ${chalk33.green("completed")}`);
|
|
21775
21912
|
if (manifest.completed_at) {
|
|
21776
|
-
console.log(` completed ${
|
|
21913
|
+
console.log(` completed ${chalk33.dim(manifest.completed_at)}`);
|
|
21777
21914
|
}
|
|
21778
21915
|
const viewUrl = await pushedRunUrl(run.projectRoot, runId);
|
|
21779
21916
|
if (viewUrl) {
|
|
21780
|
-
console.log(` view ${
|
|
21917
|
+
console.log(` view ${chalk33.underline(viewUrl)}`);
|
|
21781
21918
|
}
|
|
21782
21919
|
console.log();
|
|
21783
21920
|
console.log(
|
|
21784
|
-
|
|
21921
|
+
chalk33.dim(
|
|
21785
21922
|
" Already completed \u2014 nothing to do. Use --force to reassemble the report."
|
|
21786
21923
|
)
|
|
21787
21924
|
);
|
|
@@ -21797,13 +21934,13 @@ function reportStrictProvenanceFailure(audit, opts) {
|
|
|
21797
21934
|
}
|
|
21798
21935
|
console.error();
|
|
21799
21936
|
console.error(
|
|
21800
|
-
|
|
21937
|
+
chalk33.red(` ${symbols.error} Strict provenance audit failed:`)
|
|
21801
21938
|
);
|
|
21802
21939
|
for (const w of audit.warnings) {
|
|
21803
|
-
console.error(
|
|
21940
|
+
console.error(chalk33.red(` \u2022 ${w}`));
|
|
21804
21941
|
}
|
|
21805
21942
|
console.error(
|
|
21806
|
-
|
|
21943
|
+
chalk33.dim(
|
|
21807
21944
|
" Run was flagged as synthetic / scripted. Remove --strict-provenance to complete anyway."
|
|
21808
21945
|
)
|
|
21809
21946
|
);
|
|
@@ -21844,41 +21981,41 @@ function completeJsonPayload(canonical, extras) {
|
|
|
21844
21981
|
function printCompletionSummary(run, completedAt, assembled, extras, provenanceAudit) {
|
|
21845
21982
|
const { runId, projectRoot, manifest } = run;
|
|
21846
21983
|
const { canonical } = assembled;
|
|
21847
|
-
const count = (n) => n === 0 ?
|
|
21848
|
-
console.log(
|
|
21849
|
-
console.log(` scenario ${
|
|
21850
|
-
console.log(` url ${
|
|
21984
|
+
const count = (n) => n === 0 ? chalk33.dim("0") : chalk33.cyan(String(n));
|
|
21985
|
+
console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
|
|
21986
|
+
console.log(` scenario ${chalk33.dim(manifest.scenario_slug)}`);
|
|
21987
|
+
console.log(` url ${chalk33.dim(manifest.target_url)}`);
|
|
21851
21988
|
if (manifest.environment) {
|
|
21852
|
-
console.log(` env ${
|
|
21989
|
+
console.log(` env ${chalk33.dim(manifest.environment)}`);
|
|
21853
21990
|
}
|
|
21854
21991
|
if (manifest.build) {
|
|
21855
|
-
console.log(` build ${
|
|
21992
|
+
console.log(` build ${chalk33.dim(formatBuild(manifest.build))}`);
|
|
21856
21993
|
}
|
|
21857
21994
|
if (manifest.viewport) {
|
|
21858
|
-
console.log(` device ${
|
|
21995
|
+
console.log(` device ${chalk33.dim(formatViewport(manifest.viewport))}`);
|
|
21859
21996
|
}
|
|
21860
|
-
console.log(` status ${
|
|
21997
|
+
console.log(` status ${chalk33.green("completed")}`);
|
|
21861
21998
|
console.log(` findings ${count(canonical.findings.length)}`);
|
|
21862
21999
|
console.log(` steps ${count(canonical.steps.length)}`);
|
|
21863
|
-
console.log(` started ${
|
|
21864
|
-
console.log(` completed ${
|
|
22000
|
+
console.log(` started ${chalk33.dim(manifest.created_at)}`);
|
|
22001
|
+
console.log(` completed ${chalk33.dim(completedAt)}`);
|
|
21865
22002
|
const models = manifest.models;
|
|
21866
22003
|
if (models?.velocity_sec_per_step != null) {
|
|
21867
22004
|
console.log(
|
|
21868
|
-
` velocity ${
|
|
22005
|
+
` velocity ${chalk33.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk33.dim(`(${models.total_duration_sec}s total)`)}`
|
|
21869
22006
|
);
|
|
21870
22007
|
}
|
|
21871
22008
|
if (models && (models.walker || models.judge || models.matcher)) {
|
|
21872
22009
|
const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
|
|
21873
22010
|
const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
|
|
21874
|
-
console.log(` models ${
|
|
22011
|
+
console.log(` models ${chalk33.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
|
|
21875
22012
|
}
|
|
21876
22013
|
console.log();
|
|
21877
22014
|
console.log(
|
|
21878
|
-
` ${
|
|
22015
|
+
` ${chalk33.dim("canonical:")} ${chalk33.dim(path30.relative(projectRoot, assembled.canonicalPath))}`
|
|
21879
22016
|
);
|
|
21880
22017
|
console.log(
|
|
21881
|
-
` ${
|
|
22018
|
+
` ${chalk33.dim("report:")} ${chalk33.dim(path30.relative(projectRoot, assembled.reportPath))}`
|
|
21882
22019
|
);
|
|
21883
22020
|
if (extras.viewportWarning) {
|
|
21884
22021
|
console.log();
|
|
@@ -21886,7 +22023,7 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
|
|
|
21886
22023
|
}
|
|
21887
22024
|
if (extras.codeWarning) {
|
|
21888
22025
|
console.log();
|
|
21889
|
-
console.log(` ${
|
|
22026
|
+
console.log(` ${chalk33.yellow("!")} ${extras.codeWarning}`);
|
|
21890
22027
|
}
|
|
21891
22028
|
if (extras.revisit) printRevisit(extras.revisit);
|
|
21892
22029
|
if (assembled.verification) printVerification(assembled.verification);
|
|
@@ -21898,13 +22035,13 @@ function printProvenanceWarnings(audit) {
|
|
|
21898
22035
|
if (audit.warnings.length === 0) return;
|
|
21899
22036
|
console.log();
|
|
21900
22037
|
console.log(
|
|
21901
|
-
` ${symbols.warning} ${
|
|
22038
|
+
` ${symbols.warning} ${chalk33.yellow("Step provenance audit flagged suspicious timing:")}`
|
|
21902
22039
|
);
|
|
21903
22040
|
for (const w of audit.warnings) {
|
|
21904
|
-
console.log(` ${
|
|
22041
|
+
console.log(` ${chalk33.dim("\u2022")} ${w}`);
|
|
21905
22042
|
}
|
|
21906
22043
|
console.log(
|
|
21907
|
-
|
|
22044
|
+
chalk33.dim(
|
|
21908
22045
|
" Run appears to be programmatically batch-generated rather than an interactive walkthrough."
|
|
21909
22046
|
)
|
|
21910
22047
|
);
|
|
@@ -21936,17 +22073,17 @@ function screenshotFitsViewport(width, expected) {
|
|
|
21936
22073
|
}
|
|
21937
22074
|
async function screenshotSizeWarning(runDirPath, viewport) {
|
|
21938
22075
|
if (!viewport) return null;
|
|
21939
|
-
const dir =
|
|
22076
|
+
const dir = path30.join(runDirPath, "screenshots");
|
|
21940
22077
|
let first;
|
|
21941
22078
|
try {
|
|
21942
|
-
first = (await
|
|
22079
|
+
first = (await fs31.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
|
|
21943
22080
|
} catch {
|
|
21944
22081
|
return null;
|
|
21945
22082
|
}
|
|
21946
22083
|
if (!first) return null;
|
|
21947
22084
|
let size = null;
|
|
21948
22085
|
try {
|
|
21949
|
-
const handle = await
|
|
22086
|
+
const handle = await fs31.open(path30.join(dir, first), "r");
|
|
21950
22087
|
try {
|
|
21951
22088
|
const head = Buffer.alloc(24);
|
|
21952
22089
|
const { bytesRead } = await handle.read(head, 0, 24, 0);
|
|
@@ -21963,7 +22100,7 @@ async function screenshotSizeWarning(runDirPath, viewport) {
|
|
|
21963
22100
|
async function readWalkerSummary(runDirPath) {
|
|
21964
22101
|
try {
|
|
21965
22102
|
const data = JSON.parse(
|
|
21966
|
-
await
|
|
22103
|
+
await fs31.readFile(path30.join(runDirPath, "summary.json"), "utf8")
|
|
21967
22104
|
);
|
|
21968
22105
|
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
|
|
21969
22106
|
const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
|
|
@@ -22072,7 +22209,7 @@ function validateCanonicalWalkthrough(canonical) {
|
|
|
22072
22209
|
async function applyRecordedVerdicts(canonical, runDirPath) {
|
|
22073
22210
|
try {
|
|
22074
22211
|
const recorded = JSON.parse(
|
|
22075
|
-
await
|
|
22212
|
+
await fs31.readFile(path30.join(runDirPath, "verdicts.json"), "utf8")
|
|
22076
22213
|
);
|
|
22077
22214
|
if (Array.isArray(recorded.verdicts)) {
|
|
22078
22215
|
return applyVerdicts(canonical, recorded);
|
|
@@ -22082,7 +22219,7 @@ async function applyRecordedVerdicts(canonical, runDirPath) {
|
|
|
22082
22219
|
return canonical;
|
|
22083
22220
|
}
|
|
22084
22221
|
async function persistCanonical(runDirPath, canonical) {
|
|
22085
|
-
const canonicalPath =
|
|
22222
|
+
const canonicalPath = path30.join(runDirPath, "canonical.json");
|
|
22086
22223
|
await atomicWriteFile(
|
|
22087
22224
|
canonicalPath,
|
|
22088
22225
|
`${JSON.stringify(canonical, null, 2)}
|
|
@@ -22091,7 +22228,7 @@ async function persistCanonical(runDirPath, canonical) {
|
|
|
22091
22228
|
return canonicalPath;
|
|
22092
22229
|
}
|
|
22093
22230
|
async function persistReport(runDirPath, canonical) {
|
|
22094
|
-
const reportPath =
|
|
22231
|
+
const reportPath = path30.join(runDirPath, "report.md");
|
|
22095
22232
|
await atomicWriteFile(reportPath, renderCanonicalMarkdown(canonical));
|
|
22096
22233
|
return reportPath;
|
|
22097
22234
|
}
|
|
@@ -22191,17 +22328,17 @@ function placeLabel(p) {
|
|
|
22191
22328
|
function printRevisit(r) {
|
|
22192
22329
|
const missed = r.places.filter((p) => !p.reached);
|
|
22193
22330
|
console.log();
|
|
22194
|
-
const head = ` ${
|
|
22331
|
+
const head = ` ${chalk33.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
|
|
22195
22332
|
if (missed.length === 0) {
|
|
22196
|
-
console.log(`${head} ${
|
|
22333
|
+
console.log(`${head} ${chalk33.green("\u2713")}`);
|
|
22197
22334
|
return;
|
|
22198
22335
|
}
|
|
22199
22336
|
console.log(`${head} \u2014 not reached:`);
|
|
22200
22337
|
for (const p of missed) {
|
|
22201
|
-
console.log(` ${
|
|
22338
|
+
console.log(` ${chalk33.yellow("\u25CB")} ${placeLabel(p)}`);
|
|
22202
22339
|
}
|
|
22203
22340
|
console.log(
|
|
22204
|
-
|
|
22341
|
+
chalk33.dim(
|
|
22205
22342
|
" Fixes there stay awaiting a re-run until a walk gets there."
|
|
22206
22343
|
)
|
|
22207
22344
|
);
|
|
@@ -22209,7 +22346,7 @@ function printRevisit(r) {
|
|
|
22209
22346
|
function printVerification(verification) {
|
|
22210
22347
|
console.log();
|
|
22211
22348
|
console.log(
|
|
22212
|
-
|
|
22349
|
+
chalk33.bold(" Since this scenario's last run") + chalk33.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk33.bold(":")
|
|
22213
22350
|
);
|
|
22214
22351
|
if (verification.resolved.length > 0) {
|
|
22215
22352
|
console.log(
|
|
@@ -22218,7 +22355,7 @@ function printVerification(verification) {
|
|
|
22218
22355
|
}
|
|
22219
22356
|
if (verification.not_observed.length > 0) {
|
|
22220
22357
|
console.log(
|
|
22221
|
-
` ${symbols.pending} ${verification.not_observed.length} not seen this run ${
|
|
22358
|
+
` ${symbols.pending} ${verification.not_observed.length} not seen this run ${chalk33.dim("(not evidence of a fix)")}`
|
|
22222
22359
|
);
|
|
22223
22360
|
}
|
|
22224
22361
|
if (verification.persists.length > 0) {
|
|
@@ -22260,14 +22397,14 @@ __export(match_exports, {
|
|
|
22260
22397
|
match: () => match,
|
|
22261
22398
|
matchRecord: () => matchRecord
|
|
22262
22399
|
});
|
|
22263
|
-
import { promises as
|
|
22264
|
-
import
|
|
22265
|
-
import
|
|
22400
|
+
import { promises as fs32 } from "fs";
|
|
22401
|
+
import path31 from "path";
|
|
22402
|
+
import chalk34 from "chalk";
|
|
22266
22403
|
async function completedManifest2(runDirPath, runId, opts) {
|
|
22267
22404
|
let manifest;
|
|
22268
22405
|
try {
|
|
22269
22406
|
manifest = JSON.parse(
|
|
22270
|
-
await
|
|
22407
|
+
await fs32.readFile(path31.join(runDirPath, "walkthrough.json"), "utf8")
|
|
22271
22408
|
);
|
|
22272
22409
|
} catch {
|
|
22273
22410
|
commandError(`Run not found: ${runId}`, opts);
|
|
@@ -22296,12 +22433,12 @@ async function match(runId, opts) {
|
|
|
22296
22433
|
);
|
|
22297
22434
|
const needed = input.current.length > 0 && (input.prior.length > 0 || (input.guidance ?? []).length > 0);
|
|
22298
22435
|
const prompt = needed ? composeMatchPrompt(input) : null;
|
|
22299
|
-
const promptFilePath = opts?.promptFile ??
|
|
22436
|
+
const promptFilePath = opts?.promptFile ?? path31.join(runDirPath, "match_prompt.md");
|
|
22300
22437
|
if (prompt) {
|
|
22301
|
-
await
|
|
22438
|
+
await fs32.writeFile(promptFilePath, prompt, "utf8");
|
|
22302
22439
|
} else {
|
|
22303
22440
|
try {
|
|
22304
|
-
await
|
|
22441
|
+
await fs32.unlink(promptFilePath);
|
|
22305
22442
|
} catch {
|
|
22306
22443
|
}
|
|
22307
22444
|
}
|
|
@@ -22311,8 +22448,8 @@ async function match(runId, opts) {
|
|
|
22311
22448
|
{
|
|
22312
22449
|
...input,
|
|
22313
22450
|
needed,
|
|
22314
|
-
prompt_file: prompt ?
|
|
22315
|
-
prompt_file_abs: prompt ?
|
|
22451
|
+
prompt_file: prompt ? path31.relative(ctx.projectRoot, promptFilePath) : null,
|
|
22452
|
+
prompt_file_abs: prompt ? path31.resolve(promptFilePath) : null,
|
|
22316
22453
|
match_prompt: prompt
|
|
22317
22454
|
},
|
|
22318
22455
|
null,
|
|
@@ -22323,7 +22460,7 @@ async function match(runId, opts) {
|
|
|
22323
22460
|
}
|
|
22324
22461
|
if (!prompt) {
|
|
22325
22462
|
console.log(
|
|
22326
|
-
|
|
22463
|
+
chalk34.dim(
|
|
22327
22464
|
" No earlier findings or tester guidance for this scenario to match against \u2014 nothing to do."
|
|
22328
22465
|
)
|
|
22329
22466
|
);
|
|
@@ -22339,11 +22476,30 @@ async function matchRecord(runId, opts) {
|
|
|
22339
22476
|
});
|
|
22340
22477
|
const manifest = await completedManifest2(runDirPath, runId, opts);
|
|
22341
22478
|
if (!manifest) return;
|
|
22479
|
+
if (!opts.file && !opts.data) {
|
|
22480
|
+
commandError("Either --file <path> or --data <json> is required", opts);
|
|
22481
|
+
return;
|
|
22482
|
+
}
|
|
22342
22483
|
let answer;
|
|
22343
22484
|
try {
|
|
22344
|
-
|
|
22345
|
-
|
|
22346
|
-
|
|
22485
|
+
let raw;
|
|
22486
|
+
if (opts.data) {
|
|
22487
|
+
raw = opts.data;
|
|
22488
|
+
} else if (opts.file === "-") {
|
|
22489
|
+
const chunks = [];
|
|
22490
|
+
for await (const chunk of process.stdin) {
|
|
22491
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
22492
|
+
}
|
|
22493
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
22494
|
+
} else {
|
|
22495
|
+
raw = await fs32.readFile(opts.file, "utf8");
|
|
22496
|
+
}
|
|
22497
|
+
answer = JSON.parse(raw);
|
|
22498
|
+
} catch (err) {
|
|
22499
|
+
commandError(
|
|
22500
|
+
`Cannot read matches: ${err instanceof Error ? err.message : String(err)}`,
|
|
22501
|
+
opts
|
|
22502
|
+
);
|
|
22347
22503
|
return;
|
|
22348
22504
|
}
|
|
22349
22505
|
let result;
|
|
@@ -22382,17 +22538,17 @@ async function matchRecord(runId, opts) {
|
|
|
22382
22538
|
}
|
|
22383
22539
|
console.log();
|
|
22384
22540
|
console.log(
|
|
22385
|
-
` ${
|
|
22541
|
+
` ${chalk34.green("\u2713")} Matched ${result.linked} finding${result.linked === 1 ? "" : "s"} to earlier ones, ${result.new} first seen, ${result.rechecked_fixed} earlier re-checked fixed`
|
|
22386
22542
|
);
|
|
22387
22543
|
if (result.suppressed > 0) {
|
|
22388
22544
|
console.log(
|
|
22389
|
-
` ${
|
|
22545
|
+
` ${chalk34.yellow("!")} ${result.suppressed} finding${result.suppressed === 1 ? "" : "s"} match tester guidance (flagged with suppressed_by_guidance \u2014 dismiss them in triage if the rule holds)`
|
|
22390
22546
|
);
|
|
22391
22547
|
}
|
|
22392
22548
|
if (verification) printVerification(verification);
|
|
22393
22549
|
console.log();
|
|
22394
22550
|
console.log(
|
|
22395
|
-
|
|
22551
|
+
chalk34.dim(" Run `iamu sync` to push the linked findings to the cloud.")
|
|
22396
22552
|
);
|
|
22397
22553
|
console.log();
|
|
22398
22554
|
}
|
|
@@ -22414,7 +22570,7 @@ __export(prune_exports, {
|
|
|
22414
22570
|
prune: () => prune
|
|
22415
22571
|
});
|
|
22416
22572
|
import { confirm, isCancel as isCancel5 } from "@clack/prompts";
|
|
22417
|
-
import
|
|
22573
|
+
import chalk35 from "chalk";
|
|
22418
22574
|
function formatBytes(bytes) {
|
|
22419
22575
|
if (bytes <= 0) return "0 B";
|
|
22420
22576
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -22460,7 +22616,7 @@ async function prune(opts = {}) {
|
|
|
22460
22616
|
});
|
|
22461
22617
|
if (preview.prunedRuns.length === 0) {
|
|
22462
22618
|
console.log(
|
|
22463
|
-
|
|
22619
|
+
chalk35.dim(" No runs match the pruning criteria. Nothing to prune.")
|
|
22464
22620
|
);
|
|
22465
22621
|
return preview;
|
|
22466
22622
|
}
|
|
@@ -22469,7 +22625,7 @@ async function prune(opts = {}) {
|
|
|
22469
22625
|
message: `Prune ${mode} ${preview.prunedRuns.length} run(s) and reclaim ${formatBytes(preview.totalBytesReclaimed)}?`
|
|
22470
22626
|
});
|
|
22471
22627
|
if (isCancel5(confirmed) || !confirmed) {
|
|
22472
|
-
console.log(
|
|
22628
|
+
console.log(chalk35.yellow(" Pruning cancelled."));
|
|
22473
22629
|
return;
|
|
22474
22630
|
}
|
|
22475
22631
|
}
|
|
@@ -22480,14 +22636,14 @@ async function prune(opts = {}) {
|
|
|
22480
22636
|
}
|
|
22481
22637
|
if (result.dryRun) {
|
|
22482
22638
|
console.log(
|
|
22483
|
-
|
|
22639
|
+
chalk35.cyan(
|
|
22484
22640
|
`
|
|
22485
22641
|
[DRY RUN] Would prune ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
|
|
22486
22642
|
)
|
|
22487
22643
|
);
|
|
22488
22644
|
} else {
|
|
22489
22645
|
console.log(
|
|
22490
|
-
|
|
22646
|
+
chalk35.bold(
|
|
22491
22647
|
`
|
|
22492
22648
|
${symbols.success} Pruned ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
|
|
22493
22649
|
)
|
|
@@ -22495,18 +22651,18 @@ async function prune(opts = {}) {
|
|
|
22495
22651
|
}
|
|
22496
22652
|
if (result.prunedRuns.length === 0) {
|
|
22497
22653
|
console.log(
|
|
22498
|
-
|
|
22654
|
+
chalk35.dim(" No runs matched the pruning criteria. Nothing pruned.")
|
|
22499
22655
|
);
|
|
22500
22656
|
} else {
|
|
22501
22657
|
for (const run of result.prunedRuns) {
|
|
22502
22658
|
const mode = run.fullyDeleted ? "removed" : "screenshots purged";
|
|
22503
22659
|
const count = run.screenshotsDeleted > 0 ? `${run.screenshotsDeleted} screenshots, ` : "";
|
|
22504
22660
|
console.log(
|
|
22505
|
-
` ${
|
|
22661
|
+
` ${chalk35.dim("\u2022")} ${chalk35.bold(run.runId)} ${chalk35.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk35.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
|
|
22506
22662
|
);
|
|
22507
22663
|
}
|
|
22508
22664
|
console.log(
|
|
22509
|
-
|
|
22665
|
+
chalk35.dim(
|
|
22510
22666
|
`
|
|
22511
22667
|
Total reclaimed: ${formatBytes(result.totalBytesReclaimed)} (${result.totalScreenshotsDeleted} screenshots deleted)
|
|
22512
22668
|
`
|
|
@@ -22529,7 +22685,7 @@ init_context4();
|
|
|
22529
22685
|
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
22530
22686
|
import { dirname as dirname5, resolve as resolve3 } from "path";
|
|
22531
22687
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
22532
|
-
import
|
|
22688
|
+
import chalk38 from "chalk";
|
|
22533
22689
|
import { Command } from "commander";
|
|
22534
22690
|
|
|
22535
22691
|
// src/commands/register-cloud.ts
|
|
@@ -22703,9 +22859,9 @@ function registerProjectCommands(program2) {
|
|
|
22703
22859
|
"after",
|
|
22704
22860
|
"\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
|
|
22705
22861
|
).action(
|
|
22706
|
-
async (
|
|
22862
|
+
async (path32, opts) => {
|
|
22707
22863
|
const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
|
|
22708
|
-
await validate2(
|
|
22864
|
+
await validate2(path32, opts);
|
|
22709
22865
|
}
|
|
22710
22866
|
);
|
|
22711
22867
|
program2.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(
|
|
@@ -22930,6 +23086,15 @@ function registerWalkthroughCommands(program2) {
|
|
|
22930
23086
|
).option(
|
|
22931
23087
|
"--step <n>",
|
|
22932
23088
|
"Step whose screenshot shows the finding \u2014 its evidence (step must be recorded first)"
|
|
23089
|
+
).option(
|
|
23090
|
+
"--description <text>",
|
|
23091
|
+
"Finding narrative description below frontmatter"
|
|
23092
|
+
).option(
|
|
23093
|
+
"--recommendation <text>",
|
|
23094
|
+
"Concrete recommendation for how to fix or address the finding"
|
|
23095
|
+
).option(
|
|
23096
|
+
"--body <markdown>",
|
|
23097
|
+
"Complete markdown body (overrides --description and --recommendation)"
|
|
22933
23098
|
).option("--json", "Output as JSON").addHelpText(
|
|
22934
23099
|
"after",
|
|
22935
23100
|
'\nExamples:\n $ iamu new-finding run-123 --title "Submit fails" --type hinders --severity critical\n $ iamu new-finding --run run-123 --scenario login --title "Submit fails" --type hinders --severity critical'
|
|
@@ -22940,6 +23105,20 @@ function registerWalkthroughCommands(program2) {
|
|
|
22940
23105
|
await newFinding2({ ...opts, run });
|
|
22941
23106
|
}
|
|
22942
23107
|
);
|
|
23108
|
+
program2.command("summary [run-id]").helpGroup("Walkthrough:").description(
|
|
23109
|
+
"Record walker summary (what worked, open questions) atomically"
|
|
23110
|
+
).option("--run <run-id>", "Run ID (or pass as positional argument)").option(
|
|
23111
|
+
"--worked <note...>",
|
|
23112
|
+
"Things that worked well (can be specified multiple times)"
|
|
23113
|
+
).option(
|
|
23114
|
+
"--question <note...>",
|
|
23115
|
+
"Open questions that arose (can be specified multiple times)"
|
|
23116
|
+
).option("--model <name>", "Model identifier (e.g. flash, haiku)").option("--file <path>", "Path to summary JSON file (or - for stdin)").option("--json", "Output as JSON").action(
|
|
23117
|
+
async (runIdArg, opts) => {
|
|
23118
|
+
const { summaryCommand: summaryCommand2 } = await Promise.resolve().then(() => (init_summary(), summary_exports));
|
|
23119
|
+
await summaryCommand2(runIdArg, opts);
|
|
23120
|
+
}
|
|
23121
|
+
);
|
|
22943
23122
|
program2.command("complete <run-id>").helpGroup("Walkthrough:").description("Assemble canonical JSON + report from a walkthrough run").option("--json", "Output canonical JSON to stdout").option(
|
|
22944
23123
|
"--force",
|
|
22945
23124
|
"Reassemble canonical.json and report.md for an already completed run"
|
|
@@ -22965,17 +23144,19 @@ function registerWalkthroughCommands(program2) {
|
|
|
22965
23144
|
);
|
|
22966
23145
|
program2.command("match-record <run-id>").helpGroup("Walkthrough:").description(
|
|
22967
23146
|
"Record finding matches and re-derive the run's since-last-run delta"
|
|
22968
|
-
).
|
|
22969
|
-
|
|
22970
|
-
|
|
22971
|
-
|
|
23147
|
+
).option("--file <path>", "Path to matches JSON file (or - for stdin)").option("--data <json>", "Inline matches JSON string").option("--json", "Output match results as JSON").action(
|
|
23148
|
+
async (runId, opts) => {
|
|
23149
|
+
const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
|
|
23150
|
+
await matchRecord2(runId, opts);
|
|
23151
|
+
}
|
|
23152
|
+
);
|
|
22972
23153
|
program2.command("evaluate <run-id>").helpGroup("Walkthrough:").description("Compose evaluation prompt for human-test questions").option("--json", "Output evaluation input bundle as JSON").option("--prompt-file <path>", "Write the generated prompt to a file").action(
|
|
22973
23154
|
async (runId, opts) => {
|
|
22974
23155
|
const { evaluate: evaluate2 } = await Promise.resolve().then(() => (init_evaluate(), evaluate_exports));
|
|
22975
23156
|
await evaluate2(runId, opts);
|
|
22976
23157
|
}
|
|
22977
23158
|
);
|
|
22978
|
-
program2.command("evaluate-record <run-id>").helpGroup("Walkthrough:").description("Record evaluation verdicts into the run's verdicts.json").
|
|
23159
|
+
program2.command("evaluate-record <run-id>").helpGroup("Walkthrough:").description("Record evaluation verdicts into the run's verdicts.json").option("--file <path>", "Path to verdicts JSON file (or - for stdin)").option("--data <json>", "Inline verdicts JSON string").option("--json", "Output summary as JSON").option("-y, --yes", "Skip interactive prompts").option("--no-prompt", "Skip interactive prompt to view results").option("--no-push", "Skip pushing the updated run to Iamu Cloud").option("--view", "Open results in browser immediately without prompt").addHelpText(
|
|
22979
23160
|
"after",
|
|
22980
23161
|
"\nExamples:\n $ iamu evaluate-record <run-id> --file verdicts.json\n $ iamu evaluate-record <run-id> --file verdicts.json --no-prompt\n $ iamu evaluate-record <run-id> --file verdicts.json --no-push\n $ iamu evaluate-record <run-id> --file verdicts.json --json"
|
|
22981
23162
|
).action(
|
|
@@ -23006,23 +23187,23 @@ function registerAllCommands(program2) {
|
|
|
23006
23187
|
|
|
23007
23188
|
// src/ui/panic.ts
|
|
23008
23189
|
init_logger();
|
|
23009
|
-
import
|
|
23190
|
+
import chalk36 from "chalk";
|
|
23010
23191
|
var handlersRegistered = false;
|
|
23011
23192
|
var customSigintHandler = null;
|
|
23012
23193
|
function logFatalAndExit(title, errOrReason) {
|
|
23013
23194
|
process.stderr.write("\x1B[?25h");
|
|
23014
23195
|
if (isVerbose()) {
|
|
23015
|
-
console.error(
|
|
23196
|
+
console.error(chalk36.red(`
|
|
23016
23197
|
${title}:`));
|
|
23017
23198
|
console.error(
|
|
23018
23199
|
errOrReason instanceof Error ? errOrReason.stack || errOrReason : errOrReason
|
|
23019
23200
|
);
|
|
23020
23201
|
} else {
|
|
23021
23202
|
const msg = errOrReason instanceof Error ? errOrReason.message : String(errOrReason);
|
|
23022
|
-
console.error(
|
|
23203
|
+
console.error(chalk36.red(`
|
|
23023
23204
|
Fatal Error: ${msg}`));
|
|
23024
23205
|
console.error(
|
|
23025
|
-
|
|
23206
|
+
chalk36.dim(
|
|
23026
23207
|
" Run with --verbose or DEBUG=1 to view the full stack trace.\n"
|
|
23027
23208
|
)
|
|
23028
23209
|
);
|
|
@@ -23041,7 +23222,7 @@ function setupProcessHandlers() {
|
|
|
23041
23222
|
}
|
|
23042
23223
|
process.exit(130);
|
|
23043
23224
|
} else {
|
|
23044
|
-
console.error(
|
|
23225
|
+
console.error(chalk36.dim("\n Operation cancelled."));
|
|
23045
23226
|
process.exit(130);
|
|
23046
23227
|
}
|
|
23047
23228
|
});
|
|
@@ -23067,7 +23248,7 @@ function setupProcessHandlers() {
|
|
|
23067
23248
|
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
23068
23249
|
import { homedir as homedir4 } from "os";
|
|
23069
23250
|
import { join as join10 } from "path";
|
|
23070
|
-
import
|
|
23251
|
+
import chalk37 from "chalk";
|
|
23071
23252
|
var CACHE_FILE = join10(homedir4(), ".iamu", "update-check.json");
|
|
23072
23253
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
23073
23254
|
function isSemverGreater(latest, current) {
|
|
@@ -23120,7 +23301,7 @@ async function checkForUpdate(currentVersion) {
|
|
|
23120
23301
|
function printUpdateNotice(currentVersion, latestVersion) {
|
|
23121
23302
|
console.error();
|
|
23122
23303
|
console.error(
|
|
23123
|
-
` ${
|
|
23304
|
+
` ${chalk37.yellow("Update available:")} ${chalk37.dim(currentVersion)} \u2192 ${chalk37.green(latestVersion)} ${chalk37.dim("(run: npm i -g @iamu-dev/iamu)")}`
|
|
23124
23305
|
);
|
|
23125
23306
|
}
|
|
23126
23307
|
|
|
@@ -23165,7 +23346,7 @@ var program = new Command().name("iamu").description(
|
|
|
23165
23346
|
}
|
|
23166
23347
|
}
|
|
23167
23348
|
if (root.color === false || process.env.NO_COLOR) {
|
|
23168
|
-
|
|
23349
|
+
chalk38.level = 0;
|
|
23169
23350
|
}
|
|
23170
23351
|
if (root.verbose) {
|
|
23171
23352
|
process.env.IAMU_VERBOSE = "1";
|