@letterstory/cli 0.49.2 → 0.51.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/README.md CHANGED
@@ -491,11 +491,25 @@ letterstory arena run-status <run-id> # status: awaiting_direct
491
491
  letterstory arena direction <run-id> --choice 1
492
492
  letterstory arena direction <run-id> --custom "Lead with the hidden costs"
493
493
  letterstory arena direction <run-id> --skip # let the agents decide
494
+ letterstory arena run --topic "payroll solutions" --direction "Lead with the hidden costs" # no gate, no vote
495
+
496
+ # Watching runs (what an agent monitoring the account polls):
497
+ letterstory arena runs --status awaiting_direction # the gates waiting on you
498
+ letterstory arena runs --since 2026-10-02T00:00:00Z --limit 50
499
+ letterstory arena runs --before <next_cursor> # the next (older) page
500
+ letterstory arena latest --article <uuid> # the post's latest run, whoever started it
501
+
502
+ # What research decided each site is — and your call on one (research never overrides it):
503
+ letterstory arena roles --rival # competitors research found that you never listed
504
+ letterstory arena role set reviewsite.com --role context
505
+ letterstory arena role set rival.com --role company --company "Rival" --rival
506
+ letterstory arena role reset rival.com # hand it back to research
494
507
 
495
508
  # Arenas are callable endpoints with one-file defaults (the org's default arena is "Letterstory"):
496
509
  letterstory arena list
497
510
  letterstory arena endpoint create --name "Docs research" --defaults '{"depth":"barebones"}'
498
511
  letterstory arena endpoint config <arena-id> [--file cfg.json]
512
+ letterstory arena endpoint update <arena-id> --name "Docs research v2" # creator or org admin only
499
513
  letterstory arena endpoint delete <arena-id> --yes
500
514
 
501
515
  # Ledes: a prospect's context in, ready-to-send opening lines out (chosen from a
package/lib/cli.mjs CHANGED
@@ -54,7 +54,7 @@ import {
54
54
  } from "./commands.mjs";
55
55
 
56
56
  // Keep in sync with cli/package.json.
57
- export const VERSION = "0.49.2";
57
+ export const VERSION = "0.51.0";
58
58
 
59
59
  // Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
60
60
  // can't accidentally swallow the id as --json's value.
@@ -68,6 +68,8 @@ const RUN_OPTIONS = {
68
68
  // --no-verify turns the fact-check off; nothing sent otherwise (the default is on).
69
69
  verify: { value: (flags) => (flags["no-verify"] === true ? false : undefined) },
70
70
  mention_degree: { flag: "mention", enum: ["off", "subtle", "woven"] },
71
+ // What the fact-check does with a claim it can neither fix nor stand behind: flag or remove it.
72
+ unresolved_claims: { flag: "unresolved", enum: ["comment", "drop"] },
71
73
  // Positioning as data — a file, not flags: `--context-file ctx.json` of
72
74
  // {manifesto, stances[], priority_topics[], avoid_topics[], identity{is[],is_not[],one_liner}}.
73
75
  context: { value: (flags) => readJsonObjectFlag(flags, "context") },
@@ -116,6 +118,8 @@ export const ARGS = {
116
118
  },
117
119
  },
118
120
  existing_content: { value: (flags) => flagStr(flags["existing-content"]) },
121
+ // Your own thesis: adopted as the run's direction, so a gate never pauses for it.
122
+ direction: { flag: "direction" },
119
123
  article_id: { flag: "article" },
120
124
  ...RUN_OPTIONS,
121
125
  },
@@ -124,8 +128,32 @@ export const ARGS = {
124
128
  },
125
129
  list_arena_runs: {
126
130
  arena_id: { flag: "arena" },
131
+ article_id: { flag: "article" },
132
+ status: { flag: "status", type: "list" },
133
+ since: { flag: "since" },
134
+ before: { flag: "before" },
127
135
  limit: { flag: "limit", type: "num" },
128
136
  },
