@todoforai/cli 0.1.52 → 0.1.54

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.
@@ -42875,6 +42875,9 @@ class ApiClient {
42875
42875
  setDefaultProject(projectId) {
42876
42876
  return this.request("POST", "/api/v1/users/default-project", { projectId });
42877
42877
  }
42878
+ updateProjectSettings(projectId, settings) {
42879
+ return this.request("PUT", `/api/v1/settings/projects/${projectId}`, { projectId, settings });
42880
+ }
42878
42881
  setProjectDefaultAgent(projectId, agentSettingsId) {
42879
42882
  return this.request("POST", "/api/v1/users/project-agent-mapping", { projectId, agentSettingsId });
42880
42883
  }
@@ -43809,6 +43812,17 @@ var SERVER_TO_FRONTENDS = {
43809
43812
  function_call_request: (userId) => `${"FUNCTION_CALL_REQUEST_AGENT" /* FUNCTION_CALL_REQUEST_AGENT */}:frontend:${userId}`
43810
43813
  }
43811
43814
  };
43815
+ // ../packages/shared-fbe/src/REST_types.ts
43816
+ var MANUAL_CHANNEL = "Manual";
43817
+ function manualAnswersOf(sources) {
43818
+ const manual = sources?.find((s) => s.channel === MANUAL_CHANNEL);
43819
+ const out = {};
43820
+ for (const x of manual?.items ?? [])
43821
+ if (typeof x !== "string" && x.prompt)
43822
+ out[x.prompt] = x.text;
43823
+ return out;
43824
+ }
43825
+ var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
43812
43826
  // ../packages/shared-fbe/src/templateBody.ts
