@letterstory/cli 0.82.2 → 0.82.3

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
@@ -57,7 +57,7 @@ import {
57
57
  } from "./commands.mjs";
58
58
 
59
59
  // Keep in sync with cli/package.json.
60
- export const VERSION = "0.82.2";
60
+ export const VERSION = "0.82.3";
61
61
 
62
62
  // Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
63
63
  // can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
@@ -333,10 +333,16 @@ Collections:
333
333
  collections autopen <id> [--template <key>|none] [--kernel-family <name>|org]
334
334
  [--mention off|subtle|woven] [--outline-depth barebones|fleshed_out]
335
335
  [--direction-gate true|false] [--auto-start true|false]
336
+ [--prefer <site> …|none]
336
337
  The Auto-Pen settings the app's panel writes. --template binds a content
337
338
  template (from 'templates list') as this collection's default, which is how
338
339
  a template you author here actually gets used. A patch: unset flags are
339
- left alone.
340
+ left alone. --prefer REPLACES the preferred sources: SITES research
341
+ searches for every post's topic and reaches for first when a figure fits
342
+ (a URL is saved as its site) — your own site included; 'none' clears them.
343
+ collections cite-preferred <id> [--limit <n>] [--dry-run]
344
+ Bring published posts up to the preferred sources: each eligible post gets
345
+ at most one preferred figure, linked on the figure, then republishes.
340
346
 
341
347
  Authors (the per-collection author bank — recurring bylines):
342
348
  authors list <collection-id> List the bank + byline distribution
