@phamvuhoang/otto-core 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-help.d.ts +6 -0
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +10 -0
- package/dist/cli-help.js.map +1 -1
- package/dist/eval.d.ts +20 -1
- package/dist/eval.d.ts.map +1 -1
- package/dist/eval.js +17 -1
- package/dist/eval.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +13 -4
- package/dist/inspect.js.map +1 -1
- package/dist/loop.d.ts.map +1 -1
- package/dist/loop.js +23 -1
- package/dist/loop.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/plan-checkpoint.d.ts +47 -0
- package/dist/plan-checkpoint.d.ts.map +1 -0
- package/dist/plan-checkpoint.js +54 -0
- package/dist/plan-checkpoint.js.map +1 -0
- package/dist/plan-gate.d.ts +41 -0
- package/dist/plan-gate.d.ts.map +1 -0
- package/dist/plan-gate.js +46 -0
- package/dist/plan-gate.js.map +1 -0
- package/dist/plan-report-cli.d.ts +42 -0
- package/dist/plan-report-cli.d.ts.map +1 -0
- package/dist/plan-report-cli.js +75 -0
- package/dist/plan-report-cli.js.map +1 -0
- package/dist/plan-rubric.d.ts +68 -0
- package/dist/plan-rubric.d.ts.map +1 -0
- package/dist/plan-rubric.js +119 -0
- package/dist/plan-rubric.js.map +1 -0
- package/dist/report-explain.d.ts +33 -0
- package/dist/report-explain.d.ts.map +1 -0
- package/dist/report-explain.js +85 -0
- package/dist/report-explain.js.map +1 -0
- package/dist/report-rubric.d.ts +68 -0
- package/dist/report-rubric.d.ts.map +1 -0
- package/dist/report-rubric.js +107 -0
- package/dist/report-rubric.js.map +1 -0
- package/dist/run-bin.d.ts +2 -0
- package/dist/run-bin.d.ts.map +1 -1
- package/dist/run-bin.js +39 -17
- package/dist/run-bin.js.map +1 -1
- package/dist/run-report.d.ts +20 -0
- package/dist/run-report.d.ts.map +1 -1
- package/dist/run-report.js +41 -0
- package/dist/run-report.js.map +1 -1
- package/dist/stages.d.ts +5 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +7 -0
- package/dist/stages.js.map +1 -1
- package/package.json +1 -1
- package/templates/plan.md +95 -0
- package/templates/prompt.md +6 -0
- package/templates/quality-report.md +43 -7
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan-quality rubric (issue #63 P8, slice 1 — "measure plan quality before
|
|
3
|
+
* generating plans").
|
|
4
|
+
*
|
|
5
|
+
* A pure scorer over a spec/plan markdown document: it checks the document
|
|
6
|
+
* against the structural criteria of a world-class plan (the proven
|
|
7
|
+
* `docs/superpowers` + issue-62 shape) and reports per-criterion results, a
|
|
8
|
+
* met-count / max score, a 0..1 completeness ratio, and what is missing. It is
|
|
9
|
+
* the measurement substrate every later P8 slice reads — the `plan` stage,
|
|
10
|
+
* its eval signal ("plan-completeness rubric score ↑ across fixtures"), the
|
|
11
|
+
* `--plan-report` surface, and the human checkpoint.
|
|
12
|
+
*
|
|
13
|
+
* Sibling to `eval.ts`'s `scoreTrajectory` (a pure scorer over recorded data),
|
|
14
|
+
* but scoring a *document* rather than a run trajectory. INERT on the loop —
|
|
15
|
+
* nothing here runs a stage or changes behavior; it only describes text.
|
|
16
|
+
*
|
|
17
|
+
* The detectors are deterministic header/keyword heuristics (no tokenizer, no
|
|
18
|
+
* model call): the rubric judges *structural completeness* (does the plan have
|
|
19
|
+
* the sections a good plan has), the orthogonal question to the *semantic*
|
|
20
|
+
* quality a human/model judges at the checkpoint. Heuristic and labelled as
|
|
21
|
+
* such, mirroring P7's `ceil(chars/4)` estimate discipline.
|
|
22
|
+
*/
|
|
23
|
+
/** Count non-overlapping matches of a global regex in the document. */
|
|
24
|
+
function count(doc, re) {
|
|
25
|
+
return (doc.match(re) ?? []).length;
|
|
26
|
+
}
|
|
27
|
+
// Path-like inline code: a backticked token with a path separator or a known
|
|
28
|
+
// source-file extension — the signal a plan names the files it will touch.
|
|
29
|
+
const PATH_TOKEN_RE = /`[^`\n]*(?:\/[^`\n]*|\.(?:ts|tsx|js|jsx|mjs|cjs|md|json|py|go|rs|java|rb|sh))`/gi;
|
|
30
|
+
// Task list items: markdown checkboxes or top-of-line ordered items.
|
|
31
|
+
const CHECKBOX_RE = /(?:^|\n)\s*[-*]\s*\[[ xX]\]/g;
|
|
32
|
+
const ORDERED_ITEM_RE = /(?:^|\n)\s*\d+\.\s+\S/g;
|
|
33
|
+
/**
|
|
34
|
+
* The rubric criteria, in scorecard order. Each detector keys off the proven
|
|
35
|
+
* plan shape: a section header or the characteristic keywords/markup of that
|
|
36
|
+
* section. Order is fixed so the scorecard and the `missing` list are stable.
|
|
37
|
+
*/
|
|
38
|
+
export const PLAN_CRITERIA = [
|
|
39
|
+
{
|
|
40
|
+
criterion: "problem",
|
|
41
|
+
label: "Problem statement",
|
|
42
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+[^\n]*\bproblem\b/i.test(d),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
criterion: "decisions",
|
|
46
|
+
label: "Decisions / assumptions",
|
|
47
|
+
detect: (d) => /\b(?:assumptions?|decisions?|rationale)\b/i.test(d),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
criterion: "scopeGuard",
|
|
51
|
+
label: "Scope guard / non-goals",
|
|
52
|
+
detect: (d) => /\b(?:scope guard|non-?goals?|out of scope|not in scope|won'?t (?:do|build|change|ship))\b/i.test(d),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
criterion: "fileMap",
|
|
56
|
+
label: "File / component map",
|
|
57
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+[^\n]*\b(?:file (?:map|structure)|component map|files)\b/i.test(d) || count(d, PATH_TOKEN_RE) >= 2,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
criterion: "taskBreakdown",
|
|
61
|
+
label: "Task breakdown",
|
|
62
|
+
detect: (d) => count(d, CHECKBOX_RE) >= 2 ||
|
|
63
|
+
count(d, ORDERED_ITEM_RE) >= 2 ||
|
|
64
|
+
count(d, /(?:^|\n)#{1,6}\s+task\b/gi) >= 2,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
criterion: "testFirst",
|
|
68
|
+
label: "Failing-test-first (TDD)",
|
|
69
|
+
detect: (d) => /\b(?:failing test|test[- ]first|tdd|red[- ]green|write (?:a |the )?(?:failing )?test|tests? (?:first|before)|pinned by)\b/i.test(d),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
criterion: "verifyCommands",
|
|
73
|
+
label: "Explicit verify commands",
|
|
74
|
+
detect: (d) => /\bverify:/i.test(d) ||
|
|
75
|
+
(/\bverif(?:y|ied|ication)\b/i.test(d) &&
|
|
76
|
+
/\b(?:pnpm|npm run|vitest|tsc|node --test|node --|pytest|go test|cargo test|make)\b/i.test(d)),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
criterion: "successCriteria",
|
|
80
|
+
label: "Testable success criteria",
|
|
81
|
+
detect: (d) => /\b(?:success (?:criteria|metric)|acceptance (?:criteria|checklist|test)|done when|testing notes|testable)\b/i.test(d),
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* Score a spec/plan markdown document against {@link PLAN_CRITERIA}. Pure and
|
|
86
|
+
* deterministic — pass the spec text, the plan text, or both concatenated. Each
|
|
87
|
+
* criterion contributes equally; the per-criterion breakdown lets a consumer
|
|
88
|
+
* reweight later without changing the scorer.
|
|
89
|
+
*/
|
|
90
|
+
export function scorePlanQuality(doc) {
|
|
91
|
+
const results = PLAN_CRITERIA.map((c) => ({
|
|
92
|
+
criterion: c.criterion,
|
|
93
|
+
label: c.label,
|
|
94
|
+
met: c.detect(doc),
|
|
95
|
+
}));
|
|
96
|
+
const metCount = results.reduce((n, r) => n + (r.met ? 1 : 0), 0);
|
|
97
|
+
const maxScore = results.length;
|
|
98
|
+
return {
|
|
99
|
+
results,
|
|
100
|
+
metCount,
|
|
101
|
+
maxScore,
|
|
102
|
+
ratio: maxScore > 0 ? metCount / maxScore : 0,
|
|
103
|
+
missing: results.filter((r) => !r.met).map((r) => r.label),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const pct = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
|
107
|
+
/**
|
|
108
|
+
* Render a rubric score as a short, human-readable scorecard: a header with the
|
|
109
|
+
* met/total and percentage, a checked/unchecked line per criterion, and a
|
|
110
|
+
* one-line "missing" note when the plan is incomplete.
|
|
111
|
+
*/
|
|
112
|
+
export function formatPlanRubric(score) {
|
|
113
|
+
const header = `plan quality: ${score.metCount}/${score.maxScore} ` +
|
|
114
|
+
`(${pct.format(score.ratio * 100)}%)`;
|
|
115
|
+
const lines = score.results.map((r) => ` [${r.met ? "x" : " "}] ${r.label}`);
|
|
116
|
+
const tail = score.missing.length > 0 ? [` missing: ${score.missing.join(", ")}`] : [];
|
|
117
|
+
return [header, ...lines, ...tail].join("\n");
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=plan-rubric.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-rubric.js","sourceRoot":"","sources":["../src/plan-rubric.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type RunManifest } from "./run-report.js";
|
|
2
|
+
/**
|
|
3
|
+
* Injectable host surface for {@link runExplain} so the reader stays
|
|
4
|
+
* unit-testable without touching the real cwd/env or process stdio. Mirrors
|
|
5
|
+
* {@link import("./inspect.js").InspectDeps}.
|
|
6
|
+
*/
|
|
7
|
+
export type ExplainDeps = {
|
|
8
|
+
env: NodeJS.ProcessEnv;
|
|
9
|
+
cwd: string;
|
|
10
|
+
out: (msg: string) => void;
|
|
11
|
+
err: (msg: string) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Render one run for a non-engineer (P9 #64): the plain-language report the run
|
|
15
|
+
* emitted, then a compact "run facts" footer (source, iterations, cost) from the
|
|
16
|
+
* manifest. Pure — takes the already-read manifest and the persisted report text
|
|
17
|
+
* (or `null` when the run emitted none), returns the report string.
|
|
18
|
+
*
|
|
19
|
+
* The persisted report already leads with the layperson prose (What changed /
|
|
20
|
+
* Why / How to verify / What to watch) and keeps engineer detail below its own
|
|
21
|
+
* divider, so this surface is mostly the report plus the run's bottom-line facts.
|
|
22
|
+
* When no report was persisted (an older run, or plan/PRD `afk` mode, which emits
|
|
23
|
+
* none), it falls back to the facts with a one-line note explaining the absence.
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatPlainReport(manifest: RunManifest, reportText: string | null): string;
|
|
26
|
+
/**
|
|
27
|
+
* Drive the `otto-explain` command: resolve a run id (an explicit id, or
|
|
28
|
+
* `latest`/no arg → the most recent run under `.otto/runs/`), read its bundle,
|
|
29
|
+
* and print the plain-language report for a non-engineer. Resolves to the
|
|
30
|
+
* process exit code. Mirrors {@link import("./inspect.js").runInspect}.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runExplain(argv: string[], deps?: ExplainDeps): Promise<number>;
|
|
33
|
+
//# sourceMappingURL=report-explain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-explain.d.ts","sourceRoot":"","sources":["../src/report-explain.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAEzB;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,CAAC;AAWF;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,CA+BR;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAmCjB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { listRunIds, readManifest, readRunReport, } from "./run-report.js";
|
|
3
|
+
const defaultDeps = {
|
|
4
|
+
env: process.env,
|
|
5
|
+
cwd: process.cwd(),
|
|
6
|
+
out: (m) => process.stdout.write(`${m}\n`),
|
|
7
|
+
err: (m) => process.stderr.write(`${m}\n`),
|
|
8
|
+
};
|
|
9
|
+
const USAGE = "Usage: otto-explain [<run-id>|latest]";
|
|
10
|
+
/**
|
|
11
|
+
* Render one run for a non-engineer (P9 #64): the plain-language report the run
|
|
12
|
+
* emitted, then a compact "run facts" footer (source, iterations, cost) from the
|
|
13
|
+
* manifest. Pure — takes the already-read manifest and the persisted report text
|
|
14
|
+
* (or `null` when the run emitted none), returns the report string.
|
|
15
|
+
*
|
|
16
|
+
* The persisted report already leads with the layperson prose (What changed /
|
|
17
|
+
* Why / How to verify / What to watch) and keeps engineer detail below its own
|
|
18
|
+
* divider, so this surface is mostly the report plus the run's bottom-line facts.
|
|
19
|
+
* When no report was persisted (an older run, or plan/PRD `afk` mode, which emits
|
|
20
|
+
* none), it falls back to the facts with a one-line note explaining the absence.
|
|
21
|
+
*/
|
|
22
|
+
export function formatPlainReport(manifest, reportText) {
|
|
23
|
+
const completed = manifest.completedIterations != null
|
|
24
|
+
? `${manifest.completedIterations} of ${manifest.iterations}`
|
|
25
|
+
: `${manifest.iterations}`;
|
|
26
|
+
const facts = [];
|
|
27
|
+
facts.push("— Run facts ————————————————————————————");
|
|
28
|
+
facts.push(` What ran: ${manifest.bin} (${manifest.mode} mode)`);
|
|
29
|
+
facts.push(` Asked to do: ${manifest.inputs || "(no inputs)"}`);
|
|
30
|
+
facts.push(` Effort: ${completed} iterations · $${manifest.costUsd.toFixed(2)}`);
|
|
31
|
+
if (manifest.exitReason) {
|
|
32
|
+
facts.push(` Outcome: ${manifest.exitReason}`);
|
|
33
|
+
}
|
|
34
|
+
const lines = [];
|
|
35
|
+
if (reportText) {
|
|
36
|
+
lines.push(reportText.trimEnd());
|
|
37
|
+
lines.push("");
|
|
38
|
+
lines.push(...facts);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
lines.push(`Run ${manifest.runId}`);
|
|
42
|
+
lines.push("");
|
|
43
|
+
lines.push("This run didn't emit a plain-language report — it's an older run, or a " +
|
|
44
|
+
"plan/PRD (afk) run, which doesn't produce one yet. The run facts:");
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push(...facts);
|
|
47
|
+
}
|
|
48
|
+
return lines.join("\n");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Drive the `otto-explain` command: resolve a run id (an explicit id, or
|
|
52
|
+
* `latest`/no arg → the most recent run under `.otto/runs/`), read its bundle,
|
|
53
|
+
* and print the plain-language report for a non-engineer. Resolves to the
|
|
54
|
+
* process exit code. Mirrors {@link import("./inspect.js").runInspect}.
|
|
55
|
+
*/
|
|
56
|
+
export async function runExplain(argv, deps = defaultDeps) {
|
|
57
|
+
const arg = argv[0];
|
|
58
|
+
if (arg === "-h" || arg === "--help") {
|
|
59
|
+
deps.out(USAGE);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const workspaceDir = resolve(deps.env.OTTO_WORKSPACE ?? deps.cwd);
|
|
63
|
+
let runId;
|
|
64
|
+
if (arg && arg !== "latest") {
|
|
65
|
+
runId = arg;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const ids = listRunIds(workspaceDir);
|
|
69
|
+
if (ids.length === 0) {
|
|
70
|
+
deps.err(`No runs found under ${workspaceDir}/.otto/runs/. ` +
|
|
71
|
+
"Run Otto first, then explain the bundle it writes.");
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
runId = ids[ids.length - 1];
|
|
75
|
+
}
|
|
76
|
+
const manifest = readManifest(workspaceDir, runId);
|
|
77
|
+
if (!manifest) {
|
|
78
|
+
deps.err(`No manifest for run '${runId}' under ${workspaceDir}/.otto/runs/. ` +
|
|
79
|
+
"Check the run id (or pass `latest`).");
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
deps.out(formatPlainReport(manifest, readRunReport(workspaceDir, runId)));
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=report-explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-explain.js","sourceRoot":"","sources":["../src/report-explain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report-legibility rubric (issue #64 P9, slice 3 — "report-legibility rubric
|
|
3
|
+
* + eval signal").
|
|
4
|
+
*
|
|
5
|
+
* A pure scorer over a quality-report markdown document: it checks the document
|
|
6
|
+
* against the structural criteria of the P9 layperson-first contract shape
|
|
7
|
+
* (quality-report.md) and reports per-criterion results, a met-count / max
|
|
8
|
+
* score, a 0..1 legibility ratio, and what is missing. It is the measurement
|
|
9
|
+
* substrate for the P9 success metric ("% of reports a non-engineer understood
|
|
10
|
+
* without reading code").
|
|
11
|
+
*
|
|
12
|
+
* Sibling to `plan-rubric.ts` (which scores a spec/plan document) and to
|
|
13
|
+
* `eval.ts`'s `scoreTrajectory` (which scores a run trajectory). INERT on the
|
|
14
|
+
* loop — nothing here runs a stage or changes behaviour; it only describes text.
|
|
15
|
+
*
|
|
16
|
+
* The detectors are deterministic header/keyword heuristics (no tokenizer, no
|
|
17
|
+
* model call): the rubric judges *structural legibility* (does the report have
|
|
18
|
+
* the layperson sections the P9 contract requires), the orthogonal question to
|
|
19
|
+
* the *semantic* quality a human judges at review. Heuristic and labelled as
|
|
20
|
+
* such, mirroring plan-rubric.ts's discipline.
|
|
21
|
+
*/
|
|
22
|
+
export type ReportCriterion = "verdict" | "whatChanged" | "why" | "howToVerify" | "whatToWatch" | "uncertainty" | "engineerDivider";
|
|
23
|
+
type CriterionDef = {
|
|
24
|
+
criterion: ReportCriterion;
|
|
25
|
+
/** Human-readable label for the scorecard. */
|
|
26
|
+
label: string;
|
|
27
|
+
/** Pure predicate: does the document satisfy this criterion? */
|
|
28
|
+
detect: (doc: string) => boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The rubric criteria, in scorecard order. Each detector keys off the proven
|
|
32
|
+
* P9 quality-report shape: a section header or the characteristic
|
|
33
|
+
* keywords/markup of that section. Order is fixed so the scorecard and the
|
|
34
|
+
* `missing` list are stable.
|
|
35
|
+
*/
|
|
36
|
+
export declare const REPORT_CRITERIA: ReadonlyArray<CriterionDef>;
|
|
37
|
+
export type ReportCriterionResult = {
|
|
38
|
+
criterion: ReportCriterion;
|
|
39
|
+
label: string;
|
|
40
|
+
met: boolean;
|
|
41
|
+
};
|
|
42
|
+
export type ReportRubricScore = {
|
|
43
|
+
/** Per-criterion outcome, in fixed scorecard order. */
|
|
44
|
+
results: ReportCriterionResult[];
|
|
45
|
+
/** Criteria met. */
|
|
46
|
+
metCount: number;
|
|
47
|
+
/** Total criteria (the denominator). */
|
|
48
|
+
maxScore: number;
|
|
49
|
+
/** metCount / maxScore in 0..1; 0 when there are no criteria. */
|
|
50
|
+
ratio: number;
|
|
51
|
+
/** Labels of unmet criteria — the "what to improve" surface. */
|
|
52
|
+
missing: string[];
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Score a quality-report markdown document against {@link REPORT_CRITERIA}.
|
|
56
|
+
* Pure and deterministic — pass the full report text. Each criterion
|
|
57
|
+
* contributes equally; the per-criterion breakdown lets a consumer reweight
|
|
58
|
+
* later without changing the scorer.
|
|
59
|
+
*/
|
|
60
|
+
export declare function scoreReportLegibility(doc: string): ReportRubricScore;
|
|
61
|
+
/**
|
|
62
|
+
* Render a rubric score as a short, human-readable scorecard: a header with
|
|
63
|
+
* the met/total and percentage, a checked/unchecked line per criterion, and a
|
|
64
|
+
* one-line "missing" note when the report is incomplete.
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatReportRubric(score: ReportRubricScore): string;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=report-rubric.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-rubric.d.ts","sourceRoot":"","sources":["../src/report-rubric.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,aAAa,GACb,KAAK,GACL,aAAa,GACb,aAAa,GACb,aAAa,GACb,iBAAiB,CAAC;AAEtB,KAAK,YAAY,GAAG;IAClB,SAAS,EAAE,eAAe,CAAC;IAC3B,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAClC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,YAAY,CA0CvD,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uDAAuD;IACvD,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAepE;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAUnE"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report-legibility rubric (issue #64 P9, slice 3 — "report-legibility rubric
|
|
3
|
+
* + eval signal").
|
|
4
|
+
*
|
|
5
|
+
* A pure scorer over a quality-report markdown document: it checks the document
|
|
6
|
+
* against the structural criteria of the P9 layperson-first contract shape
|
|
7
|
+
* (quality-report.md) and reports per-criterion results, a met-count / max
|
|
8
|
+
* score, a 0..1 legibility ratio, and what is missing. It is the measurement
|
|
9
|
+
* substrate for the P9 success metric ("% of reports a non-engineer understood
|
|
10
|
+
* without reading code").
|
|
11
|
+
*
|
|
12
|
+
* Sibling to `plan-rubric.ts` (which scores a spec/plan document) and to
|
|
13
|
+
* `eval.ts`'s `scoreTrajectory` (which scores a run trajectory). INERT on the
|
|
14
|
+
* loop — nothing here runs a stage or changes behaviour; it only describes text.
|
|
15
|
+
*
|
|
16
|
+
* The detectors are deterministic header/keyword heuristics (no tokenizer, no
|
|
17
|
+
* model call): the rubric judges *structural legibility* (does the report have
|
|
18
|
+
* the layperson sections the P9 contract requires), the orthogonal question to
|
|
19
|
+
* the *semantic* quality a human judges at review. Heuristic and labelled as
|
|
20
|
+
* such, mirroring plan-rubric.ts's discipline.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The rubric criteria, in scorecard order. Each detector keys off the proven
|
|
24
|
+
* P9 quality-report shape: a section header or the characteristic
|
|
25
|
+
* keywords/markup of that section. Order is fixed so the scorecard and the
|
|
26
|
+
* `missing` list are stable.
|
|
27
|
+
*/
|
|
28
|
+
export const REPORT_CRITERIA = [
|
|
29
|
+
{
|
|
30
|
+
criterion: "verdict",
|
|
31
|
+
label: "Verdict section",
|
|
32
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+Verdict\b/i.test(d),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
criterion: "whatChanged",
|
|
36
|
+
label: "What Changed section",
|
|
37
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+What Changed\b/i.test(d),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
criterion: "why",
|
|
41
|
+
label: "Why section",
|
|
42
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+Why\b/i.test(d),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
criterion: "howToVerify",
|
|
46
|
+
label: "How To Verify section with numbered step",
|
|
47
|
+
detect: (d) => {
|
|
48
|
+
// Require BOTH the section heading AND at least one numbered step after it.
|
|
49
|
+
const headingMatch = /(?:^|\n)(#{1,6}\s+How To Verify\b)/i.exec(d);
|
|
50
|
+
if (!headingMatch)
|
|
51
|
+
return false;
|
|
52
|
+
const afterHeading = d.slice(headingMatch.index + headingMatch[0].length);
|
|
53
|
+
return /^\s*\d+\./m.test(afterHeading);
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
criterion: "whatToWatch",
|
|
58
|
+
label: "What To Watch section",
|
|
59
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+What To Watch\b/i.test(d),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
criterion: "uncertainty",
|
|
63
|
+
label: "What I Was Unsure About section",
|
|
64
|
+
detect: (d) => /(?:^|\n)#{1,6}\s+What I Was Unsure About\b/i.test(d),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
criterion: "engineerDivider",
|
|
68
|
+
label: "Engineer detail divider",
|
|
69
|
+
detect: (d) => /Engineer detail below/i.test(d),
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* Score a quality-report markdown document against {@link REPORT_CRITERIA}.
|
|
74
|
+
* Pure and deterministic — pass the full report text. Each criterion
|
|
75
|
+
* contributes equally; the per-criterion breakdown lets a consumer reweight
|
|
76
|
+
* later without changing the scorer.
|
|
77
|
+
*/
|
|
78
|
+
export function scoreReportLegibility(doc) {
|
|
79
|
+
const results = REPORT_CRITERIA.map((c) => ({
|
|
80
|
+
criterion: c.criterion,
|
|
81
|
+
label: c.label,
|
|
82
|
+
met: c.detect(doc),
|
|
83
|
+
}));
|
|
84
|
+
const metCount = results.reduce((n, r) => n + (r.met ? 1 : 0), 0);
|
|
85
|
+
const maxScore = results.length;
|
|
86
|
+
return {
|
|
87
|
+
results,
|
|
88
|
+
metCount,
|
|
89
|
+
maxScore,
|
|
90
|
+
ratio: maxScore > 0 ? metCount / maxScore : 0,
|
|
91
|
+
missing: results.filter((r) => !r.met).map((r) => r.label),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const pct = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
|
95
|
+
/**
|
|
96
|
+
* Render a rubric score as a short, human-readable scorecard: a header with
|
|
97
|
+
* the met/total and percentage, a checked/unchecked line per criterion, and a
|
|
98
|
+
* one-line "missing" note when the report is incomplete.
|
|
99
|
+
*/
|
|
100
|
+
export function formatReportRubric(score) {
|
|
101
|
+
const header = `report legibility: ${score.metCount}/${score.maxScore} ` +
|
|
102
|
+
`(${pct.format(score.ratio * 100)}%)`;
|
|
103
|
+
const lines = score.results.map((r) => ` [${r.met ? "x" : " "}] ${r.label}`);
|
|
104
|
+
const tail = score.missing.length > 0 ? [` missing: ${score.missing.join(", ")}`] : [];
|
|
105
|
+
return [header, ...lines, ...tail].join("\n");
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=report-rubric.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-rubric.js","sourceRoot":"","sources":["../src/report-rubric.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAmBH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAgC;IAC1D;QACE,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,iBAAiB;QACxB,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC;KACrD;IACD;QACE,SAAS,EAAE,aAAa;QACxB,KAAK,EAAE,sBAAsB;QAC7B,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC;KAC1D;IACD;QACE,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,aAAa;QACpB,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;KACjD;IACD;QACE,SAAS,EAAE,aAAa;QACxB,KAAK,EAAE,0CAA0C;QACjD,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,4EAA4E;YAC5E,MAAM,YAAY,GAAG,qCAAqC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,YAAY;gBAAE,OAAO,KAAK,CAAC;YAChC,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC1E,OAAO,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzC,CAAC;KACF;IACD;QACE,SAAS,EAAE,aAAa;QACxB,KAAK,EAAE,uBAAuB;QAC9B,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,mCAAmC,CAAC,IAAI,CAAC,CAAC,CAAC;KAC3D;IACD;QACE,SAAS,EAAE,aAAa;QACxB,KAAK,EAAE,iCAAiC;QACxC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,6CAA6C,CAAC,IAAI,CAAC,CAAC,CAAC;KACrE;IACD;QACE,SAAS,EAAE,iBAAiB;QAC5B,KAAK,EAAE,yBAAyB;QAChC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;KAChD;CACF,CAAC;AAqBF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,OAAO,GAA4B,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;KACnB,CAAC,CAAC,CAAC;IACJ,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAChC,OAAO;QACL,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;KAC3D,CAAC;AACJ,CAAC;AAED,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAwB;IACzD,MAAM,MAAM,GACV,sBAAsB,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,GAAG;QACzD,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAC7B,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,EAAE,CAC7C,CAAC;IACF,MAAM,IAAI,GACR,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,OAAO,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/run-bin.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export type RunBinConfig = {
|
|
|
45
45
|
issueStage?: Stage;
|
|
46
46
|
/** Single read-only gate stage used when --verify is set. Only otto-afk sets this. */
|
|
47
47
|
verifyStage?: Stage;
|
|
48
|
+
/** Single authoring gate stage used when --plan is set (#63 P8). Only otto-afk sets this. */
|
|
49
|
+
planStage?: Stage;
|
|
48
50
|
/** Gate stage used when --apply-review is set. Only otto-afk sets this. */
|
|
49
51
|
applyReviewStage?: Stage;
|
|
50
52
|
/** Run mode identifier threaded into runLoop state (e.g. "afk" / "ghafk"). */
|
package/dist/run-bin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-bin.d.ts","sourceRoot":"","sources":["../src/run-bin.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG;IACzB,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,iEAAiE;IACjE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,sFAAsF;IACtF,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;CAC/C,CAAC;AA2BF;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"run-bin.d.ts","sourceRoot":"","sources":["../src/run-bin.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG;IACzB,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,iEAAiE;IACjE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,sFAAsF;IACtF,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,6FAA6F;IAC7F,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;CAC/C,CAAC;AA2BF;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAskB7E"}
|
package/dist/run-bin.js
CHANGED
|
@@ -65,6 +65,16 @@ export async function runBin(argv, cfg) {
|
|
|
65
65
|
process.exit(code);
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
+
// --plan-report (issue #63 P8): a read-only surface scoring the authored plans
|
|
69
|
+
// under .otto/tasks/ with the plan-quality rubric. Like --context-report it
|
|
70
|
+
// prints and exits without running a stage.
|
|
71
|
+
if (flags.planReport) {
|
|
72
|
+
const { runPlanReport } = await import("./plan-report-cli.js");
|
|
73
|
+
const code = await runPlanReport();
|
|
74
|
+
if (code !== 0)
|
|
75
|
+
process.exit(code);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
68
78
|
const envMaxWait = process.env.OTTO_MAX_WAIT?.trim();
|
|
69
79
|
const maxWaitMs = flags.maxWaitMs ?? (envMaxWait ? parseDurationMs(envMaxWait) : undefined);
|
|
70
80
|
let tokenMode = flags.tokenMode ?? "off";
|
|
@@ -157,9 +167,11 @@ export async function runBin(argv, cfg) {
|
|
|
157
167
|
// the selected mode. Depends only on flags, not on the guards below.
|
|
158
168
|
const runMode = flags.verify
|
|
159
169
|
? "verify"
|
|
160
|
-
: flags.
|
|
161
|
-
? "
|
|
162
|
-
:
|
|
170
|
+
: flags.plan
|
|
171
|
+
? "plan"
|
|
172
|
+
: flags.applyReview != null
|
|
173
|
+
? "review"
|
|
174
|
+
: cfg.mode;
|
|
163
175
|
// Single source of truth for the --watch label: the per-mode resolver
|
|
164
176
|
// (otto-linear-afk → OTTO_LINEAR_LABEL) falling back to OTTO_WATCH_LABEL.
|
|
165
177
|
// Both --print-config and the runWatch call below read this, so the reported
|
|
@@ -362,16 +374,21 @@ export async function runBin(argv, cfg) {
|
|
|
362
374
|
}
|
|
363
375
|
const modeCount = (flags.issue != null ? 1 : 0) +
|
|
364
376
|
(flags.verify ? 1 : 0) +
|
|
377
|
+
(flags.plan ? 1 : 0) +
|
|
365
378
|
(flags.applyReview != null ? 1 : 0) +
|
|
366
379
|
(flags.watch ? 1 : 0);
|
|
367
380
|
if (modeCount > 1) {
|
|
368
|
-
console.error("--issue, --verify, --apply-review, and --watch are mutually exclusive");
|
|
381
|
+
console.error("--issue, --verify, --plan, --apply-review, and --watch are mutually exclusive");
|
|
369
382
|
process.exit(1);
|
|
370
383
|
}
|
|
371
384
|
if (flags.verify && !cfg.verifyStage) {
|
|
372
385
|
console.error("--verify is only supported by otto-afk");
|
|
373
386
|
process.exit(1);
|
|
374
387
|
}
|
|
388
|
+
if (flags.plan && !cfg.planStage) {
|
|
389
|
+
console.error("--plan is only supported by otto-afk");
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
375
392
|
if (flags.applyReview != null && !cfg.applyReviewStage) {
|
|
376
393
|
console.error("--apply-review is only supported by otto-afk");
|
|
377
394
|
process.exit(1);
|
|
@@ -390,21 +407,24 @@ export async function runBin(argv, cfg) {
|
|
|
390
407
|
: cfg.takesInputArg
|
|
391
408
|
? flags.rest[1]
|
|
392
409
|
: flags.rest[0];
|
|
393
|
-
|
|
394
|
-
|
|
410
|
+
// --verify and --plan are both one-shot, input-taking gates (no iterations arg).
|
|
411
|
+
const oneShot = flags.verify || flags.plan;
|
|
412
|
+
const oneShotFlag = flags.verify ? "--verify" : "--plan";
|
|
413
|
+
if (oneShot && (!cfg.takesInputArg || !inputs)) {
|
|
414
|
+
console.error(`Usage: ${cfg.bin} ${oneShotFlag} "<plan-and-prd>"`);
|
|
395
415
|
process.exit(1);
|
|
396
416
|
}
|
|
397
|
-
if (!
|
|
417
|
+
if (!oneShot && ((cfg.takesInputArg && !inputs) || !iterationsArg)) {
|
|
398
418
|
console.error(`Usage: ${cfg.bin} ${cfg.usage}`);
|
|
399
419
|
console.error(` ${cfg.bin} --help`);
|
|
400
420
|
process.exit(1);
|
|
401
421
|
}
|
|
402
|
-
//
|
|
403
|
-
if (
|
|
404
|
-
console.error(
|
|
422
|
+
// One-shot regardless of any positional count.
|
|
423
|
+
if (oneShot && iterationsArg) {
|
|
424
|
+
console.error(`${oneShotFlag} is one-shot; ignoring the iterations argument`);
|
|
405
425
|
}
|
|
406
|
-
const iterations =
|
|
407
|
-
if (!
|
|
426
|
+
const iterations = oneShot ? 1 : Number.parseInt(iterationsArg, 10);
|
|
427
|
+
if (!oneShot && (!Number.isFinite(iterations) || iterations < 1)) {
|
|
408
428
|
console.error(`Invalid iterations: ${iterationsArg}`);
|
|
409
429
|
process.exit(1);
|
|
410
430
|
}
|
|
@@ -421,11 +441,13 @@ export async function runBin(argv, cfg) {
|
|
|
421
441
|
}
|
|
422
442
|
const stages = flags.verify
|
|
423
443
|
? [cfg.verifyStage]
|
|
424
|
-
: flags.
|
|
425
|
-
? [cfg.
|
|
426
|
-
: flags.
|
|
427
|
-
? [cfg.
|
|
428
|
-
:
|
|
444
|
+
: flags.plan
|
|
445
|
+
? [cfg.planStage]
|
|
446
|
+
: flags.applyReview != null
|
|
447
|
+
? [cfg.applyReviewStage, ...cfg.stages.slice(1)]
|
|
448
|
+
: flags.issue != null
|
|
449
|
+
? [cfg.issueStage, ...cfg.stages.slice(1)]
|
|
450
|
+
: cfg.stages;
|
|
429
451
|
if (flags.detach && detachLogPath) {
|
|
430
452
|
detachAndExit({
|
|
431
453
|
logPath: detachLogPath,
|