@todoforai/cli 0.1.28 → 0.1.29

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 +158 -40
  2. package/package.json +3 -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.29",
44162
44225
  type: "module",
44163
44226
  bin: {
44164
44227
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -44176,6 +44239,8 @@ var package_default = {
44176
44239
  dev: "bun run src/index.ts"
44177
44240
  },
44178
44241
  dependencies: {
44242
+ "@shared/api": "file:../packages/shared-api",
44243
+ "@shared/credentials": "file:../packages/shared-credentials",
44179
44244
  "@todoforai/update-notifier": "^0.1.0",
44180
44245
  "cli-highlight": "^2.1.11",
44181
44246
  "diff-match-patch": "^1.0.5",
@@ -44191,7 +44256,7 @@ var package_default = {
44191
44256
  var DEFAULT_API_URL = "https://api.todofor.ai";
44192
44257
  var VERSION = package_default.version;
44193
44258
  function getEnv(name) {
44194
- return process.env[`TODOFORAI_${name}`] || process.env[`TODO4AI_${name}`] || "";
44259
+ return (process.env[`TODOFORAI_${name}`] || process.env[`TODO4AI_${name}`] || "").trim();
44195
44260
  }
44196
44261
  function printUsage() {
44197
44262
  process.stderr.write(`
@@ -44215,7 +44280,10 @@ Usage:
44215
44280
  todoforai-cli delete <todo-id> # Permanently delete a todo
44216
44281
  todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
44217
44282
  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]
44283
+ # [--title T] [--alias A] [--mime M] [--card <name>] [--json]
44284
+ todoforai-cli show list [todo-id] # List show blocks (ref, title, mime/url, card)
44285
+ # [--project <id>] [--card <name>] [--json]
44286
+ # no todo-id + --project (or $TODOFORAI_PROJECT_ID) = every todo
44219
44287
  todoforai-cli open <url> [todo-id] # Show a live http(s) url in the chat as a preview
44220
44288
  # [--title T] [--alias A] [--json]
44221
44289
  todoforai-cli recommend --template <id> # Add a template as a recommendation card (see 'todoregistry-cli create')
@@ -44288,6 +44356,7 @@ function parseCliArgs() {
44288
44356
  model: { type: "string" },
44289
44357
  group: { type: "string" },
44290
44358
  "group-name": { type: "string" },
44359
+ "group-description": { type: "string" },
44291
44360
  "list-agents": { type: "boolean", default: false },
44292
44361
  "list-models": { type: "boolean", default: false },
44293
44362
  "api-url": { type: "string" },
@@ -44300,6 +44369,7 @@ function parseCliArgs() {
44300
44369
  title: { type: "string" },
44301
44370
  alias: { type: "string" },
44302
44371
  mime: { type: "string" },
44372
+ card: { type: "string" },
44303
44373
  direction: { type: "string" },
44304
44374
  "business-context": { type: "string" },
44305
44375
  seed: { type: "string" },
@@ -44849,20 +44919,29 @@ async function autoCreateAgent(api, resolvedPath) {
44849
44919
  }
44850
44920
 
44851
44921
  // src/config.ts
44852
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
44922
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
44853
44923
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
44854
44924
  import { homedir as homedir2, platform as platform2 } from "os";
44855
- function getConfigDir() {
44925
+ function getConfigBase() {
44856
44926
  const sys = platform2();
44857
- if (sys === "win32") {
44858
- const base = process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
44859
- return join2(base, "todoai-cli");
44860
- }
44861
- if (sys === "darwin") {
44862
- return join2(homedir2(), "Library", "Application Support", "todoai-cli");
44927
+ if (sys === "win32")
44928
+ return process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
44929
+ if (sys === "darwin")
44930
+ return join2(homedir2(), "Library", "Application Support");
44931
+ return process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
44932
+ }
44933
+ function getConfigDir() {
44934
+ const base = getConfigBase();
44935
+ const dir = join2(base, "todoforai-cli");
44936
+ const legacy = join2(base, "todoai-cli");
44937
+ if (!existsSync(dir) && existsSync(legacy)) {
44938
+ try {
44939
+ renameSync(legacy, dir);
44940
+ } catch {
44941
+ return legacy;
44942
+ }
44863
44943
  }
44864
- const xdg = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
44865
- return join2(xdg, "todoai-cli");
44944
+ return dir;
44866
44945
  }
44867
44946
  function defaultScope() {
44868
44947
  return {
@@ -45505,7 +45584,16 @@ function selectProject(projects, defaultId, setDefault) {
45505
45584
  return Promise.resolve({ id: defaultId, name });
45506
45585
  }
45507
45586
  }
45587
+ const accountDefault = projects.find((p) => p?.project?.isDefault) || projects[0];
45588
+ if (!process.stdin.isTTY) {
45589
+ const id = getItemId(accountDefault);
45590
+ const name = getDisplayName(accountDefault);
45591
+ process.stderr.write(`Using account default project: ${name}
45592
+ `);
45593
+ return Promise.resolve({ id, name });
45594
+ }
45508
45595
  return (async () => {
45596
+ const defIdx = projects.indexOf(accountDefault);
45509
45597
  process.stderr.write(`
45510
45598
  Please choose a project:
45511
45599
 
@@ -45513,7 +45601,7 @@ Please choose a project:
45513
45601
  for (let i = 0;i < projects.length; i++) {
45514
45602
  const name = getDisplayName(projects[i]);
45515
45603
  const id = getItemId(projects[i]);
45516
- process.stderr.write(` [${i + 1}] ${name}
45604
+ process.stderr.write(` [${i + 1}] ${name}${i === defIdx ? " (default)" : ""}
45517
45605
  `);
45518
45606
  if (id && id !== name)
45519
45607
  process.stderr.write(` ${id}
@@ -45522,8 +45610,8 @@ Please choose a project:
45522
45610
  process.stderr.write(`
45523
45611
  `);
45524
45612
  while (true) {
45525
- const choice = await terminalLine("Please enter your numeric choice: ");
45526
- const idx = parseInt(choice, 10) - 1;
45613
+ const choice = await terminalLine(`Please enter your numeric choice [${defIdx + 1}]: `);
45614
+ const idx = choice === "" ? defIdx : parseInt(choice, 10) - 1;
45527
45615
  if (idx >= 0 && idx < projects.length) {
45528
45616
  const id = getItemId(projects[idx]);
45529
45617
  const name = getDisplayName(projects[idx]);
@@ -46772,6 +46860,32 @@ Cancelled by user (Ctrl+C)
46772
46860
  `);
46773
46861
  return;
46774
46862
  }
46863
+ if (positionals[0] === "show" && positionals[1] === "list") {
46864
+ const explicitProject = !!args.project;
46865
+ const todoId = positionals[2] || (!explicitProject ? getEnv("TODO_ID") || cfgScope.data.last_todo_id : undefined);
46866
+ const projectId2 = todoId ? undefined : args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
46867
+ if (!todoId && !projectId2) {
46868
+ process.stderr.write(`${RED}Usage: todoforai-cli show list [todo-id] [--project <id>] [--card <name>]${RESET}
46869
+ `);
46870
+ process.exit(2);
46871
+ }
46872
+ const { items } = await api.listShows({ todoId, projectId: projectId2, card: args.card });
46873
+ if (args.json) {
46874
+ console.log(JSON.stringify(items, null, 2));
46875
+ return;
46876
+ }
46877
+ if (items.length === 0) {
46878
+ process.stderr.write(`${DIM}No show blocks in ${todoId || `project ${projectId2}`}${RESET}
46879
+ `);
46880
+ return;
46881
+ }
46882
+ for (const it of items) {
46883
+ const kind = it.url ? `url ${it.url}` : it.mime || "";
46884
+ const card = it.cardRef ? ` card=${it.cardRef}` : "";
46885
+ console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}`);
46886
+ }
46887
+ return;
46888
+ }
46775
46889
  if (positionals[0] === "show") {
46776
46890
  const [, filePath, todoArg] = positionals;
46777
46891
  const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
@@ -46799,7 +46913,7 @@ Cancelled by user (Ctrl+C)
46799
46913
  blob = file;
46800
46914
  name = path3.basename(filePath);
46801
46915
  }
46802
- const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
46916
+ const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime, card: args.card });
46803
46917
  if (args.json)
46804
46918
  console.log(JSON.stringify(res, null, 2));
46805
46919
  else
@@ -46824,13 +46938,13 @@ Cancelled by user (Ctrl+C)
46824
46938
  if (positionals[0] === "recommend") {
46825
46939
  const templateId = args.template || positionals[1];
46826
46940
  if (!templateId) {
46827
- process.stderr.write(`${RED}Usage: todoforai-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--project <id>]${RESET}
46941
+ 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
46942
  `);
46829
46943
  process.stderr.write(`${DIM}Create a template first with: todoregistry-cli create --name ... --description ... --body @prompt.md${RESET}
46830
46944
  `);
46831
46945
  process.exit(2);
46832
46946
  }
46833
- let projectId2 = args.project || cfgScope.data.default_project_id;
46947
+ let projectId2 = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
46834
46948
  if (!projectId2) {
46835
46949
  const projects2 = await api.listProjects();
46836
46950
  projectId2 = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
@@ -46852,7 +46966,10 @@ Cancelled by user (Ctrl+C)
46852
46966
  ...args.title ? { title: args.title } : {},
46853
46967
  ...args.note ? { note: args.note } : {},
46854
46968
  ...priority ? { priority } : {},
46855
- ...args["business-context"] ? { businessContextId: args["business-context"] } : {}
46969
+ ...args["business-context"] ? { businessContextId: args["business-context"] } : {},
46970
+ ...args.group ? { group: args.group } : {},
46971
+ ...args["group-name"] ? { groupName: args["group-name"] } : {},
46972
+ ...args["group-description"] ? { groupDescription: args["group-description"] } : {}
46856
46973
  });
46857
46974
  if (args.json)
46858
46975
  console.log(JSON.stringify(rec, null, 2));
@@ -46862,7 +46979,7 @@ Cancelled by user (Ctrl+C)
46862
46979
  return;
46863
46980
  }
46864
46981
  if (positionals[0] === "claim" && positionals[1] === "mint") {
46865
- let seedProjectId = args.seed || args.project || cfgScope.data.default_project_id;
46982
+ let seedProjectId = args.seed || args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
46866
46983
  if (!seedProjectId) {
46867
46984
  const projects2 = await api.listProjects();
46868
46985
  seedProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
@@ -46904,7 +47021,7 @@ Cancelled by user (Ctrl+C)
46904
47021
  return;
46905
47022
  }
46906
47023
  if (positionals[0] === "next") {
46907
- let projectId2 = args.project || cfgScope.data.default_project_id;
47024
+ let projectId2 = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
46908
47025
  if (!projectId2) {
46909
47026
  const projects2 = await api.listProjects();
46910
47027
  projectId2 = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
@@ -46954,7 +47071,7 @@ Cancelled by user (Ctrl+C)
46954
47071
  return;
46955
47072
  }
46956
47073
  if (positionals[0] === "list" || positionals[0] === "ls") {
46957
- let defaultProjectId = args.project || cfgScope.data.default_project_id;
47074
+ let defaultProjectId = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
46958
47075
  if (!defaultProjectId) {
46959
47076
  const projects2 = await api.listProjects();
46960
47077
  defaultProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
@@ -47009,7 +47126,7 @@ Cancelled by user (Ctrl+C)
47009
47126
  process.stderr.write(`${DIM}${spec.description}${RESET}
47010
47127
  `);
47011
47128
  const projects2 = await api.listProjects();
47012
- let projectId2 = args.project;
47129
+ let projectId2 = args.project || getEnv("PROJECT_ID");
47013
47130
  if (!projectId2) {
47014
47131
  const cached = cfgScope.data.default_project_id;
47015
47132
  projectId2 = (cached && projects2.some((p) => getItemId(p) === cached) ? cached : null) || projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
@@ -47169,7 +47286,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
47169
47286
  } else {
47170
47287
  content = await readStdin();
47171
47288
  }
47172
- const hasProject = args.project || cfgScope.data.default_project_id;
47289
+ const envProjectId = getEnv("PROJECT_ID");
47290
+ const hasProject = args.project || envProjectId || cfgScope.data.default_project_id;
47173
47291
  const storedAgent = cfgScope.data.default_agent_settings;
47174
47292
  const hasAgent = preMatchedAgent || storedAgent?.id && !args.agent;
47175
47293
  let projects = null;
@@ -47180,8 +47298,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
47180
47298
  }
47181
47299
  let projectId;
47182
47300
  let projectName;
47183
- if (args.project) {
47184
- projectId = args.project;
47301
+ if (args.project || envProjectId) {
47302
+ projectId = args.project || envProjectId;
47185
47303
  projectName = projectId;
47186
47304
  if (projects) {
47187
47305
  const match = projects.find((p) => getItemId(p) === projectId);
@@ -47223,7 +47341,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
47223
47341
  try {
47224
47342
  todo = await api.addMessage(projectId, content, agent, undefined, undefined, undefined, groupTag || undefined, groupName);
47225
47343
  } catch (e) {
47226
- if (!args.project && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
47344
+ if (!args.project && !envProjectId && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
47227
47345
  cfgScope.clearDefaultProject();
47228
47346
  process.stderr.write(`${RED}Not authorized for cached default project ${projectName} (${projectId}) \u2014 cleared it. Re-run to pick a project.${RESET}
47229
47347
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -18,6 +18,8 @@
18
18
  "dev": "bun run src/index.ts"
19
19
  },
20
20
  "dependencies": {
21
+ "@shared/api": "file:../packages/shared-api",
22
+ "@shared/credentials": "file:../packages/shared-credentials",
21
23
  "@todoforai/update-notifier": "^0.1.0",
22
24
  "cli-highlight": "^2.1.11",
23
25
  "diff-match-patch": "^1.0.5",