@todoforai/cli 0.1.24 → 0.1.26

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/todoai.js +123 -118
  2. package/package.json +3 -5
package/dist/todoai.js CHANGED
@@ -42859,6 +42859,36 @@ class ApiClient {
42859
42859
  payload.scheduledTimestamp = scheduledTimestamp;
42860
42860
  return this.request("POST", `/api/v1/projects/${projectId}/todos`, payload);
42861
42861
  }
42862
+ async showFile(todoId, file, filename, opts = {}) {
42863
+ const form = new FormData;
42864
+ form.append("file", file, filename);
42865
+ form.append("todoId", todoId);
42866
+ for (const [k, v] of Object.entries(opts))
42867
+ if (v)
42868
+ form.append(k, v);
42869
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show`;
42870
+ const res = await fetch(url, {
42871
+ method: "POST",
42872
+ headers: { "x-api-key": this.apiKey },
42873
+ body: form,
42874
+ signal: AbortSignal.timeout(120000)
42875
+ });
42876
+ if (!res.ok)
42877
+ throw new Error(`API POST /resources/show failed: ${res.status} ${await res.text()}`);
42878
+ return res.json();
42879
+ }
42880
+ async showUrl(todoId, url, opts = {}) {
42881
+ const endpoint = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/open`;
42882
+ const res = await fetch(endpoint, {
42883
+ method: "POST",
42884
+ headers: { "x-api-key": this.apiKey, "Content-Type": "application/json" },
42885
+ body: JSON.stringify({ todoId, url, ...opts }),
42886
+ signal: AbortSignal.timeout(30000)
42887
+ });
42888
+ if (!res.ok)
42889
+ throw new Error(`API POST /resources/open failed: ${res.status} ${await res.text()}`);
42890
+ return res.json();
42891
+ }
42862
42892
  patchEdgeConfig(edgeId, updates) {
42863
42893
  return this.request("PATCH", `/api/v1/edges/${edgeId}`, { updates });
42864
42894
  }
@@ -43465,6 +43495,7 @@ var SERVER_TO_FRONTENDS = {
43465
43495
  block_update: (todoId) => `todo:${todoId}:block_update`,
43466
43496
  context_summary_updated: (todoId) => `${"context:summary_updated" /* CONTEXT_SUMMARY_UPDATED */}:${todoId}`,
43467
43497
  context_info_updated: (todoId) => `${"context:info_updated" /* CONTEXT_INFO_UPDATED */}:${todoId}`,
43498
+ predicted_reply_updated: (todoId) => `${"predict:reply_updated" /* PREDICTED_REPLY_UPDATED */}:${todoId}`,
43468
43499
  error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
43469
43500
  meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
43470
43501
  file_changed: (edgeId, path3) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path3}`,
@@ -43896,16 +43927,23 @@ When the user asks you to recommend how to grow (e.g. "recommend growth TODOs",
43896
43927
  Recommending actionable TODOs from your recommendations:
43897
43928
  To materialize growth recommendations, create recommendation cards in the project's Recommendations column. You NEVER create running todos \u2014 you only recommend templates by reference. Two tools, strict roles:
43898
43929
 
43899
- 1. FIND OR CREATE A TEMPLATE (todoregistry-cli):
43900
- - \`todoregistry-cli search <query>\` / \`todoregistry-cli list -c <category>\` / \`todoregistry-cli get <id>\` \u2014 prefer a matching existing template (proven playbook).
43901
- - If none fits, CREATE one right there (auth is inherited from your shell):
43930
+ 1. GET A TEMPLATE (todoregistry-cli) \u2014 generate-first, reuse only on a close fit:
43931
+ - Default: CREATE a template tailored to THIS business, right there (auth is inherited from your shell):
43902
43932
  \`todoregistry-cli create --name "<name>" --description "<one line>" --body @<prompt-file>\` (or \`--body -\` for stdin). It prints the new template id on stdout.
43933
+ - Before creating, you MAY \`todoregistry-cli search <query>\` / \`list -c <category>\` / \`get <id>\`; reuse an existing template ONLY when it already fits this business closely with no meaningful edits. If it needs tailoring, create a new one instead \u2014 do not recommend a generic playbook that ignores this company's specifics.
43934
+
43935
+ Write every generated body to be unmistakably about THIS company \u2014 bake the specifics from the business context into the task itself: the company name, its actual competitors, the target segment, the specific channel/platform, and the concrete metric to move. A reader should never mistake it for a generic template.
43936
+
43937
+ Body structure (REQUIRED \u2014 the body is split into the agent's system prompt and the first user turn by section headings, so follow this exactly):
43938
+ - Start with the persona/expertise and any Rules as plain prose/paragraphs at the top (no heading needed) \u2014 this becomes the agent's system prompt.
43939
+ - Then the request, using these exact standalone heading lines (letters only, each on its own line, ending with a colon): \`Your task:\`, \`Steps:\`, \`Data needed:\`. Everything under these headings becomes the first user turn.
43940
+ - Headings must match exactly (e.g. \`Your task:\`, not \`Your task (SEO):\`) or the split breaks. Put the business-specific detail in the body text under each heading, never in the heading itself.
43903
43941
 
43904
43942
  2. RECOMMEND (todoforai-cli \u2014 references a template id onto the project; never creates a running todo):
43905
43943
  \`todoforai-cli recommend --template <id> --project <projectId> --business-context <businessContextId> --note "<why>" --priority high|medium|low\`
43906
43944
  Take <projectId> and <businessContextId> from your current session context and pass them explicitly. Put the one-line WHY for THIS business in --note (the card's tooltip). The card appears in the Recommendations column for the user to review before any credits are spent.
43907
43945
 
43908
- 3. Recommend 3-6 cards (highest priority first). When creating a spec for a business-specific action, write the body so the positioning angle and what to track are part of the task. Then reply with a one-line summary per card and tell the user they're waiting in the Recommendations column for review.`,
43946
+ 3. Recommend 3-6 cards (highest priority first). Then reply with a one-line summary per card and tell the user they're waiting in the Recommendations column for review.`,
43909
43947
  mcpConfigs: {},
