@todoforai/cli 0.1.28 → 0.1.30

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 +114 -17
  2. package/package.json +1 -1
@@ -42893,6 +42893,65 @@ class ApiClient {
42893
42893
  throw new Error(`API POST /resources/open failed: ${res.status} ${await res.text()}`);
42894
42894
  return res.json();
42895
42895
  }
42896
+ async listShows(opts = {}) {
42897
+ const qs = new URLSearchParams;
42898
+ if (opts.todoId)
42899
+ qs.set("todoId", opts.todoId);
42900
+ if (opts.projectId)
42901
+ qs.set("projectId", opts.projectId);
42902
+ if (opts.card)
42903
+ qs.set("card", opts.card);
42904
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show?${qs}`;
42905
+ const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
42906
+ if (!res.ok)
42907
+ throw new Error(`API GET /resources/show failed: ${res.status} ${await res.text()}`);
42908
+ return res.json();
42909
+ }
42910
+ async upsertCard(projectId, name, html, opts = {}) {
42911
+ const form = new FormData;
42912
+ form.append("file", html, `${name}.html`);
42913
+ for (const [k, v] of Object.entries(opts))
42914
+ if (v)
42915
+ form.append(k, v);
42916
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}`;
42917
+ const res = await fetch(url, {
42918
+ method: "PUT",
42919
+ headers: { "x-api-key": this.apiKey },
42920
+ body: form,
42921
+ signal: AbortSignal.timeout(60000)
42922
+ });
42923
+ if (!res.ok)
42924
+ throw new Error(`API PUT cards failed: ${res.status} ${await res.text()}`);
42925
+ return res.json();
42926
+ }
42927
+ async listCards(projectId) {
42928
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}`;
42929
+ const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
42930
+ if (!res.ok)
42931
+ throw new Error(`API GET cards failed: ${res.status} ${await res.text()}`);
42932
+ return res.json();
42933
+ }
42934
+ async getCard(projectId, name) {
42935
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}`;
42936
+ const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
42937
+ if (!res.ok)
42938
+ throw new Error(`API GET card failed: ${res.status} ${await res.text()}`);
42939
+ return res.json();
42940
+ }
42941
+ async getCardBody(projectId, name) {
42942
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}?body=1`;
42943
+ const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
42944
+ if (!res.ok)
42945
+ throw new Error(`API GET card body failed: ${res.status} ${await res.text()}`);
42946
+ return res.text();
42947
+ }
42948
+ async deleteCard(projectId, name) {
42949
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}`;
42950
+ const res = await fetch(url, { method: "DELETE", headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
42951
+ if (!res.ok)
42952
+ throw new Error(`API DELETE card failed: ${res.status} ${await res.text()}`);
42953
+ return res.json();
42954
+ }
42896
42955
  patchEdgeConfig(edgeId, updates) {
42897
42956
  return this.request("PATCH", `/api/v1/edges/${edgeId}`, { updates });
42898
42957
  }
@@ -43199,6 +43258,8 @@ class FrontendWebSocket {
43199
43258
  // ../packages/shared-fbe/src/enums.ts
43200
43259
  var TodoStatus;
43201
43260
  ((TodoStatus2) => {
43261
+ TodoStatus2["PROPOSED"] = "PROPOSED";
43262
+ TodoStatus2["DISMISSED"] = "DISMISSED";
43202
43263
  TodoStatus2["TODO"] = "TODO";
43203
43264
  TodoStatus2["SCHEDULED"] = "SCHEDULED";
43204
43265
  TodoStatus2["PAUSED"] = "PAUSED";
@@ -43479,9 +43540,6 @@ var SERVER_TO_FRONTENDS = {
43479
43540
  hexGrid: {
43480
43541
  updated: (projectId) => `${"hexgrid:updated" /* HEXGRID_UPDATED */}:${projectId}`
43481
43542
  },
43482
- placement: {
43483
- updated: (projectId) => `${"placement:updated" /* PLACEMENT_UPDATED */}:${projectId}`
43484
- },
43485
43543
  sandbox: {
43486
43544
  state: (userId) => `${"sandbox:state" /* SANDBOX_STATE */}:${userId}`
43487
43545
  },
@@ -43939,7 +43997,7 @@ Rules for the Presence block:
43939
43997
  - \`score\` is an integer 0-100, or \`null\` when you checked but found no presence. Base scores on real findings, not guesses. Include every channel you actually checked.
43940
43998
  - Keep it consistent with the prose Online Presence section \u2014 the JSON is the structured mirror of those findings.
43941
43999
 
43942
- Growth recommendations are NOT part of this block \u2014 they live in the project's Recommendations column as placement cards (see "Recommending actionable TODOs" below). After any analysis that surfaces growth opportunities, materialize your top recommendations as cards there; the prose Next Steps section stays human-readable advice.
44000
+ Growth recommendations are NOT part of this block \u2014 they live on the project board as proposed todos (\u2727 rows on their group cards; see "Recommending actionable TODOs" below). After any analysis that surfaces growth opportunities, materialize your top recommendations as proposals there; the prose Next Steps section stays human-readable advice.
43943
44001
 
43944
44002
  Document order for the saved brand page: human-readable sections first, then the **Next Steps** section, then Open Questions (if any), and finally exactly one \`growth-plan\` block as the very last element. (Your chat reply can still end with a short spoken summary; only the saved markdown must end with the machine block.)
43945
44003
 
@@ -43951,8 +44009,14 @@ In follow-up conversations, act as an ongoing advisor: answer growth and digital
43951
44009
 
43952
44010
  When the user asks you to recommend how to grow (e.g. "recommend growth TODOs", the brand page's guided suggestion, the board's Recommend button), read the current business context (including the presence block), research only where information is missing or stale, refresh the presence block if findings changed, then materialize your top growth actions as recommendation cards (see below). The cards ARE the growth plan \u2014 do not write a separate recommendations list into the markdown.
43953
44011
 
44012
+ Strategic workstreams (board groups):
44013
+ The project board is organized into group cards, and each group should be a strategic workstream for THIS company \u2014 a front the business is (or should be) pushing on, e.g. "SEO & Content", "Outbound to agencies", "Community on Reddit", "Retention & Email". Your recommendation-generation prompt includes the board's existing groups as JSON (\`slug\`, \`name\`, and \`description\` when set \u2014 the description states the workstream's strategic intent). Treat them as the company's current strategic map:
44014
+ - FIRST fit each recommendation into an existing group when it genuinely advances that workstream \u2014 pass its slug as the \`group\` parameter of \`api_recommend_template\`. Match on strategy (read the descriptions), not keywords.
44015
+ - When your analysis surfaces a strategic direction the board doesn't cover yet, introduce it: pass a new short kebab-case \`group\` slug plus a human \`groupName\` (2-4 words, strategy-level \u2014 "Partner Channel", not "Misc" or "Growth") and a one-sentence \`groupDescription\` stating the strategic bet and its target outcome for THIS company (e.g. "Win comparison searches against Zapier and n8n to capture switchers"). The group is created with the card.
44016
+ - Keep the map tight: 3-7 workstreams total is healthy. Never invent a new group when an existing one fits, never create catch-all groups, and never mirror priority or channel-lists as groups \u2014 a group is a strategic bet, its cards are the moves.
44017
+
43954
44018
  Recommending actionable TODOs from your recommendations:
43955
- 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:
44019
+ To materialize growth recommendations, propose todos on the project board. A proposal is a real todo in PROPOSED state \u2014 it sits as a \u2727 row on its group card until the user starts (accepts) or dismisses it. You NEVER create running todos \u2014 you only propose. Two tools, strict roles:
43956
44020
 
43957
44021
  1. GET A TEMPLATE (api_create_template) \u2014 generate-first, reuse only on a close fit:
43958
44022
  - Default: CREATE a template tailored to THIS business with the \`api_create_template\` tool. It returns the new template id.
@@ -43965,12 +44029,12 @@ To materialize growth recommendations, create recommendation cards in the projec
43965
44029
  - 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.
43966
44030
  - 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.
43967
44031
 
43968
- 2. RECOMMEND (api_recommend_template \u2014 references a template id onto the project; never creates a running todo):
43969
- Call \`api_recommend_template\` with the template id, a one-line \`note\` (the WHY for THIS business \u2014 it becomes the card's tooltip) and a \`priority\` of high, medium or low. Project and business context default to the current session, so pass them only when targeting a different one. The card appears in the Recommendations column for the user to review before any credits are spent.
44032
+ 2. RECOMMEND (api_recommend_template \u2014 proposes the template as a PROPOSED todo on the project; never creates a running todo):
44033
+ Call \`api_recommend_template\` with the template id, a one-line \`note\` (the WHY for THIS business \u2014 it becomes the proposal's tooltip), a \`priority\` of high, medium or low, and the \`group\` slug of the strategic workstream it belongs to (see "Strategic workstreams" above \u2014 reuse an existing slug when one fits; a new slug + \`groupName\` creates the workstream). Project and business context default to the current session, so pass them only when targeting a different one. The proposal appears as a \u2727 row on that group's board card for the user to review before any credits are spent. Re-proposing the same template refreshes the open proposal instead of duplicating it; dismissed proposals since your last run are fed back to you \u2014 don't re-propose those, steer elsewhere.
43970
44034
 
43971
44035
  Both tools run in-process and need no machine or shell \u2014 never shell out to \`todoregistry-cli\` / \`todoforai-cli\` for this.
43972
44036
 
43973
- 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.`,
44037
+ 3. Propose 3-6 todos (highest priority first). Then reply with a one-line summary per proposal grouped by workstream, and tell the user they're waiting on the board for review.`,
43974
44038
  mcpConfigs: {},
43975
44039
  edgesMcpConfigs: {},
43976
44040
  permissions: {
@@ -44014,6 +44078,9 @@ var OUTPUT_POLICIES = {
44014
44078
  full: { firstLimit: Infinity, lastLimit: 0, hardCap: RUN_OUTPUT_CAP, lineLimit: MAX_LINE_LEN },
44015
44079
  raw: { firstLimit: Infinity, lastLimit: 0, hardCap: Infinity, lineLimit: Infinity }
44016
44080
  };
44081
+ // ../packages/shared-fbe/src/toolInstallCommand.ts
44082
+ var PYTHON_MIN_MINOR = 10;
44083
+ var PYTHON_VERSION_CHECK = `-c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3, ${PYTHON_MIN_MINOR}) else 1)'`;
44017
44084
  // ../packages/shared-fbe/src/realtime/topics.ts
44018
44085
  var TOPICS = {
44019
44086
  ["todo:new" /* NEW_TODO */]: {
@@ -44052,10 +44119,6 @@ var TOPICS = {
44052
44119
  channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
44053
44120
  audience: "frontend"
44054
44121
  },
44055
- ["placement:updated" /* PLACEMENT_UPDATED */]: {
44056
- channel: (p) => SERVER_TO_FRONTENDS.placement.updated(p.projectId),
44057
- audience: "frontend"
44058
- },
44059
44122
  ["sandbox:state" /* SANDBOX_STATE */]: {
44060
44123
  channel: (p) => SERVER_TO_FRONTENDS.sandbox.state(p.userId),
44061
44124
  audience: "frontend"
@@ -44158,7 +44221,7 @@ import { parseArgs } from "util";
44158
44221
  // package.json
44159
44222
  var package_default = {
44160
44223
  name: "@todoforai/cli",
44161
- version: "0.1.28",
44224
+ version: "0.1.30",
44162
44225
  type: "module",
44163
44226
  bin: {
44164
44227
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -44215,7 +44278,10 @@ Usage:
44215
44278
  todoforai-cli delete <todo-id> # Permanently delete a todo
44216
44279
  todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
44217
44280
  todoforai-cli show <file|-> [todo-id] # Show a file in the chat (rendered by mimetype; - reads stdin)
44218
- # [--title T] [--alias A] [--mime M] [--json]
44281
+ # [--title T] [--alias A] [--mime M] [--card <name>] [--json]
44282
+ todoforai-cli show list [todo-id] # List show blocks (ref, title, mime/url, card)
44283
+ # [--project <id>] [--card <name>] [--json]
44284
+ # no todo-id + --project (or $TODOFORAI_PROJECT_ID) = every todo
44219
44285
  todoforai-cli open <url> [todo-id] # Show a live http(s) url in the chat as a preview
44220
44286
  # [--title T] [--alias A] [--json]
44221
44287
  todoforai-cli recommend --template <id> # Add a template as a recommendation card (see 'todoregistry-cli create')
@@ -44288,6 +44354,7 @@ function parseCliArgs() {
44288
44354
  model: { type: "string" },
44289
44355
  group: { type: "string" },
44290
44356
  "group-name": { type: "string" },
44357
+ "group-description": { type: "string" },
44291
44358
  "list-agents": { type: "boolean", default: false },
44292
44359
  "list-models": { type: "boolean", default: false },
44293
44360
  "api-url": { type: "string" },
@@ -44300,6 +44367,7 @@ function parseCliArgs() {
44300
44367
  title: { type: "string" },
44301
44368
  alias: { type: "string" },
44302
44369
  mime: { type: "string" },
44370
+ card: { type: "string" },
44303
44371
  direction: { type: "string" },
44304
44372
  "business-context": { type: "string" },
44305
44373
  seed: { type: "string" },
@@ -46772,6 +46840,32 @@ Cancelled by user (Ctrl+C)
46772
46840
  `);
46773
46841
  return;
46774
46842
  }
46843
+ if (positionals[0] === "show" && positionals[1] === "list") {
46844
+ const explicitProject = !!args.project;
46845
+ const todoId = positionals[2] || (!explicitProject ? getEnv("TODO_ID") || cfgScope.data.last_todo_id : undefined);
46846
+ const projectId2 = todoId ? undefined : args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
46847
+ if (!todoId && !projectId2) {
46848
+ process.stderr.write(`${RED}Usage: todoforai-cli show list [todo-id] [--project <id>] [--card <name>]${RESET}
46849
+ `);
46850
+ process.exit(2);
46851
+ }
46852
+ const { items } = await api.listShows({ todoId, projectId: projectId2, card: args.card });
46853
+ if (args.json) {
46854
+ console.log(JSON.stringify(items, null, 2));
46855
+ return;
46856
+ }
46857
+ if (items.length === 0) {
46858
+ process.stderr.write(`${DIM}No show blocks in ${todoId || `project ${projectId2}`}${RESET}
46859
+ `);
46860
+ return;
46861
+ }
46862
+ for (const it of items) {
46863
+ const kind = it.url ? `url ${it.url}` : it.mime || "";
46864
+ const card = it.cardRef ? ` card=${it.cardRef}` : "";
46865
+ console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}`);
46866
+ }
46867
+ return;
46868
+ }
46775
46869
  if (positionals[0] === "show") {
46776
46870
  const [, filePath, todoArg] = positionals;
46777
46871
  const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
@@ -46799,7 +46893,7 @@ Cancelled by user (Ctrl+C)
46799
46893
  blob = file;
46800
46894
  name = path3.basename(filePath);
46801
46895
  }
46802
- const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
46896
+ const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime, card: args.card });
46803
46897
  if (args.json)
46804
46898
  console.log(JSON.stringify(res, null, 2));
46805
46899
  else
@@ -46824,7 +46918,7 @@ Cancelled by user (Ctrl+C)
46824
46918
  if (positionals[0] === "recommend") {
46825
46919
  const templateId = args.template || positionals[1];
46826
46920
  if (!templateId) {
46827
- process.stderr.write(`${RED}Usage: todoforai-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--project <id>]${RESET}
46921
+ process.stderr.write(`${RED}Usage: todoforai-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--group <slug> [--group-name "..."] [--group-description "..."]] [--project <id>]${RESET}
46828
46922
  `);
46829
46923
  process.stderr.write(`${DIM}Create a template first with: todoregistry-cli create --name ... --description ... --body @prompt.md${RESET}
46830
46924
  `);
@@ -46852,7 +46946,10 @@ Cancelled by user (Ctrl+C)
46852
46946
  ...args.title ? { title: args.title } : {},
46853
46947
  ...args.note ? { note: args.note } : {},
46854
46948
  ...priority ? { priority } : {},
46855
- ...args["business-context"] ? { businessContextId: args["business-context"] } : {}
46949
+ ...args["business-context"] ? { businessContextId: args["business-context"] } : {},
46950
+ ...args.group ? { group: args.group } : {},
46951
+ ...args["group-name"] ? { groupName: args["group-name"] } : {},
46952
+ ...args["group-description"] ? { groupDescription: args["group-description"] } : {}
46856
46953
  });
46857
46954
  if (args.json)
46858
46955
  console.log(JSON.stringify(rec, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",