@todoforai/cli 0.1.51 → 0.1.53
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/dist/todoforai-cli.js +79 -116
- package/package.json +1 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -42884,29 +42884,26 @@ class ApiClient {
|
|
|
42884
42884
|
setDeviceWallpaperMode(id, mode) {
|
|
42885
42885
|
return this.request("POST", `/api/v1/devices/${id}/wallpaper-mode`, { id, mode });
|
|
42886
42886
|
}
|
|
42887
|
-
|
|
42888
|
-
return this.request("GET",
|
|
42887
|
+
getBusinessContext(projectId) {
|
|
42888
|
+
return this.request("GET", `/api/v1/projects/${projectId}/business-context`);
|
|
42889
42889
|
}
|
|
42890
|
-
createBusinessContext(name) {
|
|
42891
|
-
return this.request("POST",
|
|
42890
|
+
createBusinessContext(projectId, name) {
|
|
42891
|
+
return this.request("POST", `/api/v1/projects/${projectId}/business-context`, { projectId, name });
|
|
42892
42892
|
}
|
|
42893
|
-
renameBusinessContext(
|
|
42894
|
-
return this.request("PUT", `/api/v1/business-context
|
|
42893
|
+
renameBusinessContext(projectId, name) {
|
|
42894
|
+
return this.request("PUT", `/api/v1/projects/${projectId}/business-context`, { projectId, name });
|
|
42895
42895
|
}
|
|
42896
|
-
|
|
42897
|
-
return this.request("
|
|
42896
|
+
listVoiceSources(projectId) {
|
|
42897
|
+
return this.request("GET", `/api/v1/projects/${projectId}/voice-sources`);
|
|
42898
42898
|
}
|
|
42899
|
-
|
|
42900
|
-
return this.request("
|
|
42899
|
+
collectVoiceSource(projectId, channel, opts) {
|
|
42900
|
+
return this.request("POST", "/api/v1/business-context/collect-voice-source", { projectId, channel, ...opts });
|
|
42901
42901
|
}
|
|
42902
|
-
|
|
42903
|
-
return this.request("POST", "/api/v1/business-context/
|
|
42902
|
+
removeVoiceSource(projectId, channel, opts = {}) {
|
|
42903
|
+
return this.request("POST", "/api/v1/business-context/remove-voice-source", { projectId, channel, ...opts });
|
|
42904
42904
|
}
|
|
42905
|
-
|
|
42906
|
-
return this.request("POST", "/api/v1/business-context/
|
|
42907
|
-
}
|
|
42908
|
-
refineBrandVoice(businessContextId, answers, source, correction) {
|
|
42909
|
-
return this.request("POST", "/api/v1/business-context/refine-brand-voice", { businessContextId, answers, ...source && { source }, ...correction && { correction } });
|
|
42905
|
+
refineBrandVoice(projectId, source, correction) {
|
|
42906
|
+
return this.request("POST", "/api/v1/business-context/refine-brand-voice", { projectId, ...source && { source }, ...correction && { correction } });
|
|
42910
42907
|
}
|
|
42911
42908
|
getProfile() {
|
|
42912
42909
|
return this.request("GET", "/api/v1/users/profile");
|
|
@@ -43450,7 +43447,7 @@ var TodoStatus;
|
|
|
43450
43447
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43451
43448
|
})(TodoStatus ||= {});
|
|
43452
43449
|
// ../packages/shared-fbe/src/models.ts
|
|
43453
|
-
var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5";
|
|
43450
|
+
var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5.5";
|
|
43454
43451
|
var MODEL_ALIASES = {
|
|
43455
43452
|
claude: DEFAULT_MODEL
|
|
43456
43453
|
};
|
|
@@ -43742,7 +43739,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43742
43739
|
updated: (projectId) => `${"project:surfaces_updated" /* PROJECT_SURFACES_UPDATED */}:${projectId}`
|
|
43743
43740
|
},
|
|
43744
43741
|
killSwitch: {
|
|
43745
|
-
changed: (projectId) => `${"project:kill_switch" /* KILL_SWITCH */}:${projectId}`
|
|
43742
|
+
changed: (projectId, userId) => `${"project:kill_switch" /* KILL_SWITCH */}:${projectId}${privateSuffix(userId)}`
|
|
43746
43743
|
},
|
|
43747
43744
|
sandbox: {
|
|
43748
43745
|
state: (userId) => `${"sandbox:state" /* SANDBOX_STATE */}:${userId}`
|
|
@@ -43812,6 +43809,17 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43812
43809
|
function_call_request: (userId) => `${"FUNCTION_CALL_REQUEST_AGENT" /* FUNCTION_CALL_REQUEST_AGENT */}:frontend:${userId}`
|
|
43813
43810
|
}
|
|
43814
43811
|
};
|
|
43812
|
+
// ../packages/shared-fbe/src/REST_types.ts
|
|
43813
|
+
var MANUAL_CHANNEL = "Manual";
|
|
43814
|
+
function manualAnswersOf(sources) {
|
|
43815
|
+
const manual = sources?.find((s) => s.channel === MANUAL_CHANNEL);
|
|
43816
|
+
const out = {};
|
|
43817
|
+
for (const x of manual?.items ?? [])
|
|
43818
|
+
if (typeof x !== "string" && x.prompt)
|
|
43819
|
+
out[x.prompt] = x.text;
|
|
43820
|
+
return out;
|
|
43821
|
+
}
|
|
43822
|
+
var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
|
|
43815
43823
|
// ../packages/shared-fbe/src/templateBody.ts
|
|
43816
43824
|
var USER_HEADINGS = new Set([
|
|
43817
43825
|
"your task",
|
|
@@ -44269,7 +44277,6 @@ Exactly ONE \`api_recommend_template\` call per proposal: it carries both what t
|
|
|
44269
44277
|
It runs in-process and needs no machine or shell. Never shell out to \`todoregistry-cli\` or \`todoforai-cli\` for this.
|
|
44270
44278
|
|
|
44271
44279
|
3. Propose only todos you genuinely believe fit this business, highest priority first. There is no quota: a weak card costs the user more than no card, so if you can't find good ones, propose fewer or none and say why. Favour the trivial, high-probability moves for this company, the strategic directions its owner would nod at instantly, alongside any cleverer bets. Then reply with a one-line summary per proposal grouped by workstream, and tell the user they're waiting on the board for review (or, if you proposed none, what you'd need to make a confident recommendation).`,
|
|
44272
|
-
mcpConfigs: {},
|
|
44273
44280
|
edgesMcpConfigs: {},
|
|
44274
44281
|
permissions: {
|
|
44275
44282
|
allow: [
|
|
@@ -44292,7 +44299,6 @@ var JARVIS_AGENT = {
|
|
|
44292
44299
|
ownerId: "SYSTEM",
|
|
44293
44300
|
systemMessage: "",
|
|
44294
44301
|
model: "",
|
|
44295
|
-
mcpConfigs: {},
|
|
44296
44302
|
edgesMcpConfigs: {},
|
|
44297
44303
|
createdAt: 0,
|
|
44298
44304
|
updatedAt: 0
|
|
@@ -44302,7 +44308,6 @@ var ONBOARDING_AGENT = {
|
|
|
44302
44308
|
name: "Setup",
|
|
44303
44309
|
ownerId: "SYSTEM",
|
|
44304
44310
|
systemMessage: "Workspace setup — this todo is a guided stepper and never runs an agent.",
|
|
44305
|
-
mcpConfigs: {},
|
|
44306
44311
|
edgesMcpConfigs: {},
|
|
44307
44312
|
createdAt: 0,
|
|
44308
44313
|
updatedAt: 0
|
|
@@ -44354,6 +44359,8 @@ var model_promos_default = {
|
|
|
44354
44359
|
{ family: "gpt-5-5(-pro)?", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21" },
|
|
44355
44360
|
{ family: "gpt-5-6-sol", rate: 0.85, starts: "2026-08-26", ends: "2026-09-13" },
|
|
44356
44361
|
{ family: "gpt-5-6-sol", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21" },
|
|
44362
|
+
{ family: "gpt-6-sol", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
|
|
44363
|
+
{ family: "gpt-6-luna", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
|
|
44357
44364
|
{ family: "gpt-6-astra", rate: 0.85, starts: "2026-09-05", ends: "2026-09-13", boostable: false },
|
|
44358
44365
|
{ family: "gpt-6-astra", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21", boostable: false },
|
|
44359
44366
|
{ family: "gpt-5-6-terra", rate: 0.85, starts: "2026-06-29", ends: "2026-09-13" },
|
|
@@ -44410,7 +44417,7 @@ var TOPICS = {
|
|
|
44410
44417
|
audience: "frontend"
|
|
44411
44418
|
},
|
|
44412
44419
|
["project:kill_switch" /* KILL_SWITCH */]: {
|
|
44413
|
-
channel: (p) => SERVER_TO_FRONTENDS.killSwitch.changed(p.projectId),
|
|
44420
|
+
channel: (p) => SERVER_TO_FRONTENDS.killSwitch.changed(p.projectId, p.userId),
|
|
44414
44421
|
audience: "frontend"
|
|
44415
44422
|
},
|
|
44416
44423
|
["sandbox:state" /* SANDBOX_STATE */]: {
|
|
@@ -44549,7 +44556,7 @@ import { parseArgs } from "util";
|
|
|
44549
44556
|
// package.json
|
|
44550
44557
|
var package_default = {
|
|
44551
44558
|
name: "@todoforai/cli",
|
|
44552
|
-
version: "0.1.
|
|
44559
|
+
version: "0.1.53",
|
|
44553
44560
|
type: "module",
|
|
44554
44561
|
bin: {
|
|
44555
44562
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -44607,7 +44614,7 @@ Examples:
|
|
|
44607
44614
|
tfa-cli agent list|get|update|create … # Agents (update <agent> model=…)
|
|
44608
44615
|
tfa-cli todo set <todo-id|-> title=… group=… star=true # Edit a todo's fields
|
|
44609
44616
|
tfa-cli project list|set|settings|groups|default|agent … # Projects; edit the current one
|
|
44610
|
-
tfa-cli brand
|
|
44617
|
+
tfa-cli brand [show|create|rename|voice …] # The project's brand page + voice learning
|
|
44611
44618
|
tfa-cli device list|rename|wallpaper … # Paired machines
|
|
44612
44619
|
tfa-cli list [-n 30] [--cursor N] [--all] [--status S] # List todos (paginated)
|
|
44613
44620
|
tfa-cli status <todo-id> <STATUS> # Update a todo's status
|
|
@@ -44724,8 +44731,6 @@ function parseCliArgs() {
|
|
|
44724
44731
|
card: { type: "string" },
|
|
44725
44732
|
link: { type: "boolean", default: false },
|
|
44726
44733
|
direction: { type: "string" },
|
|
44727
|
-
"business-context": { type: "string" },
|
|
44728
|
-
brand: { type: "string" },
|
|
44729
44734
|
url: { type: "string" },
|
|
44730
44735
|
"pasted-file": { type: "string" },
|
|
44731
44736
|
"from-company": { type: "boolean", default: false },
|
|
@@ -47437,8 +47442,7 @@ Examples:
|
|
|
47437
47442
|
tfa-cli agent update <agent> sysmsg="You are a terse video editor."
|
|
47438
47443
|
tfa-cli agent create name=Reviewer model=claude sysmsg="Review diffs only."
|
|
47439
47444
|
|
|
47440
|
-
From an agent shell (dst_ token) permissions,
|
|
47441
|
-
devicesConfig and delete are refused by the backend.
|
|
47445
|
+
From an agent shell (dst_ token) permissions, edgesMcpConfigs, devicesConfig and delete are refused by the backend.
|
|
47442
47446
|
`);
|
|
47443
47447
|
}
|
|
47444
47448
|
var FIELD_ALIASES = {
|
|
@@ -47839,7 +47843,6 @@ Fields (PUT /todos/{id}):
|
|
|
47839
47843
|
star true|false (starredAt now / 0)
|
|
47840
47844
|
schedule unix ms timestamp, 0 = immediate (alias: scheduledTimestamp)
|
|
47841
47845
|
agent agentSettingsId (alias: agentSettingsId)
|
|
47842
|
-
brand businessContextId (alias: businessContextId)
|
|
47843
47846
|
public true|false — refused from an agent shell (isPublic)
|
|
47844
47847
|
|
|
47845
47848
|
tfa-cli todo message <todo-id|-> <message-id> <field=value>…
|
|
@@ -47861,7 +47864,7 @@ tfa-cli project — edit the current project
|
|
|
47861
47864
|
|
|
47862
47865
|
Usage:
|
|
47863
47866
|
tfa-cli project list Projects you can access (* = default)
|
|
47864
|
-
tfa-cli project set <field=value>… name=… description=…
|
|
47867
|
+
tfa-cli project set <field=value>… name=… description=…
|
|
47865
47868
|
(isPublic refused from an agent shell)
|
|
47866
47869
|
tfa-cli project default Make it the project you land on at /
|
|
47867
47870
|
tfa-cli project agent <agent> Default agent for new todos in this project
|
|
@@ -47888,10 +47891,8 @@ var TODO_ALIASES = {
|
|
|
47888
47891
|
group: "groupTag",
|
|
47889
47892
|
schedule: "scheduledTimestamp",
|
|
47890
47893
|
agent: "agentSettingsId",
|
|
47891
|
-
brand: "businessContextId",
|
|
47892
47894
|
public: "isPublic"
|
|
47893
47895
|
};
|
|
47894
|
-
var PROJECT_ALIASES = { brand: "businessContextId" };
|
|
47895
47896
|
function fail(msg) {
|
|
47896
47897
|
process.stderr.write(`${RED}${msg}${RESET}
|
|
47897
47898
|
`);
|
|
@@ -47967,7 +47968,7 @@ async function projectCommand(api, positionals, args) {
|
|
|
47967
47968
|
if (sub === "set") {
|
|
47968
47969
|
if (!rest.length)
|
|
47969
47970
|
fail("Usage: tfa-cli project set <field=value>…");
|
|
47970
|
-
const updates = parseAssignments2(rest
|
|
47971
|
+
const updates = parseAssignments2(rest);
|
|
47971
47972
|
await api.updateProject(projectId, updates);
|
|
47972
47973
|
process.stderr.write(`${GREEN}✅ project updated: ${Object.keys(updates).join(", ")}${RESET}
|
|
47973
47974
|
`);
|
|
@@ -48074,18 +48075,17 @@ async function projectCommand(api, positionals, args) {
|
|
|
48074
48075
|
}
|
|
48075
48076
|
function printBrandHelp() {
|
|
48076
48077
|
process.stderr.write(`
|
|
48077
|
-
tfa-cli brand —
|
|
48078
|
+
tfa-cli brand — the project's brand page and its learned writing voice
|
|
48078
48079
|
|
|
48080
|
+
The project IS the brand. --project <id> or $TODOFORAI_PROJECT_ID selects it.
|
|
48079
48081
|
The brand page text itself is a file: read/write todoforai:business-context.
|
|
48080
|
-
These commands manage the contexts and the voice learned for them.
|
|
48081
48082
|
|
|
48082
48083
|
Usage:
|
|
48083
|
-
tfa-cli brand
|
|
48084
|
-
tfa-cli brand create <name>
|
|
48085
|
-
tfa-cli brand rename <
|
|
48086
|
-
tfa-cli brand select <brand|none> Active brand for the account
|
|
48084
|
+
tfa-cli brand [show] This project's brand (none = personal board)
|
|
48085
|
+
tfa-cli brand create <name> Give the project a brand page (empty .md); the board turns business
|
|
48086
|
+
tfa-cli brand rename <name>
|
|
48087
48087
|
tfa-cli brand voice Learned profile + sources
|
|
48088
|
-
tfa-cli brand voice answers [<q>=<a>…] Show / set the
|
|
48088
|
+
tfa-cli brand voice answers [<q>=<a>…] Show / set the Manual source: answers to the style questions
|
|
48089
48089
|
tfa-cli brand voice collect <channel> [--url U | --pasted-file <F|-> | --max N]
|
|
48090
48090
|
Add a writing sample source.
|
|
48091
48091
|
On this device (signed-in CLI, reply pairs):
|
|
@@ -48099,9 +48099,7 @@ Usage:
|
|
|
48099
48099
|
tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
|
|
48100
48100
|
in one pass and the correction is kept for every re-learn
|
|
48101
48101
|
|
|
48102
|
-
|
|
48103
|
-
selected brand unless --brand <id|name> is given (--pasted-file - reads stdin).
|
|
48104
|
-
Delete / reset are UI-only.
|
|
48102
|
+
--pasted-file - reads stdin. Delete is UI-only.
|
|
48105
48103
|
|
|
48106
48104
|
Questions the UI asks (keys for 'answers'):
|
|
48107
48105
|
"What makes you different from competitors?"
|
|
@@ -48109,43 +48107,30 @@ Questions the UI asks (keys for 'answers'):
|
|
|
48109
48107
|
"A frustrated customer says things aren't working. How do you reply?"
|
|
48110
48108
|
`);
|
|
48111
48109
|
}
|
|
48112
|
-
|
|
48113
|
-
const
|
|
48114
|
-
if (!
|
|
48115
|
-
|
|
48116
|
-
|
|
48117
|
-
return sel;
|
|
48118
|
-
if (contexts.length === 1)
|
|
48119
|
-
return contexts[0];
|
|
48120
|
-
fail("No brand selected — pass --brand <id|name> or 'tfa-cli brand select'");
|
|
48121
|
-
}
|
|
48122
|
-
const q = query.toLowerCase();
|
|
48123
|
-
const exact = contexts.find((c3) => c3.id === query || c3.name.toLowerCase() === q);
|
|
48124
|
-
if (exact)
|
|
48125
|
-
return exact;
|
|
48126
|
-
const partial = contexts.filter((c3) => c3.id.startsWith(query) || c3.name.toLowerCase().includes(q));
|
|
48127
|
-
if (partial.length === 1)
|
|
48128
|
-
return partial[0];
|
|
48129
|
-
fail(partial.length ? `Ambiguous brand '${query}'` : `No brand matching '${query}'`);
|
|
48110
|
+
function requireProject(args) {
|
|
48111
|
+
const projectId = args.project || getEnv2("PROJECT_ID");
|
|
48112
|
+
if (!projectId)
|
|
48113
|
+
fail("No project — pass --project <id> or set TODOFORAI_PROJECT_ID");
|
|
48114
|
+
return projectId;
|
|
48130
48115
|
}
|
|
48131
48116
|
async function brandCommand(api, positionals, args) {
|
|
48132
48117
|
const [, sub, ...rest] = positionals;
|
|
48133
|
-
|
|
48134
|
-
|
|
48135
|
-
const
|
|
48118
|
+
const projectId = requireProject(args);
|
|
48119
|
+
if (!sub || sub === "show") {
|
|
48120
|
+
const brand = await api.getBusinessContext(projectId);
|
|
48136
48121
|
if (args.json) {
|
|
48137
|
-
console.log(JSON.stringify(
|
|
48122
|
+
console.log(JSON.stringify(brand, null, 2));
|
|
48138
48123
|
return;
|
|
48139
48124
|
}
|
|
48140
|
-
|
|
48141
|
-
|
|
48125
|
+
process.stderr.write(brand ? `${brand.name} ${DIM}${brand.id}${RESET}
|
|
48126
|
+
` : `${DIM}(personal board — no brand)${RESET}
|
|
48142
48127
|
`);
|
|
48143
48128
|
return;
|
|
48144
48129
|
}
|
|
48145
48130
|
if (sub === "create") {
|
|
48146
48131
|
if (!rest[0])
|
|
48147
48132
|
fail("Usage: tfa-cli brand create <name>");
|
|
48148
|
-
const c3 = await api.createBusinessContext(rest[0]);
|
|
48133
|
+
const c3 = await api.createBusinessContext(projectId, rest[0]);
|
|
48149
48134
|
if (args.json) {
|
|
48150
48135
|
console.log(JSON.stringify(c3, null, 2));
|
|
48151
48136
|
return;
|
|
@@ -48155,25 +48140,15 @@ async function brandCommand(api, positionals, args) {
|
|
|
48155
48140
|
return;
|
|
48156
48141
|
}
|
|
48157
48142
|
if (sub === "rename") {
|
|
48158
|
-
if (!rest[1])
|
|
48159
|
-
fail("Usage: tfa-cli brand rename <brand> <name>");
|
|
48160
|
-
const c3 = await resolveBrand(api, rest[0]);
|
|
48161
|
-
await api.renameBusinessContext(c3.id, rest[1]);
|
|
48162
|
-
process.stderr.write(`${GREEN}✅ renamed to ${rest[1]}${RESET}
|
|
48163
|
-
`);
|
|
48164
|
-
return;
|
|
48165
|
-
}
|
|
48166
|
-
if (sub === "select") {
|
|
48167
48143
|
if (!rest[0])
|
|
48168
|
-
fail("Usage: tfa-cli brand
|
|
48169
|
-
|
|
48170
|
-
|
|
48171
|
-
process.stderr.write(`${GREEN}✅ selected brand: ${id ?? "none"}${RESET}
|
|
48144
|
+
fail("Usage: tfa-cli brand rename <name>");
|
|
48145
|
+
await api.renameBusinessContext(projectId, rest[0]);
|
|
48146
|
+
process.stderr.write(`${GREEN}✅ renamed to ${rest[0]}${RESET}
|
|
48172
48147
|
`);
|
|
48173
48148
|
return;
|
|
48174
48149
|
}
|
|
48175
48150
|
if (sub === "voice")
|
|
48176
|
-
return voiceCommand(api, rest, args);
|
|
48151
|
+
return voiceCommand(api, projectId, rest, args);
|
|
48177
48152
|
fail(`Unknown 'brand' subcommand: ${sub}`);
|
|
48178
48153
|
}
|
|
48179
48154
|
function dryCollect(channel, max) {
|
|
@@ -48227,26 +48202,24 @@ async function voiceDeviceCommand(rest, args) {
|
|
|
48227
48202
|
}
|
|
48228
48203
|
return false;
|
|
48229
48204
|
}
|
|
48230
|
-
async function voiceCommand(api, rest, args) {
|
|
48205
|
+
async function voiceCommand(api, projectId, rest, args) {
|
|
48231
48206
|
const [verb, ...vargs] = rest;
|
|
48232
48207
|
if (verb === "collect" && !vargs[0])
|
|
48233
|
-
fail("Usage: tfa-cli brand voice collect <channel> [--url U | --pasted-file F|- | --max N] [--dry-run]");
|
|
48234
|
-
const onboarding = await api.getOnboarding();
|
|
48208
|
+
fail("Usage: tfa-cli brand voice collect <channel> [--url U | --pasted-file F|- | --account <page-id> | --max N] [--dry-run]");
|
|
48235
48209
|
if (verb === "answers") {
|
|
48210
|
+
const answers = manualAnswersOf((await api.listVoiceSources(projectId)).sources);
|
|
48236
48211
|
if (vargs.length) {
|
|
48237
|
-
const
|
|
48212
|
+
for (const a of vargs) {
|
|
48238
48213
|
const i = a.indexOf("=");
|
|
48239
48214
|
if (i < 1)
|
|
48240
48215
|
fail(`Expected question=answer, got '${a}'`);
|
|
48241
|
-
|
|
48242
|
-
}
|
|
48243
|
-
|
|
48244
|
-
await api.patchOnboarding({ styleAnswers });
|
|
48216
|
+
answers[a.slice(0, i)] = a.slice(i + 1);
|
|
48217
|
+
}
|
|
48218
|
+
await api.collectVoiceSource(projectId, MANUAL_CHANNEL, { samples: manualSamples(answers) });
|
|
48245
48219
|
process.stderr.write(`${GREEN}✅ ${vargs.length} answer(s) saved${RESET}
|
|
48246
48220
|
`);
|
|
48247
48221
|
return;
|
|
48248
48222
|
}
|
|
48249
|
-
const answers = onboarding.styleAnswers ?? {};
|
|
48250
48223
|
if (args.json) {
|
|
48251
48224
|
console.log(JSON.stringify(answers, null, 2));
|
|
48252
48225
|
return;
|
|
@@ -48257,15 +48230,14 @@ async function voiceCommand(api, rest, args) {
|
|
|
48257
48230
|
`);
|
|
48258
48231
|
return;
|
|
48259
48232
|
}
|
|
48260
|
-
const brand = await resolveBrand(api, args.brand, (await api.getProfile()).user?.selectedBusinessContextId);
|
|
48261
48233
|
if (!verb || verb === "show") {
|
|
48262
|
-
const profile =
|
|
48263
|
-
const { sources } = await api.listVoiceSources(brand.id);
|
|
48234
|
+
const [{ sources, profile }, brand] = await Promise.all([api.listVoiceSources(projectId), api.getBusinessContext(projectId)]);
|
|
48264
48235
|
if (args.json) {
|
|
48265
48236
|
console.log(JSON.stringify({ brand, profile, sources }, null, 2));
|
|
48266
48237
|
return;
|
|
48267
48238
|
}
|
|
48268
|
-
|
|
48239
|
+
if (brand)
|
|
48240
|
+
process.stderr.write(`${brand.name} ${DIM}${brand.id}${RESET}
|
|
48269
48241
|
`);
|
|
48270
48242
|
process.stderr.write(profile ? `
|
|
48271
48243
|
${profile.profile}
|
|
@@ -48273,7 +48245,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
48273
48245
|
` : `${DIM}(no voice learned yet)${RESET}
|
|
48274
48246
|
`);
|
|
48275
48247
|
for (const s of sources)
|
|
48276
|
-
process.stderr.write(` ${s.channel} ${DIM}${s.posts ?? "?"} posts · ${s.chars ?? "?"} chars${RESET}
|
|
48248
|
+
process.stderr.write(` ${s.channel}${s.account ? ` ${s.label} ${DIM}(${s.account})${RESET}` : ""} ${DIM}${s.posts ?? "?"} posts · ${s.chars ?? "?"} chars${RESET}
|
|
48277
48249
|
`);
|
|
48278
48250
|
return;
|
|
48279
48251
|
}
|
|
@@ -48294,35 +48266,30 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
48294
48266
|
const { samples, note } = collectChannel(channel, Number(args.max ?? 40));
|
|
48295
48267
|
if (!samples.length)
|
|
48296
48268
|
fail(`${channel}: nothing usable came back${note ? ` (${note})` : ""}`);
|
|
48297
|
-
const { sources: sources2 } = await api.collectVoiceSource(
|
|
48269
|
+
const { sources: sources2 } = await api.collectVoiceSource(projectId, channel, { samples });
|
|
48298
48270
|
process.stderr.write(`${GREEN}✅ ${channel}: ${samples.length} reply pairs stored (${sources2.length} source(s))${RESET}
|
|
48299
48271
|
`);
|
|
48300
48272
|
return;
|
|
48301
48273
|
}
|
|
48302
|
-
const { sources } = await api.collectVoiceSource(
|
|
48274
|
+
const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, pasted, account: args.account });
|
|
48303
48275
|
process.stderr.write(`${GREEN}✅ ${channel} collected (${sources.length} source(s))${RESET}
|
|
48304
48276
|
`);
|
|
48305
48277
|
return;
|
|
48306
48278
|
}
|
|
48307
48279
|
if (verb === "remove") {
|
|
48308
48280
|
if (!vargs[0])
|
|
48309
|
-
fail("Usage: tfa-cli brand voice remove <channel>");
|
|
48310
|
-
await api.removeVoiceSource(
|
|
48281
|
+
fail("Usage: tfa-cli brand voice remove <channel> [--account <id>] (no --account: every source of the channel)");
|
|
48282
|
+
await api.removeVoiceSource(projectId, vargs[0], args.account ? { account: args.account } : { all: true });
|
|
48311
48283
|
process.stderr.write(`${GREEN}✅ ${vargs[0]} removed${RESET}
|
|
48312
48284
|
`);
|
|
48313
48285
|
return;
|
|
48314
48286
|
}
|
|
48315
48287
|
if (verb === "learn") {
|
|
48316
|
-
process.stderr.write(`${DIM}learning voice
|
|
48288
|
+
process.stderr.write(`${DIM}learning voice…${RESET}
|
|
48317
48289
|
`);
|
|
48318
|
-
const res = await api.refineBrandVoice(
|
|
48290
|
+
const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined);
|
|
48319
48291
|
if (!res.profile)
|
|
48320
|
-
fail("Nothing to learn from — add answers
|
|
48321
|
-
const { [brand.id]: _stale, ...styleAiAnswers } = onboarding.styleAiAnswers ?? {};
|
|
48322
|
-
await api.patchOnboarding({
|
|
48323
|
-
voiceProfiles: { ...onboarding.voiceProfiles ?? {}, [brand.id]: { ...res, updatedAt: Date.now() } },
|
|
48324
|
-
styleAiAnswers
|
|
48325
|
-
});
|
|
48292
|
+
fail(args["from-company"] ? "No brand page to learn from — 'tfa-cli brand create <name>' and fill it first" : "Nothing to learn from — add answers, collect a source, or fill the brand page first");
|
|
48326
48293
|
if (args.json) {
|
|
48327
48294
|
console.log(JSON.stringify(res, null, 2));
|
|
48328
48295
|
return;
|
|
@@ -48336,16 +48303,13 @@ ${res.profile}
|
|
|
48336
48303
|
const text = vargs.join(" ").trim();
|
|
48337
48304
|
if (!text)
|
|
48338
48305
|
fail('Usage: tfa-cli brand voice correct "<what is off>"');
|
|
48339
|
-
|
|
48340
|
-
if (!stored?.profile)
|
|
48306
|
+
if (!(await api.listVoiceSources(projectId)).profile?.profile)
|
|
48341
48307
|
fail("No voice learned yet — 'tfa-cli brand voice learn' first");
|
|
48342
48308
|
process.stderr.write(`${DIM}applying correction…${RESET}
|
|
48343
48309
|
`);
|
|
48344
|
-
const res = await api.refineBrandVoice(
|
|
48310
|
+
const res = await api.refineBrandVoice(projectId, undefined, text);
|
|
48345
48311
|
if (!res.profile)
|
|
48346
48312
|
fail("The correction pass returned nothing — try rewording it");
|
|
48347
|
-
const { [brand.id]: _stale, ...styleAiAnswers } = onboarding.styleAiAnswers ?? {};
|
|
48348
|
-
await api.patchOnboarding({ voiceProfiles: { ...onboarding.voiceProfiles ?? {}, [brand.id]: { ...res, updatedAt: Date.now() } }, styleAiAnswers });
|
|
48349
48313
|
if (args.json) {
|
|
48350
48314
|
console.log(JSON.stringify(res, null, 2));
|
|
48351
48315
|
return;
|
|
@@ -73382,7 +73346,6 @@ Cancelled by user (Ctrl+C)
|
|
|
73382
73346
|
...args.title ? { title: args.title } : {},
|
|
73383
73347
|
...args.note ? { note: args.note } : {},
|
|
73384
73348
|
...priority ? { priority } : {},
|
|
73385
|
-
...args["business-context"] ? { businessContextId: args["business-context"] } : {},
|
|
73386
73349
|
...args.group ? { group: args.group } : {},
|
|
73387
73350
|
...args["group-name"] ? { groupName: args["group-name"] } : {},
|
|
73388
73351
|
...args["group-description"] ? { groupDescription: args["group-description"] } : {}
|
|
@@ -73451,7 +73414,7 @@ Cancelled by user (Ctrl+C)
|
|
|
73451
73414
|
const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/projects/${projectId2}/recommendations/generate`, {
|
|
73452
73415
|
method: "POST",
|
|
73453
73416
|
headers: { "content-type": "application/json", "x-api-key": apiKey },
|
|
73454
|
-
body: JSON.stringify({ projectId: projectId2, ...
|
|
73417
|
+
body: JSON.stringify({ projectId: projectId2, ...direction ? { direction } : {} }),
|
|
73455
73418
|
signal: AbortSignal.timeout(30000)
|
|
73456
73419
|
});
|
|
73457
73420
|
const text = await res.text();
|