@letterstory/cli 0.76.1 → 0.81.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 +14 -0
- package/lib/cli.mjs +13 -5
- package/lib/commands/arena.mjs +1 -1
- package/lib/commands/org.mjs +222 -0
- package/lib/commands/targets.mjs +24 -6
- package/lib/commands.mjs +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -630,6 +630,20 @@ letterstory enhance offer <collection-id> [--accept] # our newer versio
|
|
|
630
630
|
letterstory autopen run --article <id> --skip finish,metadata # leave passes out of one run
|
|
631
631
|
```
|
|
632
632
|
|
|
633
|
+
## Org config
|
|
634
|
+
|
|
635
|
+
An org's whole configuration as one JSON bundle (`export_org_config`; docs/architecture/org-config.md): settings, primitives and data, keyed by name, live primitive files, no runs or credentials.
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
letterstory org export > acme.json # the bundle — the same document MCP and the API return
|
|
639
|
+
letterstory org export --sections primitives.kernels --sections data.collections
|
|
640
|
+
letterstory org export --split acme/ # one file per record, for keeping config in git
|
|
641
|
+
letterstory org join acme/ > acme.json # rebuild the identical bundle from the directory
|
|
642
|
+
letterstory org diff acme.json # what applying the bundle would change: + add, ~ change, - only in the org
|
|
643
|
+
letterstory org apply acme.json --dry-run # what apply would write; then drop --dry-run to apply
|
|
644
|
+
letterstory org apply acme.json --prune --yes # also delete records the bundle lacks
|
|
645
|
+
```
|
|
646
|
+
|
|
633
647
|
## Seers — event-driven signals
|
|
634
648
|
|
|
635
649
|
A **seer** watches an external source (a GitHub repo, a Notion workspace, a spec URL, a
|
package/lib/cli.mjs
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
cmdKernel,
|
|
38
38
|
cmdOnboard,
|
|
39
39
|
cmdSeers,
|
|
40
|
+
cmdOrg,
|
|
40
41
|
cmdTemplates,
|
|
41
42
|
cmdCompositions,
|
|
42
43
|
cmdShredder,
|
|
@@ -56,7 +57,7 @@ import {
|
|
|
56
57
|
} from "./commands.mjs";
|
|
57
58
|
|
|
58
59
|
// Keep in sync with cli/package.json.
|
|
59
|
-
export const VERSION = "0.
|
|
60
|
+
export const VERSION = "0.81.0";
|
|
60
61
|
|
|
61
62
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
62
63
|
// can't accidentally swallow the id as --json's value.
|
|
@@ -368,12 +369,13 @@ Offerings (context layered org → offering → collection; a layer sets only wh
|
|
|
368
369
|
|
|
369
370
|
Ranking targets (what the org wants to be THE answer for — company-wide or on one offering):
|
|
370
371
|
targets list [--offering <offering-id|company>] Targets grouped by scope, each with its active cap and usage
|
|
371
|
-
targets add <phrase> [--framing <f>] [--offering <offering-id|company>]
|
|
372
|
+
targets add <phrase> [--framing <f>] [--offering <offering-id|company>] [--priority high|normal|low]
|
|
372
373
|
Lands active while its scope has room, inactive after —
|
|
373
374
|
adding never fails on the cap
|
|
374
375
|
targets update <target-id> [--phrase <p>] [--framing <f> | --clear-framing] [--offering <offering-id|company>]
|
|
375
|
-
[--position <n>] [--state active|inactive|achieved|retired]
|
|
376
|
-
Moving an ACTIVE target needs room in the new scope
|
|
376
|
+
[--position <n>] [--state active|inactive|achieved|retired] [--priority high|normal|low|default]
|
|
377
|
+
Moving an ACTIVE target needs room in the new scope;
|
|
378
|
+
--priority: how much it counts in share of voice
|
|
377
379
|
targets activate | deactivate <target-id> Switch a target on/off; activating is refused at the cap
|
|
378
380
|
targets cap <offering-id|company> <n|default> Active targets allowed in that scope (preset 5; default =
|
|
379
381
|
inherit the company's cap / back to the preset)
|
|
@@ -381,7 +383,7 @@ Ranking targets (what the org wants to be THE answer for — company-wide or on
|
|
|
381
383
|
|
|
382
384
|
Arenas (adversarial clusters of research agents -> a sourced, fact-checked outline):
|
|
383
385
|
arena run --topic <text> [--source <url> …] [--must-include <text> …] [--depth barebones|fleshed_out]
|
|
384
|
-
[--gate] [--no-verify] [--provider native|exa|youcom|
|
|
386
|
+
[--gate] [--no-verify] [--provider native|exa|youcom|context-dev] [--mention off|subtle|woven]
|
|
385
387
|
[--context-file ctx.json] [--block <name> …] [--block-domain <domain> …]
|
|
386
388
|
[--arena <arena-id>] [--article <uuid>] [--no-wait]
|
|
387
389
|
Start a run and follow it. --article makes
|
|
@@ -715,6 +717,11 @@ Seers (event-driven signals -> drafts):
|
|
|
715
717
|
seers export [--file <path>] Dump the org's seers as a manifest
|
|
716
718
|
seers apply --file <path> [--dry-run] Idempotent, name-keyed upsert from a manifest
|
|
717
719
|
(never deletes; --dry-run prints the plan)
|
|
720
|
+
org export [--sections <s>…] [--file <path>] The org's whole config as one JSON bundle
|
|
721
|
+
org export --split <dir> …as one file per record (for keeping config in git)
|
|
722
|
+
org join <dir> [--file <path>] Rebuild the bundle from a --split directory
|
|
723
|
+
org diff <file|dir> What applying a bundle would change (reads only)
|
|
724
|
+
org apply <file|dir> [--dry-run] [--prune --yes] Make the org match a bundle (adds/updates; --prune deletes)
|
|
718
725
|
|
|
719
726
|
Content templates (compose structures seers bind via config.template_key):
|
|
720
727
|
templates list Custom + built-in catalog
|
|
@@ -1011,6 +1018,7 @@ const CLIENT_COMMANDS = {
|
|
|
1011
1018
|
kernel: cmdKernel,
|
|
1012
1019
|
"phantom-onboard": cmdOnboard,
|
|
1013
1020
|
seers: cmdSeers,
|
|
1021
|
+
org: cmdOrg,
|
|
1014
1022
|
templates: cmdTemplates,
|
|
1015
1023
|
compositions: cmdCompositions,
|
|
1016
1024
|
shredder: cmdShredder,
|
package/lib/commands/arena.mjs
CHANGED
|
@@ -64,7 +64,7 @@ function readJsonObjectFlag(flags, name) {
|
|
|
64
64
|
const RUN_OPTIONS = {
|
|
65
65
|
depth: { flag: "depth", enum: ["barebones", "fleshed_out"] },
|
|
66
66
|
direction_gate: { flag: "gate", type: "bool" },
|
|
67
|
-
provider: { flag: "provider", enum: ["native", "exa", "youcom", "
|
|
67
|
+
provider: { flag: "provider", enum: ["native", "exa", "youcom", "context-dev"] },
|
|
68
68
|
// --no-verify turns the fact-check off; nothing sent otherwise (the default is on).
|
|
69
69
|
verify: { value: (flags) => (flags["no-verify"] === true ? false : undefined) },
|
|
70
70
|
mention_degree: { flag: "mention", enum: ["off", "subtle", "woven"] },
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// `org` — the organization's whole configuration as one JSON bundle
|
|
2
|
+
// (docs/architecture/org-config.md). `org export` prints the same document every door
|
|
3
|
+
// returns (MCP `export_org_config`, REST, Settings' download); `--split <dir>` lays it out
|
|
4
|
+
// as one file per record for keeping config in git, and `org join <dir>` rebuilds the
|
|
5
|
+
// bundle from that directory, byte-for-byte the same JSON. `org diff <file|dir>` plans a
|
|
6
|
+
// bundle against the live org (MCP `diff_org_config`) and changes nothing; `org apply`
|
|
7
|
+
// executes that plan (`apply_org_config`), additive unless `--prune`.
|
|
8
|
+
|
|
9
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync, existsSync, statSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { CliError } from "../client.mjs";
|
|
12
|
+
import { argsFromFlags, flagBool, flagStr, ok, printResult, refuseUnknownFlags, requirePositional } from "./shared.mjs";
|
|
13
|
+
|
|
14
|
+
export const ARGS = {
|
|
15
|
+
export_org_config: {
|
|
16
|
+
sections: { flag: "sections", type: "list" },
|
|
17
|
+
},
|
|
18
|
+
diff_org_config: {
|
|
19
|
+
config: { value: (_flags, positionals) => readBundle(requirePositional(positionals, 0, "file|dir")) },
|
|
20
|
+
},
|
|
21
|
+
apply_org_config: {
|
|
22
|
+
config: { value: (_flags, positionals) => readBundle(requirePositional(positionals, 0, "file|dir")) },
|
|
23
|
+
dry_run: { flag: "dry-run", type: "bool" },
|
|
24
|
+
prune: { flag: "prune", type: "bool" },
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export async function cmdOrg(ctx) {
|
|
29
|
+
const sub = ctx.positionals[0];
|
|
30
|
+
const rest = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
31
|
+
switch (sub) {
|
|
32
|
+
case "export":
|
|
33
|
+
return orgExport(rest);
|
|
34
|
+
case "join":
|
|
35
|
+
return orgJoin(rest);
|
|
36
|
+
case "diff":
|
|
37
|
+
return orgDiff(rest);
|
|
38
|
+
case "apply":
|
|
39
|
+
return orgApply(rest);
|
|
40
|
+
default:
|
|
41
|
+
throw new CliError(`Unknown org subcommand: ${sub ?? "(none)"}. Try: export, join, diff, apply`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ROOT_FILE = "organization.json";
|
|
46
|
+
|
|
47
|
+
function slug(name) {
|
|
48
|
+
return (
|
|
49
|
+
String(name)
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
52
|
+
.replace(/^-+|-+$/g, "") || "unnamed"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function writeJson(path, value) {
|
|
57
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** One file per record under `<group>/<section>/`, every other value in its own file. */
|
|
61
|
+
export function splitBundle(bundle, dir) {
|
|
62
|
+
const { spec, ...root } = bundle;
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
writeJson(join(dir, ROOT_FILE), root);
|
|
65
|
+
let files = 1;
|
|
66
|
+
if (spec.settings) {
|
|
67
|
+
writeJson(join(dir, "settings.json"), spec.settings);
|
|
68
|
+
files++;
|
|
69
|
+
}
|
|
70
|
+
for (const group of ["primitives", "data", "tools"]) {
|
|
71
|
+
for (const [section, value] of Object.entries(spec[group] ?? {})) {
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
const sub = join(dir, group, section);
|
|
74
|
+
mkdirSync(sub, { recursive: true });
|
|
75
|
+
const used = new Map();
|
|
76
|
+
for (const record of value) {
|
|
77
|
+
const base = slug(record.name);
|
|
78
|
+
const n = (used.get(base) ?? 0) + 1;
|
|
79
|
+
used.set(base, n);
|
|
80
|
+
writeJson(join(sub, `${n === 1 ? base : `${base}-${n}`}.json`), record);
|
|
81
|
+
files++;
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
mkdirSync(join(dir, group), { recursive: true });
|
|
85
|
+
writeJson(join(dir, group, `${section}.json`), value);
|
|
86
|
+
files++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return files;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function joinBundle(dir) {
|
|
94
|
+
const rootPath = join(dir, ROOT_FILE);
|
|
95
|
+
if (!existsSync(rootPath))
|
|
96
|
+
throw new CliError(`${dir} has no ${ROOT_FILE} — produce one with \`org export --split\``);
|
|
97
|
+
const read = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
98
|
+
const root = read(rootPath);
|
|
99
|
+
const spec = {};
|
|
100
|
+
if (existsSync(join(dir, "settings.json"))) spec.settings = read(join(dir, "settings.json"));
|
|
101
|
+
for (const group of ["primitives", "data", "tools"]) {
|
|
102
|
+
spec[group] = {};
|
|
103
|
+
const gdir = join(dir, group);
|
|
104
|
+
if (!existsSync(gdir)) continue;
|
|
105
|
+
for (const entry of readdirSync(gdir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
106
|
+
if (entry.isDirectory()) {
|
|
107
|
+
spec[group][entry.name] = readdirSync(join(gdir, entry.name))
|
|
108
|
+
.filter((f) => f.endsWith(".json"))
|
|
109
|
+
.map((f) => read(join(gdir, entry.name, f)))
|
|
110
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
111
|
+
} else if (entry.name.endsWith(".json")) {
|
|
112
|
+
spec[group][entry.name.slice(0, -5)] = read(join(gdir, entry.name));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { ...root, spec };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function orgExport(ctx) {
|
|
120
|
+
const { client, flags, io } = ctx;
|
|
121
|
+
refuseUnknownFlags(ctx, ARGS, ["split", "file"]);
|
|
122
|
+
const { config } = await client.callTool("export_org_config", argsFromFlags(ARGS.export_org_config, flags));
|
|
123
|
+
const split = flagStr(flags.split);
|
|
124
|
+
const file = flagStr(flags.file);
|
|
125
|
+
if (split) {
|
|
126
|
+
const n = splitBundle(config, split);
|
|
127
|
+
ok(ctx, `Exported ${config.metadata.org} to ${split}/ (${n} files).`);
|
|
128
|
+
} else if (file) {
|
|
129
|
+
writeJson(file, config);
|
|
130
|
+
ok(ctx, `Exported ${config.metadata.org} to ${file}.`);
|
|
131
|
+
} else {
|
|
132
|
+
io.log(JSON.stringify(config, null, 2));
|
|
133
|
+
}
|
|
134
|
+
for (const w of config.metadata.warnings ?? []) io.error?.(`warning: ${w}`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function orgJoin(ctx) {
|
|
139
|
+
const { flags, io } = ctx;
|
|
140
|
+
const dir = requirePositional(ctx.positionals, 0, "dir");
|
|
141
|
+
const bundle = joinBundle(dir);
|
|
142
|
+
const file = flagStr(flags.file);
|
|
143
|
+
if (file) {
|
|
144
|
+
writeJson(file, bundle);
|
|
145
|
+
ok(ctx, `Joined ${dir}/ into ${file}.`);
|
|
146
|
+
} else io.log(JSON.stringify(bundle, null, 2));
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** A bundle file, or a `--split` directory joined back into one. */
|
|
151
|
+
export function readBundle(path) {
|
|
152
|
+
if (!existsSync(path)) throw new CliError(`${path} does not exist`);
|
|
153
|
+
if (statSync(path).isDirectory()) return joinBundle(path);
|
|
154
|
+
try {
|
|
155
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
156
|
+
} catch (e) {
|
|
157
|
+
throw new CliError(`${path} is not JSON: ${e.message}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const SYMBOL = { add: "+", change: "~", remove: "-" };
|
|
162
|
+
|
|
163
|
+
function show(v) {
|
|
164
|
+
const s = JSON.stringify(v) ?? "null";
|
|
165
|
+
return s.length > 80 ? `${s.slice(0, 77)}...` : s;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function formatPlan(plan) {
|
|
169
|
+
const lines = [];
|
|
170
|
+
for (const c of plan.changes) {
|
|
171
|
+
lines.push(
|
|
172
|
+
`${SYMBOL[c.op]} ${c.section}/${c.name}${c.op === "remove" ? " (in the org only — kept unless pruning)" : ""}`
|
|
173
|
+
);
|
|
174
|
+
for (const f of c.fields) lines.push(` ${f.path || "(value)"}: ${show(f.from)} → ${show(f.to)}`);
|
|
175
|
+
}
|
|
176
|
+
const s = plan.summary;
|
|
177
|
+
if (plan.changes.length) {
|
|
178
|
+
lines.push(
|
|
179
|
+
"",
|
|
180
|
+
`${plan.org}: ${s.add} to add, ${s.change} to change, ${s.remove} only in the org, ${s.unchanged} unchanged.`
|
|
181
|
+
);
|
|
182
|
+
} else lines.push(`${plan.org}: no changes — the live config matches the bundle (${s.unchanged} unchanged).`);
|
|
183
|
+
for (const w of plan.warnings) lines.push(`warning: ${w}`);
|
|
184
|
+
return lines.join("\n");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function orgDiff(ctx) {
|
|
188
|
+
const { client, flags, io } = ctx;
|
|
189
|
+
refuseUnknownFlags(ctx, ARGS);
|
|
190
|
+
const { plan } = await client.callTool(
|
|
191
|
+
"diff_org_config",
|
|
192
|
+
argsFromFlags(ARGS.diff_org_config, flags, ctx.positionals)
|
|
193
|
+
);
|
|
194
|
+
printResult(io, flags, plan, formatPlan);
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function formatApply(result) {
|
|
199
|
+
const lines = [result.dryRun ? "Dry run — nothing written. Would apply:" : "Applied:"];
|
|
200
|
+
for (const a of result.applied)
|
|
201
|
+
lines.push(` ${SYMBOL[a.op]} ${a.section}/${a.name}${a.detail ? ` — ${a.detail}` : ""}`);
|
|
202
|
+
if (!result.applied.length) lines.push(" (nothing)");
|
|
203
|
+
if (result.skipped.length) lines.push("Skipped:");
|
|
204
|
+
for (const a of result.skipped) lines.push(` ${SYMBOL[a.op]} ${a.section}/${a.name} — ${a.reason}`);
|
|
205
|
+
if (result.failed.length) lines.push("Failed:");
|
|
206
|
+
for (const a of result.failed) lines.push(` ${SYMBOL[a.op]} ${a.section}/${a.name} — ${a.error}`);
|
|
207
|
+
for (const w of result.plan.warnings) lines.push(`warning: ${w}`);
|
|
208
|
+
return lines.join("\n");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function orgApply(ctx) {
|
|
212
|
+
const { client, flags, io } = ctx;
|
|
213
|
+
refuseUnknownFlags(ctx, ARGS, ["yes"]);
|
|
214
|
+
if (flagBool(flags.prune) && !flagBool(flags["dry-run"]) && !flagBool(flags.yes))
|
|
215
|
+
throw new CliError("--prune deletes records missing from the bundle. Review with --dry-run, then add --yes.");
|
|
216
|
+
const { result } = await client.callTool(
|
|
217
|
+
"apply_org_config",
|
|
218
|
+
argsFromFlags(ARGS.apply_org_config, flags, ctx.positionals)
|
|
219
|
+
);
|
|
220
|
+
printResult(io, flags, result, formatApply);
|
|
221
|
+
return result.failed.length ? 1 : 0;
|
|
222
|
+
}
|
package/lib/commands/targets.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { CliError } from "../client.mjs";
|
|
|
11
11
|
import { flagStr, flagNum, printResult, argsFromFlags, refuseUnknownFlags, ok } from "./shared.mjs";
|
|
12
12
|
|
|
13
13
|
const STATUSES = ["active", "inactive", "achieved", "retired"];
|
|
14
|
+
const PRIORITIES = ["high", "normal", "low"];
|
|
14
15
|
const COMPANY = "company";
|
|
15
16
|
|
|
16
17
|
// `--offering <offering-id|company>`: company = the company layer (null). Absent = not sent.
|
|
@@ -24,6 +25,20 @@ function readOffering(flags) {
|
|
|
24
25
|
}
|
|
25
26
|
const offering = { value: readOffering };
|
|
26
27
|
|
|
28
|
+
// `--priority high|normal|low|default`: how much the target's questions count in share of
|
|
29
|
+
// voice (high = 2 votes, normal = 1, low = ½); default = back to the question-type default.
|
|
30
|
+
function readPriority(flags) {
|
|
31
|
+
const v = flagStr(flags.priority);
|
|
32
|
+
if (v === undefined) {
|
|
33
|
+
if (flags.priority === true) throw new CliError("--priority takes high, normal, low or default");
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
if (v === "default") return null;
|
|
37
|
+
if (!PRIORITIES.includes(v)) throw new CliError("--priority must be one of: high, normal, low, default");
|
|
38
|
+
return v;
|
|
39
|
+
}
|
|
40
|
+
const priority = { value: readPriority };
|
|
41
|
+
|
|
27
42
|
// How every tool this module calls gets its arguments — see `argsFromFlags` in shared.mjs.
|
|
28
43
|
// `scripts/parity-check.ts` compares these keys against each tool's published input schema.
|
|
29
44
|
export const ARGS = {
|
|
@@ -39,6 +54,7 @@ export const ARGS = {
|
|
|
39
54
|
},
|
|
40
55
|
mention_framing: "framing",
|
|
41
56
|
offering_id: offering,
|
|
57
|
+
priority,
|
|
42
58
|
},
|
|
43
59
|
update_ranking_target: {
|
|
44
60
|
target_id: { positional: 0, label: "target-id", required: true },
|
|
@@ -50,6 +66,7 @@ export const ARGS = {
|
|
|
50
66
|
status: { flag: "state", enum: STATUSES },
|
|
51
67
|
offering_id: offering,
|
|
52
68
|
position: { flag: "position", type: "num" },
|
|
69
|
+
priority,
|
|
53
70
|
},
|
|
54
71
|
set_ranking_target_cap: {
|
|
55
72
|
offering_id: {
|
|
@@ -73,9 +90,9 @@ export const ARGS = {
|
|
|
73
90
|
|
|
74
91
|
const USAGE = `Usage: targets <list|add|update|activate|deactivate|cap|suggest>
|
|
75
92
|
targets list [--offering <offering-id|company>]
|
|
76
|
-
targets add <phrase> [--framing <f>] [--offering <offering-id|company>]
|
|
93
|
+
targets add <phrase> [--framing <f>] [--offering <offering-id|company>] [--priority high|normal|low]
|
|
77
94
|
targets update <target-id> [--phrase <p>] [--framing <f> | --clear-framing] [--offering <offering-id|company>]
|
|
78
|
-
[--position <n>] [--state active|inactive|achieved|retired]
|
|
95
|
+
[--position <n>] [--state active|inactive|achieved|retired] [--priority high|normal|low|default]
|
|
79
96
|
targets activate <target-id>
|
|
80
97
|
targets deactivate <target-id>
|
|
81
98
|
targets cap <offering-id|company> <n|default>
|
|
@@ -114,7 +131,8 @@ function formatScope(s) {
|
|
|
114
131
|
|
|
115
132
|
function formatTarget(t) {
|
|
116
133
|
const framing = t.mention_framing ? ` — as ${t.mention_framing}` : "";
|
|
117
|
-
|
|
134
|
+
const prio = t.priority ? ` (${t.priority} priority)` : "";
|
|
135
|
+
return ` ${t.id} [${t.status}] ${t.phrase}${framing}${prio}`;
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
// Targets grouped under their scope, each headed by its cap and usage. A target on an
|
|
@@ -151,7 +169,7 @@ async function targetsList(ctx) {
|
|
|
151
169
|
|
|
152
170
|
async function targetsAdd(ctx) {
|
|
153
171
|
const { client, flags, positionals, io } = ctx;
|
|
154
|
-
refuseUnknownFlags(ctx, ARGS.add_ranking_target, ["offering"]);
|
|
172
|
+
refuseUnknownFlags(ctx, ARGS.add_ranking_target, ["offering", "priority"]);
|
|
155
173
|
const result = await client.callTool(
|
|
156
174
|
"add_ranking_target",
|
|
157
175
|
argsFromFlags(ARGS.add_ranking_target, flags, positionals)
|
|
@@ -172,7 +190,7 @@ async function update(ctx, flags) {
|
|
|
172
190
|
const args = argsFromFlags(ARGS.update_ranking_target, flags, positionals);
|
|
173
191
|
if (Object.keys(args).length === 1) {
|
|
174
192
|
throw new CliError(
|
|
175
|
-
"Nothing to update — pass --phrase, --framing, --clear-framing, --offering, --position or --
|
|
193
|
+
"Nothing to update — pass --phrase, --framing, --clear-framing, --offering, --position, --state or --priority."
|
|
176
194
|
);
|
|
177
195
|
}
|
|
178
196
|
const result = await client.callTool("update_ranking_target", args);
|
|
@@ -182,7 +200,7 @@ async function update(ctx, flags) {
|
|
|
182
200
|
}
|
|
183
201
|
|
|
184
202
|
async function targetsUpdate(ctx) {
|
|
185
|
-
refuseUnknownFlags(ctx, ARGS.update_ranking_target, ["framing", "clear-framing", "offering"]);
|
|
203
|
+
refuseUnknownFlags(ctx, ARGS.update_ranking_target, ["framing", "clear-framing", "offering", "priority"]);
|
|
186
204
|
return update(ctx, ctx.flags);
|
|
187
205
|
}
|
|
188
206
|
|
package/lib/commands.mjs
CHANGED
|
@@ -31,6 +31,7 @@ export * from "./commands/lede.mjs";
|
|
|
31
31
|
|
|
32
32
|
export * from "./commands/onboard.mjs";
|
|
33
33
|
export * from "./commands/seers.mjs";
|
|
34
|
+
export * from "./commands/org.mjs";
|
|
34
35
|
export * from "./commands/shredder.mjs";
|
|
35
36
|
export * from "./commands/enhance.mjs";
|
|
36
37
|
export * from "./commands/landings.mjs";
|