@letterstory/cli 0.33.10 → 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 +8 -2
- package/lib/cli.mjs +8 -6
- package/lib/commands/autopen.mjs +1 -1
- package/lib/commands/kernel.mjs +21 -36
- package/lib/commands/phantom.mjs +1 -0
- package/lib/commands/strategy.mjs +15 -1
- package/package.json +1 -1
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
|
|
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
|
-
--
|
|
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.
|
|
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.
|
|
@@ -278,7 +278,7 @@ Strategy & onboarding:
|
|
|
278
278
|
strategy company get | set [--name] [--domain] [--market <cc>] [--manifesto <text>|--manifesto-file <path>]
|
|
279
279
|
strategy positioning get | set [--topic <t> …] [--stance <s> …] [--avoid <t> …]
|
|
280
280
|
[--clear-topics] [--clear-stances] [--clear-avoid]
|
|
281
|
-
strategy competitors list | add <name> <domain>
|
|
281
|
+
strategy competitors list | add <name> <domain> | topics topics = every tracked competitor's articles, ranked
|
|
282
282
|
strategy sitemap --collection <uuid> --url <sitemap_url> [--sub <url> …] [--pattern <glob>] [--posts-only]
|
|
283
283
|
strategy topics list --collection <uuid> List the topic map's clusters/spokes
|
|
284
284
|
strategy topics set --collection <uuid> (--topic <topic-id> | --suggestion <suggestion-id>)
|
|
@@ -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
|
|
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
|
|
341
|
-
|
|
342
|
-
|
|
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
|
package/lib/commands/autopen.mjs
CHANGED
|
@@ -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
|
|
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
|
package/lib/commands/kernel.mjs
CHANGED
|
@@ -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
|
-
//
|
|
115
|
-
//
|
|
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", "
|
|
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,
|
|
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
|
-
|
|
362
|
-
|
|
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);
|
package/lib/commands/phantom.mjs
CHANGED
|
@@ -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
|
},
|
|
@@ -46,6 +46,7 @@ export const ARGS = {
|
|
|
46
46
|
name: { positional: 0, label: "name", required: true },
|
|
47
47
|
domain: { positional: 1, label: "domain", required: true },
|
|
48
48
|
},
|
|
49
|
+
list_competitor_topics: {},
|
|
49
50
|
import_sitemap: {
|
|
50
51
|
collection_id: { flag: "collection", required: true },
|
|
51
52
|
sitemap_url: { flag: "url", required: true },
|
|
@@ -173,8 +174,10 @@ async function strategyCompetitors(ctx) {
|
|
|
173
174
|
return competitorsList(rest);
|
|
174
175
|
case "add":
|
|
175
176
|
return competitorsAdd(rest);
|
|
177
|
+
case "topics":
|
|
178
|
+
return competitorsTopics(rest);
|
|
176
179
|
default:
|
|
177
|
-
throw new CliError(`Unknown strategy competitors subcommand: ${sub ?? "(none)"}. Try: list, add`);
|
|
180
|
+
throw new CliError(`Unknown strategy competitors subcommand: ${sub ?? "(none)"}. Try: list, add, topics`);
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
|
|
@@ -194,6 +197,17 @@ async function competitorsAdd(ctx) {
|
|
|
194
197
|
return 0;
|
|
195
198
|
}
|
|
196
199
|
|
|
200
|
+
// Every tracked competitor's articles in one ranked feed — the CLI/agent
|
|
201
|
+
// equivalent of the Topics tab's "What they publish" card (list_competitor_topics
|
|
202
|
+
// on the registry). For one specific competitor's full history, scrape it first
|
|
203
|
+
// via the API/MCP `scrape_competitor` / `get_competitor_topics` tools.
|
|
204
|
+
async function competitorsTopics(ctx) {
|
|
205
|
+
const { client, flags, io } = ctx;
|
|
206
|
+
const result = await client.callTool("list_competitor_topics", {});
|
|
207
|
+
printResult(io, flags, result);
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
197
211
|
// -- sitemap -------------------------------------------------------------
|
|
198
212
|
|
|
199
213
|
async function strategySitemap(ctx) {
|