@todoforai/cli 0.1.24 → 0.1.25

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 +93 -118
  2. package/package.json +3 -5
package/dist/todoai.js CHANGED
@@ -43465,6 +43465,7 @@ var SERVER_TO_FRONTENDS = {
43465
43465
  block_update: (todoId) => `todo:${todoId}:block_update`,
43466
43466
  context_summary_updated: (todoId) => `${"context:summary_updated" /* CONTEXT_SUMMARY_UPDATED */}:${todoId}`,
43467
43467
  context_info_updated: (todoId) => `${"context:info_updated" /* CONTEXT_INFO_UPDATED */}:${todoId}`,
43468
+ predicted_reply_updated: (todoId) => `${"predict:reply_updated" /* PREDICTED_REPLY_UPDATED */}:${todoId}`,
43468
43469
  error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
43469
43470
  meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
43470
43471
  file_changed: (edgeId, path3) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path3}`,
@@ -43896,16 +43897,23 @@ When the user asks you to recommend how to grow (e.g. "recommend growth TODOs",
43896
43897
  Recommending actionable TODOs from your recommendations:
43897
43898
  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
43899
 
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):
43900
+ 1. GET A TEMPLATE (todoregistry-cli) \u2014 generate-first, reuse only on a close fit:
43901
+ - Default: CREATE a template tailored to THIS business, right there (auth is inherited from your shell):
43902
43902
  \`todoregistry-cli create --name "<name>" --description "<one line>" --body @<prompt-file>\` (or \`--body -\` for stdin). It prints the new template id on stdout.
43903
+ - 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.
43904
+
43905
+ 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.
43906
+
43907
+ 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):
43908
+ - 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.
43909
+ - 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.
43910
+ - 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
43911
 
43904
43912
  2. RECOMMEND (todoforai-cli \u2014 references a template id onto the project; never creates a running todo):
43905
43913
  \`todoforai-cli recommend --template <id> --project <projectId> --business-context <businessContextId> --note "<why>" --priority high|medium|low\`
43906
43914
  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
43915
 
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.`,
43916
+ 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
43917
  mcpConfigs: {},
43910
43918
  edgesMcpConfigs: {},