@@ -452,7 +458,7 @@ Ranking targets (what the org wants to be THE answer for — company-wide or on
452
458
  Arenas (adversarial clusters of research agents -> a sourced, fact-checked outline):
453
459
  arena run --topic <text> [--source <url> …] [--must-include <text> …] [--depth barebones|fleshed_out]
454
460
  [--gate] [--no-verify] [--provider native|exa|youcom|context-dev] [--mention off|subtle|woven]
455
- [--context-file ctx.json] [--block <name> …] [--block-domain <domain> …]
461
+ [--context-file ctx.json] [--block <name> …] [--block-domain <domain> …] [--prefer <site> …]
456
462
  [--arena <arena-id>] [--article <uuid>] [--no-wait]
457
463
  Start a run and follow it. --article makes
458
464
  it HOSTED (outline written into that post,
@@ -22,6 +22,7 @@ import {
22
22
  toolField,
23
23
  followJob,
24
24
  argsFromFlags,
25
+ preferredSourcesFlag,
25
26
  } from "./shared.mjs";
26
27
 
27
28
  /** `--<flag> <path>` → the file's parsed JSON, or undefined when the flag is not set. */
@@ -75,6 +76,8 @@ const RUN_OPTIONS = {
75
76
  context: { value: (flags) => readJsonObjectFlag(flags, "context") },
76
77
  blocked_mentions: { flag: "block", type: "list" },
77
78
  blocked_domains: { flag: "block-domain", type: "list" },
79
+ // Pages (read every run) and sites research reaches for first; `--prefer none` declares none.
80
+ preferred_sources: { value: (flags) => preferredSourcesFlag(flags.prefer) },
78
81
  };
79
82
 
80
83
  /**
@@ -2,7 +2,7 @@
2
2
  // Ported from phantomstory-cli's collections.ts.
3
3
 
4
4
  import { CliError } from "../client.mjs";
5
- import { flagStr, printResult, ok, argsFromFlags } from "./shared.mjs";
5
+ import { flagStr, printResult, ok, argsFromFlags, preferredSourcesFlag } from "./shared.mjs";
6
6
  import { cmdCollection } from "./collection.mjs";
7
7
 
8
8
  // `--flag none|off|""` clears a nullable field; the server takes null for that.
@@ -47,6 +47,11 @@ export const ARGS = {
47
47
  regenerate_topic_map: { value: (flags) => (flags["no-topic-map"] === undefined ? undefined : false) },
48
48
  stock_headlines: { value: (flags) => (flags["no-headlines"] === undefined ? undefined : false) },
49
49
  },
50
+ cite_preferred_sources: {
51
+ collection_id: { positional: 0, label: "collection-id", required: true },
52
+ limit: { flag: "limit", type: "num" },
53
+ dry_run: { value: (flags) => (flags["dry-run"] === true ? true : undefined) },
54
+ },
50
55
  update_auto_pen_config: {
51
56
  collection_id: { positional: 0, label: "collection-id", required: true },
52
57
  // `--template none` clears the binding; the server takes null for that.
@@ -69,6 +74,8 @@ export const ARGS = {
69
74
  },
70
75
  mention: { object: { degree: "mention" } },
71
76
  auto_start_on_curate: stringBool("auto-start"),
77
+ // Replaces the collection's preferred sources; `--prefer none` clears them.
78
+ preferred_sources: { value: (flags) => preferredSourcesFlag(flags.prefer) },
72
79
  },
73
80
  };
74
81
 
@@ -99,9 +106,11 @@ export async function cmdCollections(ctx) {
99
106
  return collectionsAutoPen(rest);
100
107
  case "activate":
101
108
  return collectionsActivate(rest);
109
+ case "cite-preferred":
110
+ return collectionsCitePreferred(rest);
102
111
  default:
103
112
  throw new CliError(
104
- `Unknown collections subcommand: ${sub ?? "(none)"}. Try: list, new, update, delete, assign, autopen, activate`
113
+ `Unknown collections subcommand: ${sub ?? "(none)"}. Try: list, new, update, delete, assign, autopen, activate, cite-preferred`
105
114
  );
106
115
  }
107
116
  }
@@ -129,6 +138,26 @@ async function collectionsActivate(ctx) {
129
138
  return 0;
130
139
  }
131
140
 
141
+ /**
142
+ * `collections cite-preferred <id> [--limit <n>] [--dry-run]` — bring the collection's published
143
+ * posts up to its preferred sources: each eligible post places at most one preferred figure and
144
+ * republishes. The same function as the app's "Apply to published posts".
145
+ */
146
+ async function collectionsCitePreferred(ctx) {
147
+ const { client, positionals, flags, io } = ctx;
148
+ const args = argsFromFlags(ARGS.cite_preferred_sources, flags, positionals);
149
+ const result = await client.callTool("cite_preferred_sources", args);
150
+ const n = result?.started?.length ?? 0;
151
+ ok(
152
+ ctx,
153
+ result?.dry_run
154
+ ? `Would run ${n} post${n === 1 ? "" : "s"}; ${result?.skipped?.length ?? 0} skipped.`
155
+ : `Started ${n} post${n === 1 ? "" : "s"}${result?.republishes === false ? " (changes wait on the draft for approval)" : ""}; ${result?.skipped?.length ?? 0} skipped.`
156
+ );
157
+ printResult(io, flags, result);
158
+ return 0;
159
+ }
160
+
132
161
  async function collectionsUpdate(ctx) {
133
162
  const { client, positionals, flags, io } = ctx;
134
163
  const patch = argsFromFlags(ARGS.update_collection, flags, positionals);
@@ -166,7 +195,7 @@ async function collectionsAutoPen(ctx) {
166
195
  const patch = argsFromFlags(ARGS.update_auto_pen_config, flags, positionals);
167
196
  if (Object.keys(patch).length === 1) {
168
197
  throw new CliError(
169
- "Nothing to change. Pass at least one of --template, --kernel-family, --mention, --outline-depth, --direction-gate, --auto-start."
198
+ "Nothing to change. Pass at least one of --template, --kernel-family, --mention, --outline-depth, --direction-gate, --auto-start, --prefer."
170
199
  );
171
200
  }
172
201
 
@@ -32,6 +32,20 @@ export function flagList(v) {
32
32
  return typeof v === "string" ? [v] : [];
33
33
  }
34
34
 
35
+ /**
36
+ * `--prefer <site>` (repeatable or comma-separated) → preferred_sources `[{ url }]` (a site; a URL is read as its site); `--prefer none`
37
+ * → `[]` (clears a standing list); absent → undefined.
38
+ */
39
+ export function preferredSourcesFlag(v) {
40
+ if (v === undefined) return undefined;
41
+ const list = flagList(v)
42
+ .flatMap((x) => x.split(","))
43
+ .map((x) => x.trim())
44
+ .filter(Boolean);
45
+ if (list.length === 1 && list[0] === "none") return [];
46
+ return list.map((url) => ({ url }));
47
+ }
48
+
35
49
  export function requireFlag(flags, name) {
36
50
  const v = flagStr(flags[name]);
37
51
  if (v === undefined) throw new CliError(`Missing required --${name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.82.2",
3
+ "version": "0.82.3",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",