@letterstory/cli 0.25.4 → 0.25.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 +15 -5
- package/lib/commands/seers.mjs +202 -64
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from "./commands.mjs";
|
|
45
45
|
|
|
46
46
|
// Keep in sync with cli/package.json.
|
|
47
|
-
export const VERSION = "0.25.
|
|
47
|
+
export const VERSION = "0.25.6";
|
|
48
48
|
|
|
49
49
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
50
50
|
// can't accidentally swallow the id as --json's value.
|
|
@@ -455,12 +455,22 @@ Seers (event-driven signals -> drafts):
|
|
|
455
455
|
seers list List seers with recent activity
|
|
456
456
|
seers get <seer-id> One seer's full config + poll state
|
|
457
457
|
seers providers Provider catalog: config fields + behavior keys
|
|
458
|
-
seers create --name <n> --
|
|
458
|
+
seers create --name <n> --provider <p> [--collection <uuid>]
|
|
459
459
|
[--mode suggest|auto] [--interval <min>] [--config '<json>'|--config-file <path>]
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
--collection is optional: omit it for a
|
|
461
|
+
detect-only seer (scans + judges, never
|
|
462
|
+
produces). --mode auto requires one.
|
|
463
|
+
seers update <seer-id> [--name] [--collection <uuid> | --clear-collection] [--mode]
|
|
464
|
+
[--enabled true|false] [--interval <min>] [--config '<json>'|--config-file <path>]
|
|
462
465
|
seers delete <seer-id> --yes
|
|
463
|
-
seers run <seer-id>
|
|
466
|
+
seers run <seer-id> [--dry-run] [--no-produce] [--async]
|
|
467
|
+
Run now — THE DOOR (one seer run from the balance).
|
|
468
|
+
--dry-run: scan + judge, persist nothing, print signals
|
|
469
|
+
--no-produce: detect + record events only
|
|
470
|
+
--async: queue for the heartbeat, print a run id
|
|
471
|
+
seers run-status <run-id> One run: status, counts, constraints, verification
|
|
472
|
+
seers runs [--seer <seer-id>] [--limit <n>] Recent runs (origin, caller, verification)
|
|
473
|
+
seers balance Seer runs granted / used / available
|
|
464
474
|
seers events <seer-id> [--limit <n>] [--offset <n>] Detected signals w/ dedup + relevance
|
|
465
475
|
seers dismiss <seer-id> <event-id> Dismiss a signal (feeds the relevance judge)
|
|
466
476
|
seers headlines [--collection <uuid>] Seer-sourced suggestion feed
|
package/lib/commands/seers.mjs
CHANGED
|
@@ -7,12 +7,24 @@
|
|
|
7
7
|
// Connections are both headless: `seers connect github --token` (a PAT) and
|
|
8
8
|
// `seers connect notion --token` (a Notion internal-integration secret; run
|
|
9
9
|
// `seers connect notion` with no token for how to mint one and which pages to share).
|
|
10
|
-
//
|
|
11
|
-
// web scans can take minutes
|
|
10
|
+
// `seers run` is THE DOOR (lib/seers/run.ts): a direct, metered run (`seers balance`).
|
|
11
|
+
// Synchronous by default — news/regulation web scans can take minutes; pass --async to
|
|
12
|
+
// queue it and poll with `seers run-status`, --dry-run to see what it would record
|
|
13
|
+
// without recording anything, --no-produce to detect and record events only.
|
|
12
14
|
|
|
13
15
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
14
16
|
import { CliError } from "../client.mjs";
|
|
15
|
-
import { flagStr,
|
|
17
|
+
import { flagStr, flagBool, requirePositional, printResult, ok, argsFromFlags } from "./shared.mjs";
|
|
18
|
+
|
|
19
|
+
// A single-value flag the API clears with an explicit `null` (same idiom as
|
|
20
|
+
// strategy.mjs's clearableList): `--clear-collection` sends null, `--collection <uuid>`
|
|
21
|
+
// sends it, neither sends nothing (no change).
|
|
22
|
+
const clearableValue = (flag, clearFlag) => ({
|
|
23
|
+
value: (flags) => {
|
|
24
|
+
if (flags[clearFlag]) return null;
|
|
25
|
+
return flagStr(flags[flag]);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
16
28
|
|
|
17
29
|
/**
|
|
18
30
|
* How the ratcheted commands in this file source their tool inputs — see `argsFromFlags` in
|
|
@@ -20,11 +32,93 @@ import { flagStr, flagNum, flagBool, requirePositional, printResult, compact, ok
|
|
|
20
32
|
* ratchet and stays in UNMAPPED_TOOLS; that list only shrinks.
|
|
21
33
|
*/
|
|
22
34
|
export const ARGS = {
|
|
35
|
+
// THE DOOR. `seers run <id>` — a direct, metered run. `--dry-run` scans + judges and
|
|
36
|
+
// persists nothing (returns the signals it would have recorded, with verdicts);
|
|
37
|
+
// `--no-produce` detects and records events only; `--async` queues it and prints a
|
|
38
|
+
// run id to poll with `seers run-status`.
|
|
23
39
|
run_seer: {
|
|
24
40
|
seer_id: { positional: 0, label: "seer-id", required: true },
|
|
41
|
+
dry_run: { flag: "dry-run", type: "bool" },
|
|
42
|
+
produce: { value: (flags) => (flags["no-produce"] === true ? false : undefined) },
|
|
43
|
+
async: { flag: "async", type: "bool" },
|
|
44
|
+
},
|
|
45
|
+
get_seer_run: {
|
|
46
|
+
run_id: { positional: 0, label: "run-id", required: true },
|
|
47
|
+
},
|
|
48
|
+
list_seer_runs: {
|
|
49
|
+
seer_id: { flag: "seer" },
|
|
50
|
+
limit: { flag: "limit", type: "num" },
|
|
51
|
+
},
|
|
52
|
+
get_seer_run_balance: {},
|
|
53
|
+
list_seers: {},
|
|
54
|
+
get_seer: {
|
|
55
|
+
seer_id: { positional: 0, label: "seer-id", required: true },
|
|
56
|
+
},
|
|
57
|
+
create_seer: {
|
|
58
|
+
name: { flag: "name", required: true },
|
|
59
|
+
// Optional (2026-09-18): omit it for a detect-only seer — it can scan and judge
|
|
60
|
+
// forever but never produce (auto mode is rejected without one).
|
|
61
|
+
collection_id: { flag: "collection" },
|
|
62
|
+
provider: { flag: "provider", required: true },
|
|
63
|
+
mode: { flag: "mode", enum: ["suggest", "auto"] },
|
|
64
|
+
poll_interval_minutes: { flag: "interval", type: "num" },
|
|
65
|
+
// The whole config document — provider source params plus the behavior keys
|
|
66
|
+
// (auto_draft_scope, template_key, mention_degree, kernel_family, relevance{…}).
|
|
67
|
+
config: { value: (flags) => readJsonObjectFlag(flags, "config") ?? {} },
|
|
68
|
+
},
|
|
69
|
+
update_seer: {
|
|
70
|
+
seer_id: { positional: 0, label: "seer-id", required: true },
|
|
71
|
+
name: { flag: "name" },
|
|
72
|
+
// --collection <uuid> sets it, --clear-collection sends null (the seer becomes
|
|
73
|
+
// detect-only — rejected if it's mode "auto"), neither leaves it unchanged.
|
|
74
|
+
collection_id: clearableValue("collection", "clear-collection"),
|
|
75
|
+
mode: { flag: "mode", enum: ["suggest", "auto"] },
|
|
76
|
+
enabled: { value: (flags) => readEnabledFlag(flags) },
|
|
77
|
+
poll_interval_minutes: { flag: "interval", type: "num" },
|
|
78
|
+
config: { value: (flags) => readJsonObjectFlag(flags, "config") },
|
|
79
|
+
},
|
|
80
|
+
delete_seer: {
|
|
81
|
+
seer_id: { positional: 0, label: "seer-id", required: true },
|
|
82
|
+
},
|
|
83
|
+
list_seer_events: {
|
|
84
|
+
seer_id: { positional: 0, label: "seer-id", required: true },
|
|
85
|
+
limit: { flag: "limit", type: "num" },
|
|
86
|
+
offset: { flag: "offset", type: "num" },
|
|
87
|
+
},
|
|
88
|
+
dismiss_seer_event: {
|
|
89
|
+
seer_id: { positional: 0, label: "seer-id", required: true },
|
|
90
|
+
event_id: { positional: 1, label: "event-id", required: true },
|
|
91
|
+
},
|
|
92
|
+
list_seer_headlines: {
|
|
93
|
+
collection_id: { flag: "collection" },
|
|
94
|
+
limit: { flag: "limit", type: "num" },
|
|
95
|
+
offset: { flag: "offset", type: "num" },
|
|
96
|
+
},
|
|
97
|
+
list_held_drafts: {
|
|
98
|
+
collection_id: { flag: "collection" },
|
|
99
|
+
},
|
|
100
|
+
resolve_held_draft: {
|
|
101
|
+
article_id: { positional: 0, label: "article-id", required: true },
|
|
102
|
+
// `seers release <id>` / `seers discard <id>` — the subcommand IS the action.
|
|
103
|
+
action: { value: (_flags, _positionals, extra) => extra.action },
|
|
104
|
+
},
|
|
105
|
+
list_seer_providers: {},
|
|
106
|
+
get_seer_connections: {},
|
|
107
|
+
set_github_connection: {
|
|
108
|
+
access_token: { flag: "token", required: true },
|
|
109
|
+
},
|
|
110
|
+
set_notion_connection: {
|
|
111
|
+
access_token: { flag: "token", required: true },
|
|
25
112
|
},
|
|
26
113
|
};
|
|
27
114
|
|
|
115
|
+
function readEnabledFlag(flags) {
|
|
116
|
+
const enabledRaw = flagStr(flags.enabled);
|
|
117
|
+
if (enabledRaw === undefined) return undefined;
|
|
118
|
+
if (enabledRaw !== "true" && enabledRaw !== "false") throw new CliError('--enabled must be "true" or "false"');
|
|
119
|
+
return enabledRaw === "true";
|
|
120
|
+
}
|
|
121
|
+
|
|
28
122
|
export async function cmdSeers(ctx) {
|
|
29
123
|
const sub = ctx.positionals[0];
|
|
30
124
|
const rest = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
@@ -45,6 +139,13 @@ export async function cmdSeers(ctx) {
|
|
|
45
139
|
return seersDelete(rest);
|
|
46
140
|
case "run":
|
|
47
141
|
return seersRun(rest);
|
|
142
|
+
case "run-status":
|
|
143
|
+
case "run-get":
|
|
144
|
+
return seersRunStatus(rest);
|
|
145
|
+
case "runs":
|
|
146
|
+
return seersRuns(rest);
|
|
147
|
+
case "balance":
|
|
148
|
+
return seersBalance(rest);
|
|
48
149
|
case "events":
|
|
49
150
|
return seersEvents(rest);
|
|
50
151
|
case "dismiss":
|
|
@@ -69,7 +170,7 @@ export async function cmdSeers(ctx) {
|
|
|
69
170
|
return seersApply(rest);
|
|
70
171
|
default:
|
|
71
172
|
throw new CliError(
|
|
72
|
-
`Unknown seers subcommand: ${sub ?? "(none)"}. Try: list, get, create, update, delete, run, events, dismiss, headlines, held, release, discard, providers, connections, connect, export, apply`
|
|
173
|
+
`Unknown seers subcommand: ${sub ?? "(none)"}. Try: list, get, create, update, delete, run, run-status, runs, balance, events, dismiss, headlines, held, release, discard, providers, connections, connect, export, apply`
|
|
73
174
|
);
|
|
74
175
|
}
|
|
75
176
|
}
|
|
@@ -113,7 +214,7 @@ function formatSeerLine(s) {
|
|
|
113
214
|
|
|
114
215
|
async function seersList(ctx) {
|
|
115
216
|
const { client, flags, io } = ctx;
|
|
116
|
-
const result = await client.callTool("list_seers",
|
|
217
|
+
const result = await client.callTool("list_seers", argsFromFlags(ARGS.list_seers, flags));
|
|
117
218
|
printResult(io, flags, result, (r) => {
|
|
118
219
|
const lines = (r.seers ?? []).map(formatSeerLine);
|
|
119
220
|
if (!lines.length) lines.push("No seers yet. Create one with `seers create` (see `seers providers`).");
|
|
@@ -128,36 +229,15 @@ async function seersList(ctx) {
|
|
|
128
229
|
|
|
129
230
|
async function seersGet(ctx) {
|
|
130
231
|
const { client, positionals, flags, io } = ctx;
|
|
131
|
-
const
|
|
132
|
-
const result = await client.callTool("get_seer", { seer_id: seerId });
|
|
232
|
+
const result = await client.callTool("get_seer", argsFromFlags(ARGS.get_seer, flags, positionals));
|
|
133
233
|
printResult(io, flags, result);
|
|
134
234
|
return 0;
|
|
135
235
|
}
|
|
136
236
|
|
|
137
|
-
function seerWriteArgsFromFlags(flags) {
|
|
138
|
-
const enabledRaw = flagStr(flags.enabled);
|
|
139
|
-
if (enabledRaw !== undefined && enabledRaw !== "true" && enabledRaw !== "false") {
|
|
140
|
-
throw new CliError('--enabled must be "true" or "false"');
|
|
141
|
-
}
|
|
142
|
-
return compact({
|
|
143
|
-
name: flagStr(flags.name),
|
|
144
|
-
collection_id: flagStr(flags.collection),
|
|
145
|
-
mode: flagStr(flags.mode),
|
|
146
|
-
poll_interval_minutes: flagNum(flags.interval),
|
|
147
|
-
enabled: enabledRaw === undefined ? undefined : enabledRaw === "true",
|
|
148
|
-
config: readJsonObjectFlag(flags, "config"),
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
237
|
async function seersCreate(ctx) {
|
|
153
238
|
const { client, flags, io } = ctx;
|
|
154
|
-
const args =
|
|
155
|
-
|
|
156
|
-
if (!args.collection_id) throw new CliError("Missing required --collection <uuid>");
|
|
157
|
-
const provider = flagStr(flags.provider);
|
|
158
|
-
if (!provider) throw new CliError("Missing required --provider (run `seers providers` for the catalog)");
|
|
159
|
-
delete args.enabled; // create has no enabled field; new seers start enabled
|
|
160
|
-
const result = await client.callTool("create_seer", { ...args, provider, config: args.config ?? {} });
|
|
239
|
+
const args = argsFromFlags(ARGS.create_seer, flags);
|
|
240
|
+
const result = await client.callTool("create_seer", args);
|
|
161
241
|
ok(ctx, "Seer created.");
|
|
162
242
|
printResult(io, flags, result, (r) => formatSeerLine(r.seer));
|
|
163
243
|
return 0;
|
|
@@ -165,12 +245,11 @@ async function seersCreate(ctx) {
|
|
|
165
245
|
|
|
166
246
|
async function seersUpdate(ctx) {
|
|
167
247
|
const { client, positionals, flags, io } = ctx;
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
if (Object.keys(args).length === 0) {
|
|
248
|
+
const args = argsFromFlags(ARGS.update_seer, flags, positionals);
|
|
249
|
+
if (Object.keys(args).length <= 1) {
|
|
171
250
|
throw new CliError("Nothing to update — pass --name/--collection/--mode/--enabled/--interval/--config");
|
|
172
251
|
}
|
|
173
|
-
const result = await client.callTool("update_seer",
|
|
252
|
+
const result = await client.callTool("update_seer", args);
|
|
174
253
|
ok(ctx, "Seer updated.");
|
|
175
254
|
printResult(io, flags, result, (r) => formatSeerLine(r.seer));
|
|
176
255
|
return 0;
|
|
@@ -178,26 +257,84 @@ async function seersUpdate(ctx) {
|
|
|
178
257
|
|
|
179
258
|
async function seersDelete(ctx) {
|
|
180
259
|
const { client, positionals, flags, io } = ctx;
|
|
181
|
-
const seerId = requirePositional(positionals, 0, "seer-id");
|
|
182
260
|
if (!flagBool(flags.yes)) throw new CliError("Deleting a seer removes its detected events. Re-run with --yes.");
|
|
183
|
-
const result = await client.callTool("delete_seer",
|
|
261
|
+
const result = await client.callTool("delete_seer", argsFromFlags(ARGS.delete_seer, flags, positionals));
|
|
184
262
|
ok(ctx, "Seer deleted.");
|
|
185
263
|
printResult(io, flags, result);
|
|
186
264
|
return 0;
|
|
187
265
|
}
|
|
188
266
|
|
|
267
|
+
function describeRun(r) {
|
|
268
|
+
const run = r.run ?? r;
|
|
269
|
+
const counts = `Detected ${r.detected ?? run.detected ?? 0}, produced ${r.produced ?? run.produced ?? 0} (auto-drafted ${
|
|
270
|
+
r.auto_drafted ?? run.auto_drafted ?? 0
|
|
271
|
+
}, suggested-only ${r.suggested_only ?? run.suggested_only ?? 0}, dismissed ${r.dismissed ?? run.dismissed ?? 0}).`;
|
|
272
|
+
const lines = [counts];
|
|
273
|
+
if (run.run_id)
|
|
274
|
+
lines.push(
|
|
275
|
+
`Run ${run.run_id} [${run.status}] origin=${run.origin} dry_run=${run.dry_run} produce=${run.produce}`
|
|
276
|
+
);
|
|
277
|
+
if (run.verification?.checks?.length) {
|
|
278
|
+
lines.push(
|
|
279
|
+
"Verification: " +
|
|
280
|
+
run.verification.checks.map((c) => `${c.id}=${c.verdict}${c.detail ? ` (${c.detail})` : ""}`).join("; ")
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (Array.isArray(run.signals) && run.signals.length) {
|
|
284
|
+
lines.push("", "Signals (dry run — nothing recorded):");
|
|
285
|
+
for (const sig of run.signals) {
|
|
286
|
+
lines.push(
|
|
287
|
+
` [${sig.relevance?.decision ?? "?"} ${
|
|
288
|
+
typeof sig.relevance?.score === "number" ? sig.relevance.score.toFixed(2) : "-"
|
|
289
|
+
}] ${sig.title}${sig.url ? ` ${sig.url}` : ""}`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return lines.join("\n");
|
|
294
|
+
}
|
|
295
|
+
|
|
189
296
|
async function seersRun(ctx) {
|
|
190
297
|
const { client, positionals, flags, io } = ctx;
|
|
191
298
|
const args = argsFromFlags(ARGS.run_seer, flags, positionals);
|
|
192
|
-
|
|
299
|
+
if (!args.async)
|
|
300
|
+
ok(ctx, `${args.dry_run ? "Dry run" : "Polling now"} — web-scan providers can take a few minutes…`);
|
|
193
301
|
const result = await client.callTool("run_seer", args);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
flags,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
302
|
+
if (result?.status === "queued" && result?.run_id) {
|
|
303
|
+
ok(ctx, `Run queued as ${result.run_id}. Poll it with: ${ctx.bin} seers run-status ${result.run_id}`);
|
|
304
|
+
printResult(io, flags, result);
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
printResult(io, flags, result, describeRun);
|
|
308
|
+
return 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function seersRunStatus(ctx) {
|
|
312
|
+
const { client, positionals, flags, io } = ctx;
|
|
313
|
+
const result = await client.callTool("get_seer_run", argsFromFlags(ARGS.get_seer_run, flags, positionals));
|
|
314
|
+
printResult(io, flags, result, describeRun);
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function seersRuns(ctx) {
|
|
319
|
+
const { client, flags, io } = ctx;
|
|
320
|
+
const result = await client.callTool("list_seer_runs", argsFromFlags(ARGS.list_seer_runs, flags));
|
|
321
|
+
printResult(io, flags, result, (r) => {
|
|
322
|
+
const lines = (r.runs ?? []).map(
|
|
323
|
+
(run) =>
|
|
324
|
+
`${run.run_id} [${run.status}] ${run.created_at} ${run.seer_name ?? run.seer_id ?? "?"} origin=${run.origin}${
|
|
325
|
+
run.dry_run ? " dry" : ""
|
|
326
|
+
} detected=${run.detected ?? "-"} produced=${run.produced ?? "-"}`
|
|
327
|
+
);
|
|
328
|
+
if (!lines.length) lines.push("No seer runs yet.");
|
|
329
|
+
return lines.join("\n");
|
|
330
|
+
});
|
|
331
|
+
return 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function seersBalance(ctx) {
|
|
335
|
+
const { client, flags, io } = ctx;
|
|
336
|
+
const result = await client.callTool("get_seer_run_balance", argsFromFlags(ARGS.get_seer_run_balance, flags));
|
|
337
|
+
printResult(io, flags, result, (r) => `Seer runs: ${r.available} available (${r.used} of ${r.granted} used).`);
|
|
201
338
|
return 0;
|
|
202
339
|
}
|
|
203
340
|
|
|
@@ -205,8 +342,7 @@ async function seersRun(ctx) {
|
|
|
205
342
|
|
|
206
343
|
async function seersEvents(ctx) {
|
|
207
344
|
const { client, positionals, flags, io } = ctx;
|
|
208
|
-
const
|
|
209
|
-
const args = compact({ seer_id: seerId, limit: flagNum(flags.limit), offset: flagNum(flags.offset) });
|
|
345
|
+
const args = argsFromFlags(ARGS.list_seer_events, flags, positionals);
|
|
210
346
|
const result = await client.callTool("list_seer_events", args);
|
|
211
347
|
printResult(io, flags, result, (r) => {
|
|
212
348
|
const lines = (r.events ?? []).map((e) => {
|
|
@@ -223,9 +359,10 @@ async function seersEvents(ctx) {
|
|
|
223
359
|
|
|
224
360
|
async function seersDismiss(ctx) {
|
|
225
361
|
const { client, positionals, flags, io } = ctx;
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
362
|
+
const result = await client.callTool(
|
|
363
|
+
"dismiss_seer_event",
|
|
364
|
+
argsFromFlags(ARGS.dismiss_seer_event, flags, positionals)
|
|
365
|
+
);
|
|
229
366
|
ok(ctx, "Event dismissed.");
|
|
230
367
|
printResult(io, flags, result);
|
|
231
368
|
return 0;
|
|
@@ -233,12 +370,7 @@ async function seersDismiss(ctx) {
|
|
|
233
370
|
|
|
234
371
|
async function seersHeadlines(ctx) {
|
|
235
372
|
const { client, flags, io } = ctx;
|
|
236
|
-
const
|
|
237
|
-
collection_id: flagStr(flags.collection),
|
|
238
|
-
limit: flagNum(flags.limit),
|
|
239
|
-
offset: flagNum(flags.offset),
|
|
240
|
-
});
|
|
241
|
-
const result = await client.callTool("list_seer_headlines", args);
|
|
373
|
+
const result = await client.callTool("list_seer_headlines", argsFromFlags(ARGS.list_seer_headlines, flags));
|
|
242
374
|
printResult(io, flags, result, (r) => {
|
|
243
375
|
const lines = (r.headlines ?? []).map(
|
|
244
376
|
(h) => `${h.id} [${h.status}] ${h.title ?? h.headline ?? "(untitled)"}`
|
|
@@ -251,8 +383,7 @@ async function seersHeadlines(ctx) {
|
|
|
251
383
|
|
|
252
384
|
async function seersHeld(ctx) {
|
|
253
385
|
const { client, flags, io } = ctx;
|
|
254
|
-
const
|
|
255
|
-
const result = await client.callTool("list_held_drafts", args);
|
|
386
|
+
const result = await client.callTool("list_held_drafts", argsFromFlags(ARGS.list_held_drafts, flags));
|
|
256
387
|
printResult(io, flags, result, (r) => {
|
|
257
388
|
const lines = (r.drafts ?? []).map(
|
|
258
389
|
(d) => `${d.articleId} [${d.kind}] ${d.title} — ${d.reason} (dropped ${d.dropped}, unsure ${d.unsure})`
|
|
@@ -266,8 +397,10 @@ async function seersHeld(ctx) {
|
|
|
266
397
|
|
|
267
398
|
async function seersResolveHeld(ctx, action) {
|
|
268
399
|
const { client, positionals, flags, io } = ctx;
|
|
269
|
-
const
|
|
270
|
-
|
|
400
|
+
const result = await client.callTool(
|
|
401
|
+
"resolve_held_draft",
|
|
402
|
+
argsFromFlags(ARGS.resolve_held_draft, flags, positionals, { action })
|
|
403
|
+
);
|
|
271
404
|
ok(ctx, action === "release" ? "Draft released — it can publish through the normal path." : "Draft discarded.");
|
|
272
405
|
printResult(io, flags, result);
|
|
273
406
|
return 0;
|
|
@@ -277,7 +410,7 @@ async function seersResolveHeld(ctx, action) {
|
|
|
277
410
|
|
|
278
411
|
async function seersProviders(ctx) {
|
|
279
412
|
const { client, flags, io } = ctx;
|
|
280
|
-
const result = await client.callTool("list_seer_providers",
|
|
413
|
+
const result = await client.callTool("list_seer_providers", argsFromFlags(ARGS.list_seer_providers, flags));
|
|
281
414
|
printResult(io, flags, result, (r) => {
|
|
282
415
|
const blocks = (r.providers ?? []).map((p) => {
|
|
283
416
|
const fields = p.fields
|
|
@@ -296,7 +429,7 @@ async function seersProviders(ctx) {
|
|
|
296
429
|
|
|
297
430
|
async function seersConnections(ctx) {
|
|
298
431
|
const { client, flags, io } = ctx;
|
|
299
|
-
const result = await client.callTool("get_seer_connections",
|
|
432
|
+
const result = await client.callTool("get_seer_connections", argsFromFlags(ARGS.get_seer_connections, flags));
|
|
300
433
|
printResult(io, flags, result, (r) => {
|
|
301
434
|
const gh = r.github.connected
|
|
302
435
|
? `GitHub: connected as ${r.github.account_login} (${r.github.connected_at})`
|
|
@@ -313,9 +446,11 @@ async function seersConnect(ctx) {
|
|
|
313
446
|
const which = ctx.positionals[0];
|
|
314
447
|
const rest = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
315
448
|
if (which === "github") {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
449
|
+
if (!flagStr(rest.flags.token)) throw new CliError("Missing required --token <github-pat> (repo read access)");
|
|
450
|
+
const result = await rest.client.callTool(
|
|
451
|
+
"set_github_connection",
|
|
452
|
+
argsFromFlags(ARGS.set_github_connection, rest.flags)
|
|
453
|
+
);
|
|
319
454
|
ok(rest, "GitHub connected.");
|
|
320
455
|
printResult(rest.io, rest.flags, result);
|
|
321
456
|
return 0;
|
|
@@ -340,7 +475,10 @@ async function seersConnect(ctx) {
|
|
|
340
475
|
);
|
|
341
476
|
return 0;
|
|
342
477
|
}
|
|
343
|
-
const result = await rest.client.callTool(
|
|
478
|
+
const result = await rest.client.callTool(
|
|
479
|
+
"set_notion_connection",
|
|
480
|
+
argsFromFlags(ARGS.set_notion_connection, rest.flags)
|
|
481
|
+
);
|
|
344
482
|
ok(rest, `Notion connected${result.workspace_name ? ` to ${result.workspace_name}` : ""}.`);
|
|
345
483
|
ok(rest, "Reminder: the integration only watches pages you've shared with it in Notion.");
|
|
346
484
|
printResult(rest.io, rest.flags, result);
|