@letterstory/cli 0.33.11 → 0.34.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
@@ -389,10 +389,16 @@ letterstory lede endpoint config <endpoint-id> [--file cfg.json]
389
389
 
390
390
  # 2. Have a kernel write the draft
391
391
  letterstory kernel list
392
- letterstory kernel run --article <uuid> --kernel <uuid> [--brief "…" | --brief-file brief.md]
392
+ letterstory kernel write <endpoint-id> --article <uuid> [--kernel <uuid>] [--file brief.md]
393
393
  letterstory kernel status <kernel-job-id>
394
394
  ```
395
395
 
396
+ `kernel run` (and the `submit_kernel_run` tool behind it) was removed on 2026-09-27. Its one
397
+ job is `kernel write <endpoint-id> --article <uuid>`: with no `--body`/`--file` it writes from
398
+ the article's own content, and `kernel status` still polls the job it starts. Its
399
+ `--brief`/`--brief-file` flags have no replacement: the input is `--body`/`--file`, and
400
+ guidance that is not content goes in `--directives`.
401
+
396
402
  ### The standalone kernel endpoint
397
403
 
398
404
  `kernel write` and `kernel polish` hit the standalone door: prose comes back, nothing is
@@ -403,7 +409,7 @@ letterstory kernel endpoint create --name "Writer"
403
409
  letterstory kernel endpoint delete <endpoint-id> --yes
404
410
 
405
411
  letterstory kernel write <endpoint-id> --file outline.md \
406
- --brief-mode outline --person first --author "Ada" --shredder <endpoint-id>
412
+ --read-as outline --person first --author "Ada" --shredder <endpoint-id>
407
413
  # Follows the run and prints the ARTICLE when it lands. --no-wait returns the run id instead.
408
414
  ```
409
415
 
package/lib/cli.mjs CHANGED
@@ -45,7 +45,7 @@ import {
45
45
  } from "./commands.mjs";
46
46
 
47
47
  // Keep in sync with cli/package.json.
48
- export const VERSION = "0.33.11";
48
+ export const VERSION = "0.34.0";
49
49
 
50
50
  // Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
51
51
  // can't accidentally swallow the id as --json's value.
