@todoforai/cli 0.1.27 → 0.1.28

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.
@@ -5,5 +5,5 @@ import { existsSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
7
  const src = fileURLToPath(new URL("../src/index.ts", import.meta.url));
8
- const dist = fileURLToPath(new URL("../dist/todoai.js", import.meta.url));
8
+ const dist = fileURLToPath(new URL("../dist/todoforai-cli.js", import.meta.url));
9
9
  await import(existsSync(src) ? src : dist);
@@ -41771,9 +41771,9 @@ var require_route = __commonJS((exports, module) => {
41771
41771
  var conversions = require_conversions();
41772
41772
  function buildGraph() {
41773
41773
  const graph = {};
41774
- const models = Object.keys(conversions);
41775
- for (let len = models.length, i = 0;i < len; i++) {
41776
- graph[models[i]] = {
41774
+ const models2 = Object.keys(conversions);
41775
+ for (let len = models2.length, i = 0;i < len; i++) {
41776
+ graph[models2[i]] = {
41777
41777
  distance: -1,
41778
41778
  parent: null
41779
41779
  };
@@ -41805,23 +41805,23 @@ var require_route = __commonJS((exports, module) => {
41805
41805
  };
41806
41806
  }
41807
41807
  function wrapConversion(toModel, graph) {
41808
- const path4 = [graph[toModel].parent, toModel];
41808
+ const path3 = [graph[toModel].parent, toModel];
41809
41809
  let fn = conversions[graph[toModel].parent][toModel];
41810
41810
  let cur = graph[toModel].parent;
41811
41811
  while (graph[cur].parent) {
41812
- path4.unshift(graph[cur].parent);
41812
+ path3.unshift(graph[cur].parent);
41813
41813
  fn = link(conversions[graph[cur].parent][cur], fn);
41814
41814
  cur = graph[cur].parent;
41815
41815
  }
41816
- fn.conversion = path4;
41816
+ fn.conversion = path3;
41817
41817
  return fn;
41818
41818
  }
41819
41819
  module.exports = function(fromModel) {
41820
41820
  const graph = deriveBFS(fromModel);
41821
41821
  const conversion = {};
41822
- const models = Object.keys(graph);
41823
- for (let len = models.length, i = 0;i < len; i++) {
41824
- const toModel = models[i];
41822
+ const models2 = Object.keys(graph);
41823
+ for (let len = models2.length, i = 0;i < len; i++) {
41824
+ const toModel = models2[i];
41825
41825
  const node = graph[toModel];
41826
41826
  if (node.parent === null) {
41827
41827
  continue;
@@ -41837,7 +41837,7 @@ var require_color_convert = __commonJS((exports, module) => {
41837
41837
  var conversions = require_conversions();
41838
41838
  var route = require_route();
41839
41839
  var convert = {};
41840
- var models = Object.keys(conversions);
41840
+ var models2 = Object.keys(conversions);
41841
41841
  function wrapRaw(fn) {
41842
41842
  const wrappedFn = function(...args) {
41843
41843
  const arg0 = args[0];
@@ -41876,7 +41876,7 @@ var require_color_convert = __commonJS((exports, module) => {
41876
41876
  }
41877
41877
  return wrappedFn;
41878
41878
  }
41879
- models.forEach((fromModel) => {
41879
+ models2.forEach((fromModel) => {
41880
41880
  convert[fromModel] = {};
41881
41881
  Object.defineProperty(convert[fromModel], "channels", { value: conversions[fromModel].channels });
41882
41882
  Object.defineProperty(convert[fromModel], "labels", { value: conversions[fromModel].labels });
@@ -42039,7 +42039,7 @@ var require_has_flag = __commonJS((exports, module) => {
42039
42039
 
42040
42040
  // node_modules/supports-color/index.js
42041
42041
  var require_supports_color = __commonJS((exports, module) => {
42042
- var os4 = __require("os");
42042
+ var os3 = __require("os");
42043
42043
  var tty = __require("tty");
42044
42044
  var hasFlag = require_has_flag();
42045
42045
  var { env } = process;
@@ -42087,7 +42087,7 @@ var require_supports_color = __commonJS((exports, module) => {
42087
42087
  return min;
42088
42088
  }
42089
42089
  if (process.platform === "win32") {
42090
- const osRelease = os4.release().split(".");
42090
+ const osRelease = os3.release().split(".");
42091
42091
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
42092
42092
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
42093
42093
  }
@@ -42665,7 +42665,7 @@ import { realpathSync, readFileSync as readFileSync3 } from "fs";
42665
42665
  import { resolve as resolve3 } from "path";
42666
42666
  import { fileURLToPath } from "url";
42667
42667
  import { homedir as homedir3 } from "os";
42668
- import path4 from "path";
42668
+ import path3 from "path";
42669
42669
 
42670
42670
  // node_modules/@todoforai/update-notifier/src/index.ts
42671
42671
  import fs from "fs";
@@ -42731,7 +42731,7 @@ function randomTip() {
42731
42731
  return TIPS[Math.floor(Math.random() * TIPS.length)];
42732
42732
  }
42733
42733
 
42734
- // ../edge/bun/src/api.ts
42734
+ // ../packages/shared-api/src/api.ts
42735
42735
  function restBasePath(apiKey) {
42736
42736
  return apiKey.startsWith("dst_") ? "/dst/v1" : "/api/v1";
42737
42737
  }
@@ -42812,8 +42812,8 @@ class ApiClient {
42812
42812
  deleteProject(projectId) {
42813
42813
  return this.request("DELETE", `/api/v1/projects/${projectId}`);
42814
42814
  }
42815
- createTodo(projectId, content, agentSettings) {
42816
- return this.request("POST", `/api/v1/projects/${projectId}/todos`, { content, agentSettings });
42815
+ createTodo(projectId, content, agentSettings, groupTag, groupName) {
42816
+ return this.request("POST", `/api/v1/projects/${projectId}/todos`, { content, agentSettings, groupTag, groupName });
42817
42817
  }
42818
42818
  listTodos(projectId, opts) {
42819
42819
  if (!projectId)
@@ -42851,12 +42851,16 @@ class ApiClient {
42851
42851
  getAgentSettings(id) {
42852
42852
  return this.request("GET", `/api/v1/agents/${id}`);
42853
42853
  }
42854
- addMessage(projectId, content, agentSettings, todoId, attachments, scheduledTimestamp) {
42854
+ addMessage(projectId, content, agentSettings, todoId, attachments, scheduledTimestamp, groupTag, groupName) {
42855
42855
  const payload = { content, agentSettings, attachments: attachments || [] };
42856
42856
  if (todoId)
42857
42857
  payload.todoId = todoId;
42858
42858
  if (scheduledTimestamp)
42859
42859
  payload.scheduledTimestamp = scheduledTimestamp;
42860
+ if (!todoId && groupTag)
42861
+ payload.groupTag = groupTag;
42862
+ if (!todoId && groupName)
42863
+ payload.groupName = groupName;
42860
42864
  return this.request("POST", `/api/v1/projects/${projectId}/todos`, payload);
42861
42865
  }
42862
42866
  async showFile(todoId, file, filename, opts = {}) {
@@ -42898,6 +42902,9 @@ class ApiClient {
42898
42902
  updateAgentSettings(agentId, agentSettingsId, updates) {
42899
42903
  return this.request("PUT", `/api/v1/agents/${agentId}/settings`, { agentSettingsId, updates });
42900
42904
  }
42905
+ deleteAgentSettings(agentSettingsId) {
42906
+ return this.request("DELETE", `/api/v1/agents/${agentSettingsId}`);
42907
+ }
42901
42908
  getGlobalAgentSettings() {
42902
42909
  return this.request("GET", "/api/v1/agents/global");
42903
42910
  }
@@ -42933,11 +42940,11 @@ class ApiClient {
42933
42940
  throw new Error(`Spec '${specId}' not found (${res.status})`);
42934
42941
  return res.json();
42935
42942
  }
42936
- async initDeviceLogin(clientName = "edge") {
42943
+ async initDeviceLogin(clientName = "edge", opts = {}) {
42937
42944
  const res = await fetch(`${this.apiUrl}/api/v1/cli/login/init`, {
42938
42945
  method: "POST",
42939
42946
  headers: { "content-type": "application/json" },
42940
- body: JSON.stringify({ clientName })
42947
+ body: JSON.stringify({ clientName, ...opts })
42941
42948
  });
42942
42949
  if (!res.ok)
42943
42950
  throw new Error(`Device login init failed: ${res.status}`);
@@ -42957,35 +42964,50 @@ class ApiClient {
42957
42964
  return res.json();
42958
42965
  }
42959
42966
  }
42960
-
42961
- // ../edge/bun/src/frontend-ws.ts
42967
+ // ../packages/shared-api/src/frontend-ws.ts
42962
42968
  import WebSocket from "ws";
42963
42969
 
42964
- // ../edge/bun/src/config.ts
42965
- import path2 from "path";
42966
- import os2 from "os";
42970
+ // ../packages/shared-credentials/src/index.ts
42971
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
42972
+ import { join, dirname } from "path";
42973
+ import { homedir, platform } from "os";
42967
42974
  function normalizeApiUrl(url) {
42968
42975
  if (url.startsWith("localhost"))
42969
42976
  return `http://${url}`;
42970
42977
  if (!url.startsWith("http://") && !url.startsWith("https://"))
42971
42978
  return `https://${url}`;
42972
- return url;
42979
+ return url.replace(/\/+$/, "");
42973
42980
  }
42974
- var SUBCOMMANDS = new Set(["login", "logout", "update"]);
42975
42981
  function credentialsPath() {
42976
- const sys = os2.platform();
42977
- if (sys === "win32") {
42978
- return path2.join(os2.homedir(), "AppData", "Roaming", "todoforai", "credentials.json");
42979
- }
42980
- if (sys === "darwin") {
42981
- return path2.join(os2.homedir(), "Library", "Application Support", "todoforai", "credentials.json");
42982
+ return platform() === "win32" ? join(homedir(), "AppData", "Roaming", "todoforai", "credentials.json") : platform() === "darwin" ? join(homedir(), "Library", "Application Support", "todoforai", "credentials.json") : join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "todoforai", "credentials.json");
42983
+ }
42984
+ function read() {
42985
+ try {
42986
+ return JSON.parse(readFileSync(credentialsPath(), "utf-8"));
42987
+ } catch {
42988
+ return {};
42982
42989
  }
42983
- const xdg = process.env.XDG_CONFIG_HOME || path2.join(os2.homedir(), ".config");
42984
- return path2.join(xdg, "todoforai", "credentials.json");
42985
42990
  }
42986
- var CREDENTIALS_PATH = credentialsPath();
42991
+ function writeSecure(data) {
42992
+ const p = credentialsPath();
42993
+ mkdirSync(dirname(p), { recursive: true });
42994
+ writeFileSync(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
42995
+ try {
42996
+ chmodSync(p, 384);
42997
+ } catch {}
42998
+ }
42999
+ function readCredential(apiUrl) {
43000
+ const c = read();
43001
+ const v = c[apiUrl] || c.apiToken || c.apiKey;
43002
+ return typeof v === "string" ? v : "";
43003
+ }
43004
+ function writeCredential(apiUrl, apiKey) {
43005
+ const c = read();
43006
+ c[apiUrl] = apiKey;
43007
+ writeSecure(c);
43008
+ }
42987
43009
 
42988
- // ../edge/bun/src/frontend-ws.ts
43010
+ // ../packages/shared-api/src/frontend-ws.ts
42989
43011
  var log = (level, ...args) => console.log(`[frontend-ws:${level}]`, ...args);
42990
43012
 
42991
43013
  class FrontendWebSocket {
@@ -43174,48 +43196,6 @@ class FrontendWebSocket {
43174
43196
  });
43175
43197
  }
43176
43198
  }
43177
-
43178
- // src/args.ts
43179
- import { parseArgs } from "util";
43180
- // package.json
43181
- var package_default = {
43182
- name: "@todoforai/cli",
43183
- version: "0.1.27",
43184
- type: "module",
43185
- bin: {
43186
- "todoforai-cli": "bin/todoforai-cli.js",
43187
- todoai: "bin/todoforai-cli.js"
43188
- },
43189
- files: [
43190
- "bin",
43191
- "dist/todoai.js"
43192
- ],
43193
- scripts: {
43194
- build: "bun build src/index.ts --target=bun --outfile dist/todoai.js --external ws",
43195
- prepublishOnly: "bun run build",
43196
- start: "bun run src/index.ts",
43197
- dev: "bun run src/index.ts"
43198
- },
43199
- dependencies: {
43200
- "@shared/credentials": "file:../packages/shared-credentials",
43201
- "@todoforai/update-notifier": "^0.1.0",
43202
- "cli-highlight": "^2.1.11",
43203
- "diff-match-patch": "^1.0.5",
43204
- ws: "^8.18.0"
43205
- },
43206
- peerDependencies: {
43207
- "@todoforai/edge": ">=0.12.0"
43208
- },
43209
- peerDependenciesMeta: {
43210
- "@todoforai/edge": {
43211
- optional: true
43212
- }
43213
- },
43214
- devDependencies: {
43215
- "@types/ws": "^8.5.13",
43216
- typescript: "^5.7.0"
43217
- }
43218
- };
43219
43199
  // ../packages/shared-fbe/src/enums.ts
43220
43200
  var TodoStatus;
43221
43201
  ((TodoStatus2) => {
@@ -43238,6 +43218,59 @@ var TodoStatus;
43238
43218
  TodoStatus2["ARCHIVED"] = "ARCHIVED";
43239
43219
  TodoStatus2["DELETED"] = "DELETED";
43240
43220
  })(TodoStatus ||= {});
43221
+ // ../packages/shared-fbe/src/models.ts
43222
+ var DEFAULT_MODEL = "anthropic:anthropic/claude-sonnet-5";
43223
+ var MODEL_ALIASES = {
43224
+ claude: DEFAULT_MODEL
43225
+ };
43226
+ var PROVIDER_BY_AUTHOR = {
43227
+ anthropic: "anthropic",
43228
+ openai: "openai",
43229
+ deepseek: "deepseek",
43230
+ poolside: "poolside",
43231
+ "x-ai": "xai",
43232
+ "z-ai": "z.ai",
43233
+ google: "google-ai-studio",
43234
+ moonshotai: "opencode_go"
43235
+ };
43236
+ function isQualifiedModel(model) {
43237
+ const id = model.trim();
43238
+ const colon = id.indexOf(":");
43239
+ const slash = id.indexOf("/");
43240
+ return colon > 0 && colon < id.length - 1 && (slash === -1 || colon < slash);
43241
+ }
43242
+ function qualifyModel(model) {
43243
+ const id = model?.trim();
43244
+ if (!id)
43245
+ return { model: DEFAULT_MODEL };
43246
+ if (isQualifiedModel(id))
43247
+ return { model: id };
43248
+ const suffix = id.match(/\([^)]+\)$/)?.[0] ?? "";
43249
+ const bare = suffix ? id.slice(0, -suffix.length) : id;
43250
+ const aliased = MODEL_ALIASES[bare.toLowerCase()];
43251
+ if (aliased)
43252
+ return { model: aliased + suffix };
43253
+ const lower = bare.toLowerCase();
43254
+ const provider = PROVIDER_BY_AUTHOR[lower.split("/")[0]];
43255
+ if (provider && lower.includes("/"))
43256
+ return { model: `${provider}:${lower}${suffix}` };
43257
+ return { model: DEFAULT_MODEL, fallback: id };
43258
+ }
43259
+ function qualifiedModelIds(listing, filter) {
43260
+ const q = filter?.toLowerCase();
43261
+ const ids = [];
43262
+ for (const m of listing?.data ?? []) {
43263
+ const id = String(m.id);
43264
+ if (m.provider) {
43265
+ ids.push(`${m.provider.toLowerCase()}:${id}`);
43266
+ continue;
43267
+ }
43268
+ const r = qualifyModel(id);
43269
+ if (!r.fallback)
43270
+ ids.push(r.model);
43271
+ }
43272
+ return q ? ids.filter((id) => id.toLowerCase().includes(q)) : ids;
43273
+ }
43241
43274
  // ../packages/shared-fbe/src/billing.ts
43242
43275
  var USAGE_WINDOWS = {
43243
43276
  SESSION_MS: 5 * 60 * 60 * 1000,
@@ -43288,7 +43321,7 @@ var mimetypes_default = {
43288
43321
  gif: { mime: "image/gif", type: "image" },
43289
43322
  webp: { mime: "image/webp", type: "image" },
43290
43323
  bmp: { mime: "image/bmp", type: "image" },
43291
- svg: { mime: "image/svg+xml", type: "text" },
43324
+ svg: { mime: "image/svg+xml", type: "image" },
43292
43325
  heic: { mime: "image/heic", type: "image", conversionProfile: "to_jpeg_90" },
43293
43326
  heif: { mime: "image/heif", type: "image", conversionProfile: "to_jpeg_90" },
43294
43327
  mp3: { mime: "audio/mpeg", type: "audio" },
@@ -43496,7 +43529,7 @@ var SERVER_TO_FRONTENDS = {
43496
43529
  predicted_reply_updated: (todoId) => `${"predict:reply_updated" /* PREDICTED_REPLY_UPDATED */}:${todoId}`,
43497
43530
  error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
43498
43531
  meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
43499
- file_changed: (edgeId, path3) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path3}`,
43532
+ file_changed: (edgeId, path2) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path2}`,
43500
43533
  notify: (todoId) => `${"NOTIFY" /* NOTIFY */}:${todoId}`
43501
43534
  },
43502
43535
  notify: {
@@ -43744,13 +43777,10 @@ function getBlockServerPrefix(block) {
43744
43777
  const prefixMatch = agentPattern.match(/^(.*?:)BASH(?:\(|$)/);
43745
43778
  return prefixMatch ? prefixMatch[1] : "";
43746
43779
  }
43747
- var BASH_LIKE_BLOCK_TYPES = new Set(["bash", "machine_exec"]);
43748
43780
  function getBlockPatterns(block) {
43749
43781
  if (Array.isArray(block.generalized_pattern))
43750
43782
  return block.generalized_pattern;
43751
- const agentPattern = typeof block.generalized_pattern === "string" ? block.generalized_pattern : "";
43752
- const isBashLike = BASH_LIKE_BLOCK_TYPES.has(block.type.toLowerCase()) || /:BASH(\(|$)/.test(agentPattern);
43753
- if (isBashLike && block.cmd) {
43783
+ if (block.type.toLowerCase() === "bash" && block.cmd) {
43754
43784
  const prefix = getBlockServerPrefix(block);
43755
43785
  return generalizeBashCmd(block.cmd).map((p) => prefix + p);
43756
43786
  }
@@ -43848,8 +43878,7 @@ function getBlockNewPatterns(block, permissions) {
43848
43878
  if (Array.isArray(block.generalized_pattern)) {
43849
43879
  return getNewPatterns(block.generalized_pattern, permissions);
43850
43880
  }
43851
- const agentPattern = typeof block.generalized_pattern === "string" ? block.generalized_pattern : "";
43852
- const isBash = (block.type.toLowerCase() === "bash" || agentPattern.includes(":BASH(")) && !!block.cmd;
43881
+ const isBash = block.type.toLowerCase() === "bash" && !!block.cmd;
43853
43882
  if (!isBash) {
43854
43883
  return getBlockPatterns(block).filter((p) => !isPatternAllowed(permissions, p));
43855
43884
  }
@@ -44124,6 +44153,40 @@ var TOPICS = {
44124
44153
  audience: "frontend"
44125
44154
  }
44126
44155
  };
44156
+ // src/args.ts
44157
+ import { parseArgs } from "util";
44158
+ // package.json
44159
+ var package_default = {
44160
+ name: "@todoforai/cli",
44161
+ version: "0.1.28",
44162
+ type: "module",
44163
+ bin: {
44164
+ "todoforai-cli": "bin/todoforai-cli.js",
44165
+ "tfa-cli": "bin/todoforai-cli.js",
44166
+ todoai: "bin/todoforai-cli.js"
44167
+ },
44168
+ files: [
44169
+ "bin",
44170
+ "dist/todoforai-cli.js"
44171
+ ],
44172
+ scripts: {
44173
+ build: "bun build src/index.ts --target=bun --outfile dist/todoforai-cli.js --external ws",
44174
+ prepublishOnly: "bun run build",
44175
+ start: "bun run src/index.ts",
44176
+ dev: "bun run src/index.ts"
44177
+ },
44178
+ dependencies: {
44179
+ "@todoforai/update-notifier": "^0.1.0",
44180
+ "cli-highlight": "^2.1.11",
44181
+ "diff-match-patch": "^1.0.5",
44182
+ ws: "^8.18.0"
44183
+ },
44184
+ devDependencies: {
44185
+ "@types/ws": "^8.5.13",
44186
+ typescript: "^5.7.0"
44187
+ }
44188
+ };
44189
+
44127
44190
  // src/args.ts
44128
44191
  var DEFAULT_API_URL = "https://api.todofor.ai";
44129
44192
  var VERSION = package_default.version;
@@ -44132,7 +44195,7 @@ function getEnv(name) {
44132
44195
  }
44133
44196
  function printUsage() {
44134
44197
  process.stderr.write(`
44135
- todoforai-cli \u2014 TODOforAI CLI (Bun)
44198
+ todoforai-cli \u2014 TODOforAI CLI (Bun). Aliases: tfa-cli, todoai.
44136
44199
 
44137
44200
  Usage:
44138
44201
  todoforai-cli login # Browser-based device auth
@@ -44163,6 +44226,8 @@ Options:
44163
44226
  --path <dir> Workspace path (default: cwd)
44164
44227
  --project <id> Project ID
44165
44228
  --agent, -a <name> Agent name (partial match)
44229
+ --group <slug> Group new todo; omitted inherits TODOFORAI_GROUP_ID
44230
+ --group-name <name> Display name for --group (last write wins)
44166
44231
  --model <model> Override the agent's model for this todo
44167
44232
  (e.g. anthropic:anthropic/claude-opus-5, openai:openai/gpt-5.6-sol)
44168
44233
  --list-agents List available agents (name, id, workspace paths) and exit
@@ -44221,6 +44286,8 @@ function parseCliArgs() {
44221
44286
  project: { type: "string" },
44222
44287
  agent: { type: "string", short: "a" },
44223
44288
  model: { type: "string" },
44289
+ group: { type: "string" },
44290
+ "group-name": { type: "string" },
44224
44291
  "list-agents": { type: "boolean", default: false },
44225
44292
  "list-models": { type: "boolean", default: false },
44226
44293
  "api-url": { type: "string" },
@@ -44601,8 +44668,8 @@ import { resolve, basename } from "path";
44601
44668
  // src/ensure-bridge.ts
44602
44669
  import { spawn, spawnSync } from "child_process";
44603
44670
  import fs2 from "fs";
44604
- import path3 from "path";
44605
- import os3 from "os";
44671
+ import path2 from "path";
44672
+ import os2 from "os";
44606
44673
  function hasBridge() {
44607
44674
  const probe = spawnSync("todoforai-bridge", ["--version"], { stdio: "ignore" });
44608
44675
  return probe.status === 0;
@@ -44671,31 +44738,44 @@ function ensureBridgeCredentials(apiUrl) {
44671
44738
  const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
44672
44739
  return login.status === 0;
44673
44740
  }
44674
- function bridgeAlreadyRunning() {
44675
- if (process.platform === "win32")
44676
- return Promise.resolve(false);
44677
- return new Promise((resolve) => {
44678
- const r = spawn("pgrep", ["-f", "todoforai-bridge"]);
44679
- let out = "";
44680
- r.stdout.on("data", (d) => out += d);
44681
- r.on("error", () => resolve(false));
44682
- r.on("close", (code) => resolve(code === 0 && out.trim().length > 0));
44683
- });
44741
+ async function bridgeState(apiUrl, apiKey, deviceId) {
44742
+ try {
44743
+ const devices = await new ApiClient(apiUrl, apiKey).listDevices();
44744
+ return Array.isArray(devices) && devices.some((device) => device.id === deviceId && device.status === "ONLINE") ? "online" : "offline";
44745
+ } catch {
44746
+ return "unknown";
44747
+ }
44684
44748
  }
44685
- async function ensureBridgeRunning(apiUrl, _apiKey) {
44686
- if (await bridgeAlreadyRunning())
44687
- return;
44749
+ async function waitForBridgeOnline(apiUrl, apiKey, deviceId, timeoutMs = 5000) {
44750
+ const deadline = Date.now() + timeoutMs;
44751
+ do {
44752
+ const state = await bridgeState(apiUrl, apiKey, deviceId);
44753
+ if (state !== "offline")
44754
+ return state === "online";
44755
+ await new Promise((resolve) => setTimeout(resolve, 150));
44756
+ } while (Date.now() < deadline);
44757
+ return false;
44758
+ }
44759
+ async function ensureBridgeRunning(apiUrl, apiKey) {
44688
44760
  if (!hasBridge()) {
44689
44761
  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");
44690
- return;
44762
+ return false;
44691
44763
  }
44692
44764
  if (!ensureBridgeCredentials(apiUrl)) {
44693
44765
  console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
44694
- return;
44766
+ return false;
44695
44767
  }
44696
- const logDir = path3.join(os3.homedir(), ".todoforai");
44768
+ const deviceId = bridgeDeviceId(apiUrl);
44769
+ if (!deviceId) {
44770
+ console.error("\x1B[33mBridge not started: could not determine this device's id.\x1B[0m");
44771
+ return false;
44772
+ }
44773
+ const initialState = await bridgeState(apiUrl, apiKey, deviceId);
44774
+ if (initialState === "online" || initialState === "unknown")
44775
+ return initialState === "online";
44776
+ const logDir = path2.join(os2.homedir(), ".todoforai");
44697
44777
  fs2.mkdirSync(logDir, { recursive: true });
44698
- const logFile = path3.join(logDir, "bridge.log");
44778
+ const logFile = path2.join(logDir, "bridge.log");
44699
44779
  const out = fs2.openSync(logFile, "a");
44700
44780
  const child = spawn("todoforai-bridge", bridgeRunArgs(apiUrl), {
44701
44781
  detached: true,
@@ -44712,13 +44792,13 @@ async function ensureBridgeRunning(apiUrl, _apiKey) {
44712
44792
  });
44713
44793
  child.unref();
44714
44794
  if (!child.pid)
44715
- return;
44716
- const shortLog = logFile.replace(os3.homedir(), "~");
44717
- setTimeout(() => {
44718
- if (exited && exitCode !== 0) {
44719
- console.error(`\x1B[2mBridge not running (exit ${exitCode}), see ${shortLog}\x1B[0m`);
44720
- }
44721
- }, 500);
44795
+ return false;
44796
+ if (await waitForBridgeOnline(apiUrl, apiKey, deviceId))
44797
+ return true;
44798
+ const shortLog = logFile.replace(os2.homedir(), "~");
44799
+ const reason = exited ? `exit ${exitCode}` : "startup timed out";
44800
+ console.error(`\x1B[33mBridge not ready (${reason}), see ${shortLog}\x1B[0m`);
44801
+ return false;
44722
44802
  }
44723
44803
 
44724
44804
  // src/agent.ts
@@ -44752,9 +44832,13 @@ async function autoCreateAgent(api, resolvedPath) {
44752
44832
  return resp;
44753
44833
  }
44754
44834
  const edges = await api.listEdges();
44755
- const edgeId = Array.isArray(edges) && edges.length ? edges[0].id : null;
44756
- if (!edgeId)
44835
+ if (!Array.isArray(edges) || edges.length === 0) {
44757
44836
  throw new Error("No bridge device or edge available to configure workspace path");
44837
+ }
44838
+ if (edges.length > 1) {
44839
+ throw new Error("No local bridge device found and multiple legacy edges are available; choose an existing agent explicitly");
44840
+ }
44841
+ const edgeId = edges[0].id;
44758
44842
  await api.setAgentEdgeMcpConfig(agentId, agentSettingsId, edgeId, "todoai_edge", { workspacePaths: [resolvedPath] });
44759
44843
  const matches = await api.listAgentSettings({ workspacePath: resolvedPath });
44760
44844
  if (matches.length)
@@ -44765,20 +44849,20 @@ async function autoCreateAgent(api, resolvedPath) {
44765
44849
  }
44766
44850
 
44767
44851
  // src/config.ts
44768
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
44769
- import { dirname, join, resolve as resolve2 } from "path";
44770
- import { homedir, platform } from "os";
44852
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
44853
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
44854
+ import { homedir as homedir2, platform as platform2 } from "os";
44771
44855
  function getConfigDir() {
44772
- const sys = platform();
44856
+ const sys = platform2();
44773
44857
  if (sys === "win32") {
44774
- const base = process.env.APPDATA || join(homedir(), "AppData", "Roaming");
44775
- return join(base, "todoai-cli");
44858
+ const base = process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
44859
+ return join2(base, "todoai-cli");
44776
44860
  }
44777
44861
  if (sys === "darwin") {
44778
- return join(homedir(), "Library", "Application Support", "todoai-cli");
44862
+ return join2(homedir2(), "Library", "Application Support", "todoai-cli");
44779
44863
  }
44780
- const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
44781
- return join(xdg, "todoai-cli");
44864
+ const xdg = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
44865
+ return join2(xdg, "todoai-cli");
44782
44866
  }
44783
44867
  function defaultScope() {
44784
44868
  return {
@@ -44830,10 +44914,10 @@ class ConfigStore {
44830
44914
  data;
44831
44915
  constructor(pathArg) {
44832
44916
  if (pathArg) {
44833
- const p = resolve2(pathArg.replace(/^~/, homedir()));
44834
- this.path = p.endsWith(".json") ? p : join(p, "config.json");
44917
+ const p = resolve2(pathArg.replace(/^~/, homedir2()));
44918
+ this.path = p.endsWith(".json") ? p : join2(p, "config.json");
44835
44919
  } else {
44836
- this.path = join(getConfigDir(), "config.json");
44920
+ this.path = join2(getConfigDir(), "config.json");
44837
44921
  }
44838
44922
  this.data = this.load();
44839
44923
  }
@@ -44841,7 +44925,7 @@ class ConfigStore {
44841
44925
  if (!existsSync(this.path))
44842
44926
  return defaultConfig();
44843
44927
  try {
44844
- const raw = JSON.parse(readFileSync(this.path, "utf-8"));
44928
+ const raw = JSON.parse(readFileSync2(this.path, "utf-8"));
44845
44929
  delete raw.default_api_key;
44846
44930
  const cfg = migrate(raw);
44847
44931
  const needsMigration = raw.per_api_url == null;
@@ -44856,8 +44940,8 @@ class ConfigStore {
44856
44940
  }
44857
44941
  save() {
44858
44942
  try {
44859
- mkdirSync(dirname(this.path), { recursive: true });
44860
- writeFileSync(this.path, JSON.stringify(this.data, null, 2), "utf-8");
44943
+ mkdirSync2(dirname2(this.path), { recursive: true });
44944
+ writeFileSync2(this.path, JSON.stringify(this.data, null, 2), "utf-8");
44861
44945
  } catch {}
44862
44946
  }
44863
44947
  scope(apiUrl) {
@@ -44917,39 +45001,6 @@ class ScopedConfig {
44917
45001
  this.store.save();
44918
45002
  }
44919
45003
  }
44920
-
44921
- // ../packages/shared-credentials/src/index.ts
44922
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
44923
- import { join as join2, dirname as dirname2 } from "path";
44924
- import { homedir as homedir2, platform as platform2 } from "os";
44925
- function credentialsPath2() {
44926
- 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");
44927
- }
44928
- function read() {
44929
- try {
44930
- return JSON.parse(readFileSync2(credentialsPath2(), "utf-8"));
44931
- } catch {
44932
- return {};
44933
- }
44934
- }
44935
- function writeSecure(data) {
44936
- const p = credentialsPath2();
44937
- mkdirSync2(dirname2(p), { recursive: true });
44938
- writeFileSync2(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
44939
- try {
44940
- chmodSync(p, 384);
44941
- } catch {}
44942
- }
44943
- function readCredential(apiUrl) {
44944
- const c2 = read();
44945
- const v = c2[apiUrl] || c2.apiToken || c2.apiKey;
44946
- return typeof v === "string" ? v : "";
44947
- }
44948
- function writeCredential(apiUrl, apiKey) {
44949
- const c2 = read();
44950
- c2[apiUrl] = apiKey;
44951
- writeSecure(c2);
44952
- }
44953
45004
  // src/logo.ts
44954
45005
  var LETTERS = {
44955
45006
  t: [" x ", "xxxx", " x ", " xll", " xll", " xxx"],
@@ -45023,6 +45074,12 @@ function printLogo() {
45023
45074
  `);
45024
45075
  }
45025
45076
 
45077
+ // src/urls.ts
45078
+ function getFrontendUrl(apiUrl, todoId) {
45079
+ const local = apiUrl.includes("localhost:4000") || apiUrl.includes("127.0.0.1:4000");
45080
+ return `${local ? "http://localhost:3000" : "https://todofor.ai"}/t/${todoId}`;
45081
+ }
45082
+
45026
45083
  // src/inspect.ts
45027
45084
  var xmlAttr = (v) => JSON.stringify(String(v));
45028
45085
  var DROP_IF_EMPTY = new Set(["runMeta", "meta", "attachments", "blocks"]);
@@ -45854,17 +45911,6 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
45854
45911
  "block:start_catfile"
45855
45912
  ]);
45856
45913
  const signalActivity = () => opts.activityEvent?.set();
45857
- let edgeId;
45858
- let rootPath = "";
45859
- if (opts.agentSettings) {
45860
- const emc = opts.agentSettings.edgesMcpConfigs || {};
45861
- edgeId = Object.keys(emc)[0];
45862
- if (edgeId) {
45863
- const ec = emc[edgeId];
45864
- const tc = ec?.todoai_edge || ec?.todoai || {};
45865
- rootPath = (tc.workspacePaths || [])[0] || "";
45866
- }
45867
- }
45868
45914
  const blocksStore = new Map;
45869
45915
  const diffStore = diffStoreByWs.get(ws) ?? new Map;
45870
45916
  diffStoreByWs.set(ws, diffStore);
@@ -46051,6 +46097,12 @@ ${YELLOW}*${RESET} ${YELLOW}${blockType}${RESET}${extra}
46051
46097
  `);
46052
46098
  signalActivity();
46053
46099
  }
46100
+ } else if (msgType === "NOTIFY") {
46101
+ const sevColor = payload.severity === "error" ? RED : YELLOW;
46102
+ process.stderr.write(`
46103
+ ${sevColor}\u26A0 ${payload.title || "Notice"}${RESET}${payload.message ? ` ${DIM}${payload.message}${RESET}` : ""}
46104
+ `);
46105
+ signalActivity();
46054
46106
  } else if (msgType === "todo:status") {
46055
46107
  const status = payload.status || "";
46056
46108
  process.stderr.write(`
@@ -46081,6 +46133,9 @@ ${DIM}[todo:status] ${status}${RESET}
46081
46133
  const status = result?.payload?.status || result?.type || "unknown";
46082
46134
  process.stderr.write(`Warning: Stopped: ${status}
46083
46135
  `);
46136
+ process.exitCode = 1;
46137
+ } else {
46138
+ process.exitCode = 0;
46084
46139
  }
46085
46140
  return true;
46086
46141
  } catch (e) {
@@ -46124,20 +46179,6 @@ async function listAgentsCommand(api, opts) {
46124
46179
  }
46125
46180
  }
46126
46181
 
46127
- // src/list-models.ts
46128
- async function listModelsCommand(api, opts) {
46129
- const res = await api.listModels();
46130
- const q = opts.filter?.toLowerCase();
46131
- const ids = (res?.data || []).map((m) => String(m.id).includes("/") ? `${String(m.id).split("/")[0]}:${m.id}` : String(m.id)).filter((id) => !q || id.toLowerCase().includes(q));
46132
- if (opts.json) {
46133
- console.log(JSON.stringify(ids, null, 2));
46134
- return;
46135
- }
46136
- for (const id of ids)
46137
- process.stderr.write(`${BRAND}${id}${RESET}
46138
- `);
46139
- }
46140
-
46141
46182
  // src/agent-command.ts
46142
46183
  function printAgentHelp() {
46143
46184
  process.stderr.write(`
@@ -46147,6 +46188,7 @@ Usage:
46147
46188
  todoforai-cli agent list List agents (name, model, id, paths)
46148
46189
  todoforai-cli agent get <agent> Show a single agent's settings
46149
46190
  todoforai-cli agent update <agent> <field=value>\u2026 Update one or more settings
46191
+ todoforai-cli agent delete <agent> Delete an agent configuration (asks to confirm)
46150
46192
 
46151
46193
  <agent> is a name or id (unique partial name also works).
46152
46194
  Fields map directly to agent settings; values are parsed as JSON when possible
@@ -46244,6 +46286,32 @@ async function agentCommand(api, positionals, args, formatPath) {
46244
46286
  }
46245
46287
  const summary = Object.keys(updates).map((k) => `${k}=${JSON.stringify(updated[k])}`).join(" ");
46246
46288
  process.stderr.write(`${GREEN}\u2705 ${getDisplayName(agent)} updated: ${summary}${RESET}
46289
+ `);
46290
+ return;
46291
+ }
46292
+ if (sub === "delete" || sub === "rm") {
46293
+ const query = positionals[2];
46294
+ if (!query) {
46295
+ process.stderr.write(`${RED}Usage: todoforai-cli agent delete <name|id>${RESET}
46296
+ `);
46297
+ process.exit(2);
46298
+ }
46299
+ const agents = await api.listAgentSettings();
46300
+ const agent = resolveAgent(agents, query);
46301
+ const name = getDisplayName(agent);
46302
+ if (!process.stdin.isTTY) {
46303
+ process.stderr.write(`${RED}Refusing to delete '${name}' non-interactively (delete needs an interactive confirm).${RESET}
46304
+ `);
46305
+ process.exit(2);
46306
+ }
46307
+ const ok = await singleChar(`${RED}Delete agent '${name}'? [y/N] ${RESET}`);
46308
+ if (ok !== "y") {
46309
+ process.stderr.write(`Aborted
46310
+ `);
46311
+ return;
46312
+ }
46313
+ await api.deleteAgentSettings(getItemId(agent));
46314
+ process.stderr.write(`${GREEN}\u2705 Deleted ${name}${RESET}
46247
46315
  `);
46248
46316
  return;
46249
46317
  }
@@ -46449,18 +46517,12 @@ async function listTodosCommand(api, defaultProjectId, argv) {
46449
46517
 
46450
46518
  // src/index.ts
46451
46519
  try {
46452
- const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
46520
+ const pkgPath = path3.resolve(fileURLToPath(import.meta.url), "../../package.json");
46453
46521
  checkForUpdates(JSON.parse(readFileSync3(pkgPath, "utf-8")));
46454
46522
  } catch {}
46455
- function formatPathWithTilde(path5) {
46523
+ function formatPathWithTilde(path4) {
46456
46524
  const home = homedir3();
46457
- return path5.startsWith(home) ? path5.replace(home, "~") : path5;
46458
- }
46459
- function getFrontendUrl(apiUrl, projectId, todoId) {
46460
- if (apiUrl.includes("localhost:4000") || apiUrl.includes("127.0.0.1:4000")) {
46461
- return `http://localhost:3000/${projectId}/${todoId}`;
46462
- }
46463
- return `https://todofor.ai/${projectId}/${todoId}`;
46525
+ return path4.startsWith(home) ? path4.replace(home, "~") : path4;
46464
46526
  }
46465
46527
  async function interactiveLoop(ws, api, todoId, projectId, agent, json, autoApprove, cfg) {
46466
46528
  while (true) {
@@ -46537,6 +46599,8 @@ Cancelled by user (Ctrl+C)
46537
46599
  process.exit(130);
46538
46600
  });
46539
46601
  const { values: args, positionals } = parseCliArgs();
46602
+ const groupTag = args.group === undefined ? getEnv("GROUP_ID") : String(args.group);
46603
+ const groupName = args["group-name"] === undefined ? undefined : String(args["group-name"]);
46540
46604
  if (positionals[0] === "start") {
46541
46605
  if (!positionals[1] && !args.template) {
46542
46606
  process.stderr.write(`${RED}Usage: todoforai-cli start <todo-id>${RESET}
@@ -46699,7 +46763,8 @@ Cancelled by user (Ctrl+C)
46699
46763
  process.exit(2);
46700
46764
  }
46701
46765
  const todo2 = await api.getTodo(todoId);
46702
- const msg = await api.addMessage(todo2.projectId, content2, todo2.agentSettings || { id: todo2.agentSettingsId }, todoId);
46766
+ const agent2 = todo2.agentSettings || await api.getAgentSettings(todo2.agentSettingsId);
46767
+ const msg = await api.addMessage(todo2.projectId, content2, agent2, todoId);
46703
46768
  if (args.json)
46704
46769
  console.log(JSON.stringify(msg, null, 2));
46705
46770
  else
@@ -46732,7 +46797,7 @@ Cancelled by user (Ctrl+C)
46732
46797
  process.exit(1);
46733
46798
  }
46734
46799
  blob = file;
46735
- name = path4.basename(filePath);
46800
+ name = path3.basename(filePath);
46736
46801
  }
46737
46802
  const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
46738
46803
  if (args.json)
@@ -46875,7 +46940,13 @@ Cancelled by user (Ctrl+C)
46875
46940
  return;
46876
46941
  }
46877
46942
  if (args["list-models"]) {
46878
- await listModelsCommand(api, { json: !!args.json, filter: positionals[0] });
46943
+ const ids = qualifiedModelIds(await api.listModels(), positionals[0]);
46944
+ if (args.json)
46945
+ console.log(JSON.stringify(ids, null, 2));
46946
+ else
46947
+ for (const id of ids)
46948
+ process.stderr.write(`${BRAND}${id}${RESET}
46949
+ `);
46879
46950
  return;
46880
46951
  }
46881
46952
  if (positionals[0] === "agent") {
@@ -46922,14 +46993,14 @@ Cancelled by user (Ctrl+C)
46922
46993
  }
46923
46994
  const mode = args.debug ? "debug" : args.detailed ? "detailed" : "default";
46924
46995
  const format = args["format-anthropic"] ? "anthropic" : "compact";
46925
- printFullChat(todo2, getFrontendUrl(apiUrl, todo2.projectId, todoId), slice, mode, format);
46996
+ printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format);
46926
46997
  return;
46927
46998
  }
46928
46999
  if (process.stderr.isTTY)
46929
47000
  printLogo();
46930
47001
  if (args.template) {
46931
47002
  if (!args["no-bridge"] && !args["no-watch"])
46932
- ensureBridgeRunning(apiUrl, apiKey);
47003
+ await ensureBridgeRunning(apiUrl, apiKey);
46933
47004
  const templateId = args.template;
46934
47005
  const spec = await api.getRegistrySpec(templateId);
46935
47006
  process.stderr.write(`${DIM}Template:${RESET} ${BRAND}${spec.name}${RESET}
@@ -46948,10 +47019,13 @@ Cancelled by user (Ctrl+C)
46948
47019
  `);
46949
47020
  process.exit(1);
46950
47021
  }
46951
- const todo2 = await api.startFromSpec(projectId2, templateId);
47022
+ const todo2 = await api.startFromSpec(projectId2, templateId, {
47023
+ ...groupTag ? { groupTag } : {},
47024
+ ...groupName ? { groupName } : {}
47025
+ });
46952
47026
  const todoId = todo2.id;
46953
47027
  cfgScope.setLastTodoId(todoId);
46954
- const frontendUrl2 = getFrontendUrl(apiUrl, projectId2, todoId);
47028
+ const frontendUrl2 = getFrontendUrl(apiUrl, todoId);
46955
47029
  if (args.json) {
46956
47030
  console.log(JSON.stringify({ ...todo2, frontend_url: frontendUrl2 }, null, 2));
46957
47031
  } else if (args["no-watch"]) {
@@ -46996,7 +47070,7 @@ ${"\u2500".repeat(40)}
46996
47070
  }
46997
47071
  if (args.resume || args.continue) {
46998
47072
  if (!args["no-bridge"])
46999
- ensureBridgeRunning(apiUrl, apiKey);
47073
+ await ensureBridgeRunning(apiUrl, apiKey);
47000
47074
  const todoId = args.resume || cfgScope.data.last_todo_id;
47001
47075
  if (!todoId) {
47002
47076
  process.stderr.write(`Error: No recent todo found
@@ -47013,7 +47087,7 @@ ${"\u2500".repeat(40)}
47013
47087
  }
47014
47088
  process.stderr.write(`
47015
47089
  ${"\u2500".repeat(40)}
47016
- Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
47090
+ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
47017
47091
  `);
47018
47092
  const ws2 = new FrontendWebSocket(apiUrl, apiKey);
47019
47093
  await ws2.connect();
@@ -47037,8 +47111,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
47037
47111
  }
47038
47112
  const ws = args["no-watch"] ? null : new FrontendWebSocket(apiUrl, apiKey);
47039
47113
  const wsReady = ws ? ws.connect() : null;
47040
- if (!args["no-bridge"] && !args["no-watch"])
47041
- ensureBridgeRunning(apiUrl, apiKey);
47114
+ const bridgeReady = !args["no-bridge"] && !args["no-watch"] ? ensureBridgeRunning(apiUrl, apiKey) : null;
47042
47115
  let preMatchedAgent = null;
47043
47116
  let agents = null;
47044
47117
  if (args.agent) {
@@ -47133,6 +47206,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
47133
47206
  }
47134
47207
  if (wsReady)
47135
47208
  await wsReady;
47209
+ if (bridgeReady)
47210
+ await bridgeReady;
47136
47211
  if (args.model)
47137
47212
  agent = { ...agent, model: args.model };
47138
47213
  if (args["raw-sysmsg"]) {
@@ -47146,7 +47221,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
47146
47221
  cfg.addToHistory(content);
47147
47222
  let todo;
47148
47223
  try {
47149
- todo = await api.addMessage(projectId, content, agent);
47224
+ todo = await api.addMessage(projectId, content, agent, undefined, undefined, undefined, groupTag || undefined, groupName);
47150
47225
  } catch (e) {
47151
47226
  if (!args.project && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
47152
47227
  cfgScope.clearDefaultProject();
@@ -47158,7 +47233,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
47158
47233
  }
47159
47234
  const actualTodoId = todo.id || crypto.randomUUID();
47160
47235
  cfgScope.setLastTodoId(actualTodoId);
47161
- const frontendUrl = getFrontendUrl(apiUrl, projectId, actualTodoId);
47236
+ const frontendUrl = getFrontendUrl(apiUrl, actualTodoId);
47162
47237
  if (args.json) {
47163
47238
  console.log(JSON.stringify({ ...todo, frontend_url: frontendUrl }, null, 2));
47164
47239
  } else if (!ws) {
@@ -47183,7 +47258,7 @@ ${"\u2500".repeat(40)}
47183
47258
  await ws.close();
47184
47259
  }
47185
47260
  }
47186
- main().then(() => process.exit(0)).catch((e) => {
47261
+ main().then(() => process.exit(process.exitCode ?? 0)).catch((e) => {
47187
47262
  process.stderr.write(`Error: ${e.message}
47188
47263
  `);
47189
47264
  process.exit(1);
package/package.json CHANGED
@@ -1,36 +1,28 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",
7
+ "tfa-cli": "bin/todoforai-cli.js",
7
8
  "todoai": "bin/todoforai-cli.js"
8
9
  },
9
10
  "files": [
10
11
  "bin",
11
- "dist/todoai.js"
12
+ "dist/todoforai-cli.js"
12
13
  ],
13
14
  "scripts": {
14
- "build": "bun build src/index.ts --target=bun --outfile dist/todoai.js --external ws",
15
+ "build": "bun build src/index.ts --target=bun --outfile dist/todoforai-cli.js --external ws",
15
16
  "prepublishOnly": "bun run build",
16
17
  "start": "bun run src/index.ts",
17
18
  "dev": "bun run src/index.ts"
18
19
  },
19
20
  "dependencies": {
20
- "@shared/credentials": "file:../packages/shared-credentials",
21
21
  "@todoforai/update-notifier": "^0.1.0",
22
22
  "cli-highlight": "^2.1.11",
23
23
  "diff-match-patch": "^1.0.5",
24
24
  "ws": "^8.18.0"
25
25
  },
26
- "peerDependencies": {
27
- "@todoforai/edge": ">=0.12.0"
28
- },
29
- "peerDependenciesMeta": {
30
- "@todoforai/edge": {
31
- "optional": true
32
- }
33
- },
34
26
  "devDependencies": {
35
27
  "@types/ws": "^8.5.13",
36
28
  "typescript": "^5.7.0"