137
+ get_latest_arena_run: {
138
+ article_id: { flag: "article", required: true },
139
+ },
140
+ // What research decided each site is, and your call on one (never overridden by the model).
141
+ list_source_roles: {
142
+ role: { flag: "role", enum: ["authority", "company", "context"] },
143
+ rival: { flag: "rival", type: "bool" },
144
+ decided_by: { flag: "decided-by", enum: ["model", "customer", "staff"] },
145
+ limit: { flag: "limit", type: "num" },
146
+ },
147
+ set_source_role: {
148
+ site: { positional: 1, label: "site", required: true },
149
+ role: { flag: "role", enum: ["authority", "company", "context"], required: true },
150
+ company: { flag: "company" },
151
+ rival: { flag: "rival", type: "bool" },
152
+ note: { flag: "note" },
153
+ },
154
+ reset_source_role: {
155
+ site: { positional: 1, label: "site", required: true },
156
+ },
129
157
  submit_arena_direction: {
130
158
  run_id: { positional: 0, label: "run-id", required: true },
131
159
  choice: { flag: "choice", type: "num" },
@@ -144,6 +172,11 @@ export const ARGS = {
144
172
  delete_arena: {
145
173
  arena_id: { positional: 1, label: "arena-id", required: true },
146
174
  },
175
+ update_arena: {
176
+ arena_id: { positional: 1, label: "arena-id", required: true },
177
+ name: { flag: "name" },
178
+ defaults: { value: (flags) => readJsonObjectFlag(flags, "defaults") },
179
+ },
147
180
  // The arena's configuration as ONE file: `arena endpoint config <id>` prints it,
148
181
  // `arena endpoint config <id> --file cfg.json` replaces it with the file's document.
149
182
  get_arena_config: {
@@ -167,6 +200,12 @@ export async function cmdArena(ctx) {
167
200
  return arenaRunStatus(rest);
168
201
  case "runs":
169
202
  return arenaRuns(rest);
203
+ case "latest":
204
+ return arenaLatest(rest);
205
+ case "roles":
206
+ return arenaRoles(rest);
207
+ case "role":
208
+ return arenaRole(rest);
170
209
  case "direction":
171
210
  return arenaDirection(rest);
172
211
  case "cancel":
@@ -181,7 +220,7 @@ export async function cmdArena(ctx) {
181
220
  return arenaEndpoint(rest);
182
221
  default:
183
222
  throw new CliError(
184
- `Unknown arena subcommand: ${sub ?? "(none)"}. Try: run, run-status, runs, direction, cancel, balance, list, endpoint`
223
+ `Unknown arena subcommand: ${sub ?? "(none)"}. Try: run, run-status, runs, latest, direction, cancel, balance, list, endpoint, roles, role`
185
224
  );
186
225
  }
187
226
  }
@@ -262,6 +301,47 @@ async function arenaRuns(ctx) {
262
301
  return 0;
263
302
  }
264
303
 
304
+ async function arenaLatest(ctx) {
305
+ const { client, flags, io } = ctx;
306
+ const args = argsFromFlags(ARGS.get_latest_arena_run, flags, ctx.positionals);
307
+ const result = await client.callTool("get_latest_arena_run", args);
308
+ printResult(io, flags, result);
309
+ return 0;
310
+ }
311
+
312
+ async function arenaRoles(ctx) {
313
+ const { client, flags, io } = ctx;
314
+ const result = await client.callTool(
315
+ "list_source_roles",
316
+ argsFromFlags(ARGS.list_source_roles, flags, ctx.positionals)
317
+ );
318
+ printResult(io, flags, result);
319
+ return 0;
320
+ }
321
+
322
+ /** `arena role set <site> --role …` / `arena role reset <site>` — your call on one site. */
323
+ async function arenaRole(ctx) {
324
+ const { client, positionals, flags, io } = ctx;
325
+ const action = positionals[0];
326
+ if (action === "set") {
327
+ const args = argsFromFlags(ARGS.set_source_role, flags, positionals);
328
+ const result = await client.callTool("set_source_role", args);
329
+ ok(ctx, `${result?.role?.site ?? args.site} is now ${args.role}${args.rival ? " (a rival)" : ""}.`);
330
+ printResult(io, flags, result);
331
+ return 0;
332
+ }
333
+ if (action === "reset") {
334
+ const result = await client.callTool(
335
+ "reset_source_role",
336
+ argsFromFlags(ARGS.reset_source_role, flags, positionals)
337
+ );
338
+ ok(ctx, result?.reset ? "Handed back to research." : "Research had not recorded that site.");
339
+ printResult(io, flags, result);
340
+ return 0;
341
+ }
342
+ throw new CliError(`Unknown arena role action: ${action ?? "(none)"}. Try: set, reset`);
343
+ }
344
+
265
345
  async function arenaDirection(ctx) {
266
346
  const { client, flags, io } = ctx;
267
347
  const runId = requirePositional(ctx.positionals, 0, "run-id");
@@ -327,6 +407,14 @@ async function arenaEndpoint(ctx) {
327
407
  printResult(io, flags, result);
328
408
  return 0;
329
409
  }
410
+ if (action === "update" || action === "edit") {
411
+ const id = requirePositional(positionals, 1, "arena-id");
412
+ const args = argsFromFlags(ARGS.update_arena, flags, positionals);
413
+ const result = await client.callTool("update_arena", args);
414
+ ok(ctx, `Updated arena ${id}.`);
415
+ printResult(io, flags, result);
416
+ return 0;
417
+ }
330
418
  if (action === "config") {
331
419
  const id = requirePositional(positionals, 1, "arena-id");
332
420
  if (flagStr(flags.file) === undefined) {
@@ -344,5 +432,5 @@ async function arenaEndpoint(ctx) {
344
432
  printResult(io, flags, result);
345
433
  return 0;
346
434
  }
347
- throw new CliError(`Unknown arena endpoint action: ${action ?? "(none)"}. Try: create, delete, config`);
435
+ throw new CliError(`Unknown arena endpoint action: ${action ?? "(none)"}. Try: create, update, delete, config`);
348
436
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.49.2",
3
+ "version": "0.51.0",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",