43911
43919
  permissions: {
@@ -43981,6 +43989,10 @@ var TOPICS = {
43981
43989
  channel: (p) => SERVER_TO_FRONTENDS.todo.context_info_updated(p.todoId),
43982
43990
  audience: "frontend"
43983
43991
  },
43992
+ ["predict:reply_updated" /* PREDICTED_REPLY_UPDATED */]: {
43993
+ channel: (p) => SERVER_TO_FRONTENDS.todo.predicted_reply_updated(p.todoId),
43994
+ audience: "frontend"
43995
+ },
43984
43996
  ["hexgrid:updated" /* HEXGRID_UPDATED */]: {
43985
43997
  channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
43986
43998
  audience: "frontend"
@@ -44113,12 +44125,13 @@ Usage:
44113
44125
  todoforai-cli status <todo-id> <STATUS> # Update a todo's status (run 'status --help' for the full list)
44114
44126
  todoforai-cli delete <todo-id> # Permanently delete a todo
44115
44127
  todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
44128
+ todoforai-cli show <file|-> [todo-id] # Show a file in the chat (rendered by mimetype; - reads stdin)
44129
+ # [--title T] [--alias A] [--mime M] [--json]
44130
+ todoforai-cli open <url> [todo-id] # Show a live http(s) url in the chat as a preview
44131
+ # [--title T] [--alias A] [--json]
44116
44132
  todoforai-cli recommend --template <id> # Add a template as a recommendation card (see 'todoregistry-cli create')
44117
44133
  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
44134
+ todoforai-cli next [--direction "<text>"] # Ask the analyzer for growth recommendation cards (optional free-text steer)
44122
44135
 
44123
44136
  Options:
44124
44137
  --path <dir> Workspace path (default: cwd)
@@ -44192,6 +44205,9 @@ function parseCliArgs() {
44192
44205
  note: { type: "string" },
44193
44206
  priority: { type: "string" },
44194
44207
  title: { type: "string" },
44208
+ alias: { type: "string" },
44209
+ mime: { type: "string" },
44210
+ direction: { type: "string" },
44195
44211
  "business-context": { type: "string" },
44196
44212
  seed: { type: "string" },
44197
44213
  emails: { type: "string" },
@@ -46405,92 +46421,6 @@ async function listTodosCommand(api, defaultProjectId, argv) {
46405
46421
  `);
46406
46422
  }
46407
46423
 
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
46424
  // src/index.ts
46495
46425
  try {
46496
46426
  const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
@@ -46735,8 +46665,8 @@ Cancelled by user (Ctrl+C)
46735
46665
  return;
46736
46666
  }
46737
46667
  if (positionals[0] === "addmessage") {
46738
- const [, todoId, ...rest2] = positionals;
46739
- const content2 = rest2.join(" ") || await readStdin();
46668
+ const [, todoId, ...rest] = positionals;
46669
+ const content2 = rest.join(" ") || await readStdin();
46740
46670
  if (!todoId || !content2) {
46741
46671
  process.stderr.write(`${RED}Usage: todoforai-cli addmessage <todo-id> "content"${RESET}
46742
46672
  `);
@@ -46751,6 +46681,55 @@ Cancelled by user (Ctrl+C)
46751
46681
  `);
46752
46682
  return;
46753
46683
  }
46684
+ if (positionals[0] === "show") {
46685
+ const [, filePath, todoArg] = positionals;
46686
+ const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
46687
+ if (!filePath || !todoId) {
46688
+ process.stderr.write(`${RED}Usage: todoforai-cli show <file|-> [todo-id]${RESET}
46689
+ `);
46690
+ process.exit(2);
46691
+ }
46692
+ let blob, name;
46693
+ if (filePath === "-") {
46694
+ blob = new Blob([await Bun.readableStreamToArrayBuffer(Bun.stdin.stream())]);
46695
+ if (blob.size === 0) {
46696
+ process.stderr.write(`${RED}No data on stdin${RESET}
46697
+ `);
46698
+ process.exit(1);
46699
+ }
46700
+ name = "stdin";
46701
+ } else {
46702
+ const file = Bun.file(resolve3(filePath));
46703
+ if (!await file.exists()) {
46704
+ process.stderr.write(`${RED}File not found: ${filePath}${RESET}
46705
+ `);
46706
+ process.exit(1);
46707
+ }
46708
+ blob = file;
46709
+ name = path4.basename(filePath);
46710
+ }
46711
+ const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
46712
+ if (args.json)
46713
+ console.log(JSON.stringify(res, null, 2));
46714
+ else
46715
+ console.log(res.ref);
46716
+ return;
46717
+ }
46718
+ if (positionals[0] === "open") {
46719
+ const [, url, todoArg] = positionals;
46720
+ const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
46721
+ if (!url || !todoId) {
46722
+ process.stderr.write(`${RED}Usage: todoforai-cli open <url> [todo-id]${RESET}
46723
+ `);
46724
+ process.exit(2);
46725
+ }
46726
+ const res = await api.showUrl(todoId, url, { title: args.title, alias: args.alias });
46727
+ if (args.json)
46728
+ console.log(JSON.stringify(res, null, 2));
46729
+ else
46730
+ console.log(res.ref);
46731
+ return;
46732
+ }
46754
46733
  if (positionals[0] === "recommend") {
46755
46734
  const templateId = args.template || positionals[1];
46756
46735
  if (!templateId) {
@@ -46833,7 +46812,7 @@ Cancelled by user (Ctrl+C)
46833
46812
  `);
46834
46813
  return;
46835
46814
  }
46836
- if (positionals[0] === "steering" || positionals[0] === "next") {
46815
+ if (positionals[0] === "next") {
46837
46816
  let projectId2 = args.project || cfgScope.data.default_project_id;
46838
46817
  if (!projectId2) {
46839
46818
  const projects2 = await api.listProjects();
@@ -46844,29 +46823,25 @@ Cancelled by user (Ctrl+C)
46844
46823
  `);
46845
46824
  process.exit(2);
46846
46825
  }
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}
46826
+ const direction = args.direction || positionals.slice(1).join(" ").trim() || undefined;
46827
+ const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/projects/${projectId2}/recommendations/generate`, {
46828
+ method: "POST",
46829
+ headers: { "content-type": "application/json", "x-api-key": apiKey },
46830
+ body: JSON.stringify({ projectId: projectId2, ...args["business-context"] ? { businessContextId: args["business-context"] } : {}, ...direction ? { direction } : {} }),
46831
+ signal: AbortSignal.timeout(30000)
46832
+ });
46833
+ const text = await res.text();
46834
+ if (!res.ok) {
46835
+ process.stderr.write(`${RED}Generate failed: ${res.status} ${text}${RESET}
46866
46836
  `);
46867
- return;
46837
+ process.exit(1);
46868
46838
  }
46869
- await steeringCommand(sctx, positionals.slice(1));
46839
+ const out = JSON.parse(text);
46840
+ if (args.json)
46841
+ console.log(JSON.stringify(out, null, 2));
46842
+ else
46843
+ process.stderr.write(`${GREEN}\u2705 Generating recommendations \u2014 analyzer todo ${out.todoId}${RESET}
46844
+ `);
46870
46845
  return;
46871
46846
  }
46872
46847
  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.25",
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
  }