@letterstory/cli 0.177.10 → 0.177.11

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
@@ -68,6 +68,7 @@ import {
68
68
  cmdAsk,
69
69
  cmdIdeas,
70
70
  cmdPlan,
71
+ cmdMemory,
71
72
  cmdSite,
72
73
  cmdEvals,
73
74
  cmdOfferings,
@@ -87,7 +88,7 @@ import {
87
88
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
88
89
 
89
90
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
90
- export const VERSION = "0.177.10";
91
+ export const VERSION = "0.177.11";
91
92
 
92
93
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
93
94
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -435,6 +436,8 @@ export const BOOLEAN_FLAGS = new Set([
435
436
  "dry",
436
437
  // admin billing emails --bounced
437
438
  "bounced",
439
+ // `memory ingest --include-website` reads the org's own site too: value-less.
440
+ "include-website",
438
441
  // models add: train once the links are added. models confirm: keep the found links not named, or don't train yet.
439
442
  "train",
440
443
  "keep-rest",
@@ -1806,6 +1809,10 @@ Insights:
1806
1809
  plan later remove <queued-id> Take a queued row back off
1807
1810
  plan strategies [--site host] [--archived] The goals typed on Ideas, each a strategy
1808
1811
  plan strategy add "<words…>" [--site host] Keep a goal in your words as a strategy
1812
+ plan strategy archive <strategy-id> [--restore] Put one away (its card leaves the Plan), or bring it back
1813
+ memory ingest [--text|--text-file <path|->] [--link <url>]… [--include-website] [--mcp-url <url>] Push context to Memory: a reviewable proposal
1814
+ memory apply <ingestion-id> --changes <id>,<id> Save the proposed changes you accept
1815
+ memory add --section <id> --body <text> [--title] [--origin] File one entry in a custom memory section
1809
1816
  plan strategy archive <strategy-id> [--restore] Put one away (its goal leaves Ideas), or bring it back
1810
1817
  insights summary [--period …] [--part <block>]... Measure Overview: AI named/cited rates, share of voice, search, targets
1811
1818
  insights reads [--period …] [--site all|own|phantoms|<key>]... [--path <path>] [--engine <operator>]
@@ -2203,6 +2210,7 @@ const CLIENT_COMMANDS = {
2203
2210
  ask: cmdAsk,
2204
2211
  ideas: cmdIdeas,
2205
2212
  plan: cmdPlan,
2213
+ memory: cmdMemory,
2206
2214
  site: cmdSite,
2207
2215
  evals: cmdEvals,
2208
2216
  visits: cmdVisits,
@@ -0,0 +1,88 @@
1
+ // `memory` — push context into Memory from a terminal, the way an agent does it over the MCP: thin wrappers over
2
+ // the same tools, nothing of their own.
3
+ // memory ingest [--text <t>|--text-file <path|->] [--link <url>]… [--include-website] [--mcp-url <url> [--mcp-token <t>] [--mcp-focus <words>]]
4
+ // → ingest_context (a reviewable proposal; nothing is saved yet)
5
+ // memory apply <ingestion-id> --changes <change-id>[,<change-id>…] → apply_context (saves the accepted changes)
6
+ // memory add --section <id> (--body <t>|--body-file <path|->) [--title <t>] [--origin <where it was read>]
7
+ // → add_memory_entry (one entry in a custom section)
8
+
9
+ import { CliError } from "../client.mjs";
10
+ import { argsFromFlags, flagCsv, flagStr, printResult, readTextInput, refuseUnknownFlags } from "./shared.mjs";
11
+
12
+ export const ARGS = {
13
+ ingest_context: {
14
+ text: { value: (flags) => readTextInput(flags, "text", "text-file") },
15
+ links: { flag: "link", type: "list" },
16
+ include_website: { flag: "include-website", type: "bool" },
17
+ mcp_servers: {
18
+ value: (flags) => {
19
+ const url = flagStr(flags["mcp-url"]);
20
+ const token = flagStr(flags["mcp-token"]);
21
+ const focus = flagStr(flags["mcp-focus"]);
22
+ if (!url) return undefined;
23
+ return [{ url, ...(token ? { authorization_token: token } : {}), ...(focus ? { focus } : {}) }];
24
+ },
25
+ },
26
+ },
27
+ apply_context: {
28
+ ingestion_id: { positional: 0, required: true, label: "ingestion-id" },
29
+ accepted_change_ids: { value: (flags) => flagCsv(flags.changes) },
30
+ },
31
+ add_memory_entry: {
32
+ section_id: { flag: "section", required: true },
33
+ body: { value: (flags) => readTextInput(flags, "body", "body-file") },
34
+ title: "title",
35
+ origin: "origin",
36
+ },
37
+ };
38
+
39
+ export async function cmdMemory(ctx) {
40
+ const { client, flags, io } = ctx;
41
+ const sub = ctx.positionals[0];
42
+ const rest = ctx.positionals.slice(1);
43
+ if (sub === "ingest") {
44
+ refuseUnknownFlags(ctx, ARGS.ingest_context, ["text-file", "mcp-url", "mcp-token", "mcp-focus"]);
45
+ const args = argsFromFlags(ARGS.ingest_context, flags, rest);
46
+ if (!args.text && !args.links?.length && !args.include_website && !args.mcp_servers) {
47
+ throw new CliError(
48
+ "Give it something to read: --text, --text-file, --link, --include-website or --mcp-url."
49
+ );
50
+ }
51
+ const result = await client.callTool("ingest_context", args);
52
+ printResult(io, flags, result, (r) =>
53
+ [
54
+ `Pass ${r.ingestion_id}: ${r.summary}`,
55
+ `${r.changes.length} proposed change${r.changes.length === 1 ? "" : "s"}. Nothing is saved yet.`,
56
+ ...r.changes.map((c) => ` ${c.id} [${c.area}] ${c.label}`),
57
+ r.changes.length ? `Save the ones you accept: memory apply ${r.ingestion_id} --changes <id>,<id>` : "",
58
+ ]
59
+ .filter(Boolean)
60
+ .join("\n")
61
+ );
62
+ return 0;
63
+ }
64
+ if (sub === "apply") {
65
+ refuseUnknownFlags(ctx, ARGS.apply_context, ["changes"]);
66
+ const args = argsFromFlags(ARGS.apply_context, flags, rest);
67
+ if (!args.accepted_change_ids?.length) throw new CliError("Say which changes to save: --changes <id>[,<id>…].");
68
+ const result = await client.callTool("apply_context", args);
69
+ printResult(
70
+ io,
71
+ flags,
72
+ result,
73
+ (r) => `Saved ${r.applied_change_ids.length} change${r.applied_change_ids.length === 1 ? "" : "s"}.`
74
+ );
75
+ return 0;
76
+ }
77
+ if (sub === "add") {
78
+ refuseUnknownFlags(ctx, ARGS.add_memory_entry, ["body-file"]);
79
+ const args = argsFromFlags(ARGS.add_memory_entry, flags, rest);
80
+ if (!args.body) throw new CliError("Say what to remember: --body <text> or --body-file <path|->.");
81
+ const result = await client.callTool("add_memory_entry", args);
82
+ printResult(io, flags, result, (r) => `Filed${r.entry?.id ? `: ${r.entry.id}` : ""}.`);
83
+ return 0;
84
+ }
85
+ throw new CliError(
86
+ "Usage: memory ingest [--text|--text-file|--link|--include-website|--mcp-url] | memory apply <ingestion-id> --changes <ids> | memory add --section <id> --body <text>"
87
+ );
88
+ }
package/lib/commands.mjs CHANGED
@@ -48,6 +48,7 @@ export * from "./commands/repo.mjs";
48
48
  export * from "./commands/build.mjs";
49
49
  export * from "./commands/ask.mjs";
50
50
  export * from "./commands/plan.mjs";
51
+ export * from "./commands/memory.mjs";
51
52
  export * from "./commands/site.mjs";
52
53
  export * from "./commands/evals.mjs";
53
54
  export * from "./commands/visits.mjs";
package/lib/documents.mjs CHANGED
@@ -59,6 +59,8 @@ export const DOCUMENT_FLAG_PAIRS = [
59
59
  ["body", "file"],
60
60
  ["brief", "brief-file"],
61
61
  ["text", "text-file"],
62
+ // `memory add --body-file`: one entry's prose.
63
+ ["body", "body-file"],
62
64
  // An offering's (or the company's) manifesto is prose — a PDF or a .docx is fair game.
63
65
  ["manifesto", "manifesto-file"],
64
66
  // A repo change's brief (repo.mjs `repo start`) — prose, so a .docx or PDF spec is fine.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.177.10",
3
+ "version": "0.177.11",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",