@letterstory/cli 0.33.4 → 0.33.6

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 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.4";
48
+ export const VERSION = "0.33.6";
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.
@@ -170,6 +170,12 @@ export const ARGS = {
170
170
  // `[{"name":"...","treatment":"never"|"mention_once"|"mention_often"|"lead_company",
171
171
  // "linkable":false}, ...]` (linkable optional, default true).
172
172
  entities: { value: (flags) => readJsonFileFlag(flags, "entities-file") },
173
+ // Your own guidance for the writer (2026-09-25): how to write, not what is true. Sent
174
+ // on its own field and appended AFTER the claims are read from the input, so nothing in
175
+ // it becomes a claim the article must carry. Inline for a sentence, a file for a page.
176
+ directives: {
177
+ value: (flags) => flagStr(flags.directives) ?? readTextFileFlag(flags, "directives-file"),
178
+ },
173
179
  },
174
180
  create_kernel_endpoint: {
175
181
  name: { flag: "name", required: true },
@@ -192,6 +198,26 @@ export const ARGS = {
192
198
  endpoint_id: { positional: 1, label: "endpoint-id", required: true },
193
199
  config: { value: (flags) => readJsonFileFlag(flags, "file") },
194
200
  },
201
+ // The eval itself is one JSON file (--file), not flags — `detector` is a discriminated
202
+ // union with a different shape per kind, which flags-per-field cannot represent without
203
+ // nine different flag sets. Each field below reads the same file; a small re-parse per
204
+ // field, harmless for a one-shot CLI command against a small file.
205
+ add_kernel_custom_eval: {
206
+ kernel_id: { positional: 1, label: "kernel-id", required: true },
207
+ // `value` sources aren't checked against `required` (see shared.mjs readArgSource) —
208
+ // a missing id/name/detector reaches the tool call and comes back as the tool's own
209
+ // "Invalid arguments" error, same as any other malformed submission.
210
+ id: { value: (flags) => readJsonFileFlag(flags, "file")?.id },
211
+ name: { value: (flags) => readJsonFileFlag(flags, "file")?.name },
212
+ description: { value: (flags) => readJsonFileFlag(flags, "file")?.description },
213
+ detector: { value: (flags) => readJsonFileFlag(flags, "file")?.detector },
214
+ repair: { value: (flags) => readJsonFileFlag(flags, "file")?.repair },
215
+ regimes: { value: (flags) => readJsonFileFlag(flags, "file")?.regimes },
216
+ },
217
+ remove_kernel_custom_eval: {
218
+ kernel_id: { positional: 1, label: "kernel-id", required: true },
219
+ eval_id: { positional: 2, label: "eval-id", required: true },
220
+ },
195
221
  };
196
222
 
197
223
  export async function cmdKernel(ctx) {
@@ -209,6 +235,8 @@ export async function cmdKernel(ctx) {
209
235
  return kernelEndpoints(rest);
210
236
  case "endpoint":
211
237
  return kernelEndpoint(rest);
238
+ case "eval":
239
+ return kernelEval(rest);
212
240
  case "write":
213
241
  return kernelWrite(rest);
214
242
  case "polish":
@@ -221,7 +249,7 @@ export async function cmdKernel(ctx) {
221
249
  return kernelBalance(rest);
222
250
  default:
223
251
  throw new CliError(
224
- `Unknown kernel subcommand: ${sub ?? "(none)"}. Try: list, endpoints, endpoint, write, polish, run-status, runs, balance, run, status`
252
+ `Unknown kernel subcommand: ${sub ?? "(none)"}. Try: list, endpoints, endpoint, eval, write, polish, run-status, runs, balance, run, status`
225
253
  );
226
254
  }
227
255
  }
@@ -396,6 +424,54 @@ async function kernelEndpoint(ctx) {
396
424
  throw new CliError(`Unknown kernel endpoint action: ${action ?? "(none)"}. Try: create, delete, config`);
397
425
  }
398
426
 
427
+ /**
428
+ * `kernel eval add|rm` — an org-owned kernel's `rules.custom[]`, its own evals filed
429
+ * alongside the kernels service's built-in rules.
430
+ *
431
+ * `add` reads the whole eval (id/name/description/detector/repair/regimes) from one
432
+ * JSON file rather than flags: `detector` is a discriminated union with a different
433
+ * shape per kind, which a flag-per-field mapping (the pattern the rest of this file
434
+ * uses) cannot represent without nine different flag sets. Every genuinely new id
435
+ * lands at status "proposed" on the kernels service and is never auto-activated —
436
+ * this command cannot change that, only file the proposal.
437
+ */
438
+ async function kernelEval(ctx) {
439
+ const { client, positionals, flags, io } = ctx;
440
+ const action = positionals[0];
441
+ if (action === "add") {
442
+ if (flagStr(flags.file) === undefined) {
443
+ throw new CliError(
444
+ "Provide the eval as JSON with --file <path> (id/name/description/detector/repair/regimes)."
445
+ );
446
+ }
447
+ const args = argsFromFlags(ARGS.add_kernel_custom_eval, flags, positionals);
448
+ const result = await client.callTool("add_kernel_custom_eval", args);
449
+ const filing = toolField(result, "custom_evals");
450
+ if (filing?.filed?.includes(args.id)) ok(ctx, `Filed "${args.id}" as a proposal (status: proposed).`);
451
+ else if (filing?.skipped?.includes(args.id))
452
+ ok(
453
+ ctx,
454
+ `"${args.id}" was already filed from an earlier submission — this only updated the kernel's own declaration.`
455
+ );
456
+ printResult(io, flags, result);
457
+ return 0;
458
+ }
459
+ if (action === "rm" || action === "remove" || action === "delete") {
460
+ const args = argsFromFlags(ARGS.remove_kernel_custom_eval, flags, positionals);
461
+ if (!flags.yes) {
462
+ io.error(
463
+ `This only stops declaring the eval on this kernel — it does NOT retire anything already filed or approved server-side. Re-run with --yes to confirm: ${ctx.bin} kernel eval rm ${args.kernel_id} ${args.eval_id} --yes`
464
+ );
465
+ return 1;
466
+ }
467
+ const result = await client.callTool("remove_kernel_custom_eval", args);
468
+ ok(ctx, `Stopped declaring "${args.eval_id}" on this kernel.`);
469
+ printResult(io, flags, result);
470
+ return 0;
471
+ }
472
+ throw new CliError(`Unknown kernel eval action: ${action ?? "(none)"}. Try: add, rm`);
473
+ }
474
+
399
475
  /**
400
476
  * The OTHER door: run the same pipeline against a document you already have, instead of
401
477
  * writing a new one. Decomposed into a brief and GENERATED FRESH (content_kind: "article",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.33.4",
3
+ "version": "0.33.6",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",