@@ -319,7 +319,7 @@ Arenas (adversarial clusters of research agents -> a sourced, fact-checked outli
319
319
  arena endpoint delete <arena-id> --yes
320
320
 
321
321
  Auto-Pen (research -> kernel -> Enhance, in one run — and the ONLY path that
322
- applies a collection's content template; kernel run does not):
322
+ applies a collection's content template; kernel write --article does not):
323
323
  autopen start --article <uuid> Start the run; takes 10-40 min
324
324
  autopen status --article <uuid> Step states + the template in use
325
325
  autopen cancel --article <uuid> --yes Stop a run in flight
@@ -337,9 +337,11 @@ Writing kernels (kernels write the draft; they do not research — run research
337
337
  kernel list List available writing kernels
338
338
 
339
339
  Into an article you already have (metered on the AI-usage gate):
340
- kernel run --article <uuid> --kernel <uuid> [--brief <text>|--brief-file <path>]
341
- Submit a run; takes 15-20 min
342
- kernel status <kernel-job-id> Check a run's status
340
+ kernel write <endpoint-id> --article <uuid> [--kernel <uuid>] [--body <text>|--file <path>]
341
+ Write into the article (from its own
342
+ content when no input is given);
343
+ takes 15-20 min
344
+ kernel status <kernel-job-id> Check that run's status
343
345
 
344
346
  Standalone — prose back, nothing saved into an article (metered in kernel-run credits):
345
347
  kernel endpoints Your kernel endpoints
@@ -3,7 +3,7 @@
3
3
  //
4
4
  // This is the command that makes a TEMPLATE take effect. A template bound with
5
5
  // `collections autopen <id> --template-key <key>` is read inside the Auto-Pen run and
6
- // nowhere else, so `kernel run` (which hands the draft's current text straight to a
6
+ // nowhere else, so `kernel write --article` (which hands the draft's current text straight to a
7
7
  // kernel) will not produce a templated post no matter how the collection is configured.
8
8
  //
9
9
  // A run takes 10-40 minutes, so `start` returns as soon as it is queued. `status` both
@@ -3,11 +3,13 @@
3
3
  // TWO PRODUCTS SHARE THIS NOUN, and the subcommands are split along that line because
4
4
  // confusing them wastes fifteen minutes and a credit:
5
5
  //
6
- // kernel run/status writes INTO an article you already have (submit_kernel_run).
7
- // Metered on the AI-usage gate; needs --article.
8
6
  // kernel write/… the STANDALONE endpoint (run_kernel) — you get prose back and
9
7
  // nothing is saved into an article. Metered in kernel-run
10
8
  // credits, the balance the Kernels page shows.
9
+ // kernel write --article the same tool writing INTO an article you already have (with no
10
+ // + kernel status input, from the article's own content). Metered on the AI-usage
11
+ // gate; follow it with `kernel status <kernel-job-id>`. This
12
+ // replaced `kernel run` (and its tool) on 2026-09-27.
11
13
  //
12
14
  // The standalone door had MCP tools and no CLI at all, so the only way to reach the product
13
15
  // the Kernels page bills for was `letterstory mcp call run_kernel` with hand-written JSON.
@@ -85,21 +87,6 @@ function variablesFromFlags(flags) {
85
87
  }
86
88
 
87
89
  export const ARGS = {
88
- // The article-bound door: submit a kernel run INTO an article you already have.
89
- submit_kernel_run: {
90
- article_id: { flag: "article", required: true },
91
- kernel_id: { flag: "kernel", required: true },
92
- // A brief overrides the article's own content as the instruction; --brief-file reads
93
- // it from a path. Omit both to write from the article as it stands.
94
- brief: { value: (flags) => flagStr(flags.brief) ?? readBodyInput({ file: flagStr(flags["brief-file"]) }) },
95
- // How to read --brief: auto (default) treats a short topic as instruction and a pasted
96
- // draft/article as source material to decompose first — your own finished post is
97
- // `article`. Same flag name as run_kernel's.
98
- content_kind: {
99
- flag: "read-as",
100
- enum: ["auto", "instruction", "document", "article", "outline", "research", "notes"],
101
- },
102
- },
103
90
  // The standalone door. Two subcommands share this one tool: `write` and `polish` both
104
91
  // send `input`, `polish` defaulting content_kind to "article" — the map is one, so a field
105
92
  // the tool gains is a field BOTH subcommands must learn (or knowingly gap) — that is the
@@ -111,14 +98,14 @@ export const ARGS = {
111
98
  target_word_count: { flag: "words", type: "num" },
112
99
  target_word_tolerance: { flag: "words-tolerance", type: "num" },
113
100
  // Write into an article instead of handing the text back (2026-09-27): the one call that
114
- // replaces `kernel submit`. With `--article` and no input, the article's own content is
115
- // the input.
101
+ // replaced `kernel run`. With `--article` and no input, the article's own content is the
102
+ // input.
116
103
  article_id: { flag: "article" },
117
104
  label: "label",
118
105
  // The flag is --read-as, which is what shipped; an earlier draft called it --brief-mode.
119
106
  content_kind: {
120
107
  flag: "read-as",
121
- enum: ["auto", "instruction", "document", "article", "outline", "research", "notes"],
108
+ enum: ["auto", "instruction", "article", "outline", "research", "notes"],
122
109
  },
123
110
  // The two products, in one run: every round is shredded before the kernel's jury sees
124
111
  // it, so the verdict lands on the text that actually ships. Costs a shred as well as a
@@ -159,8 +146,6 @@ export const ARGS = {
159
146
  object: {
160
147
  tier: { flag: "shred-tier", enum: ["cheap", "mid", "strong", "frontier"] },
161
148
  families: { flag: "shred-family", type: "list" },
162
- revise_shredded: { flag: "shred-revise", type: "bool" },
163
- revision_model: "shred-revision-model",
164
149
  skip_voice_bearing: { flag: "shred-skip-voice-bearing", type: "bool" },
165
150
  judge: { flag: "shred-judge", type: "bool" },
166
151
  attempts_per_unit: { flag: "shred-attempts", type: "num" },
@@ -248,8 +233,6 @@ export async function cmdKernel(ctx) {
248
233
  case "list":
249
234
  case "ls":
250
235
  return kernelList(rest);
251
- case "run":
252
- return kernelRun(rest);
253
236
  case "status":
254
237
  return kernelStatus(rest);
255
238
  case "endpoints":
@@ -274,7 +257,7 @@ export async function cmdKernel(ctx) {
274
257
  return kernelBalance(rest);
275
258
  default:
276
259
  throw new CliError(
277
- `Unknown kernel subcommand: ${sub ?? "(none)"}. Try: list, endpoints, endpoint, eval, update, archive, write, polish, run-status, runs, balance, run, status`
260
+ `Unknown kernel subcommand: ${sub ?? "(none)"}. Try: list, endpoints, endpoint, eval, update, archive, write, polish, run-status, runs, balance, status`
278
261
  );
279
262
  }
280
263
  }
@@ -286,15 +269,6 @@ async function kernelList(ctx) {
286
269
  return 0;
287
270
  }
288
271
 
289
- async function kernelRun(ctx) {
290
- const { client, flags, io } = ctx;
291
- const args = argsFromFlags(ARGS.submit_kernel_run, flags, ctx.positionals);
292
- const result = await client.callTool("submit_kernel_run", args);
293
- ok(ctx, `Kernel run started. Check progress with: kernel status ${result?.kernel_job_id ?? "<kernel-job-id>"}`);
294
- printResult(io, flags, result);
295
- return 0;
296
- }
297
-
298
272
  async function kernelStatus(ctx) {
299
273
  const { client, positionals, flags, io } = ctx;
300
274
  const jobId = requirePositional(positionals, 0, "kernel-job-id");
@@ -358,10 +332,21 @@ async function kernelEndpoints(ctx) {
358
332
  async function kernelWrite(ctx) {
359
333
  const { client, positionals, flags, io } = ctx;
360
334
  const args = argsFromFlags(ARGS.run_kernel, flags, positionals);
361
- if (args.input === undefined) {
362
- throw new CliError("Provide the brief with --body <text>, --file <path>, or --file - (stdin).");
335
+ // With --article the article's own content is the input, so only a standalone write needs one.
336
+ if (args.input === undefined && args.article_id === undefined) {
337
+ throw new CliError(
338
+ "Provide the brief with --body <text>, --file <path>, or --file - (stdin), or --article <uuid> to write from that article."
339
+ );
363
340
  }
364
341
  const result = await client.callTool("run_kernel", args);
342
+ // An article-bound run is a kernel job, polled by `kernel status` (get_kernel_run_status),
343
+ // not an endpoint run: the finished draft lands in the article, not here.
344
+ const kernelJobId = toolField(result, "kernel_job_id");
345
+ if (kernelJobId) {
346
+ ok(ctx, `Kernel run started into the article. Check progress with: kernel status ${kernelJobId}`);
347
+ printResult(io, flags, result);
348
+ return 0;
349
+ }
365
350
  const runId = toolField(result, "run_id");
366
351
  if (!runId) {
367
352
  printResult(io, flags, result);
@@ -59,6 +59,7 @@ export const ARGS = {
59
59
  customer_ref: { flag: "customer-ref" },
60
60
  configure: { value: (flags) => (flags["no-configure"] === true ? false : undefined) },
61
61
  config: { value: (flags) => readJsonObjectFlag(flags, "config") },
62
+ composition: { value: (flags) => readJsonObjectFlag(flags, "composition") },
62
63
  client_domain: { flag: "client-domain" },
63
64
  dry_run: { flag: "dry-run", type: "bool" },
64
65
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.33.11",
3
+ "version": "0.34.0",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",