43910
43948
  edgesMcpConfigs: {},
43911
43949
  permissions: {
@@ -43981,6 +44019,10 @@ var TOPICS = {
43981
44019
  channel: (p) => SERVER_TO_FRONTENDS.todo.context_info_updated(p.todoId),
43982
44020
  audience: "frontend"
43983
44021
  },
44022
+ ["predict:reply_updated" /* PREDICTED_REPLY_UPDATED */]: {
44023
+ channel: (p) => SERVER_TO_FRONTENDS.todo.predicted_reply_updated(p.todoId),
44024
+ audience: "frontend"
44025
+ },
43984
44026
  ["hexgrid:updated" /* HEXGRID_UPDATED */]: {
43985
44027
  channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
43986
44028
  audience: "frontend"
@@ -44113,12 +44155,13 @@ Usage:
44113
44155
  todoforai-cli status <todo-id> <STATUS> # Update a todo's status (run 'status --help' for the full list)
44114
44156
  todoforai-cli delete <todo-id> # Permanently delete a todo
44115
44157
  todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
44158
+ todoforai-cli show <file|-> [todo-id] # Show a file in the chat (rendered by mimetype; - reads stdin)
44159
+ # [--title T] [--alias A] [--mime M] [--json]
44160
+ todoforai-cli open <url> [todo-id] # Show a live http(s) url in the chat as a preview
44161
+ # [--title T] [--alias A] [--json]
44116
44162
  todoforai-cli recommend --template <id> # Add a template as a recommendation card (see 'todoregistry-cli create')
44117
44163
  todoforai-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> ownership links for a project you own
44118
- todoforai-cli steering get # Show recommendation steering (direction + boosted/muted categories)
44119
- todoforai-cli steering direction "<text>" # Set the direction the generator should follow
44120
- todoforai-cli steering boost|mute <cat> # Favour / avoid a category
44121
- todoforai-cli next # Generate steered recommendation cards
44164
+ todoforai-cli next [--direction "<text>"] # Ask the analyzer for growth recommendation cards (optional free-text steer)
44122
44165
 
44123
44166
  Options:
44124
44167
  --path <dir> Workspace path (default: cwd)
@@ -44192,6 +44235,9 @@ function parseCliArgs() {
44192
44235
  note: { type: "string" },
44193
44236
  priority: { type: "string" },
44194
44237
  title: { type: "string" },
44238
+ alias: { type: "string" },
44239
+ mime: { type: "string" },
44240
+ direction: { type: "string" },
44195
44241
  "business-context": { type: "string" },
44196
44242
  seed: { type: "string" },
44197
44243
  emails: { type: "string" },
@@ -46405,92 +46451,6 @@ async function listTodosCommand(api, defaultProjectId, argv) {
46405
46451
  `);
46406
46452
  }
46407
46453
 
46408
- // src/steering-command.ts
46409
- var RED2 = "\x1B[31m";
46410
- var GREEN2 = "\x1B[32m";
46411
- var DIM2 = "\x1B[2m";
46412
- var RESET2 = "\x1B[0m";
46413
- async function rest(ctx, method, path4, body) {
46414
- const res = await fetch(`${ctx.apiUrl}${restBasePath(ctx.apiKey)}${path4}`, {
46415
- method,
46416
- headers: { "content-type": "application/json", "x-api-key": ctx.apiKey },
46417
- ...body ? { body: JSON.stringify(body) } : {},
46418
- signal: AbortSignal.timeout(30000)
46419
- });
46420
- const text = await res.text();
46421
- if (!res.ok)
46422
- throw new Error(`${method} ${path4} failed: ${res.status} ${text}`);
46423
- return text ? JSON.parse(text) : null;
46424
- }
46425
- function printSteering(s) {
46426
- process.stderr.write(`${GREEN2}Direction:${RESET2} ${s.direction || DIM2 + "(none)" + RESET2}
46427
- `);
46428
- process.stderr.write(`${GREEN2}Boosted:${RESET2} ${s.boostedCategoryIds?.join(", ") || DIM2 + "(none)" + RESET2}
46429
- `);
46430
- process.stderr.write(`${GREEN2}Muted:${RESET2} ${s.mutedCategoryIds?.join(", ") || DIM2 + "(none)" + RESET2}
46431
- `);
46432
- }
46433
- var USAGE = `${RED2}Usage:
46434
- todoforai-cli steering get [--project <id>]
46435
- todoforai-cli steering direction "<text>" [--project <id>] (empty text clears it)
46436
- todoforai-cli steering boost <categoryId> [--project <id>]
46437
- todoforai-cli steering mute <categoryId> [--project <id>]
46438
- todoforai-cli steering clear [--project <id>] (clears all steering)${RESET2}`;
46439
- async function steeringCommand(ctx, argv) {
46440
- const sub = argv[0];
46441
- const path4 = `/projects/${ctx.projectId}/recommendation-steering`;
46442
- if (!sub || sub === "get") {
46443
- const s = await rest(ctx, "GET", path4);
46444
- if (ctx.json)
46445
- console.log(JSON.stringify(s, null, 2));
46446
- else
46447
- printSteering(s);
46448
- return;
46449
- }
46450
- if (sub === "direction") {
46451
- const text = argv.slice(1).join(" ").trim();
46452
- const s = await rest(ctx, "PATCH", path4, { projectId: ctx.projectId, direction: text || null });
46453
- if (ctx.json)
46454
- console.log(JSON.stringify(s, null, 2));
46455
- else
46456
- process.stderr.write(`${GREEN2}\u2705 ${text ? `Direction set: ${text}` : "Direction cleared"}${RESET2}
46457
- `);
46458
- return;
46459
- }
46460
- if (sub === "boost" || sub === "mute") {
46461
- const categoryId = argv[1];
46462
- if (!categoryId) {
46463
- process.stderr.write(USAGE + `
46464
- `);
46465
- process.exit(2);
46466
- }
46467
- const current = await rest(ctx, "GET", path4);
46468
- const key = sub === "boost" ? "boostedCategoryIds" : "mutedCategoryIds";
46469
- const other = sub === "boost" ? "mutedCategoryIds" : "boostedCategoryIds";
46470
- const next = Array.from(new Set([...current[key] ?? [], categoryId]));
46471
- const nextOther = (current[other] ?? []).filter((c2) => c2 !== categoryId);
46472
- const s = await rest(ctx, "PATCH", path4, { projectId: ctx.projectId, [key]: next, [other]: nextOther });
46473
- if (ctx.json)
46474
- console.log(JSON.stringify(s, null, 2));
46475
- else
46476
- process.stderr.write(`${GREEN2}\u2705 ${sub === "boost" ? "Boosted" : "Muted"} ${categoryId}${RESET2}
46477
- `);
46478
- return;
46479
- }
46480
- if (sub === "clear") {
46481
- const s = await rest(ctx, "PATCH", path4, { projectId: ctx.projectId, direction: null, boostedCategoryIds: [], mutedCategoryIds: [] });
46482
- if (ctx.json)
46483
- console.log(JSON.stringify(s, null, 2));
46484
- else
46485
- process.stderr.write(`${GREEN2}\u2705 Steering cleared${RESET2}
46486
- `);
46487
- return;
46488
- }
46489
- process.stderr.write(USAGE + `
46490
- `);
46491
- process.exit(2);
46492
- }
46493
-
46494
46454
  // src/index.ts
46495
46455
  try {
46496
46456
  const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
@@ -46735,8 +46695,8 @@ Cancelled by user (Ctrl+C)
46735
46695
  return;
46736
46696
  }
46737
46697
  if (positionals[0] === "addmessage") {
46738
- const [, todoId, ...rest2] = positionals;
46739
- const content2 = rest2.join(" ") || await readStdin();
46698
+ const [, todoId, ...rest] = positionals;
46699
+ const content2 = rest.join(" ") || await readStdin();
46740
46700
  if (!todoId || !content2) {
46741
46701
  process.stderr.write(`${RED}Usage: todoforai-cli addmessage <todo-id> "content"${RESET}
46742
46702
  `);
@@ -46751,6 +46711,55 @@ Cancelled by user (Ctrl+C)
46751
46711
  `);
46752
46712
  return;
46753
46713
  }
46714
+ if (positionals[0] === "show") {
46715
+ const [, filePath, todoArg] = positionals;
46716
+ const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
46717
+ if (!filePath || !todoId) {
46718
+ process.stderr.write(`${RED}Usage: todoforai-cli show <file|-> [todo-id]${RESET}
46719
+ `);
46720
+ process.exit(2);
46721
+ }
46722
+ let blob, name;
46723
+ if (filePath === "-") {
46724
+ blob = new Blob([await Bun.readableStreamToArrayBuffer(Bun.stdin.stream())]);
46725
+ if (blob.size === 0) {
46726
+ process.stderr.write(`${RED}No data on stdin${RESET}
46727
+ `);
46728
+ process.exit(1);
46729
+ }
46730
+ name = "stdin";
46731
+ } else {
46732
+ const file = Bun.file(resolve3(filePath));
46733
+ if (!await file.exists()) {
46734
+ process.stderr.write(`${RED}File not found: ${filePath}${RESET}
46735
+ `);
46736
+ process.exit(1);
46737
+ }
46738
+ blob = file;
46739
+ name = path4.basename(filePath);
46740
+ }
46741
+ const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
46742
+ if (args.json)
46743
+ console.log(JSON.stringify(res, null, 2));
46744
+ else
46745
+ console.log(res.ref);
46746
+ return;
46747
+ }
46748
+ if (positionals[0] === "open") {
46749
+ const [, url, todoArg] = positionals;
46750
+ const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
46751
+ if (!url || !todoId) {
46752
+ process.stderr.write(`${RED}Usage: todoforai-cli open <url> [todo-id]${RESET}
46753
+ `);
46754
+ process.exit(2);
46755
+ }
46756
+ const res = await api.showUrl(todoId, url, { title: args.title, alias: args.alias });
46757
+ if (args.json)
46758
+ console.log(JSON.stringify(res, null, 2));
46759
+ else
46760
+ console.log(res.ref);
46761
+ return;
46762
+ }
46754
46763
  if (positionals[0] === "recommend") {
46755
46764
  const templateId = args.template || positionals[1];
46756
46765
  if (!templateId) {
@@ -46833,7 +46842,7 @@ Cancelled by user (Ctrl+C)
46833
46842
  `);
46834
46843
  return;
46835
46844
  }
46836
- if (positionals[0] === "steering" || positionals[0] === "next") {
46845
+ if (positionals[0] === "next") {
46837
46846
  let projectId2 = args.project || cfgScope.data.default_project_id;
46838
46847
  if (!projectId2) {
46839
46848
  const projects2 = await api.listProjects();
@@ -46844,29 +46853,25 @@ Cancelled by user (Ctrl+C)
46844
46853
  `);
46845
46854
  process.exit(2);
46846
46855
  }
46847
- const sctx = { apiUrl, apiKey, projectId: projectId2, json: !!args.json };
46848
- if (positionals[0] === "next") {
46849
- const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/projects/${projectId2}/recommendations/generate`, {
46850
- method: "POST",
46851
- headers: { "content-type": "application/json", "x-api-key": apiKey },
46852
- body: JSON.stringify({ projectId: projectId2, ...args["business-context"] ? { businessContextId: args["business-context"] } : {} }),
46853
- signal: AbortSignal.timeout(30000)
46854
- });
46855
- const text = await res.text();
46856
- if (!res.ok) {
46857
- process.stderr.write(`${RED}Generate failed: ${res.status} ${text}${RESET}
46858
- `);
46859
- process.exit(1);
46860
- }
46861
- const out = JSON.parse(text);
46862
- if (args.json)
46863
- console.log(JSON.stringify(out, null, 2));
46864
- else
46865
- process.stderr.write(`${GREEN}\u2705 Generating recommendations \u2014 analyzer todo ${out.todoId}${RESET}
46856
+ const direction = args.direction || positionals.slice(1).join(" ").trim() || undefined;
46857
+ const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/projects/${projectId2}/recommendations/generate`, {
46858
+ method: "POST",
46859
+ headers: { "content-type": "application/json", "x-api-key": apiKey },
46860
+ body: JSON.stringify({ projectId: projectId2, ...args["business-context"] ? { businessContextId: args["business-context"] } : {}, ...direction ? { direction } : {} }),
46861
+ signal: AbortSignal.timeout(30000)
46862
+ });
46863
+ const text = await res.text();
46864
+ if (!res.ok) {
46865
+ process.stderr.write(`${RED}Generate failed: ${res.status} ${text}${RESET}
46866
46866
  `);
46867
- return;
46867
+ process.exit(1);
46868
46868
  }
46869
- await steeringCommand(sctx, positionals.slice(1));
46869
+ const out = JSON.parse(text);
46870
+ if (args.json)
46871
+ console.log(JSON.stringify(out, null, 2));
46872
+ else
46873
+ process.stderr.write(`${GREEN}\u2705 Generating recommendations \u2014 analyzer todo ${out.todoId}${RESET}
46874
+ `);
46870
46875
  return;
46871
46876
  }
46872
46877
  if (args["list-agents"]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -14,8 +14,7 @@
14
14
  "build": "bun build src/index.ts --target=bun --outfile dist/todoai.js --external ws",
15
15
  "prepublishOnly": "bun run build",
16
16
  "start": "bun run src/index.ts",
17
- "dev": "bun run src/index.ts",
18
- "postinstall": "rm -rf node_modules/@todoforai/edge && ln -s ../../../edge/bun node_modules/@todoforai/edge"
17
+ "dev": "bun run src/index.ts"
19
18
  },
20
19
  "dependencies": {
21
20
  "@shared/credentials": "file:../packages/shared-credentials",
@@ -25,7 +24,7 @@
25
24
  "ws": "^8.18.0"
26
25
  },
27
26
  "peerDependencies": {
28
- "@todoforai/edge": "file:../edge/bun"
27
+ "@todoforai/edge": ">=0.12.0"
29
28
  },
30
29
  "peerDependenciesMeta": {
31
30
  "@todoforai/edge": {
@@ -34,7 +33,6 @@
34
33
  },
35
34
  "devDependencies": {
36
35
  "@types/ws": "^8.5.13",
37
- "@todoforai/edge": "file:../edge/bun",
38
36
  "typescript": "^5.7.0"
39
37
  }
40
38
  }