@letterstory/cli 0.123.0 → 0.125.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/lib/cli.mjs +7 -1
- package/lib/commands/admin.mjs +79 -0
- package/lib/commands/repo.mjs +25 -1
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -61,13 +61,15 @@ import {
|
|
|
61
61
|
} from "./commands.mjs";
|
|
62
62
|
|
|
63
63
|
// Keep in sync with cli/package.json.
|
|
64
|
-
export const VERSION = "0.
|
|
64
|
+
export const VERSION = "0.125.0";
|
|
65
65
|
|
|
66
66
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
67
67
|
// can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
|
|
68
68
|
// `negate` spelling) in a command's ARGS map must be listed; boolean-flags.test.ts enforces it.
|
|
69
69
|
export const BOOLEAN_FLAGS = new Set([
|
|
70
70
|
"json",
|
|
71
|
+
// admin models runs: only the starred ones.
|
|
72
|
+
"starred",
|
|
71
73
|
// shredder file / arena file: save the version as a candidate beside live.
|
|
72
74
|
"candidate",
|
|
73
75
|
// site tree: include children a fold-by-default family would hide; items over the whole subtree.
|
|
@@ -843,6 +845,8 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
843
845
|
session (a trial per arm), linked to its
|
|
844
846
|
ledger entry. Written by \`npm run lab --
|
|
845
847
|
publish\` in the kernels repo.
|
|
848
|
+
admin models runs [--model M] [--starred] | run <id> | star|unstar <id> | scoreboard --model M
|
|
849
|
+
The model hub's saved runs and blind arena (models.letter.dev)
|
|
846
850
|
admin spend summary [--range day|week|month|year | --from D --to D] [--grain day|week|month]
|
|
847
851
|
| reconcile [--from D --to D] | pnl [--range day|week|month|year | --from D --to D]
|
|
848
852
|
| reference add --vendor V [--key K] --metric billed_usd --value N --from D [--to D] --ref "…"
|
|
@@ -1332,6 +1336,8 @@ Repo changes (Claude Code edits your repo in a sandbox; the Letterstory GitHub A
|
|
|
1332
1336
|
repo list [--status running|succeeded|no_changes|failed|cancelled] [--limit n] [--audit <id>]
|
|
1333
1337
|
[--source site_audit_issue|site_quick_check|letterprove_install]
|
|
1334
1338
|
repo publish <run-id> Make PR on a preview in review
|
|
1339
|
+
repo revise <run-id> --instructions <text>|--instructions-file <path|->
|
|
1340
|
+
Ask for more on a preview; it takes over the preview's link
|
|
1335
1341
|
repo cancel <run-id> Stop it and delete the sandbox; no PR (Discard a preview)
|
|
1336
1342
|
|
|
1337
1343
|
Feedback (tell us what's broken, missing, unclear, or wanted):
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// admin brand-detection <org-id> (fleet:read)
|
|
20
20
|
// admin brand-detection <org-id> [--term t]... [--exact t]... [--exclude p]... [--note "…"] | --clear [--note "…"] (fleet:instrument)
|
|
21
21
|
// admin recipes list|show|vocab|export|import|save|delete … (recipes:read / recipes:write)
|
|
22
|
+
// admin models runs|run|star|unstar|scoreboard … (models:read / models:write)
|
|
22
23
|
// admin replays [--org <id>] [--target <id>] [--status …] [--limit n] (fleet:read)
|
|
23
24
|
// admin replay --org <id> [<response-id> | --target <id>] [--surface …] (fleet:replay)
|
|
24
25
|
// admin create-org --domain acme.com [--name "Acme"] [--test] [--no-key] [--key-name N]
|
|
@@ -3962,6 +3963,83 @@ function spendEntryFromFlags(flags) {
|
|
|
3962
3963
|
});
|
|
3963
3964
|
}
|
|
3964
3965
|
|
|
3966
|
+
const MODELS = "/api/superadmin/models";
|
|
3967
|
+
const MODELS_USAGE =
|
|
3968
|
+
"Usage: admin models runs [--model ballpoint-1] [--starred] [--limit N] | run <id> | star <id> | unstar <id> | scoreboard --model ballpoint-1";
|
|
3969
|
+
|
|
3970
|
+
/** The model hub's saved runs and blind arena (models.letter.dev), same functions as its pages. */
|
|
3971
|
+
async function cmdAdminModels(ctx) {
|
|
3972
|
+
const { flags, positionals, io } = ctx;
|
|
3973
|
+
const sub = positionals[0];
|
|
3974
|
+
switch (sub) {
|
|
3975
|
+
case "runs": {
|
|
3976
|
+
const params = new URLSearchParams(
|
|
3977
|
+
compact({
|
|
3978
|
+
model: flagStr(flags.model),
|
|
3979
|
+
starred: flagBool(flags.starred) ? "true" : undefined,
|
|
3980
|
+
limit: flagNum(flags.limit)?.toString(),
|
|
3981
|
+
})
|
|
3982
|
+
);
|
|
3983
|
+
const result = await staffRequest(ctx, "GET", `${MODELS}/runs${params.size ? `?${params}` : ""}`);
|
|
3984
|
+
printResult(io, flags, result, (r) =>
|
|
3985
|
+
(r.runs ?? [])
|
|
3986
|
+
.map(
|
|
3987
|
+
(x) =>
|
|
3988
|
+
`${x.created_at.slice(0, 16)} ${x.starred ? "★" : " "} ${x.model.padEnd(14)} ${String(x.outputs).padStart(3)} outputs ${String(x.votes).padStart(3)} votes ${x.contenders.join(", ")} ${x.id}`
|
|
3989
|
+
)
|
|
3990
|
+
.join("\n")
|
|
3991
|
+
);
|
|
3992
|
+
return 0;
|
|
3993
|
+
}
|
|
3994
|
+
case "run": {
|
|
3995
|
+
const id = positionals[1];
|
|
3996
|
+
if (!id) throw new CliError("Usage: admin models run <id>");
|
|
3997
|
+
const result = await staffRequest(ctx, "GET", `${MODELS}/runs/${id}`);
|
|
3998
|
+
printResult(io, flags, result, (r) =>
|
|
3999
|
+
[
|
|
4000
|
+
`${r.model} · ${r.created_at} · ${JSON.stringify(r.input)}`,
|
|
4001
|
+
...(r.outputs ?? []).map(
|
|
4002
|
+
(o) =>
|
|
4003
|
+
` [${o.slot}] ${o.contender}${o.ours ? " (ours)" : ""}: ${o.error ? `error: ${o.error}` : Object.values(o.output).join(" — ")}`
|
|
4004
|
+
),
|
|
4005
|
+
].join("\n")
|
|
4006
|
+
);
|
|
4007
|
+
return 0;
|
|
4008
|
+
}
|
|
4009
|
+
case "star":
|
|
4010
|
+
case "unstar": {
|
|
4011
|
+
const id = positionals[1];
|
|
4012
|
+
if (!id) throw new CliError(`Usage: admin models ${sub} <id>`);
|
|
4013
|
+
const result = await staffRequest(ctx, "PATCH", `${MODELS}/runs/${id}`, { starred: sub === "star" });
|
|
4014
|
+
ok(ctx, `${sub === "star" ? "Starred" : "Unstarred"} ${result.id}`);
|
|
4015
|
+
printResult(io, flags, result, () => "");
|
|
4016
|
+
return 0;
|
|
4017
|
+
}
|
|
4018
|
+
case "scoreboard": {
|
|
4019
|
+
const model = flagStr(flags.model);
|
|
4020
|
+
if (!model) throw new CliError("Usage: admin models scoreboard --model ballpoint-1");
|
|
4021
|
+
const result = await staffRequest(
|
|
4022
|
+
ctx,
|
|
4023
|
+
"GET",
|
|
4024
|
+
`${MODELS}/arena/scoreboard?model=${encodeURIComponent(model)}`
|
|
4025
|
+
);
|
|
4026
|
+
printResult(io, flags, result, (r) =>
|
|
4027
|
+
(r.opponents ?? []).length
|
|
4028
|
+
? r.opponents
|
|
4029
|
+
.map(
|
|
4030
|
+
(o) =>
|
|
4031
|
+
`${r.model} vs ${o.opponent}: ${o.wins}-${o.losses} (${o.ties} ties, ${o.both_bad} both bad) · ${o.win_rate == null ? "—" : `${Math.round(o.win_rate * 100)}% [${Math.round(o.low * 100)}–${Math.round(o.high * 100)}]`} · ${o.voters} voters`
|
|
4032
|
+
)
|
|
4033
|
+
.join("\n")
|
|
4034
|
+
: `No votes on ${r.model} yet.`
|
|
4035
|
+
);
|
|
4036
|
+
return 0;
|
|
4037
|
+
}
|
|
4038
|
+
default:
|
|
4039
|
+
throw new CliError(MODELS_USAGE);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
|
|
3965
4043
|
async function cmdAdminSpend(ctx) {
|
|
3966
4044
|
const { flags, positionals, io } = ctx;
|
|
3967
4045
|
const sub = positionals[0];
|
|
@@ -4767,6 +4845,7 @@ export async function cmdAdmin(ctx) {
|
|
|
4767
4845
|
return cmdAdminReplays({ ...ctx, positionals: ctx.positionals.slice(1) }, sub);
|
|
4768
4846
|
if (sub === "crm") return cmdAdminCrm({ ...ctx, positionals: ctx.positionals.slice(1) });
|
|
4769
4847
|
if (sub === "spend") return cmdAdminSpend({ ...ctx, positionals: ctx.positionals.slice(1) });
|
|
4848
|
+
if (sub === "models") return cmdAdminModels({ ...ctx, positionals: ctx.positionals.slice(1) });
|
|
4770
4849
|
if (sub === "create-org") return cmdAdminCreateOrg({ ...ctx, positionals: ctx.positionals.slice(1) });
|
|
4771
4850
|
if (sub === "ask") return cmdAdminAsk({ ...ctx, positionals: ctx.positionals.slice(1) });
|
|
4772
4851
|
if (sub === "patterns") return cmdAdminPatterns({ ...ctx, positionals: ctx.positionals.slice(1) });
|
package/lib/commands/repo.mjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// repo activity → get_repo_change_activity (what the sandbox is doing; --follow polls)
|
|
5
5
|
// repo list → list_repo_changes repo cancel → cancel_repo_change
|
|
6
6
|
// repo publish → publish_repo_change (Make PR on a preview)
|
|
7
|
+
// repo revise → revise_repo_change (ask for more on a preview, before the PR)
|
|
7
8
|
// repo repos → list_github_app_repos repo branches → list_repo_branches
|
|
8
9
|
// repo prs → list_open_pull_requests (every open PR, whoever opened it)
|
|
9
10
|
// repo install-letterprove → letterprove_open_install_pr
|
|
@@ -167,6 +168,10 @@ const ARGS = {
|
|
|
167
168
|
get_repo_change_activity: { run_id: { positional: 0, required: true, label: "run-id" } },
|
|
168
169
|
cancel_repo_change: { run_id: { positional: 0, required: true, label: "run-id" } },
|
|
169
170
|
publish_repo_change: { run_id: { positional: 0, required: true, label: "run-id" } },
|
|
171
|
+
revise_repo_change: {
|
|
172
|
+
run_id: { positional: 0, required: true, label: "run-id" },
|
|
173
|
+
instructions: { value: (flags) => readTextInput(flags, "instructions", "instructions-file") },
|
|
174
|
+
},
|
|
170
175
|
list_repo_changes: {
|
|
171
176
|
status: { flag: "status", enum: ["running", "succeeded", "no_changes", "failed", "cancelled", "sent"] },
|
|
172
177
|
limit: { flag: "limit", type: "num" },
|
|
@@ -195,8 +200,14 @@ function describe(run) {
|
|
|
195
200
|
`${run.id} ${run.repo}@${run.baseBranch} ${run.status} (${run.phase}) $${run.costUsd.toFixed(2)}`,
|
|
196
201
|
];
|
|
197
202
|
if (run.preview?.url) lines.push(` Preview: ${run.preview.url}`);
|
|
203
|
+
for (const [i, change] of (run.preview?.changes ?? []).entries())
|
|
204
|
+
lines.push(` ${i + 1}. ${change.path} ${change.note}${change.url ? ` ${change.url}` : ""}`);
|
|
198
205
|
if (run.preview?.state === "failed") lines.push(` Preview failed: ${run.preview.detail ?? ""}`);
|
|
199
|
-
if (run.
|
|
206
|
+
if (run.preview?.revisionRunId) lines.push(` Follow-up being built: ${run.preview.revisionRunId}`);
|
|
207
|
+
else if (run.phase === "review") {
|
|
208
|
+
lines.push(` Make PR: letterstory repo publish ${run.id}`);
|
|
209
|
+
lines.push(` Ask for more: letterstory repo revise ${run.id} --instructions "…"`);
|
|
210
|
+
}
|
|
200
211
|
if (run.prUrl) lines.push(` PR: ${run.prUrl} (${run.prState ?? "state unknown"})`);
|
|
201
212
|
if (run.errorCode) lines.push(` Error (${run.errorCode}): ${run.errorDetail ?? ""}`);
|
|
202
213
|
if (run.summary) lines.push(...run.summary.split("\n").map((l) => ` ${l}`));
|
|
@@ -235,6 +246,8 @@ export async function cmdRepo(ctx) {
|
|
|
235
246
|
return repoCancel(rest);
|
|
236
247
|
case "publish":
|
|
237
248
|
return repoPublish(rest);
|
|
249
|
+
case "revise":
|
|
250
|
+
return repoRevise(rest);
|
|
238
251
|
case "repos":
|
|
239
252
|
return repoRepos(rest);
|
|
240
253
|
case "branches":
|
|
@@ -549,6 +562,17 @@ async function repoPublish(ctx) {
|
|
|
549
562
|
return 0;
|
|
550
563
|
}
|
|
551
564
|
|
|
565
|
+
/** `repo revise <run-id> --instructions <text>`: a follow-up on a preview, built on top of it. */
|
|
566
|
+
async function repoRevise(ctx) {
|
|
567
|
+
const { client, flags, io } = ctx;
|
|
568
|
+
const args = argsFromFlags(ARGS.revise_repo_change, flags, ctx.positionals);
|
|
569
|
+
if (!args.instructions) throw new CliError("Missing --instructions (or --instructions-file <path|->)");
|
|
570
|
+
const result = await client.callTool("revise_repo_change", args);
|
|
571
|
+
ok(ctx, result.message);
|
|
572
|
+
printResult(io, flags, result.run, describe);
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
|
|
552
576
|
async function repoRepos(ctx) {
|
|
553
577
|
const { client, flags, io } = ctx;
|
|
554
578
|
const result = await client.callTool("list_github_app_repos", argsFromFlags(ARGS.list_github_app_repos, flags));
|