@todoforai/cli 0.1.50 → 0.1.52

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.
Files changed (2) hide show
  1. package/dist/todoforai-cli.js +152 -107
  2. package/package.json +1 -1
@@ -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
- listBusinessContexts() {
42888
- return this.request("GET", "/api/v1/business-context");
42887
+ getBusinessContext(projectId) {
42888
+ return this.request("GET", `/api/v1/projects/${projectId}/business-context`);
42889
42889
  }
42890
- createBusinessContext(name) {
42891
- return this.request("POST", "/api/v1/business-context", { name });
42890
+ createBusinessContext(projectId, name) {
42891
+ return this.request("POST", `/api/v1/projects/${projectId}/business-context`, { projectId, name });
42892
42892
  }
42893
- renameBusinessContext(id, name) {
42894
- return this.request("PUT", `/api/v1/business-context/${id}`, { id, name });
42893
+ renameBusinessContext(projectId, name) {
42894
+ return this.request("PUT", `/api/v1/projects/${projectId}/business-context`, { projectId, name });
42895
42895
  }
42896
- selectBusinessContext(contextId) {
42897
- return this.request("POST", "/api/v1/business-context/select", { contextId });
42896
+ listVoiceSources(projectId) {
42897
+ return this.request("GET", `/api/v1/projects/${projectId}/voice-sources`);
42898
42898
  }
42899
- listVoiceSources(businessContextId) {
42900
- return this.request("GET", `/api/v1/business-context/voice-sources?id=${encodeURIComponent(businessContextId)}`);
42899
+ collectVoiceSource(projectId, channel, opts) {
42900
+ return this.request("POST", "/api/v1/business-context/collect-voice-source", { projectId, channel, ...opts });
42901
42901
  }
42902
- collectVoiceSource(businessContextId, channel, opts) {
42903
- return this.request("POST", "/api/v1/business-context/collect-voice-source", { businessContextId, channel, ...opts });
42902
+ removeVoiceSource(projectId, channel, opts = {}) {
42903
+ return this.request("POST", "/api/v1/business-context/remove-voice-source", { projectId, channel, ...opts });
42904
42904
  }
42905
- removeVoiceSource(businessContextId, channel) {
42906
- return this.request("POST", "/api/v1/business-context/remove-voice-source", { businessContextId, channel });
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");
@@ -42926,6 +42923,15 @@ class ApiClient {
42926
42923
  upsertProjectGroup(projectId, slug, fields) {
42927
42924
  return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(slug)}`, { projectId, slug, ...fields });
42928
42925
  }
42926
+ listProjectSurfaces(projectId) {
42927
+ return this.request("GET", `/api/v1/projects/${projectId}/surfaces`);
42928
+ }
42929
+ upsertProjectSurface(projectId, surfaceId, fields) {
42930
+ return this.request("PUT", `/api/v1/projects/${projectId}/surfaces/${encodeURIComponent(surfaceId)}`, { projectId, surfaceId, ...fields });
42931
+ }
42932
+ deleteProjectSurface(projectId, surfaceId) {
42933
+ return this.request("DELETE", `/api/v1/projects/${projectId}/surfaces/${encodeURIComponent(surfaceId)}`, { projectId, surfaceId });
42934
+ }
42929
42935
  reorderProjectGroups(projectId, slugs) {
42930
42936
  return this.request("PUT", `/api/v1/projects/${projectId}/groups-order`, { projectId, slugs });
42931
42937
  }
@@ -43441,7 +43447,7 @@ var TodoStatus;
43441
43447
  TodoStatus2["DELETED"] = "DELETED";
43442
43448
  })(TodoStatus ||= {});
43443
43449
  // ../packages/shared-fbe/src/models.ts
43444
- var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5";
43450
+ var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5.5";
43445
43451
  var MODEL_ALIASES = {
43446
43452
  claude: DEFAULT_MODEL
43447
43453
  };
@@ -43729,8 +43735,11 @@ var SERVER_TO_FRONTENDS = {
43729
43735
  hexGrid: {
43730
43736
  updated: (projectId) => `${"hexgrid:updated" /* HEXGRID_UPDATED */}:${projectId}`
43731
43737
  },
43738
+ surfaces: {
43739
+ updated: (projectId) => `${"project:surfaces_updated" /* PROJECT_SURFACES_UPDATED */}:${projectId}`
43740
+ },
43732
43741
  killSwitch: {
43733
- changed: (projectId) => `${"project:kill_switch" /* KILL_SWITCH */}:${projectId}`
43742
+ changed: (projectId, userId) => `${"project:kill_switch" /* KILL_SWITCH */}:${projectId}${privateSuffix(userId)}`
43734
43743
  },
43735
43744
  sandbox: {
43736
43745
  state: (userId) => `${"sandbox:state" /* SANDBOX_STATE */}:${userId}`
@@ -44342,6 +44351,8 @@ var model_promos_default = {
44342
44351
  { family: "gpt-5-5(-pro)?", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21" },
44343
44352
  { family: "gpt-5-6-sol", rate: 0.85, starts: "2026-08-26", ends: "2026-09-13" },
44344
44353
  { family: "gpt-5-6-sol", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21" },
44354
+ { family: "gpt-6-sol", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
44355
+ { family: "gpt-6-luna", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
44345
44356
  { family: "gpt-6-astra", rate: 0.85, starts: "2026-09-05", ends: "2026-09-13", boostable: false },
44346
44357
  { family: "gpt-6-astra", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21", boostable: false },
44347
44358
  { family: "gpt-5-6-terra", rate: 0.85, starts: "2026-06-29", ends: "2026-09-13" },
@@ -44393,8 +44404,12 @@ var TOPICS = {
44393
44404
  channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
44394
44405
  audience: "frontend"
44395
44406
  },
44407
+ ["project:surfaces_updated" /* PROJECT_SURFACES_UPDATED */]: {
44408
+ channel: (p) => SERVER_TO_FRONTENDS.surfaces.updated(p.projectId),
44409
+ audience: "frontend"
44410
+ },
44396
44411
  ["project:kill_switch" /* KILL_SWITCH */]: {
44397
- channel: (p) => SERVER_TO_FRONTENDS.killSwitch.changed(p.projectId),
44412
+ channel: (p) => SERVER_TO_FRONTENDS.killSwitch.changed(p.projectId, p.userId),
44398
44413
  audience: "frontend"
44399
44414
  },
44400
44415
  ["sandbox:state" /* SANDBOX_STATE */]: {
@@ -44533,7 +44548,7 @@ import { parseArgs } from "util";
44533
44548
  // package.json
44534
44549
  var package_default = {
44535
44550
  name: "@todoforai/cli",
44536
- version: "0.1.50",
44551
+ version: "0.1.52",
44537
44552
  type: "module",
44538
44553
  bin: {
44539
44554
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -44591,7 +44606,7 @@ Examples:
44591
44606
  tfa-cli agent list|get|update|create … # Agents (update <agent> model=…)
44592
44607
  tfa-cli todo set <todo-id|-> title=… group=… star=true # Edit a todo's fields
44593
44608
  tfa-cli project list|set|settings|groups|default|agent … # Projects; edit the current one
44594
- tfa-cli brand list|create|select|voice … # Brands (business contexts) + voice learning
44609
+ tfa-cli brand [show|create|rename|voice …] # The project's brand page + voice learning
44595
44610
  tfa-cli device list|rename|wallpaper … # Paired machines
44596
44611
  tfa-cli list [-n 30] [--cursor N] [--all] [--status S] # List todos (paginated)
44597
44612
  tfa-cli status <todo-id> <STATUS> # Update a todo's status
@@ -44708,8 +44723,6 @@ function parseCliArgs() {
44708
44723
  card: { type: "string" },
44709
44724
  link: { type: "boolean", default: false },
44710
44725
  direction: { type: "string" },
44711
- "business-context": { type: "string" },
44712
- brand: { type: "string" },
44713
44726
  url: { type: "string" },
44714
44727
  "pasted-file": { type: "string" },
44715
44728
  "from-company": { type: "boolean", default: false },
@@ -47823,7 +47836,6 @@ Fields (PUT /todos/{id}):
47823
47836
  star true|false (starredAt now / 0)
47824
47837
  schedule unix ms timestamp, 0 = immediate (alias: scheduledTimestamp)
47825
47838
  agent agentSettingsId (alias: agentSettingsId)
47826
- brand businessContextId (alias: businessContextId)
47827
47839
  public true|false — refused from an agent shell (isPublic)
47828
47840
 
47829
47841
  tfa-cli todo message <todo-id|-> <message-id> <field=value>…
@@ -47845,7 +47857,7 @@ tfa-cli project — edit the current project
47845
47857
 
47846
47858
  Usage:
47847
47859
  tfa-cli project list Projects you can access (* = default)
47848
- tfa-cli project set <field=value>… name=… description=… brand=<businessContextId>
47860
+ tfa-cli project set <field=value>… name=… description=…
47849
47861
  (isPublic refused from an agent shell)
47850
47862
  tfa-cli project default Make it the project you land on at /
47851
47863
  tfa-cli project agent <agent> Default agent for new todos in this project
@@ -47853,11 +47865,18 @@ Usage:
47853
47865
  tfa-cli project groups List todo groups
47854
47866
  tfa-cli project groups set <slug> <field=value>… Create/update a group: name description pinned archived order
47855
47867
  tfa-cli project groups reorder <slug>… Set wall order
47868
+ tfa-cli project surfaces List the board's card surfaces (live refs shown as cards)
47869
+ tfa-cli project surfaces set <id> ref=<ref> [title=…] [group=<slug>] [order=N]
47870
+ Show a ref on the board: a todo id, <todoId>:<alias>
47871
+ (shown artifact) or an http(s) url. group= puts it
47872
+ inside that group's card; group="" makes it standalone.
47873
+ tfa-cli project surfaces rm <id> Take it off the board
47856
47874
 
47857
47875
  Examples:
47858
47876
  tfa-cli project set name="Q4 launch"
47859
47877
  tfa-cli project groups set marketing name=Marketing description="Top-of-funnel work"
47860
47878
  tfa-cli project groups reorder marketing sales ops
47879
+ tfa-cli project surfaces set weekly-kpis ref=$TODOFORAI_TODO_ID:kpi title="Weekly KPIs" group=marketing
47861
47880
  `);
47862
47881
  }
47863
47882
  var TODO_ALIASES = {
@@ -47865,10 +47884,9 @@ var TODO_ALIASES = {
47865
47884
  group: "groupTag",
47866
47885
  schedule: "scheduledTimestamp",
47867
47886
  agent: "agentSettingsId",
47868
- brand: "businessContextId",
47869
47887
  public: "isPublic"
47870
47888
  };
47871
- var PROJECT_ALIASES = { brand: "businessContextId" };
47889
+ var PROJECT_ALIASES = {};
47872
47890
  function fail(msg) {
47873
47891
  process.stderr.write(`${RED}${msg}${RESET}
47874
47892
  `);
@@ -48010,22 +48028,58 @@ async function projectCommand(api, positionals, args) {
48010
48028
  }
48011
48029
  fail(`Unknown 'project groups' verb: ${verb}`);
48012
48030
  }
48031
+ if (sub === "surfaces") {
48032
+ const [verb, ...sargs] = rest;
48033
+ if (!verb) {
48034
+ const surfaces = await api.listProjectSurfaces(projectId);
48035
+ if (args.json) {
48036
+ console.log(JSON.stringify(surfaces, null, 2));
48037
+ return;
48038
+ }
48039
+ for (const s of surfaces)
48040
+ process.stderr.write(`${s.id} ${s.ref} ${DIM}${[s.title, s.group && `in ${s.group}`].filter(Boolean).join(" ")}${RESET}
48041
+ `);
48042
+ return;
48043
+ }
48044
+ if (verb === "set") {
48045
+ const [id, ...fields] = sargs;
48046
+ if (!id)
48047
+ fail("Usage: tfa-cli project surfaces set <id> ref=<ref> [title=…] [group=<slug>] [order=N]");
48048
+ const surface = await api.upsertProjectSurface(projectId, id, parseAssignments2(fields));
48049
+ if (args.json) {
48050
+ console.log(JSON.stringify(surface, null, 2));
48051
+ return;
48052
+ }
48053
+ process.stderr.write(`${GREEN}✅ surface ${id} shows ${surface.ref}${surface.group ? ` in ${surface.group}` : ""}${RESET}
48054
+ `);
48055
+ return;
48056
+ }
48057
+ if (verb === "rm") {
48058
+ const [id] = sargs;
48059
+ if (!id)
48060
+ fail("Usage: tfa-cli project surfaces rm <id>");
48061
+ await api.deleteProjectSurface(projectId, id);
48062
+ process.stderr.write(`${GREEN}✅ surface ${id} removed${RESET}
48063
+ `);
48064
+ return;
48065
+ }
48066
+ fail(`Unknown 'project surfaces' verb: ${verb}`);
48067
+ }
48013
48068
  fail(`Unknown 'project' subcommand: ${sub}`);
48014
48069
  }
48015
48070
  function printBrandHelp() {
48016
48071
  process.stderr.write(`
48017
- tfa-cli brand — business contexts (brands) and the learned writing voice
48072
+ tfa-cli brand — the project's brand page and its learned writing voice
48018
48073
 
48074
+ The project IS the brand. --project <id> or $TODOFORAI_PROJECT_ID selects it.
48019
48075
  The brand page text itself is a file: read/write todoforai:business-context.
48020
- These commands manage the contexts and the voice learned for them.
48021
48076
 
48022
48077
  Usage:
48023
- tfa-cli brand list Contexts (* = selected)
48024
- tfa-cli brand create <name> New brand (empty .md page)
48025
- tfa-cli brand rename <brand> <name>
48026
- tfa-cli brand select <brand|none> Active brand for the account
48078
+ tfa-cli brand [show] This project's brand (none = personal board)
48079
+ tfa-cli brand create <name> Give the project a brand page (empty .md); the board turns business
48080
+ tfa-cli brand rename <name>
48027
48081
  tfa-cli brand voice Learned profile + sources
48028
- tfa-cli brand voice answers [<q>=<a>…] Show / set the brand-voice answers (strings)
48082
+ tfa-cli brand voice answers [<q>=<a>…] Show / set the Manual source: answers to the style questions
48029
48083
  tfa-cli brand voice collect <channel> [--url U | --pasted-file <F|-> | --max N]
48030
48084
  Add a writing sample source.
48031
48085
  On this device (signed-in CLI, reply pairs):
@@ -48039,9 +48093,7 @@ Usage:
48039
48093
  tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
48040
48094
  in one pass and the correction is kept for every re-learn
48041
48095
 
48042
- <brand> is an id or name (unique partial works). Voice subcommands use the
48043
- selected brand unless --brand <id|name> is given (--pasted-file - reads stdin).
48044
- Delete / reset are UI-only.
48096
+ --pasted-file - reads stdin. Delete is UI-only.
48045
48097
 
48046
48098
  Questions the UI asks (keys for 'answers'):
48047
48099
  "What makes you different from competitors?"
@@ -48049,43 +48101,30 @@ Questions the UI asks (keys for 'answers'):
48049
48101
  "A frustrated customer says things aren't working. How do you reply?"
48050
48102
  `);
48051
48103
  }
48052
- async function resolveBrand(api, query, selectedId) {
48053
- const contexts = await api.listBusinessContexts();
48054
- if (!query) {
48055
- const sel = contexts.find((c3) => c3.id === selectedId);
48056
- if (sel)
48057
- return sel;
48058
- if (contexts.length === 1)
48059
- return contexts[0];
48060
- fail("No brand selected — pass --brand <id|name> or 'tfa-cli brand select'");
48061
- }
48062
- const q = query.toLowerCase();
48063
- const exact = contexts.find((c3) => c3.id === query || c3.name.toLowerCase() === q);
48064
- if (exact)
48065
- return exact;
48066
- const partial = contexts.filter((c3) => c3.id.startsWith(query) || c3.name.toLowerCase().includes(q));
48067
- if (partial.length === 1)
48068
- return partial[0];
48069
- fail(partial.length ? `Ambiguous brand '${query}'` : `No brand matching '${query}'`);
48104
+ function requireProject(args) {
48105
+ const projectId = args.project || getEnv2("PROJECT_ID");
48106
+ if (!projectId)
48107
+ fail("No project — pass --project <id> or set TODOFORAI_PROJECT_ID");
48108
+ return projectId;
48070
48109
  }
48071
48110
  async function brandCommand(api, positionals, args) {
48072
48111
  const [, sub, ...rest] = positionals;
48073
- if (!sub || sub === "list") {
48074
- const contexts = await api.listBusinessContexts();
48075
- const selected = (await api.getProfile()).user?.selectedBusinessContextId;
48112
+ const projectId = requireProject(args);
48113
+ if (!sub || sub === "show") {
48114
+ const brand = await api.getBusinessContext(projectId);
48076
48115
  if (args.json) {
48077
- console.log(JSON.stringify(contexts.map((c3) => ({ ...c3, selected: c3.id === selected })), null, 2));
48116
+ console.log(JSON.stringify(brand, null, 2));
48078
48117
  return;
48079
48118
  }
48080
- for (const c3 of contexts)
48081
- process.stderr.write(`${c3.id === selected ? "*" : " "} ${c3.name} ${DIM}${c3.id}${RESET}
48119
+ process.stderr.write(brand ? `${brand.name} ${DIM}${brand.id}${RESET}
48120
+ ` : `${DIM}(personal board — no brand)${RESET}
48082
48121
  `);
48083
48122
  return;
48084
48123
  }
48085
48124
  if (sub === "create") {
48086
48125
  if (!rest[0])
48087
48126
  fail("Usage: tfa-cli brand create <name>");
48088
- const c3 = await api.createBusinessContext(rest[0]);
48127
+ const c3 = await api.createBusinessContext(projectId, rest[0]);
48089
48128
  if (args.json) {
48090
48129
  console.log(JSON.stringify(c3, null, 2));
48091
48130
  return;
@@ -48095,25 +48134,15 @@ async function brandCommand(api, positionals, args) {
48095
48134
  return;
48096
48135
  }
48097
48136
  if (sub === "rename") {
48098
- if (!rest[1])
48099
- fail("Usage: tfa-cli brand rename <brand> <name>");
48100
- const c3 = await resolveBrand(api, rest[0]);
48101
- await api.renameBusinessContext(c3.id, rest[1]);
48102
- process.stderr.write(`${GREEN}✅ renamed to ${rest[1]}${RESET}
48103
- `);
48104
- return;
48105
- }
48106
- if (sub === "select") {
48107
48137
  if (!rest[0])
48108
- fail("Usage: tfa-cli brand select <brand|none>");
48109
- const id = rest[0] === "none" ? null : (await resolveBrand(api, rest[0])).id;
48110
- await api.selectBusinessContext(id);
48111
- process.stderr.write(`${GREEN}✅ selected brand: ${id ?? "none"}${RESET}
48138
+ fail("Usage: tfa-cli brand rename <name>");
48139
+ await api.renameBusinessContext(projectId, rest[0]);
48140
+ process.stderr.write(`${GREEN}✅ renamed to ${rest[0]}${RESET}
48112
48141
  `);
48113
48142
  return;
48114
48143
  }
48115
48144
  if (sub === "voice")
48116
- return voiceCommand(api, rest, args);
48145
+ return voiceCommand(api, projectId, rest, args);
48117
48146
  fail(`Unknown 'brand' subcommand: ${sub}`);
48118
48147
  }
48119
48148
  function dryCollect(channel, max) {
@@ -48167,26 +48196,30 @@ async function voiceDeviceCommand(rest, args) {
48167
48196
  }
48168
48197
  return false;
48169
48198
  }
48170
- async function voiceCommand(api, rest, args) {
48199
+ async function voiceCommand(api, projectId, rest, args) {
48171
48200
  const [verb, ...vargs] = rest;
48172
48201
  if (verb === "collect" && !vargs[0])
48173
- fail("Usage: tfa-cli brand voice collect <channel> [--url U | --pasted-file F|- | --max N] [--dry-run]");
48174
- const onboarding = await api.getOnboarding();
48202
+ fail("Usage: tfa-cli brand voice collect <channel> [--url U | --pasted-file F|- | --account <page-id> | --max N] [--dry-run]");
48203
+ const brand = await api.getBusinessContext(projectId);
48204
+ if (!brand)
48205
+ fail("This project has no brand — 'tfa-cli brand create <name>' first");
48175
48206
  if (verb === "answers") {
48207
+ const { sources } = await api.listVoiceSources(projectId);
48208
+ const manual = sources.find((s) => s.channel === "Manual");
48209
+ const answers = Object.fromEntries((manual?.items ?? []).filter((x) => x?.prompt).map((x) => [x.prompt, x.text]));
48176
48210
  if (vargs.length) {
48177
- const given = Object.fromEntries(vargs.map((a) => {
48211
+ for (const a of vargs) {
48178
48212
  const i = a.indexOf("=");
48179
48213
  if (i < 1)
48180
48214
  fail(`Expected question=answer, got '${a}'`);
48181
- return [a.slice(0, i), a.slice(i + 1)];
48182
- }));
48183
- const styleAnswers = { ...onboarding.styleAnswers ?? {}, ...given };
48184
- await api.patchOnboarding({ styleAnswers });
48215
+ answers[a.slice(0, i)] = a.slice(i + 1);
48216
+ }
48217
+ const samples = Object.entries(answers).filter(([, t]) => t.trim()).map(([prompt, text]) => ({ prompt, text }));
48218
+ await api.collectVoiceSource(projectId, "Manual", { samples });
48185
48219
  process.stderr.write(`${GREEN}✅ ${vargs.length} answer(s) saved${RESET}
48186
48220
  `);
48187
48221
  return;
48188
48222
  }
48189
- const answers = onboarding.styleAnswers ?? {};
48190
48223
  if (args.json) {
48191
48224
  console.log(JSON.stringify(answers, null, 2));
48192
48225
  return;
@@ -48197,10 +48230,8 @@ async function voiceCommand(api, rest, args) {
48197
48230
  `);
48198
48231
  return;
48199
48232
  }
48200
- const brand = await resolveBrand(api, args.brand, (await api.getProfile()).user?.selectedBusinessContextId);
48201
48233
  if (!verb || verb === "show") {
48202
- const profile = onboarding.voiceProfiles?.[brand.id];
48203
- const { sources } = await api.listVoiceSources(brand.id);
48234
+ const { sources, profile } = await api.listVoiceSources(projectId);
48204
48235
  if (args.json) {
48205
48236
  console.log(JSON.stringify({ brand, profile, sources }, null, 2));
48206
48237
  return;
@@ -48213,7 +48244,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
48213
48244
  ` : `${DIM}(no voice learned yet)${RESET}
48214
48245
  `);
48215
48246
  for (const s of sources)
48216
- process.stderr.write(` ${s.channel} ${DIM}${s.posts ?? "?"} posts · ${s.chars ?? "?"} chars${RESET}
48247
+ process.stderr.write(` ${s.channel}${s.account ? ` ${s.label} ${DIM}(${s.account})${RESET}` : ""} ${DIM}${s.posts ?? "?"} posts · ${s.chars ?? "?"} chars${RESET}
48217
48248
  `);
48218
48249
  return;
48219
48250
  }
@@ -48234,20 +48265,20 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
48234
48265
  const { samples, note } = collectChannel(channel, Number(args.max ?? 40));
48235
48266
  if (!samples.length)
48236
48267
  fail(`${channel}: nothing usable came back${note ? ` (${note})` : ""}`);
48237
- const { sources: sources2 } = await api.collectVoiceSource(brand.id, channel, { samples });
48268
+ const { sources: sources2 } = await api.collectVoiceSource(projectId, channel, { samples });
48238
48269
  process.stderr.write(`${GREEN}✅ ${channel}: ${samples.length} reply pairs stored (${sources2.length} source(s))${RESET}
48239
48270
  `);
48240
48271
  return;
48241
48272
  }
48242
- const { sources } = await api.collectVoiceSource(brand.id, channel, { url: args.url, pasted });
48273
+ const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, pasted, account: args.account });
48243
48274
  process.stderr.write(`${GREEN}✅ ${channel} collected (${sources.length} source(s))${RESET}
48244
48275
  `);
48245
48276
  return;
48246
48277
  }
48247
48278
  if (verb === "remove") {
48248
48279
  if (!vargs[0])
48249
- fail("Usage: tfa-cli brand voice remove <channel>");
48250
- await api.removeVoiceSource(brand.id, vargs[0]);
48280
+ fail("Usage: tfa-cli brand voice remove <channel> [--account <id>] (no --account: every source of the channel)");
48281
+ await api.removeVoiceSource(projectId, vargs[0], args.account ? { account: args.account } : { all: true });
48251
48282
  process.stderr.write(`${GREEN}✅ ${vargs[0]} removed${RESET}
48252
48283
  `);
48253
48284
  return;
@@ -48255,14 +48286,9 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
48255
48286
  if (verb === "learn") {
48256
48287
  process.stderr.write(`${DIM}learning voice for ${brand.name}…${RESET}
48257
48288
  `);
48258
- const res = await api.refineBrandVoice(brand.id, onboarding.styleAnswers ?? {}, args["from-company"] ? "company" : undefined);
48289
+ const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined);
48259
48290
  if (!res.profile)
48260
48291
  fail("Nothing to learn from — add answers or collect a source first");
48261
- const { [brand.id]: _stale, ...styleAiAnswers } = onboarding.styleAiAnswers ?? {};
48262
- await api.patchOnboarding({
48263
- voiceProfiles: { ...onboarding.voiceProfiles ?? {}, [brand.id]: { ...res, updatedAt: Date.now() } },
48264
- styleAiAnswers
48265
- });
48266
48292
  if (args.json) {
48267
48293
  console.log(JSON.stringify(res, null, 2));
48268
48294
  return;
@@ -48276,16 +48302,13 @@ ${res.profile}
48276
48302
  const text = vargs.join(" ").trim();
48277
48303
  if (!text)
48278
48304
  fail('Usage: tfa-cli brand voice correct "<what is off>"');
48279
- const stored = onboarding.voiceProfiles?.[brand.id];
48280
- if (!stored?.profile)
48305
+ if (!(await api.listVoiceSources(projectId)).profile?.profile)
48281
48306
  fail("No voice learned yet — 'tfa-cli brand voice learn' first");
48282
48307
  process.stderr.write(`${DIM}applying correction…${RESET}
48283
48308
  `);
48284
- const res = await api.refineBrandVoice(brand.id, onboarding.styleAnswers ?? {}, undefined, text);
48309
+ const res = await api.refineBrandVoice(projectId, undefined, text);
48285
48310
  if (!res.profile)
48286
48311
  fail("The correction pass returned nothing — try rewording it");
48287
- const { [brand.id]: _stale, ...styleAiAnswers } = onboarding.styleAiAnswers ?? {};
48288
- await api.patchOnboarding({ voiceProfiles: { ...onboarding.voiceProfiles ?? {}, [brand.id]: { ...res, updatedAt: Date.now() } }, styleAiAnswers });
48289
48312
  if (args.json) {
48290
48313
  console.log(JSON.stringify(res, null, 2));
48291
48314
  return;
@@ -69765,6 +69788,22 @@ var zUsageUpdate = object({
69765
69788
  return;
69766
69789
  })
69767
69790
  });
69791
+ var zNoticeSeverity = union([
69792
+ literal("info"),
69793
+ literal("warning"),
69794
+ literal("error"),
69795
+ string2()
69796
+ ]);
69797
+ var zNotice = object({
69798
+ severity: zNoticeSeverity,
69799
+ title: string2().min(1),
69800
+ description: defaultOnError(string2().nullish(), () => {
69801
+ return;
69802
+ }),
69803
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
69804
+ return;
69805
+ })
69806
+ });
69768
69807
  var zCompactionId = string2();
69769
69808
  var zCompactionStatus = union([
69770
69809
  literal("in_progress"),
@@ -69833,6 +69872,9 @@ var zSessionUpdate = union([
69833
69872
  zUsageUpdate.and(object({
69834
69873
  sessionUpdate: literal("usage_update")
69835
69874
  })),
69875
+ zNotice.and(object({
69876
+ sessionUpdate: literal("notice")
69877
+ })),
69836
69878
  zCompactionUpdate.and(object({
69837
69879
  sessionUpdate: literal("compaction_update")
69838
69880
  })),
@@ -69894,6 +69936,7 @@ var zSessionConfigOptionsCapabilities = object({
69894
69936
  return;
69895
69937
  })
69896
69938
  });
69939
+ var zNoticeCapabilities = record(string2(), unknown());
69897
69940
  var zClientSessionCapabilities = object({
69898
69941
  compaction: defaultOnError(zCompactionCapabilities.nullish(), () => {
69899
69942
  return;
@@ -69901,6 +69944,9 @@ var zClientSessionCapabilities = object({
69901
69944
  configOptions: defaultOnError(zSessionConfigOptionsCapabilities.nullish(), () => {
69902
69945
  return;
69903
69946
  }),
69947
+ notices: defaultOnError(zNoticeCapabilities.nullish(), () => {
69948
+ return;
69949
+ }),
69904
69950
  _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
69905
69951
  return;
69906
69952
  })
@@ -73299,7 +73345,6 @@ Cancelled by user (Ctrl+C)
73299
73345
  ...args.title ? { title: args.title } : {},
73300
73346
  ...args.note ? { note: args.note } : {},
73301
73347
  ...priority ? { priority } : {},
73302
- ...args["business-context"] ? { businessContextId: args["business-context"] } : {},
73303
73348
  ...args.group ? { group: args.group } : {},
73304
73349
  ...args["group-name"] ? { groupName: args["group-name"] } : {},
73305
73350
  ...args["group-description"] ? { groupDescription: args["group-description"] } : {}
@@ -73368,7 +73413,7 @@ Cancelled by user (Ctrl+C)
73368
73413
  const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/projects/${projectId2}/recommendations/generate`, {
73369
73414
  method: "POST",
73370
73415
  headers: { "content-type": "application/json", "x-api-key": apiKey },
73371
- body: JSON.stringify({ projectId: projectId2, ...args["business-context"] ? { businessContextId: args["business-context"] } : {}, ...direction ? { direction } : {} }),
73416
+ body: JSON.stringify({ projectId: projectId2, ...direction ? { direction } : {} }),
73372
73417
  signal: AbortSignal.timeout(30000)
73373
73418
  });
73374
73419
  const text = await res.text();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.50",
3
+ "version": "0.1.52",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",