43813
43827
  var USER_HEADINGS = new Set([
43814
43828
  "your task",
@@ -44266,7 +44280,6 @@ Exactly ONE \`api_recommend_template\` call per proposal: it carries both what t
44266
44280
  It runs in-process and needs no machine or shell. Never shell out to \`todoregistry-cli\` or \`todoforai-cli\` for this.
44267
44281
 
44268
44282
  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).`,
44269
- mcpConfigs: {},
44270
44283
  edgesMcpConfigs: {},
44271
44284
  permissions: {
44272
44285
  allow: [
@@ -44289,7 +44302,6 @@ var JARVIS_AGENT = {
44289
44302
  ownerId: "SYSTEM",
44290
44303
  systemMessage: "",
44291
44304
  model: "",
44292
- mcpConfigs: {},
44293
44305
  edgesMcpConfigs: {},
44294
44306
  createdAt: 0,
44295
44307
  updatedAt: 0
@@ -44299,7 +44311,6 @@ var ONBOARDING_AGENT = {
44299
44311
  name: "Setup",
44300
44312
  ownerId: "SYSTEM",
44301
44313
  systemMessage: "Workspace setup — this todo is a guided stepper and never runs an agent.",
44302
- mcpConfigs: {},
44303
44314
  edgesMcpConfigs: {},
44304
44315
  createdAt: 0,
44305
44316
  updatedAt: 0
@@ -44548,7 +44559,7 @@ import { parseArgs } from "util";
44548
44559
  // package.json
44549
44560
  var package_default = {
44550
44561
  name: "@todoforai/cli",
44551
- version: "0.1.52",
44562
+ version: "0.1.54",
44552
44563
  type: "module",
44553
44564
  bin: {
44554
44565
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -47434,8 +47445,7 @@ Examples:
47434
47445
  tfa-cli agent update <agent> sysmsg="You are a terse video editor."
47435
47446
  tfa-cli agent create name=Reviewer model=claude sysmsg="Review diffs only."
47436
47447
 
47437
- From an agent shell (dst_ token) permissions, mcpConfigs, edgesMcpConfigs,
47438
- devicesConfig and delete are refused by the backend.
47448
+ From an agent shell (dst_ token) permissions, edgesMcpConfigs, devicesConfig and delete are refused by the backend.
47439
47449
  `);
47440
47450
  }
47441
47451
  var FIELD_ALIASES = {
@@ -47853,7 +47863,7 @@ function printProjectHelp() {
47853
47863
  process.stderr.write(`
47854
47864
  tfa-cli project — edit the current project
47855
47865
 
47856
- --project <id> or $TODOFORAI_PROJECT_ID selects the project.
47866
+ --project <id>, $TODOFORAI_PROJECT_ID, or the configured default selects the project.
47857
47867
 
47858
47868
  Usage:
47859
47869
  tfa-cli project list Projects you can access (* = default)
@@ -47886,7 +47896,7 @@ var TODO_ALIASES = {
47886
47896
  agent: "agentSettingsId",
47887
47897
  public: "isPublic"
47888
47898
  };
47889
- var PROJECT_ALIASES = {};
47899
+ var NO_PROJECT = "No project — pass --project <id> or set TODOFORAI_PROJECT_ID";
47890
47900
  function fail(msg) {
47891
47901
  process.stderr.write(`${RED}${msg}${RESET}
47892
47902
  `);
@@ -47937,9 +47947,8 @@ async function todoCommand(api, positionals, args) {
47937
47947
  process.stderr.write(`${GREEN}✅ ${todoId} updated: ${Object.keys(updates).join(", ")}${RESET}
47938
47948
  `);
47939
47949
  }
47940
- async function projectCommand(api, positionals, args) {
47950
+ async function projectCommand(api, positionals, args, projectId) {
47941
47951
  const [, sub, ...rest] = positionals;
47942
- const projectId = args.project || getEnv2("PROJECT_ID");
47943
47952
  if (!sub) {
47944
47953
  printProjectHelp();
47945
47954
  process.exit(0);
@@ -47958,11 +47967,11 @@ async function projectCommand(api, positionals, args) {
47958
47967
  return;
47959
47968
  }
47960
47969
  if (!projectId)
47961
- fail("No project — pass --project <id> or set TODOFORAI_PROJECT_ID");
47970
+ fail(NO_PROJECT);
47962
47971
  if (sub === "set") {
47963
47972
  if (!rest.length)
47964
47973
  fail("Usage: tfa-cli project set <field=value>…");
47965
- const updates = parseAssignments2(rest, PROJECT_ALIASES);
47974
+ const updates = parseAssignments2(rest);
47966
47975
  await api.updateProject(projectId, updates);
47967
47976
  process.stderr.write(`${GREEN}✅ project updated: ${Object.keys(updates).join(", ")}${RESET}
47968
47977
  `);
@@ -48071,7 +48080,7 @@ function printBrandHelp() {
48071
48080
  process.stderr.write(`
48072
48081
  tfa-cli brand — the project's brand page and its learned writing voice
48073
48082
 
48074
- The project IS the brand. --project <id> or $TODOFORAI_PROJECT_ID selects it.
48083
+ The project IS the brand. --project <id>, $TODOFORAI_PROJECT_ID, or the configured default selects it.
48075
48084
  The brand page text itself is a file: read/write todoforai:business-context.
48076
48085
 
48077
48086
  Usage:
@@ -48101,15 +48110,9 @@ Questions the UI asks (keys for 'answers'):
48101
48110
  "A frustrated customer says things aren't working. How do you reply?"
48102
48111
  `);
48103
48112
  }
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;
48109
- }
48110
- async function brandCommand(api, positionals, args) {
48113
+ async function brandCommand(api, positionals, args, scoped) {
48111
48114
  const [, sub, ...rest] = positionals;
48112
- const projectId = requireProject(args);
48115
+ const projectId = scoped || fail(NO_PROJECT);
48113
48116
  if (!sub || sub === "show") {
48114
48117
  const brand = await api.getBusinessContext(projectId);
48115
48118
  if (args.json) {
@@ -48200,13 +48203,8 @@ async function voiceCommand(api, projectId, rest, args) {
48200
48203
  const [verb, ...vargs] = rest;
48201
48204
  if (verb === "collect" && !vargs[0])
48202
48205
  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");
48206
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]));
48207
+ const answers = manualAnswersOf((await api.listVoiceSources(projectId)).sources);
48210
48208
  if (vargs.length) {
48211
48209
  for (const a of vargs) {
48212
48210
  const i = a.indexOf("=");
@@ -48214,8 +48212,7 @@ async function voiceCommand(api, projectId, rest, args) {
48214
48212
  fail(`Expected question=answer, got '${a}'`);
48215
48213
  answers[a.slice(0, i)] = a.slice(i + 1);
48216
48214
  }
48217
- const samples = Object.entries(answers).filter(([, t]) => t.trim()).map(([prompt, text]) => ({ prompt, text }));
48218
- await api.collectVoiceSource(projectId, "Manual", { samples });
48215
+ await api.collectVoiceSource(projectId, MANUAL_CHANNEL, { samples: manualSamples(answers) });
48219
48216
  process.stderr.write(`${GREEN}✅ ${vargs.length} answer(s) saved${RESET}
48220
48217
  `);
48221
48218
  return;
@@ -48231,12 +48228,13 @@ async function voiceCommand(api, projectId, rest, args) {
48231
48228
  return;
48232
48229
  }
48233
48230
  if (!verb || verb === "show") {
48234
- const { sources, profile } = await api.listVoiceSources(projectId);
48231
+ const [{ sources, profile }, brand] = await Promise.all([api.listVoiceSources(projectId), api.getBusinessContext(projectId)]);
48235
48232
  if (args.json) {
48236
48233
  console.log(JSON.stringify({ brand, profile, sources }, null, 2));
48237
48234
  return;
48238
48235
  }
48239
- process.stderr.write(`${brand.name} ${DIM}${brand.id}${RESET}
48236
+ if (brand)
48237
+ process.stderr.write(`${brand.name} ${DIM}${brand.id}${RESET}
48240
48238
  `);
48241
48239
  process.stderr.write(profile ? `
48242
48240
  ${profile.profile}
@@ -48284,11 +48282,11 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
48284
48282
  return;
48285
48283
  }
48286
48284
  if (verb === "learn") {
48287
- process.stderr.write(`${DIM}learning voice for ${brand.name}…${RESET}
48285
+ process.stderr.write(`${DIM}learning voice…${RESET}
48288
48286
  `);
48289
48287
  const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined);
48290
48288
  if (!res.profile)
48291
- fail("Nothing to learn from — add answers or collect a source first");
48289
+ 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");
48292
48290
  if (args.json) {
48293
48291
  console.log(JSON.stringify(res, null, 2));
48294
48292
  return;
@@ -73166,12 +73164,13 @@ Cancelled by user (Ctrl+C)
73166
73164
  await todoCommand(api2, positionals, args);
73167
73165
  return;
73168
73166
  }
73167
+ const scopedProject = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id || undefined;
73169
73168
  if (positionals[0] === "project") {
73170
- await projectCommand(api2, positionals, args);
73169
+ await projectCommand(api2, positionals, args, scopedProject);
73171
73170
  return;
73172
73171
  }
73173
73172
  if (positionals[0] === "brand") {
73174
- await brandCommand(api2, positionals, args);
73173
+ await brandCommand(api2, positionals, args, scopedProject);
73175
73174
  return;
73176
73175
  }
73177
73176
  if (positionals[0] === "device") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",