@webappwiz/cli 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -39
- package/{index-6km6e845.js → index-g81gg9gz.js} +123 -32
- package/{index-3p0t2exn.js → index-htwb54c6.js} +5 -7
- package/index-pyjg1rtk.js +373 -0
- package/index.js +10 -43
- package/judge.d.ts +12 -48
- package/judge.js +2 -4
- package/mode.d.ts +4 -6
- package/package.json +4 -8
- package/progress.d.ts +64 -0
- package/report.d.ts +14 -30
- package/rules.d.ts +0 -10
- package/rules.js +2 -4
- package/webappwiz.js +10 -43
- package/cost.d.ts +0 -45
- package/index-3k3rtw49.js +0 -128
- package/index-jbrs6gqt.js +0 -418
- package/signoff.d.ts +0 -63
- package/signoff.js +0 -7
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// judge.ts
|
|
2
|
+
import {
|
|
3
|
+
agentCommand,
|
|
4
|
+
Files,
|
|
5
|
+
Harness,
|
|
6
|
+
prompt as reviewPrompt
|
|
7
|
+
} from "@webappwiz/rules";
|
|
8
|
+
import { ConsoleLogger, color as color4 } from "webappwiz/log";
|
|
9
|
+
import {
|
|
10
|
+
NodeFs,
|
|
11
|
+
NodeGlob,
|
|
12
|
+
NodePs as NodePs2
|
|
13
|
+
} from "webappwiz/system";
|
|
14
|
+
import { SystemClock } from "webappwiz/time";
|
|
15
|
+
|
|
16
|
+
// changed.ts
|
|
17
|
+
import { NodePs } from "webappwiz/system";
|
|
18
|
+
async function changed(dir, ref, opts = {}) {
|
|
19
|
+
const ps = opts.ps ?? new NodePs;
|
|
20
|
+
const files = new Set;
|
|
21
|
+
for (const argv of [
|
|
22
|
+
["diff", "--name-only", "--diff-filter=d", "--relative", ref],
|
|
23
|
+
["ls-files", "--others", "--exclude-standard"]
|
|
24
|
+
]) {
|
|
25
|
+
for (const line of (await git(ps, dir, argv)).split(`
|
|
26
|
+
`)) {
|
|
27
|
+
if (line !== "") {
|
|
28
|
+
files.add(line);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return files;
|
|
33
|
+
}
|
|
34
|
+
async function git(ps, dir, argv) {
|
|
35
|
+
const { exitCode, stdout, stderr } = await ps.spawnCapture([
|
|
36
|
+
"git",
|
|
37
|
+
"-C",
|
|
38
|
+
dir,
|
|
39
|
+
...argv
|
|
40
|
+
]);
|
|
41
|
+
if (exitCode !== 0) {
|
|
42
|
+
throw new Error(`git ${argv[0]} failed in ${dir}: ${stderr.trim() || `exit ${exitCode}`}`);
|
|
43
|
+
}
|
|
44
|
+
return stdout;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// mode.ts
|
|
48
|
+
function mode({ print, agent, exec }) {
|
|
49
|
+
const named = [
|
|
50
|
+
print === true ? "--print" : undefined,
|
|
51
|
+
agent === undefined ? undefined : "--agent",
|
|
52
|
+
exec === undefined ? undefined : "--exec"
|
|
53
|
+
].filter((flag) => flag !== undefined);
|
|
54
|
+
if (named.length > 1) {
|
|
55
|
+
throw new Error(`${named.join(" and ")} are different things to do with one run, so pass one`);
|
|
56
|
+
}
|
|
57
|
+
return print === true ? "print" : "run";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// progress.ts
|
|
61
|
+
import { color as color3 } from "webappwiz/log";
|
|
62
|
+
import { Duration, SystemTimer } from "webappwiz/time";
|
|
63
|
+
|
|
64
|
+
// report.ts
|
|
65
|
+
import { color as color2 } from "webappwiz/log";
|
|
66
|
+
|
|
67
|
+
// table.ts
|
|
68
|
+
import { color } from "webappwiz/log";
|
|
69
|
+
var table = (rows) => {
|
|
70
|
+
const width = (cell) => color.strip(cell).length;
|
|
71
|
+
const widths = rows[0]?.map((_, i) => Math.max(...rows.map((row) => width(row[i] ?? ""))));
|
|
72
|
+
return rows.map((row) => row.map((cell, i) => cell.padEnd((widths?.[i] ?? 0) + cell.length - width(cell))).join(" ").trimEnd());
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// report.ts
|
|
76
|
+
var count = (total, word) => `${total} ${word}${total === 1 ? "" : "s"}`;
|
|
77
|
+
var divider = (name) => {
|
|
78
|
+
if (name === undefined) {
|
|
79
|
+
return color2.dim("-".repeat(72));
|
|
80
|
+
}
|
|
81
|
+
const opening = `--- ${name} `;
|
|
82
|
+
return color2.dim(opening.padEnd(Math.max(72, opening.length + 3), "-"));
|
|
83
|
+
};
|
|
84
|
+
var compact = new Intl.NumberFormat("en", { notation: "compact" });
|
|
85
|
+
var tokens = (bytes) => Math.ceil(bytes / 4);
|
|
86
|
+
function planned({
|
|
87
|
+
files,
|
|
88
|
+
rules,
|
|
89
|
+
calls,
|
|
90
|
+
estimate,
|
|
91
|
+
concurrency,
|
|
92
|
+
agent
|
|
93
|
+
}) {
|
|
94
|
+
const rows = [
|
|
95
|
+
[color2.dim("files"), String(files)],
|
|
96
|
+
[color2.dim("rules"), String(rules)],
|
|
97
|
+
[color2.dim("calls"), String(calls)],
|
|
98
|
+
...concurrency === undefined ? [] : [[color2.dim("workers"), String(concurrency)]],
|
|
99
|
+
[color2.dim("reading"), `${compact.format(estimate)}+ tokens`]
|
|
100
|
+
];
|
|
101
|
+
if (agent !== undefined) {
|
|
102
|
+
rows.push([color2.dim("agent"), agent]);
|
|
103
|
+
}
|
|
104
|
+
return ["", ...table(rows).map((line) => ` ${line}`), ""];
|
|
105
|
+
}
|
|
106
|
+
function finished({
|
|
107
|
+
rules,
|
|
108
|
+
files,
|
|
109
|
+
violations,
|
|
110
|
+
took,
|
|
111
|
+
tokens: tokens2,
|
|
112
|
+
worker,
|
|
113
|
+
workerTokens,
|
|
114
|
+
done,
|
|
115
|
+
total
|
|
116
|
+
}) {
|
|
117
|
+
const heading = `${color2.gray(`[${done}/${total}]`)} ` + color2.gray(`(${count(rules.length, "rule")}, ${count(files, "file")})`);
|
|
118
|
+
const spent = tokens2 === undefined ? "" : ` ${compact.format(tokens2)} tokens` + (workerTokens === undefined ? "" : ` (w${worker + 1}: ${compact.format(workerTokens)})`);
|
|
119
|
+
const tail = `${color2.gray(`in ${took.human()}${spent}`)}`;
|
|
120
|
+
if (violations.length === 0) {
|
|
121
|
+
return [`${color2.green("✓")} ${heading}: clean ${tail}`];
|
|
122
|
+
}
|
|
123
|
+
return [
|
|
124
|
+
`${color2.red("✗")} ${heading}: ${count(violations.length, "problem")} ${tail}`,
|
|
125
|
+
...violations.flatMap(finding)
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
function finding(violation) {
|
|
129
|
+
const level = violation.level === "error" ? color2.red("error") : color2.yellow(violation.level);
|
|
130
|
+
const lines = [
|
|
131
|
+
` ${color2.bold(`${violation.file}:${violation.line}`)} ${level} ${violation.message} ${color2.gray(`(${violation.id})`)}`
|
|
132
|
+
];
|
|
133
|
+
if (violation.code !== "") {
|
|
134
|
+
lines.push(color2.gray(` │ ${violation.code}`));
|
|
135
|
+
}
|
|
136
|
+
return lines;
|
|
137
|
+
}
|
|
138
|
+
function summary(violations, took, tokens2) {
|
|
139
|
+
const spent = tokens2 === undefined ? "" : ` ${compact.format(tokens2)} tokens total`;
|
|
140
|
+
const elapsed = color2.gray(`in ${took.human()}${spent}`);
|
|
141
|
+
if (violations.length === 0) {
|
|
142
|
+
return `${color2.green("✓ no violations")} ${elapsed}`;
|
|
143
|
+
}
|
|
144
|
+
const errors = violations.filter((violation) => violation.level === "error").length;
|
|
145
|
+
const line = `✖ ${count(violations.length, "problem")} (${count(errors, "error")}, ${count(violations.length - errors, "warning")})`;
|
|
146
|
+
return `${errors > 0 ? color2.red(line) : color2.yellow(line)} ${elapsed}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// progress.ts
|
|
150
|
+
var terminal = () => ({
|
|
151
|
+
tty: process.stdout.isTTY === true,
|
|
152
|
+
write: (text) => process.stdout.write(text)
|
|
153
|
+
});
|
|
154
|
+
var BAR = 20;
|
|
155
|
+
var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
156
|
+
function render(view, frame = 0) {
|
|
157
|
+
const spin = view.files === 0 ? " " : FRAMES[frame % FRAMES.length] ?? " ";
|
|
158
|
+
const filled = Math.round(BAR * view.done / Math.max(1, view.total));
|
|
159
|
+
const bar = color3.green("█".repeat(filled)) + color3.dim("░".repeat(BAR - filled));
|
|
160
|
+
const judging = view.files === 0 ? "" : ` · judging ${count(view.files, "file")}`;
|
|
161
|
+
const spent = view.tokens === undefined ? "" : ` · ${compact.format(view.tokens)} tokens`;
|
|
162
|
+
const found = view.done === 0 ? "" : ` · ${view.problems === 0 ? "clean so far" : count(view.problems, "problem")}`;
|
|
163
|
+
return `${color3.green(spin)} ${bar} ${color3.gray(`${view.done}/${view.total} calls${judging}${spent}${found}`)}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
class Progress {
|
|
167
|
+
screen;
|
|
168
|
+
total;
|
|
169
|
+
done = 0;
|
|
170
|
+
files = 0;
|
|
171
|
+
problems = 0;
|
|
172
|
+
tokens;
|
|
173
|
+
drawn = false;
|
|
174
|
+
frame = 0;
|
|
175
|
+
ticking;
|
|
176
|
+
constructor(screen, total, opts = {}) {
|
|
177
|
+
this.screen = screen;
|
|
178
|
+
this.total = total;
|
|
179
|
+
const timer = opts.timer ?? new SystemTimer;
|
|
180
|
+
this.ticking = timer.setInterval(() => {
|
|
181
|
+
this.frame += 1;
|
|
182
|
+
this.draw();
|
|
183
|
+
}, Duration.ms(100));
|
|
184
|
+
}
|
|
185
|
+
started(files) {
|
|
186
|
+
this.files += files;
|
|
187
|
+
this.draw();
|
|
188
|
+
}
|
|
189
|
+
finished(files, spent, problems = 0) {
|
|
190
|
+
this.files -= files;
|
|
191
|
+
this.done += 1;
|
|
192
|
+
this.problems += problems;
|
|
193
|
+
if (spent !== undefined) {
|
|
194
|
+
this.tokens = (this.tokens ?? 0) + spent;
|
|
195
|
+
}
|
|
196
|
+
this.draw();
|
|
197
|
+
}
|
|
198
|
+
stop() {
|
|
199
|
+
this.ticking.dispose();
|
|
200
|
+
this.erase();
|
|
201
|
+
}
|
|
202
|
+
erase() {
|
|
203
|
+
if (this.drawn) {
|
|
204
|
+
this.screen.write("\x1B[1A\r\x1B[0J");
|
|
205
|
+
this.drawn = false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
draw() {
|
|
209
|
+
const line = render({
|
|
210
|
+
done: this.done,
|
|
211
|
+
total: this.total,
|
|
212
|
+
files: this.files,
|
|
213
|
+
tokens: this.tokens,
|
|
214
|
+
problems: this.problems
|
|
215
|
+
}, this.frame);
|
|
216
|
+
this.erase();
|
|
217
|
+
this.screen.write(`${line}
|
|
218
|
+
`);
|
|
219
|
+
this.drawn = true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// judge.ts
|
|
224
|
+
var estimated = (reviews) => tokens(reviews.reduce((bytes, review) => bytes + review.bytes, 0));
|
|
225
|
+
var title = (rule) => /^#\s+(.+)$/m.exec(rule.document)?.[1]?.trim() ?? rule.id;
|
|
226
|
+
|
|
227
|
+
class JudgeCommands {
|
|
228
|
+
rules;
|
|
229
|
+
screen;
|
|
230
|
+
log;
|
|
231
|
+
fs;
|
|
232
|
+
ps;
|
|
233
|
+
clock;
|
|
234
|
+
glob;
|
|
235
|
+
constructor(rules, opts = {}) {
|
|
236
|
+
this.rules = rules;
|
|
237
|
+
this.screen = opts.screen ?? terminal();
|
|
238
|
+
this.log = opts.log ?? new ConsoleLogger;
|
|
239
|
+
this.fs = opts.fs ?? new NodeFs;
|
|
240
|
+
this.ps = opts.ps ?? new NodePs2;
|
|
241
|
+
this.clock = opts.clock ?? new SystemClock;
|
|
242
|
+
this.glob = opts.glob ?? new NodeGlob;
|
|
243
|
+
}
|
|
244
|
+
ls() {
|
|
245
|
+
const rows = [["id", "rule", "level", "files"].map(color4.dim)];
|
|
246
|
+
for (const rule of this.rules.rules) {
|
|
247
|
+
rows.push([rule.id, title(rule), rule.level, rule.files]);
|
|
248
|
+
}
|
|
249
|
+
this.log.info(table(rows).join(`
|
|
250
|
+
`));
|
|
251
|
+
}
|
|
252
|
+
show(opts) {
|
|
253
|
+
const all = this.rules.rules;
|
|
254
|
+
const rule = all.find((candidate) => candidate.id === opts.id);
|
|
255
|
+
if (!rule) {
|
|
256
|
+
throw new Error(`no rule "${opts.id}". Known ids: ${all.map((candidate) => candidate.id).join(", ")}`);
|
|
257
|
+
}
|
|
258
|
+
const rows = [
|
|
259
|
+
[color4.dim("id"), rule.id],
|
|
260
|
+
[color4.dim("rule"), title(rule)],
|
|
261
|
+
[color4.dim("level"), rule.level],
|
|
262
|
+
[color4.dim("files"), rule.files]
|
|
263
|
+
];
|
|
264
|
+
this.log.info(table(rows).join(`
|
|
265
|
+
`));
|
|
266
|
+
this.log.info("");
|
|
267
|
+
this.log.info(rule.document.trim());
|
|
268
|
+
}
|
|
269
|
+
async judge(opts) {
|
|
270
|
+
const how = mode(opts);
|
|
271
|
+
const config = this.rules;
|
|
272
|
+
const rules = config.rules;
|
|
273
|
+
const dir = opts.dir.replace(/\/+$/, "") || "/";
|
|
274
|
+
const only = opts.since === undefined ? undefined : await changed(dir, opts.since, { ps: this.ps });
|
|
275
|
+
if (only?.size === 0) {
|
|
276
|
+
this.log.info(`nothing has changed since ${opts.since}`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const files = new Files({ log: this.log, fs: this.fs, glob: this.glob });
|
|
280
|
+
const reviews = await files.plan(rules, dir, {
|
|
281
|
+
chunk: opts.chunk,
|
|
282
|
+
only
|
|
283
|
+
});
|
|
284
|
+
if (how === "print") {
|
|
285
|
+
for (const review of reviews) {
|
|
286
|
+
this.log.info(`
|
|
287
|
+
${divider(`${review.label} (${count(review.files.length, "file")})`)}
|
|
288
|
+
`);
|
|
289
|
+
this.log.info(reviewPrompt(review));
|
|
290
|
+
}
|
|
291
|
+
this.log.info(`
|
|
292
|
+
${divider()}`);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const read = new Set(reviews.flatMap((review) => review.files)).size;
|
|
296
|
+
const agent = this.agent(config, opts);
|
|
297
|
+
const concurrency = opts["concurrency-override"] ?? config.concurrency;
|
|
298
|
+
const started = this.clock.now();
|
|
299
|
+
this.log.info(planned({
|
|
300
|
+
files: read,
|
|
301
|
+
rules: rules.length,
|
|
302
|
+
calls: reviews.length,
|
|
303
|
+
estimate: estimated(reviews),
|
|
304
|
+
concurrency,
|
|
305
|
+
agent: agent.label
|
|
306
|
+
}).join(`
|
|
307
|
+
`));
|
|
308
|
+
const found = [];
|
|
309
|
+
const byWorker = new Map;
|
|
310
|
+
let spent;
|
|
311
|
+
const progress = opts.ci !== true && this.screen.tty ? new Progress(this.screen, reviews.length) : undefined;
|
|
312
|
+
const deferred = [];
|
|
313
|
+
const harness = new Harness({
|
|
314
|
+
log: this.log,
|
|
315
|
+
ps: this.ps,
|
|
316
|
+
clock: this.clock
|
|
317
|
+
});
|
|
318
|
+
harness.events.on("started", ({ at }) => progress?.started(reviews[at]?.files.length ?? 0));
|
|
319
|
+
harness.events.on("finished", (review) => {
|
|
320
|
+
const at = reviews[review.at];
|
|
321
|
+
if (!at) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
let workerTokens;
|
|
325
|
+
if (review.tokens !== undefined) {
|
|
326
|
+
workerTokens = (byWorker.get(review.worker) ?? 0) + review.tokens;
|
|
327
|
+
byWorker.set(review.worker, workerTokens);
|
|
328
|
+
spent = (spent ?? 0) + review.tokens;
|
|
329
|
+
}
|
|
330
|
+
const violations2 = files.violations(at, review.findings, dir);
|
|
331
|
+
found[review.at] = violations2;
|
|
332
|
+
progress?.finished(at.files.length, review.tokens, violations2.length);
|
|
333
|
+
const lines = finished({
|
|
334
|
+
rules: review.rules,
|
|
335
|
+
files: at.files.length,
|
|
336
|
+
violations: violations2,
|
|
337
|
+
took: review.took,
|
|
338
|
+
tokens: review.tokens,
|
|
339
|
+
worker: review.worker,
|
|
340
|
+
workerTokens,
|
|
341
|
+
done: review.done,
|
|
342
|
+
total: review.total
|
|
343
|
+
});
|
|
344
|
+
if (progress) {
|
|
345
|
+
deferred[review.done - 1] = lines;
|
|
346
|
+
} else {
|
|
347
|
+
for (const line of lines) {
|
|
348
|
+
this.log.info(line);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
try {
|
|
353
|
+
await harness.run(reviews, agent, { cwd: dir, concurrency });
|
|
354
|
+
} finally {
|
|
355
|
+
progress?.stop();
|
|
356
|
+
}
|
|
357
|
+
for (const line of deferred.flat()) {
|
|
358
|
+
this.log.info(line);
|
|
359
|
+
}
|
|
360
|
+
const violations = found.flat();
|
|
361
|
+
this.log.info("");
|
|
362
|
+
this.log.info(summary(violations, this.clock.now().subtract(started), spent));
|
|
363
|
+
const errors = violations.filter((violation) => violation.level === "error").length;
|
|
364
|
+
if (errors > 0) {
|
|
365
|
+
throw new Error(count(errors, "error"));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
agent(config, opts) {
|
|
369
|
+
return agentCommand(opts.exec === undefined ? { agent: opts.agent ?? config.agent } : opts);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export { table, JudgeCommands };
|
package/index.js
CHANGED
|
@@ -6,17 +6,13 @@ import {
|
|
|
6
6
|
update,
|
|
7
7
|
update1 as update2,
|
|
8
8
|
version
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-g81gg9gz.js";
|
|
10
10
|
import {
|
|
11
|
-
JUDGE_RULES
|
|
12
|
-
|
|
13
|
-
} from "./index-3p0t2exn.js";
|
|
14
|
-
import {
|
|
15
|
-
Signoff
|
|
16
|
-
} from "./index-3k3rtw49.js";
|
|
11
|
+
JUDGE_RULES
|
|
12
|
+
} from "./index-htwb54c6.js";
|
|
17
13
|
import {
|
|
18
14
|
JudgeCommands
|
|
19
|
-
} from "./index-
|
|
15
|
+
} from "./index-pyjg1rtk.js";
|
|
20
16
|
|
|
21
17
|
// index.ts
|
|
22
18
|
import { NodeFs, NodeGlob } from "webappwiz/system";
|
|
@@ -34,14 +30,7 @@ webappwiz.command("update").description("pin every webappwiz dependency in a tre
|
|
|
34
30
|
default: version,
|
|
35
31
|
description: "version to pin to"
|
|
36
32
|
}).action((opts, { log, fs }) => update2({ ...opts, log, fs }));
|
|
37
|
-
var judge = ({ log, fs, ps, clock, glob }) => new JudgeCommands(JUDGE_RULES, {
|
|
38
|
-
signoffRules: SIGNOFF_RULES,
|
|
39
|
-
log,
|
|
40
|
-
fs,
|
|
41
|
-
ps,
|
|
42
|
-
clock,
|
|
43
|
-
glob
|
|
44
|
-
});
|
|
33
|
+
var judge = ({ log, fs, ps, clock, glob }) => new JudgeCommands(JUDGE_RULES, { log, fs, ps, clock, glob });
|
|
45
34
|
webappwiz.command("judge").description("check a directory against the config, one agent per glob").arg("dir", t.string(), {
|
|
46
35
|
default: ".",
|
|
47
36
|
description: "directory to judge (default: .)"
|
|
@@ -52,39 +41,17 @@ webappwiz.command("judge").description("check a directory against the config, on
|
|
|
52
41
|
}).option("print", t.boolean(), {
|
|
53
42
|
default: false,
|
|
54
43
|
description: "print the prompts and run no agent at all"
|
|
55
|
-
}).option("estimate", t.boolean(), {
|
|
56
|
-
default: false,
|
|
57
|
-
description: "print what a run would read, and run nothing"
|
|
58
44
|
}).option("chunk", t.number(), {
|
|
59
45
|
default: 25,
|
|
60
46
|
description: "files per review"
|
|
61
47
|
}).option("since", t.optional(t.string()), {
|
|
62
48
|
description: "only check files added or changed since this git ref"
|
|
63
|
-
}).option("
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}).action((opts, deps) => judge(deps).judge(opts));
|
|
67
|
-
webappwiz.command("signoff").description("weigh a change against the rules that ask for a person").arg("dir", t.string(), {
|
|
68
|
-
default: ".",
|
|
69
|
-
description: "directory whose change is weighed (default: .)"
|
|
70
|
-
}).option("agent", t.optional(t.enum(Object.keys(AGENTS))), {
|
|
71
|
-
description: "model to weigh it with (default: the config's agent)"
|
|
72
|
-
}).option("exec", t.optional(t.string()), {
|
|
73
|
-
description: "command the prompt is passed to, instead of --agent"
|
|
74
|
-
}).option("print", t.boolean(), {
|
|
49
|
+
}).option("concurrency-override", t.optional(t.number()), {
|
|
50
|
+
description: "agent calls in flight at once, over the config's concurrency"
|
|
51
|
+
}).option("ci", t.boolean(), {
|
|
75
52
|
default: false,
|
|
76
|
-
description: "
|
|
77
|
-
}).
|
|
78
|
-
default: "main",
|
|
79
|
-
description: "the ref the change is measured against"
|
|
80
|
-
}).option("budget", t.number(), {
|
|
81
|
-
default: 200000,
|
|
82
|
-
description: "confirm before reading more than this many tokens"
|
|
83
|
-
}).action((opts, { log, ps, clock }) => new Signoff(SIGNOFF_RULES, JUDGE_RULES.agent, {
|
|
84
|
-
log,
|
|
85
|
-
ps,
|
|
86
|
-
clock
|
|
87
|
-
}).run(opts));
|
|
53
|
+
description: "line-by-line output with no live progress block"
|
|
54
|
+
}).action((opts, deps) => judge(deps).judge(opts));
|
|
88
55
|
var rules = webappwiz.group("rules").description("list and print the rules, to run or to read yourself");
|
|
89
56
|
rules.command("ls").description("list the rules").action((_opts, deps) => judge(deps).ls());
|
|
90
57
|
rules.command("show").description("print one rule in full, by the id `rules ls` gives it").arg("id", t.string(), { description: "rule id" }).action((opts, deps) => judge(deps).show(opts));
|
package/judge.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type RuleSet } from "@webappwiz/rules";
|
|
2
2
|
import { type Logger } from "webappwiz/log";
|
|
3
3
|
import { type Fs, type Glob, type Ps } from "webappwiz/system";
|
|
4
4
|
import { type Clock } from "webappwiz/time";
|
|
5
|
-
|
|
6
|
-
export interface Confirm {
|
|
7
|
-
confirm(question: string): boolean | Promise<boolean>;
|
|
8
|
-
}
|
|
5
|
+
import { type Screen } from "./progress.js";
|
|
9
6
|
export interface ShowOptions {
|
|
10
7
|
/** The rule to print, as `rules ls` lists it. */
|
|
11
8
|
id: string;
|
|
@@ -17,27 +14,19 @@ export interface JudgeOptions {
|
|
|
17
14
|
exec?: string;
|
|
18
15
|
/** Print the prompts to the logger and spawn nothing. */
|
|
19
16
|
print?: boolean;
|
|
20
|
-
/** Print what a run would read and stop. */
|
|
21
|
-
estimate?: boolean;
|
|
22
17
|
/** Files per review. */
|
|
23
18
|
chunk: number;
|
|
24
19
|
/** Narrows the run to what git says has changed since this ref. */
|
|
25
20
|
since?: string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
21
|
+
/** Agent calls in flight at once, over the config's `concurrency`. */
|
|
22
|
+
"concurrency-override"?: number;
|
|
23
|
+
/** Line-by-line output with no live block, the way a log wants it. */
|
|
24
|
+
ci?: boolean;
|
|
28
25
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Answers on the terminal, and answers no without one: a run nobody is watching
|
|
31
|
-
* should stop and say the number rather than block forever waiting to be told
|
|
32
|
-
* to go ahead.
|
|
33
|
-
*/
|
|
34
|
-
export declare const ask: Confirm;
|
|
35
26
|
/** What a `JudgeCommands` runs through, and what else it lists. */
|
|
36
27
|
export interface JudgeCommandsOptions {
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
/** Who is asked before a run goes over budget; the terminal by default. */
|
|
40
|
-
confirmer?: Confirm;
|
|
28
|
+
/** Where live progress draws; this process's terminal by default. */
|
|
29
|
+
screen?: Screen;
|
|
41
30
|
log?: Logger;
|
|
42
31
|
fs?: Fs;
|
|
43
32
|
ps?: Ps;
|
|
@@ -46,24 +35,18 @@ export interface JudgeCommandsOptions {
|
|
|
46
35
|
}
|
|
47
36
|
export declare class JudgeCommands {
|
|
48
37
|
private rules;
|
|
49
|
-
private
|
|
50
|
-
private confirmer;
|
|
38
|
+
private screen;
|
|
51
39
|
private log;
|
|
52
40
|
private fs;
|
|
53
41
|
private ps;
|
|
54
42
|
private clock;
|
|
55
43
|
private glob;
|
|
56
44
|
constructor(rules: RuleSet, opts?: JudgeCommandsOptions);
|
|
57
|
-
/**
|
|
58
|
-
* Lists every rule there is, one row each, ids first for citing. The rules a
|
|
59
|
-
* run checks and the ones only a reader applies are one list with a SET
|
|
60
|
-
* column, because "what rules are there" is one question.
|
|
61
|
-
*/
|
|
45
|
+
/** Lists every rule there is, one row each, ids first for citing. */
|
|
62
46
|
ls(): void;
|
|
63
47
|
/**
|
|
64
48
|
* Prints one rule in full: what it covers, and the document an agent is
|
|
65
|
-
* given, verbatim. Take the id from `rules ls` or from a finding.
|
|
66
|
-
* how a reader applies a rule nothing runs for them.
|
|
49
|
+
* given, verbatim. Take the id from `rules ls` or from a finding.
|
|
67
50
|
*/
|
|
68
51
|
show(opts: ShowOptions): void;
|
|
69
52
|
/**
|
|
@@ -72,28 +55,9 @@ export declare class JudgeCommands {
|
|
|
72
55
|
* it spawns nothing and prints the prompts instead, for an agent that would
|
|
73
56
|
* rather hand them to subagents of its own.
|
|
74
57
|
*
|
|
75
|
-
* `since` narrows the run to what git says has changed
|
|
76
|
-
* what it may read before asking whether you meant it. Under `estimate` it
|
|
77
|
-
* prints that size and stops, which is the answer to "what would this cost"
|
|
78
|
-
* without having to guess a budget low enough to be refused.
|
|
58
|
+
* `since` narrows the run to what git says has changed.
|
|
79
59
|
*/
|
|
80
60
|
judge(opts: JudgeOptions): Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* Measures what one call cost over the files it was handed and leaves that
|
|
83
|
-
* behind, so the next `--estimate` on this agent has something better than a
|
|
84
|
-
* floor. Per call rather than per token, because that is how the charge
|
|
85
|
-
* falls: an agent pays for its own system prompt once per spawn, whatever it
|
|
86
|
-
* was asked to read, so a figure taken from a two-call run still holds for a
|
|
87
|
-
* fifteen-call one.
|
|
88
|
-
*
|
|
89
|
-
* A run nobody priced records nothing, and a failed write is said aloud
|
|
90
|
-
* rather than thrown: the agents have already been paid for by this point,
|
|
91
|
-
* and losing the measurement costs the next estimate accuracy, not the run.
|
|
92
|
-
*/
|
|
93
|
-
private record;
|
|
94
61
|
/** The agent a command runs with: what it was told, else the config's. */
|
|
95
62
|
private agent;
|
|
96
|
-
/** The model a run asks, or undefined for an `--exec` command, which is a
|
|
97
|
-
* model nothing here can name or price. */
|
|
98
|
-
private model;
|
|
99
63
|
}
|
package/judge.js
CHANGED
package/mode.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** What a command does with the plan it has made. */
|
|
2
|
-
export type Mode = "print" | "
|
|
3
|
-
/** The flags that choose between the
|
|
2
|
+
export type Mode = "print" | "run";
|
|
3
|
+
/** The flags that choose between the two, on top of the two that name an
|
|
4
4
|
* agent. */
|
|
5
5
|
export interface ModeOptions {
|
|
6
6
|
/** A model to ask. */
|
|
@@ -9,15 +9,13 @@ export interface ModeOptions {
|
|
|
9
9
|
exec?: string;
|
|
10
10
|
/** Print to the logger and spawn nothing. */
|
|
11
11
|
print?: boolean;
|
|
12
|
-
/** Print what a run would read, and spawn nothing. */
|
|
13
|
-
estimate?: boolean;
|
|
14
12
|
}
|
|
15
13
|
/**
|
|
16
|
-
* Which of the
|
|
14
|
+
* Which of the two a caller asked for, with running the default: a command
|
|
17
15
|
* given nothing but a directory is one somebody means to run.
|
|
18
16
|
*
|
|
19
17
|
* Naming two is an error rather than a quiet winner. All of these say what to
|
|
20
18
|
* do with one plan, and letting one silently beat the other would leave a
|
|
21
19
|
* caller unsure which of the two things they asked for they got.
|
|
22
20
|
*/
|
|
23
|
-
export declare function mode({ print,
|
|
21
|
+
export declare function mode({ print, agent, exec }: ModeOptions): Mode;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webappwiz/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "The webappwiz CLI: judge code against rules
|
|
3
|
+
"version": "0.0.9",
|
|
4
|
+
"description": "The webappwiz CLI: judge code against rules and manage agent skills",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jared Johnson",
|
|
7
7
|
"repository": {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@webappwiz/rules": "^0.0.
|
|
21
|
-
"webappwiz": "^0.0.
|
|
20
|
+
"@webappwiz/rules": "^0.0.9",
|
|
21
|
+
"webappwiz": "^0.0.9"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"typescript": "^7"
|
|
@@ -41,10 +41,6 @@
|
|
|
41
41
|
"./judge": {
|
|
42
42
|
"types": "./judge.d.ts",
|
|
43
43
|
"default": "./judge.js"
|
|
44
|
-
},
|
|
45
|
-
"./signoff": {
|
|
46
|
-
"types": "./signoff.d.ts",
|
|
47
|
-
"default": "./signoff.js"
|
|
48
44
|
}
|
|
49
45
|
},
|
|
50
46
|
"bin": {
|