@todoforai/cli 0.1.18 → 0.1.20

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 +188 -55
  2. package/package.json +2 -1
package/dist/todoai.js CHANGED
@@ -43147,6 +43147,7 @@ var package_default = {
43147
43147
  postinstall: "rm -rf node_modules/@todoforai/edge && ln -s ../../../edge/bun node_modules/@todoforai/edge"
43148
43148
  },
43149
43149
  dependencies: {
43150
+ "@shared/credentials": "file:../packages/shared-credentials",
43150
43151
  "@todoforai/update-notifier": "^0.1.0",
43151
43152
  "cli-highlight": "^2.1.11",
43152
43153
  "diff-match-patch": "^1.0.5",
@@ -43336,7 +43337,8 @@ var NON_SHELL_TYPES = new Set([
43336
43337
  var USER_INPUT_BLOCK_TYPES = new Set([
43337
43338
  "question" /* Question */,
43338
43339
  "login_prompt" /* Login */,
43339
- "business_onboarding" /* BusinessOnboarding */
43340
+ "business_onboarding" /* BusinessOnboarding */,
43341
+ "open_browser" /* OpenBrowser */
43340
43342
  ]);
43341
43343
  var EDGE_TOOL_TYPES = new Set([
43342
43344
  "read" /* Read */,
@@ -43363,7 +43365,6 @@ var API_TOOL_TYPES = new Set([
43363
43365
  "api_get_agent" /* ApiGetAgent */,
43364
43366
  "api_update_agent" /* ApiUpdateAgent */,
43365
43367
  "api_list_business_contexts" /* ApiListBusinessContexts */,
43366
- "api_get_business_context" /* ApiGetBusinessContext */,
43367
43368
  "api_get_context_item" /* ApiGetContextItem */,
43368
43369
  "api_create_context_item" /* ApiCreateContextItem */,
43369
43370
  "api_update_context_item" /* ApiUpdateContextItem */,
@@ -43396,6 +43397,9 @@ var SERVER_TO_FRONTENDS = {
43396
43397
  hexGrid: {
43397
43398
  updated: (projectId) => `${"hexgrid:updated" /* HEXGRID_UPDATED */}:${projectId}`
43398
43399
  },
43400
+ recommendation: {
43401
+ updated: (projectId) => `${"recommendation:updated" /* RECOMMENDATION_UPDATED */}:${projectId}`
43402
+ },
43399
43403
  sandbox: {
43400
43404
  state: (userId) => `${"sandbox:state" /* SANDBOX_STATE */}:${userId}`
43401
43405
  },
@@ -43839,15 +43843,70 @@ Cover these sections (skip any without data):
43839
43843
 
43840
43844
  If anything important remains unclear, add an **Open Questions** section at the end.
43841
43845
 
43846
+ Maintain a single machine-readable **Growth Plan** block so the user's brand page can render a live presence map and a ranked, actionable growth plan. Write it as one fenced code block tagged \`growth-plan\` containing JSON (the user never sees this raw \u2014 it powers the presence hexes and the Growth Plan panel). Keep exactly one such block; rewrite it in full each time you update it. Concrete example (emit strictly valid JSON like this \u2014 no comments, no ranges, no "|" alternatives):
43847
+
43848
+ \`\`\`growth-plan
43849
+ {
43850
+ "presence": {
43851
+ "linkedin-networking": { "score": 45, "handle": "https://linkedin.com/company/example", "note": "Company page exists but posts infrequently" },
43852
+ "x-twitter-presence": { "score": 60, "handle": "@example", "note": "Active, regular posts" },
43853
+ "newsletter": { "score": null, "note": "No newsletter signup found" }
43854
+ },
43855
+ "recommendations": [
43856
+ {
43857
+ "activityId": "linkedin-networking",
43858
+ "title": "Publish two founder-led LinkedIn posts each week",
43859
+ "rationale": "Target buyers are active on LinkedIn, but the page is nearly dormant.",
43860
+ "angle": "Explain the operational pain through short customer scenarios.",
43861
+ "track": "Qualified profile visits and demo requests per week",
43862
+ "priority": "high"
43863
+ }
43864
+ ]
43865
+ }
43866
+ \`\`\`
43867
+
43868
+ Rules for the Growth Plan block:
43869
+ - \`activityId\` MUST be one of these exact ids: facebook-posting, x-twitter-presence, tiktok-content, instagram-content, linkedin-networking, reddit-engagement, blog-writing, seo-optimization, backlink-building, keyword-research, aso-optimization, llm-optimization, google-ads-management, meta-ads-management, lead-outreach, crm-management, newsletter, affiliate-program, partnership-outreach, analytics-monitoring.
43870
+ - \`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.
43871
+ - \`recommendations\`: 3-6 items. \`priority\` is exactly one of "high", "medium", "low"; put the single highest-leverage action first. Choose channels that fit THIS business's model, audience, and stage \u2014 don't recommend all 20; prioritize gaps where presence is weak/absent but impact is high. Make \`title\` a concrete next action, \`angle\` the actual message/positioning to use, and \`track\` a specific metric or cadence to record.
43872
+ - Keep it consistent with the prose Online Presence and Next Steps sections \u2014 the JSON is the structured mirror of that advice.
43873
+
43874
+ 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.)
43875
+
43842
43876
  Guide the user one step at a time \u2014 they should always know the single next thing to do:
43843
43877
  - When you need input (brand voice, tone, target audience, which channel to prioritize), prefer the question tool with short multi-choice options over asking open-ended prose questions.
43844
43878
  - After completing any analysis or context update, always close with a short **Next Steps** section: 2-4 concrete, prioritized tips tailored to this specific business (e.g. SEO/content gaps, social channels worth activating, positioning against the competitors you found, conversion or messaging improvements). Base tips on what you actually observed, keep each to one or two sentences, and lead with the one you'd do first.
43845
43879
 
43846
- In follow-up conversations, act as an ongoing advisor: answer growth and digital-presence questions using the business context, refresh your analysis when asked, and suggest what to tackle next.`,
43880
+ In follow-up conversations, act as an ongoing advisor: answer growth and digital-presence questions using the business context, refresh your analysis when asked, and suggest what to tackle next.
43881
+
43882
+ When the user asks you to recommend how to grow (e.g. "recommend growth TODOs", the brand page's guided suggestion), treat it as a growth-plan request: read the current business context (including any existing \`growth-plan\` block), research only where information is missing or stale, then update the business context with a refreshed Growth Plan block and reply in chat with the same ranked recommendations in plain language \u2014 lead with the single highest-leverage action and why. This must work at any point in the conversation, not just after a full website analysis.
43883
+
43884
+ Recommending actionable TODOs from your recommendations:
43885
+ When the user asks you to generate/recommend TODOs (e.g. "generate TODOs", the board's Generate button), turn your top recommendations into recommendation cards in the NEXT column. You NEVER create running todos \u2014 you only recommend templates by reference. Two tools, strict roles:
43886
+
43887
+ 1. FIND OR CREATE A TEMPLATE (todoregistry-cli):
43888
+ - \`todoregistry-cli search <query>\` / \`todoregistry-cli list -c <category>\` / \`todoregistry-cli get <id>\` \u2014 prefer a matching existing template (proven playbook).
43889
+ - If none fits, CREATE one right there (auth is inherited from your shell):
43890
+ \`todoregistry-cli create --name "<name>" --description "<one line>" --body @<prompt-file>\` (or \`--body -\` for stdin). It prints the new template id on stdout.
43891
+
43892
+ 2. RECOMMEND (todoforai-cli \u2014 references a template id onto the project; never creates a running todo):
43893
+ \`todoforai-cli recommend --template <id> --project <projectId> --business-context <businessContextId> --note "<why>" --priority high|medium|low\`
43894
+ Take <projectId> and <businessContextId> from your current session context and pass them explicitly. The card appears PAUSED in the NEXT column for the user to review before any credits are spent.
43895
+
43896
+ 3. Recommend 3-6 templates matching your Growth Plan (highest priority first), then reply with a one-line summary per card and tell the user they're waiting in the NEXT column for review.`,
43847
43897
  mcpConfigs: {},
43848
43898
  edgesMcpConfigs: {},
43849
43899
  permissions: {
43850
- allow: ["builtin:webfetch", "builtin:google_search", "cloud:google_rag", "builtin:business_context", "builtin:todoforai_api", "builtin:question"],
43900
+ allow: [
43901
+ "builtin:webfetch",
43902
+ "builtin:google_search",
43903
+ "cloud:google_rag",
43904
+ "builtin:todoforai_api",
43905
+ "builtin:question",
43906
+ "cloud:BASH(cmd: todoregistry-cli *)",
43907
+ "cloud:BASH(cmd: todoforai-cli *)",
43908
+ "cloud:BASH(cmd: curl *)"
43909
+ ],
43851
43910
  ask: [],
43852
43911
  deny: []
43853
43912
  },
@@ -43914,6 +43973,10 @@ var TOPICS = {
43914
43973
  channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
43915
43974
  audience: "frontend"
43916
43975
  },
43976
+ ["recommendation:updated" /* RECOMMENDATION_UPDATED */]: {
43977
+ channel: (p) => SERVER_TO_FRONTENDS.recommendation.updated(p.projectId),
43978
+ audience: "frontend"
43979
+ },
43917
43980
  ["sandbox:state" /* SANDBOX_STATE */]: {
43918
43981
  channel: (p) => SERVER_TO_FRONTENDS.sandbox.state(p.userId),
43919
43982
  audience: "frontend"
@@ -44037,6 +44100,7 @@ Usage:
44037
44100
  todoforai-cli status <todo-id> <STATUS> # Update a todo's status (run 'status --help' for the full list)
44038
44101
  todoforai-cli delete <todo-id> # Permanently delete a todo
44039
44102
  todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
44103
+ todoforai-cli recommend --template <id> # Add a template as a NEXT-column card (see 'todoregistry-cli create')
44040
44104
 
44041
44105
  Options:
44042
44106
  --path <dir> Workspace path (default: cwd)
@@ -44045,6 +44109,7 @@ Options:
44045
44109
  --list-agents List available agents (name, id, workspace paths) and exit
44046
44110
  --api-url <url> API URL
44047
44111
  --api-key <key> API key
44112
+ --user-id <id> Admin HTTP impersonation; requires --no-watch
44048
44113
  --inspect, -i <todo-id>[@<slice>] Print chat log (read-only)
44049
44114
  --template, -t <id> Start from a registry template
44050
44115
  --input <key=value> Template input (repeatable)
@@ -44100,9 +44165,14 @@ function parseCliArgs() {
44100
44165
  "list-agents": { type: "boolean", default: false },
44101
44166
  "api-url": { type: "string" },
44102
44167
  "api-key": { type: "string" },
44168
+ "user-id": { type: "string" },
44103
44169
  inspect: { type: "string", short: "i" },
44104
44170
  template: { type: "string", short: "t" },
44105
44171
  input: { type: "string", multiple: true },
44172
+ note: { type: "string" },
44173
+ priority: { type: "string" },
44174
+ title: { type: "string" },
44175
+ "business-context": { type: "string" },
44106
44176
  resume: { type: "string", short: "r" },
44107
44177
  continue: { type: "boolean", short: "c", default: false },
44108
44178
  "non-interactive": { type: "boolean", short: "n", default: false },
@@ -44548,16 +44618,20 @@ function ensureBridgeCredentials(apiUrl) {
44548
44618
  const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
44549
44619
  return login.status === 0;
44550
44620
  }
44551
- function lastLogLine(logFile) {
44552
- try {
44553
- const lines = fs2.readFileSync(logFile, "utf-8").split(`
44554
- `).filter(Boolean);
44555
- return lines.length ? lines[lines.length - 1] : null;
44556
- } catch {
44557
- return null;
44558
- }
44621
+ function bridgeAlreadyRunning() {
44622
+ if (process.platform === "win32")
44623
+ return Promise.resolve(false);
44624
+ return new Promise((resolve) => {
44625
+ const r = spawn("pgrep", ["-f", "todoforai-bridge"]);
44626
+ let out = "";
44627
+ r.stdout.on("data", (d) => out += d);
44628
+ r.on("error", () => resolve(false));
44629
+ r.on("close", (code) => resolve(code === 0 && out.trim().length > 0));
44630
+ });
44559
44631
  }
44560
- function ensureBridgeRunning(apiUrl, _apiKey) {
44632
+ async function ensureBridgeRunning(apiUrl, _apiKey) {
44633
+ if (await bridgeAlreadyRunning())
44634
+ return;
44561
44635
  if (!hasBridge()) {
44562
44636
  console.error("\x1B[2mBridge not started: `todoforai-bridge` was not found on PATH. Install TODOforAI Bridge, or pass --no-bridge (or deprecated --no-edge) to silence this.\x1B[0m");
44563
44637
  return;
@@ -44584,20 +44658,12 @@ function ensureBridgeRunning(apiUrl, _apiKey) {
44584
44658
  exitCode = code;
44585
44659
  });
44586
44660
  child.unref();
44587
- const pid = child.pid;
44588
- if (!pid)
44661
+ if (!child.pid)
44589
44662
  return;
44590
44663
  const shortLog = logFile.replace(os3.homedir(), "~");
44591
44664
  setTimeout(() => {
44592
- if (!exited) {
44593
- console.error(`\x1B[2mStarted bridge (pid ${pid}), logs: ${shortLog}\x1B[0m`);
44594
- return;
44595
- }
44596
- if (exitCode === 0) {
44597
- console.error(`\x1B[2mBridge exited cleanly. Logs: ${shortLog}\x1B[0m`);
44598
- } else {
44599
- const reason = lastLogLine(logFile);
44600
- console.error(`\x1B[33mBridge exited early (exit ${exitCode}): ${reason ?? `check logs: ${shortLog}`}\x1B[0m`);
44665
+ if (exited && exitCode !== 0) {
44666
+ console.error(`\x1B[2mBridge not running (exit ${exitCode}), see ${shortLog}\x1B[0m`);
44601
44667
  }
44602
44668
  }, 500);
44603
44669
  }
@@ -44793,40 +44859,38 @@ class ScopedConfig {
44793
44859
  }
44794
44860
  }
44795
44861
 
44796
- // src/credentials.ts
44797
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
44798
- import { dirname as dirname2, join as join2 } from "path";
44862
+ // ../packages/shared-credentials/src/index.ts
44863
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
44864
+ import { join as join2, dirname as dirname2 } from "path";
44799
44865
  import { homedir as homedir2, platform as platform2 } from "os";
44800
44866
  function credentialsPath2() {
44801
- const sys = platform2();
44802
- if (sys === "win32")
44803
- return join2(homedir2(), "AppData", "Roaming", "todoforai", "credentials.json");
44804
- if (sys === "darwin")
44805
- return join2(homedir2(), "Library", "Application Support", "todoforai", "credentials.json");
44806
- const xdg = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
44807
- return join2(xdg, "todoforai", "credentials.json");
44867
+ return platform2() === "win32" ? join2(homedir2(), "AppData", "Roaming", "todoforai", "credentials.json") : platform2() === "darwin" ? join2(homedir2(), "Library", "Application Support", "todoforai", "credentials.json") : join2(process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config"), "todoforai", "credentials.json");
44808
44868
  }
44809
- var CREDENTIALS_PATH2 = credentialsPath2();
44810
44869
  function read() {
44811
- if (!existsSync2(CREDENTIALS_PATH2))
44812
- return {};
44813
44870
  try {
44814
- return JSON.parse(readFileSync2(CREDENTIALS_PATH2, "utf-8"));
44871
+ return JSON.parse(readFileSync2(credentialsPath2(), "utf-8"));
44815
44872
  } catch {
44816
44873
  return {};
44817
44874
  }
44818
44875
  }
44876
+ function writeSecure(data) {
44877
+ const p = credentialsPath2();
44878
+ mkdirSync2(dirname2(p), { recursive: true });
44879
+ writeFileSync2(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
44880
+ try {
44881
+ chmodSync(p, 384);
44882
+ } catch {}
44883
+ }
44819
44884
  function readCredential(apiUrl) {
44820
- const v = read()[apiUrl];
44885
+ const c2 = read();
44886
+ const v = c2[apiUrl] || c2.apiToken || c2.apiKey;
44821
44887
  return typeof v === "string" ? v : "";
44822
44888
  }
44823
44889
  function writeCredential(apiUrl, apiKey) {
44824
- const data = read();
44825
- data[apiUrl] = apiKey;
44826
- mkdirSync2(dirname2(CREDENTIALS_PATH2), { recursive: true });
44827
- writeFileSync2(CREDENTIALS_PATH2, JSON.stringify(data, null, 2), "utf-8");
44890
+ const c2 = read();
44891
+ c2[apiUrl] = apiKey;
44892
+ writeSecure(c2);
44828
44893
  }
44829
-
44830
44894
  // src/logo.ts
44831
44895
  var LETTERS = {
44832
44896
  t: [" x ", "xxxx", " x ", " xll", " xll", " xxx"],
@@ -44837,11 +44901,10 @@ var LETTERS = {
44837
44901
  c: [" ", "xxxx", "x ", "xlll", "xlll", "xxxx"],
44838
44902
  e: [" ", "xxxx", "x x", "xxxx", "xlll", "xxxx"],
44839
44903
  a: [" ", "xxxx", " x", "xxxx", "xllx", "xxxx"],
44840
- i: ["x", " ", "x", "x", "x", "x"],
44841
- "4": [" ", " x ", " xx ", "xlxl", "xxxx", "llxl"]
44904
+ i: ["x", " ", "x", "x", "x", "x"]
44842
44905
  };
44843
44906
  var GAP = " ";
44844
- var WORD = "todo4ai";
44907
+ var WORD = "todoforai";
44845
44908
  function renderHalfBlock(top, bot) {
44846
44909
  const W = c("\x1B[38;2;249;110;46m");
44847
44910
  const G = c("\x1B[38;2;140;60;20m");
@@ -46428,8 +46491,8 @@ Cancelled by user (Ctrl+C)
46428
46491
  return;
46429
46492
  }
46430
46493
  if (args["reset-config"]) {
46431
- const { existsSync: existsSync3, unlinkSync } = await import("fs");
46432
- if (existsSync3(cfg.path)) {
46494
+ const { existsSync: existsSync2, unlinkSync } = await import("fs");
46495
+ if (existsSync2(cfg.path)) {
46433
46496
  unlinkSync(cfg.path);
46434
46497
  console.log(`Configuration reset: ${formatPathWithTilde(cfg.path)}`);
46435
46498
  } else
@@ -46438,6 +46501,15 @@ Cancelled by user (Ctrl+C)
46438
46501
  }
46439
46502
  const apiUrl = normalizeApiUrl(args["api-url"] || getEnv("API_URL") || DEFAULT_API_URL);
46440
46503
  const cfgScope = cfg.scope(apiUrl);
46504
+ if (args["user-id"]) {
46505
+ const url = new URL(apiUrl);
46506
+ const loopback = ["localhost", "127.0.0.1", "::1", "[::1]"].includes(url.hostname);
46507
+ if (url.protocol !== "http:" || !loopback) {
46508
+ process.stderr.write(`Error: --user-id is restricted to HTTP loopback URLs
46509
+ `);
46510
+ process.exit(2);
46511
+ }
46512
+ }
46441
46513
  async function deviceLogin() {
46442
46514
  const loginApi = new ApiClient(apiUrl, "");
46443
46515
  const { code, url, expiresIn } = await loginApi.initDeviceLogin("edge");
@@ -46497,7 +46569,22 @@ Cancelled by user (Ctrl+C)
46497
46569
  if (!apiKey) {
46498
46570
  apiKey = await deviceLogin();
46499
46571
  }
46500
- const api = new ApiClient(apiUrl, apiKey);
46572
+ const api = new ApiClient(apiUrl, apiKey, args["user-id"]);
46573
+ if (args["user-id"] && args.safe) {
46574
+ process.stderr.write(`Error: --safe is not supported with --user-id
46575
+ `);
46576
+ process.exit(2);
46577
+ }
46578
+ if (args["user-id"] && (args.resume || args.continue)) {
46579
+ process.stderr.write(`Error: resume is not supported with --user-id
46580
+ `);
46581
+ process.exit(2);
46582
+ }
46583
+ if (args["user-id"] && args.template && !args["no-watch"]) {
46584
+ process.stderr.write(`Error: --user-id uses admin HTTP impersonation and requires --no-watch
46585
+ `);
46586
+ process.exit(2);
46587
+ }
46501
46588
  if (positionals[0] === "status") {
46502
46589
  const [, todoId, status] = positionals;
46503
46590
  if (!todoId || !status) {
@@ -46535,6 +46622,46 @@ Cancelled by user (Ctrl+C)
46535
46622
  console.log(JSON.stringify(msg, null, 2));
46536
46623
  else
46537
46624
  process.stderr.write(`${GREEN}\u2705 Message added to ${todoId}${RESET}
46625
+ `);
46626
+ return;
46627
+ }
46628
+ if (positionals[0] === "recommend") {
46629
+ const templateId = args.template || positionals[1];
46630
+ if (!templateId) {
46631
+ process.stderr.write(`${RED}Usage: todoforai-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--project <id>]${RESET}
46632
+ `);
46633
+ process.stderr.write(`${DIM}Create a template first with: todoregistry-cli create --name ... --description ... --body @prompt.md${RESET}
46634
+ `);
46635
+ process.exit(2);
46636
+ }
46637
+ let projectId2 = args.project || cfgScope.data.default_project_id;
46638
+ if (!projectId2) {
46639
+ const projects2 = await api.listProjects();
46640
+ projectId2 = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
46641
+ }
46642
+ if (!projectId2) {
46643
+ process.stderr.write(`${RED}No project found \u2014 pass --project <id>${RESET}
46644
+ `);
46645
+ process.exit(2);
46646
+ }
46647
+ const priority = args.priority;
46648
+ if (priority && !["high", "medium", "low"].includes(priority)) {
46649
+ process.stderr.write(`${RED}--priority must be high, medium, or low${RESET}
46650
+ `);
46651
+ process.exit(2);
46652
+ }
46653
+ const rec = await api.recommend({
46654
+ projectId: projectId2,
46655
+ templateId,
46656
+ ...args.title ? { title: args.title } : {},
46657
+ ...args.note ? { note: args.note } : {},
46658
+ ...priority ? { priority } : {},
46659
+ ...args["business-context"] ? { businessContextId: args["business-context"] } : {}
46660
+ });
46661
+ if (args.json)
46662
+ console.log(JSON.stringify(rec, null, 2));
46663
+ else
46664
+ process.stderr.write(`${GREEN}\u2705 Recommended template ${templateId} on project ${projectId2}${RESET}
46538
46665
  `);
46539
46666
  return;
46540
46667
  }
@@ -46715,6 +46842,15 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
46715
46842
  await ws2.close();
46716
46843
  return;
46717
46844
  }
46845
+ if (args["user-id"] && !args["no-watch"]) {
46846
+ process.stderr.write(`Error: --user-id uses admin HTTP impersonation and requires --no-watch
46847
+ `);
46848
+ process.exit(2);
46849
+ }
46850
+ const ws = args["no-watch"] ? null : new FrontendWebSocket(apiUrl, apiKey);
46851
+ const wsReady = ws ? ws.connect() : null;
46852
+ if (!args["no-bridge"] && !args["no-watch"])
46853
+ ensureBridgeRunning(apiUrl, apiKey);
46718
46854
  let preMatchedAgent = null;
46719
46855
  let agents = null;
46720
46856
  if (args.agent) {
@@ -46766,8 +46902,6 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
46766
46902
  }
46767
46903
  process.stderr.write(`${DIM}Tip: ${randomTip()}${RESET}
46768
46904
  `);
46769
- if (!args["no-bridge"] && !args["no-watch"])
46770
- ensureBridgeRunning(apiUrl, apiKey);
46771
46905
  let content;
46772
46906
  if (positionals.length > 0) {
46773
46907
  content = positionals.join(" ");
@@ -46809,9 +46943,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
46809
46943
  } else {
46810
46944
  agent = await selectAgent(agents, cfgScope.data.default_agent_name, (name, settings) => cfgScope.setDefaultAgent(name, settings));
46811
46945
  }
46812
- const ws = args["no-watch"] ? null : new FrontendWebSocket(apiUrl, apiKey);
46813
- if (ws)
46814
- await ws.connect();
46946
+ if (wsReady)
46947
+ await wsReady;
46815
46948
  if (args.model)
46816
46949
  agent = { ...agent, model: args.model };
46817
46950
  if (args["raw-sysmsg"]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "dist/todoai.js",
@@ -16,6 +16,7 @@
16
16
  "dev": "bun run src/index.ts"
17
17
  },
18
18
  "dependencies": {
19
+ "@shared/credentials": "file:../packages/shared-credentials",
19
20
  "@todoforai/update-notifier": "^0.1.0",
20
21
  "cli-highlight": "^2.1.11",
21
22
  "diff-match-patch": "^1.0.5",