@todoforai/cli 0.1.61 → 0.1.63

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.
@@ -42675,25 +42675,53 @@ var require_dist = __commonJS((exports) => {
42675
42675
  });
42676
42676
 
42677
42677
  // src/index.ts
42678
- import { realpathSync as realpathSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
42679
- import { resolve as resolve4 } from "path";
42678
+ import { realpathSync as realpathSync2, readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
42679
+ import { resolve as resolve5 } from "path";
42680
42680
  import { fileURLToPath } from "url";
42681
- import { homedir as homedir3 } from "os";
42681
+ import { homedir as homedir5 } from "os";
42682
42682
  import path4 from "path";
42683
42683
 
42684
42684
  // node_modules/@todoforai/update-notifier/src/index.ts
42685
42685
  import fs from "node:fs";
42686
42686
  import path from "node:path";
42687
42687
  import os from "node:os";
42688
- function isLinkedInstall() {
42688
+ import { spawn, spawnSync } from "node:child_process";
42689
+ function installer() {
42690
+ let real;
42689
42691
  try {
42690
- return !fs.realpathSync(process.argv[1] || "").includes(`${path.sep}node_modules${path.sep}`);
42692
+ real = fs.realpathSync(process.argv[1] || "");
42691
42693
  } catch {
42692
- return false;
42694
+ return null;
42693
42695
  }
42696
+ if (!real.includes(`${path.sep}node_modules${path.sep}`))
42697
+ return null;
42698
+ return real.includes(path.join(".bun", "install", "global")) ? "bun" : "npm";
42694
42699
  }
42695
42700
  var TTL_MS = 24 * 60 * 60 * 1000;
42696
42701
  var CACHE_DIR = path.join(os.homedir(), ".config", "todoforai");
42702
+ var cacheFileFor = (name) => path.join(CACHE_DIR, `notifier-${encodeURIComponent(name)}.json`);
42703
+ function readCache(name) {
42704
+ try {
42705
+ return JSON.parse(fs.readFileSync(cacheFileFor(name), "utf8"));
42706
+ } catch {
42707
+ return {};
42708
+ }
42709
+ }
42710
+ function writeCache(name, cache) {
42711
+ try {
42712
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
42713
+ fs.writeFileSync(cacheFileFor(name), JSON.stringify(cache));
42714
+ } catch {}
42715
+ }
42716
+ function refreshCache(name, cache) {
42717
+ if (Date.now() - (cache.ts ?? 0) <= TTL_MS)
42718
+ return;
42719
+ writeCache(name, { ...cache, ts: Date.now() });
42720
+ fetch(`https://registry.npmjs.org/${name}/latest`, { signal: AbortSignal.timeout(3000) }).then((r) => r.ok ? r.json() : null).then((j) => {
42721
+ if (j?.version)
42722
+ writeCache(name, { ...cache, ts: Date.now(), latest: j.version });
42723
+ }).catch(() => {});
42724
+ }
42697
42725
  function cmpVer(a, b) {
42698
42726
  const pa = a.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
42699
42727
  const pb = b.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -42705,13 +42733,11 @@ function cmpVer(a, b) {
42705
42733
  return 0;
42706
42734
  }
42707
42735
  function checkForUpdates(pkg) {
42708
- if (!process.stderr.isTTY || process.env.CI || process.env.NO_UPDATE_NOTIFIER || isLinkedInstall())
42736
+ if (!process.stderr.isTTY || process.env.CI || process.env.NO_UPDATE_NOTIFIER || !installer())
42709
42737
  return;
42710
- const cacheFile = path.join(CACHE_DIR, `notifier-${encodeURIComponent(pkg.name)}.json`);
42711
- let cache = {};
42712
- try {
42713
- cache = JSON.parse(fs.readFileSync(cacheFile, "utf8"));
42714
- } catch {}
42738
+ if (process.argv.includes("--json"))
42739
+ return;
42740
+ const cache = readCache(pkg.name);
42715
42741
  if (cache.latest && cmpVer(cache.latest, pkg.version) > 0) {
42716
42742
  process.stderr.write(`
42717
42743
  \x1B[33m Update available: \x1B[2m${pkg.version}\x1B[22m → \x1B[1m${cache.latest}\x1B[0m
@@ -42719,17 +42745,67 @@ function checkForUpdates(pkg) {
42719
42745
 
42720
42746
  `);
42721
42747
  }
42722
- if (Date.now() - (cache.ts ?? 0) > TTL_MS) {
42748
+ refreshCache(pkg.name, cache);
42749
+ }
42750
+ function backgroundUpdate(pkg) {
42751
+ const mgr = installer();
42752
+ if (process.env.CI || process.env.NO_UPDATE_NOTIFIER || !mgr)
42753
+ return;
42754
+ const cache = readCache(pkg.name);
42755
+ const latest = cache.latest;
42756
+ if (latest && cmpVer(latest, pkg.version) > 0 && cache.tried !== latest) {
42757
+ const next = { ...cache, tried: latest };
42758
+ writeCache(pkg.name, next);
42723
42759
  try {
42724
- fs.mkdirSync(CACHE_DIR, { recursive: true });
42725
- fs.writeFileSync(cacheFile, JSON.stringify({ ...cache, ts: Date.now() }));
42760
+ const args = mgr === "bun" ? ["add", "-g", `${pkg.name}@${latest}`] : ["i", "-g", `${pkg.name}@${latest}`];
42761
+ const win = process.platform === "win32";
42762
+ spawn(win ? `${mgr}.cmd` : mgr, args, { detached: true, stdio: "ignore", shell: win, windowsHide: true }).unref();
42726
42763
  } catch {}
42727
- fetch(`https://registry.npmjs.org/${pkg.name}/latest`, { signal: AbortSignal.timeout(3000) }).then((r) => r.ok ? r.json() : null).then((j) => {
42728
- if (!j?.version)
42729
- return;
42730
- fs.writeFileSync(cacheFile, JSON.stringify({ ts: Date.now(), latest: j.version }));
42731
- }).catch(() => {});
42764
+ refreshCache(pkg.name, next);
42765
+ return;
42732
42766
  }
42767
+ checkForUpdates(pkg);
42768
+ }
42769
+ var log = (msg) => process.stderr.write(msg + `
42770
+ `);
42771
+ function install(pkg, mgr, latest) {
42772
+ log(`\x1B[33m Updating ${pkg.name} ${pkg.version} → ${latest} ...\x1B[0m`);
42773
+ const cmd = process.platform === "win32" ? `${mgr}.cmd` : mgr;
42774
+ const args = mgr === "bun" ? ["add", "-g", `${pkg.name}@${latest}`] : ["i", "-g", `${pkg.name}@${latest}`];
42775
+ const r = spawnSync(cmd, args, { stdio: "inherit", timeout: 120000 });
42776
+ if (r.status !== 0) {
42777
+ writeCache(pkg.name, { ...readCache(pkg.name), failed: latest });
42778
+ log(`\x1B[31m Update to ${latest} failed — run manually: ${mgr} ${args.join(" ")}\x1B[0m`);
42779
+ return "failed";
42780
+ }
42781
+ log(`\x1B[32m ✓ Updated to ${latest}\x1B[0m (active from the next start)`);
42782
+ writeCache(pkg.name, { ts: Date.now(), latest });
42783
+ return "updated";
42784
+ }
42785
+ async function fetchLatest(name, timeoutMs) {
42786
+ try {
42787
+ const r = await fetch(`https://registry.npmjs.org/${name}/latest`, { signal: AbortSignal.timeout(timeoutMs) });
42788
+ return r.ok ? (await r.json())?.version : undefined;
42789
+ } catch {
42790
+ return;
42791
+ }
42792
+ }
42793
+ async function selfUpdate(pkg) {
42794
+ const mgr = installer();
42795
+ if (!mgr) {
42796
+ log(`\x1B[33mNot a package-manager install — update manually: npm i -g ${pkg.name}\x1B[0m`);
42797
+ return "unsupported";
42798
+ }
42799
+ const latest = await fetchLatest(pkg.name, 1e4);
42800
+ if (!latest) {
42801
+ log("\x1B[31mCould not reach the npm registry.\x1B[0m");
42802
+ return "failed";
42803
+ }
42804
+ if (cmpVer(latest, pkg.version) <= 0) {
42805
+ log(`\x1B[32m✓ Already up to date (${pkg.version}).\x1B[0m`);
42806
+ return "current";
42807
+ }
42808
+ return install(pkg, mgr, latest);
42733
42809
  }
42734
42810
 
42735
42811
  // src/tips.ts
@@ -42981,6 +43057,9 @@ class ApiClient {
42981
43057
  getAgentSettings(id) {
42982
43058
  return this.request("GET", `/api/v1/agents/${id}`);
42983
43059
  }
43060
+ importSession(input) {
43061
+ return this.request("POST", "/api/v1/todos/import", input, 120000);
43062
+ }
42984
43063
  addMessage(projectId, content, agentSettings, todoId, attachments, scheduledTimestamp, groupTag, groupName) {
42985
43064
  const payload = { content, agentSettings, attachments: attachments || [] };
42986
43065
  if (todoId)
@@ -43055,6 +43134,19 @@ class ApiClient {
43055
43134
  throw new Error(`API GET /resources/show failed: ${res.status} ${await res.text()}`);
43056
43135
  return res.json();
43057
43136
  }
43137
+ async shareShow(todoId, key) {
43138
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show/${encodeURIComponent(todoId)}/${encodeURIComponent(key)}/share`;
43139
+ const res = await fetch(url, { method: "POST", headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
43140
+ if (!res.ok)
43141
+ throw new Error(`API POST /resources/show/share failed: ${res.status} ${await res.text()}`);
43142
+ return res.json();
43143
+ }
43144
+ async unshareShow(todoId, key) {
43145
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show/${encodeURIComponent(todoId)}/${encodeURIComponent(key)}/share`;
43146
+ const res = await fetch(url, { method: "DELETE", headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
43147
+ if (!res.ok)
43148
+ throw new Error(`API DELETE /resources/show/share failed: ${res.status} ${await res.text()}`);
43149
+ }
43058
43150
  async removeShow(todoId, key) {
43059
43151
  const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show/${encodeURIComponent(todoId)}/${encodeURIComponent(key)}`;
43060
43152
  const res = await fetch(url, {
@@ -43232,7 +43324,7 @@ function writeCredential(apiUrl, apiKey) {
43232
43324
  }
43233
43325
 
43234
43326
  // ../packages/shared-api/src/frontend-ws.ts
43235
- var log = (level, ...args) => console.log(`[frontend-ws:${level}]`, ...args);
43327
+ var log2 = (level, ...args) => console.log(`[frontend-ws:${level}]`, ...args);
43236
43328
 
43237
43329
  class FrontendWebSocket {
43238
43330
  apiUrl;
@@ -43314,7 +43406,7 @@ class FrontendWebSocket {
43314
43406
  resolve(false);
43315
43407
  });
43316
43408
  sock.on("error", (err) => {
43317
- log("error", "WS error:", err.message);
43409
+ log2("error", "WS error:", err.message);
43318
43410
  if (current())
43319
43411
  this.connected = false;
43320
43412
  resolve(false);
@@ -43597,6 +43689,11 @@ function qualifiedModelIds(listing, filter) {
43597
43689
  return q ? ids.filter((id) => id.toLowerCase().includes(q)) : ids;
43598
43690
  }
43599
43691
  // ../packages/shared-fbe/src/billing.ts
43692
+ var HOBBY_ALLOWED = new Set([
43693
+ "anthropic/claude-sonnet-5.5",
43694
+ "anthropic/claude-sonnet-5",
43695
+ "anthropic/claude-haiku-4.5"
43696
+ ]);
43600
43697
  var USAGE_WINDOWS = {
43601
43698
  SESSION_MS: 5 * 60 * 60 * 1000,
43602
43699
  WEEK_MS: 7 * 24 * 60 * 60 * 1000,
@@ -43746,6 +43843,75 @@ var NON_SHELL_TYPES = new Set([
43746
43843
  "IOS" /* IOS */
43747
43844
  ]);
43748
43845
  // ../packages/shared-fbe/src/blocks/index.ts
43846
+ var ToolType;
43847
+ ((ToolType2) => {
43848
+ ToolType2["Read"] = "read";
43849
+ ToolType2["Create"] = "create";
43850
+ ToolType2["Write"] = "write";
43851
+ ToolType2["Edit"] = "edit";
43852
+ ToolType2["ModifyFile"] = "modify_file";
43853
+ ToolType2["Bash"] = "bash";
43854
+ ToolType2["Grep"] = "grep";
43855
+ ToolType2["List"] = "list";
43856
+ ToolType2["Skill"] = "skill";
43857
+ ToolType2["ApiGetCurrentContext"] = "api_get_current_context";
43858
+ ToolType2["ApiGetTodo"] = "api_get_todo";
43859
+ ToolType2["ApiAddTodoMessage"] = "api_add_todo_message";
43860
+ ToolType2["ApiUpdateTodoStatus"] = "api_update_todo_status";
43861
+ ToolType2["ApiDeleteTodo"] = "api_delete_todo";
43862
+ ToolType2["ApiListProjects"] = "api_list_projects";
43863
+ ToolType2["ApiGetProject"] = "api_get_project";
43864
+ ToolType2["ApiCreateProject"] = "api_create_project";
43865
+ ToolType2["ApiListProjectTodos"] = "api_list_project_todos";
43866
+ ToolType2["ApiCreateTodo"] = "api_create_todo";
43867
+ ToolType2["ApiListAgents"] = "api_list_agents";
43868
+ ToolType2["ApiGetAgent"] = "api_get_agent";
43869
+ ToolType2["ApiUpdateAgent"] = "api_update_agent";
43870
+ ToolType2["ApiListBusinessContexts"] = "api_list_business_contexts";
43871
+ ToolType2["ApiGetContextItem"] = "api_get_context_item";
43872
+ ToolType2["ApiCreateContextItem"] = "api_create_context_item";
43873
+ ToolType2["ApiUpdateContextItem"] = "api_update_context_item";
43874
+ ToolType2["ApiDeleteContextItem"] = "api_delete_context_item";
43875
+ ToolType2["ApiListEdges"] = "api_list_edges";
43876
+ ToolType2["ApiGetEdge"] = "api_get_edge";
43877
+ ToolType2["ApiListMcps"] = "api_list_mcps";
43878
+ ToolType2["ApiGetUsage"] = "api_get_usage";
43879
+ ToolType2["ApiGetBalance"] = "api_get_balance";
43880
+ ToolType2["ApiGetApiSchema"] = "api_get_api_schema";
43881
+ ToolType2["ApiListBoard"] = "api_list_board";
43882
+ ToolType2["ApiUpsertGroup"] = "api_upsert_group";
43883
+ ToolType2["ApiCreateTemplate"] = "api_create_template";
43884
+ ToolType2["ApiRecommendTemplate"] = "api_recommend_template";
43885
+ ToolType2["ImageGen"] = "image_gen";
43886
+ ToolType2["HtmlSnippet"] = "html_snippet";
43887
+ ToolType2["LivePreview"] = "live_preview";
43888
+ ToolType2["OpenBrowser"] = "open_browser";
43889
+ ToolType2["Show"] = "show";
43890
+ ToolType2["Text"] = "text";
43891
+ ToolType2["Reason"] = "reason";
43892
+ ToolType2["Error"] = "error";
43893
+ ToolType2["Compact"] = "compact";
43894
+ ToolType2["Question"] = "question";
43895
+ ToolType2["Login"] = "login_prompt";
43896
+ ToolType2["BusinessOnboarding"] = "business_onboarding";
43897
+ ToolType2["RequestMcpAccess"] = "request_mcp_access";
43898
+ ToolType2["EdgeNotRunning"] = "edge_not_running";
43899
+ ToolType2["WebFetch"] = "webfetch";
43900
+ ToolType2["GoogleSearch"] = "google_search";
43901
+ ToolType2["WebSearch"] = "web_search";
43902
+ ToolType2["VaultSearch"] = "vault_search";
43903
+ ToolType2["VaultResolve"] = "vault_resolve";
43904
+ ToolType2["VaultWrite"] = "vault_write";
43905
+ ToolType2["VaultWriteBatch"] = "vault_write_batch";
43906
+ ToolType2["VaultUpdate"] = "vault_update";
43907
+ ToolType2["Explore"] = "explore";
43908
+ ToolType2["Plan"] = "plan";
43909
+ ToolType2["Review"] = "review";
43910
+ ToolType2["DeviceList"] = "device_list";
43911
+ ToolType2["Mcp"] = "mcp";
43912
+ ToolType2["NamedTool"] = "named_tool";
43913
+ })(ToolType ||= {});
43914
+ var TOOL_TYPE_VALUES = new Set(Object.values(ToolType));
43749
43915
  var USER_INPUT_BLOCK_TYPES = new Set([
43750
43916
  "question" /* Question */,
43751
43917
  "login_prompt" /* Login */,
@@ -43901,6 +44067,15 @@ function manualAnswersOf(sources) {
43901
44067
  }
43902
44068
  var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
43903
44069
  var senderLabel = (s) => s.kind === "team" ? `${s.name} · TODO for AI team` : s.kind === "member" ? `${s.name} · teammate` : `${s.name} · your agent`;
44070
+ // ../packages/shared-fbe/src/jarvisEvents.ts
44071
+ var STATE = {
44072
+ ["READY" /* READY */]: "agent ended its run and handed back; not opened yet",
44073
+ ["READY_CHECKED" /* READY_CHECKED */]: "agent ended its run and handed back; its window was opened",
44074
+ ["DONE" /* DONE */]: "done and closed",
44075
+ ["ERROR" /* ERROR */]: "failed; not opened yet",
44076
+ ["ERROR_CHECKED" /* ERROR_CHECKED */]: "failed; its window was opened",
44077
+ ["WAITING_FOR_USER" /* WAITING_FOR_USER */]: "blocked, waiting on the user's answer or approval"
44078
+ };
43904
44079
  // ../packages/shared-fbe/src/templateBody.ts
43905
44080
  var USER_HEADINGS = new Set([
43906
44081
  "your task",
@@ -44418,7 +44593,7 @@ var tool_catalog_default = {
44418
44593
  installer: "npm",
44419
44594
  label: "Gmail",
44420
44595
  summary: "Read and send Gmail from your own account.",
44421
- statusCmd: "zele whoami 2>/dev/null | grep 'email:' | grep -m1 .",
44596
+ statusCmd: "zele whoami 2>/dev/null | grep -o 'email: *[^ ]*' | tr -d ' ' | paste -sd ' ' - | grep -m1 .",
44422
44597
  loginNeedsPty: true,
44423
44598
  loginCmd: "zele login --method google",
44424
44599
  oauthUrlPattern: "(https://accounts\\.google\\.com\\S+)",
@@ -44437,7 +44612,7 @@ var tool_catalog_default = {
44437
44612
  binName: "zele",
44438
44613
  label: "Google Calendar",
44439
44614
  summary: "See your Google Calendar and book, move or cancel events.",
44440
- statusCmd: "zele whoami 2>/dev/null | grep 'email:' | grep -m1 .",
44615
+ statusCmd: "zele whoami 2>/dev/null | grep -o 'email: *[^ ]*' | tr -d ' ' | paste -sd ' ' - | grep -m1 .",
44441
44616
  loginNeedsPty: true,
44442
44617
  loginCmd: "zele login --method google",
44443
44618
  oauthUrlPattern: "(https://accounts\\.google\\.com\\S+)",
@@ -44478,6 +44653,7 @@ var tool_catalog_default = {
44478
44653
  versionCmd: "linkedin-api --version 2>/dev/null | sed -n 1p"
44479
44654
  },
44480
44655
  "figma-api": {
44656
+ preinstallCloud: true,
44481
44657
  category: "design",
44482
44658
  pkg: "@todoforai/figma-api",
44483
44659
  installer: "npm",
@@ -44612,6 +44788,20 @@ var tool_catalog_default = {
44612
44788
  ],
44613
44789
  description: "AI music generation, custom lyrics, multiple genres."
44614
44790
  },
44791
+ "tfa-suno": {
44792
+ preinstallCloud: true,
44793
+ category: "media",
44794
+ pkg: "@todoforai/suno-api",
44795
+ installer: "npm",
44796
+ label: "Suno (TODOforAI)",
44797
+ summary: "Generate music and songs from a prompt, billed to your TODOforAI credit.",
44798
+ platformAuth: true,
44799
+ versionCmd: "tfa-suno --version 2>/dev/null | sed -n 1p",
44800
+ credentialPaths: [
44801
+ "~/.config/todoforai/credentials.json"
44802
+ ],
44803
+ description: "Suno music generation via TODOforAI — no Suno key needed, billed to the user's TODOforAI credit (generate $0.12 for 2 songs, refunded if the task fails). Same commands as suno-api: `generate [prompt] [--customMode --lyrics <file> --style --title] [--instrumental] [--model V6]` polls until done and prints audio URLs; `--no-poll` + `details <taskId> [--wait]`; `stems`, `midi`, `lyrics-ts`, `replace-section`. Audio URLs expire after 14 days — download the mp3s you keep. Prefer this over suno-api unless the user has their own sunoapi.org key."
44804
+ },
44615
44805
  ntn: {
44616
44806
  preinstallCloud: true,
44617
44807
  category: "utility",
@@ -44648,7 +44838,7 @@ var tool_catalog_default = {
44648
44838
  preinstallCloud: true,
44649
44839
  label: "GitHub",
44650
44840
  summary: "Work with your GitHub repos, pull requests and issues.",
44651
- statusCmd: "gh auth status 2>&1 | grep 'Logged in to' | grep -m1 .",
44841
+ statusCmd: "gh auth status 2>&1 | grep -o 'Logged in to [^ ]* [a-z]* [^ ]*' | paste -sd ' ' - | grep -m1 .",
44652
44842
  loginCmd: "gh auth login --hostname github.com --git-protocol https",
44653
44843
  oauthUrlPattern: "(https://github\\.com/login/device\\S*)",
44654
44844
  deviceCodePattern: "one-time code:\\s*([A-Z0-9]{4}-[A-Z0-9]{4})",
@@ -44665,19 +44855,22 @@ var tool_catalog_default = {
44665
44855
  installer: "npm",
44666
44856
  label: "MCP (mcporter)",
44667
44857
  summary: "Use tools from any MCP server.",
44668
- statusCmd: "mcporter list 2>/dev/null | grep -v 'No MCP servers' | grep -m1 .",
44669
- description: "MCP servers as CLI: `mcporter list [server] [--schema]`, `mcporter call <server>.<tool> key=value` (`key=@file`), `mcporter config add <name> <cmd-or-url>`, `mcporter auth <server>`. `--help` for more.",
44858
+ statusCmd: 'mcporter config list --json 2>/dev/null | node -e \'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{const s=JSON.parse(d||"{}").servers||[];if(!s.length)process.exit(1);console.log("Configured MCP servers (one line each; run `mcporter list <name>` before first use):");for(const x of s)console.log(`- ${x.name} — ${x.description?x.description+" ":""}(${x.baseUrl?new URL(x.baseUrl).host:x.command})`)})\'',
44859
+ description: "MCP servers as CLI: `mcporter list <server>` (its tools), `mcporter call <server>.<tool> key=value` (`key=@file`), `mcporter auth <server>`, `mcporter config add <name> <cmd-or-url>`.",
44670
44860
  versionCmd: "mcporter --version 2>/dev/null | sed -n 1p",
44671
44861
  preinstallJarvis: true,
44672
44862
  preinstallCloud: true
44673
44863
  },
44674
44864
  glab: {
44865
+ preinstallCloud: true,
44866
+ cloudInstallCmd: "curl -fsSL https://gitlab.com/gitlab-org/cli/-/releases/v1.119.0/downloads/glab_1.119.0_linux_amd64.tar.gz | tar -xz -C /tmp bin/glab && install -m0755 /tmp/bin/glab /usr/local/bin/glab",
44867
+ cloudVerifyCmd: "glab --version",
44675
44868
  category: "development",
44676
44869
  pkg: "glab",
44677
44870
  installer: "binary",
44678
44871
  label: "GitLab",
44679
44872
  summary: "Work with your GitLab repos, merge requests and pipelines.",
44680
- statusCmd: "glab auth status 2>&1 | grep 'Logged in to' | grep -m1 .",
44873
+ statusCmd: "glab auth status 2>&1 | grep -o 'Logged in to [^ ]* [a-z]* [^ ]*' | paste -sd ' ' - | grep -m1 .",
44681
44874
  loginCmd: "glab auth login --device",
44682
44875
  credentialPaths: ["~/.config/glab-cli/config.yml"],
44683
44876
  description: "GitLab equivalent of `gh`.",
@@ -44703,6 +44896,7 @@ var tool_catalog_default = {
44703
44896
  canStreamLogin: true
44704
44897
  },
44705
44898
  eas: {
44899
+ preinstallCloud: true,
44706
44900
  category: "deployment",
44707
44901
  pkg: "eas-cli",
44708
44902
  installer: "npm",
@@ -44718,6 +44912,7 @@ var tool_catalog_default = {
44718
44912
  versionCmd: "eas --version 2>/dev/null | sed -n 1p"
44719
44913
  },
44720
44914
  netlify: {
44915
+ preinstallCloud: true,
44721
44916
  category: "deployment",
44722
44917
  pkg: "netlify-cli",
44723
44918
  installer: "npm",
@@ -44738,7 +44933,7 @@ var tool_catalog_default = {
44738
44933
  installer: "npm",
44739
44934
  label: "Firebase",
44740
44935
  summary: "Deploy and manage Firebase hosting, database and functions.",
44741
- statusCmd: "firebase login:list 2>&1 | grep -oiE '[[:alnum:]._%+-]+@[[:alnum:].-]+\\.[[:alpha:]]{2,}' | grep -m1 .",
44936
+ statusCmd: "firebase login:list 2>&1 | grep -oiE '[[:alnum:]._%+-]+@[[:alnum:].-]+\\.[[:alpha:]]{2,}' | sort -u | paste -sd ' ' - | grep -m1 .",
44742
44937
  loginCmd: "firebase login --no-localhost --reauth",
44743
44938
  loginNeedsPty: true,
44744
44939
  stdinPaste: true,
@@ -44778,11 +44973,12 @@ var tool_catalog_default = {
44778
44973
  credentialPaths: ["~/.config/stripe/config.toml"],
44779
44974
  description: "`stripe listen` forwards webhooks locally; `stripe get/post /v1/...` for raw API.",
44780
44975
  versionCmd: "stripe --version 2>/dev/null | sed -n 1p",
44781
- deviceCodePattern: "verification code:\\s*([A-Z0-9]{4}-[A-Z0-9]{4})",
44782
- oauthUrlPattern: "(https://access\\.stripe\\.com/stripecli/\\S+)",
44783
- canStreamLogin: true
44976
+ oauthUrlPattern: "(https://dashboard\\.stripe\\.com/stripecli/confirm_auth\\S+)"
44784
44977
  },
44785
44978
  flyctl: {
44979
+ preinstallCloud: true,
44980
+ cloudInstallCmd: "curl -fsSL https://github.com/superfly/flyctl/releases/download/v0.4.108/flyctl_0.4.108_Linux_x86_64.tar.gz | tar -xz -C /usr/local/bin flyctl",
44981
+ cloudVerifyCmd: "flyctl version",
44786
44982
  category: "cloud",
44787
44983
  pkg: "flyctl",
44788
44984
  installer: "binary",
@@ -44810,6 +45006,7 @@ var tool_catalog_default = {
44810
45006
  versionCmd: "supabase --version 2>/dev/null | sed -n 1p"
44811
45007
  },
44812
45008
  railway: {
45009
+ preinstallCloud: true,
44813
45010
  category: "cloud",
44814
45011
  pkg: "@railway/cli",
44815
45012
  installer: "npm",
@@ -44851,6 +45048,7 @@ var tool_catalog_default = {
44851
45048
  versionCmd: "shop-app --version 2>/dev/null | sed -n 1p"
44852
45049
  },
44853
45050
  "datadog-ci": {
45051
+ preinstallCloud: true,
44854
45052
  category: "monitoring",
44855
45053
  pkg: "@datadog/datadog-ci",
44856
45054
  installer: "npm",
@@ -44879,7 +45077,7 @@ var tool_catalog_default = {
44879
45077
  installer: "npm",
44880
45078
  label: "TODOforAI",
44881
45079
  summary: "Create and follow up on your TODOs, and show results in the chat.",
44882
- description: 'Manage the user\'s OWN TODOforAI tasks (not code `// TODO` comments). Delegate/spin off work: `tfa-cli "prompt"` creates a real TODO with its own agent and chat (`-n` wait for completion, `--no-watch` fire-and-forget, `--project/--agent/--model`, `--resume <id>`, `addmessage <id> "text"`). Inspect: `list [--status open]`, `--inspect <id>`. Edit any todo without opening it: `status <id> DONE|READY|ARCHIVED|POSTPONED|CANCELLED|DELETED` (close, reopen, shelve, trash — `todo restore <id>` undoes a delete) and `todo set <id|-> title=… group=… star=true schedule=<unix-ms> agent=… brand=…` (own todo fields). Both take ONE id, so a loop over `list --json | jq -r \'.items[].id\'` is how you close, retitle, refile or archive MANY todos at once; `project set|settings|groups …` (name, settings, todo groups), `agent create|update <agent> model=… sysmsg=…` (permissions/MCP/device wiring and delete are refused from an agent shell). Surface results into chat: `show <file|-> [todo-id]` (renders by mimetype; `--card <name>` via project renderer; `--alias <name>` updates in place), `open <url>` embeds a live preview. Notifications: `inbox [needs-you|messages|activity] [--unread]` reads the user\'s bell feed (what finished/failed/needs them while away), `inbox seen [tab]` marks it read; `notify "Title" "message" [--href /t/<id>]` leaves a note in the user\'s Messages tab + phone push (shown as from you); `--to <email>` reaches a member of the current project instead (`project members` lists them, 20/day) — nobody else; same text is delivered once, max 10/day to the user, use for things they must see later (not chat replies). This is the ONLY way to display a local file (image, pdf, html) in the chat: markdown `![](path.png)` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows.',
45080
+ description: 'Manage the user\'s OWN TODOforAI tasks (not code `// TODO` comments). Delegate/spin off work: `tfa-cli "prompt"` creates a real TODO with its own agent and chat (`-n` wait for completion, `--no-watch` fire-and-forget, `--project/--agent/--model`, `--resume <id>`, `addmessage <id> "text"`). Inspect: `list [--status open]`, `--inspect <id>`. Edit any todo without opening it: `status <id> DONE|READY|ARCHIVED|POSTPONED|CANCELLED|DELETED` (close, reopen, shelve, trash — `todo restore <id>` undoes a delete) and `todo set <id|-> title=… group=… star=true schedule=<unix-ms> agent=… brand=…` (own todo fields). Both take ONE id, so a loop over `list --json | jq -r \'.items[].id\'` is how you close, retitle, refile or archive MANY todos at once; `project set|settings|groups …` (name, settings, todo groups), `agent create|update <agent> model=… sysmsg=…` (permissions/MCP/device wiring and delete are refused from an agent shell). Surface results into chat: `show <file|-> [todo-id]` (renders by mimetype, private to the todo\'s readers; `--card <name>` via project renderer; `--alias <name>` updates in place, `--alias <todoId>:<name>` updates another todo\'s artifact; `show share <ref>` makes it public and prints the link, `show unshare <ref>` makes it private again), `open <url>` embeds a live preview. Pick by where the output lives: read once → plain reply; interactive, this chat only → `html_snippet`; kept/revised later → `show --alias`, then `show share` only if someone outside needs a link; format the user asked for (pptx, docx…) → `show` the file. Notifications: `inbox [needs-you|messages|activity] [--unread]` reads the user\'s bell feed (what finished/failed/needs them while away), `inbox seen [tab]` marks it read; `notify "Title" "message" [--href /t/<id>]` leaves a note in the user\'s Messages tab + phone push (shown as from you); `--to <email>` reaches a member of the current project instead (`project members` lists them, 20/day) — nobody else; same text is delivered once, max 10/day to the user, use for things they must see later (not chat replies). This is the ONLY way to display a local file (image, pdf, html) in the chat: markdown `![](path.png)` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows.',
44883
45081
  installCmd: "npm install -g @todoforai/cli",
44884
45082
  versionCmd: "tfa-cli --version 2>/dev/null | sed -n 1p",
44885
45083
  preinstallCloud: true,
@@ -45006,7 +45204,7 @@ var tool_catalog_default = {
45006
45204
  label: "Google Cloud",
45007
45205
  summary: "Manage your Google Cloud projects and services.",
45008
45206
  needsPython: true,
45009
- installCmd: `curl -fsSL https://dl.google.com/dl/cloudsdk/channels/rapid/google-cloud-sdk.tar.gz | tar -xzC "$HOME" && CLOUDSDK_PYTHON="$py" "$HOME/google-cloud-sdk/install.sh" --quiet --install-python false --usage-reporting false --path-update false --command-completion false && mkdir -p "$HOME/.todoforai/tools/bin" && rm -f "$HOME/.todoforai/tools/bin/gcloud" && printf '#!/bin/sh\\nCLOUDSDK_PYTHON="\${CLOUDSDK_PYTHON:-%s}" exec "$HOME/google-cloud-sdk/bin/gcloud" "$@"\\n' "$py" > "$HOME/.todoforai/tools/bin/gcloud" && chmod +x "$HOME/.todoforai/tools/bin/gcloud"`,
45207
+ installCmd: `case "$(uname -s)-$(uname -m)" in Darwin-arm64) p=darwin-arm;; Darwin-*) p=darwin-x86_64;; Linux-aarch64|Linux-arm64) p=linux-arm;; *) p=linux-x86_64;; esac && tmp="$HOME/.gcloud-new.$$" && rm -rf "$tmp" && mkdir -p "$tmp" && curl -fsSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-$p.tar.gz" | tar -xzC "$tmp" && rm -rf "$HOME/google-cloud-sdk" && mv "$tmp/google-cloud-sdk" "$HOME/google-cloud-sdk" && rm -rf "$tmp" && mkdir -p "$HOME/.todoforai/tools/bin" && rm -f "$HOME/.todoforai/tools/bin/gcloud" && printf '#!/bin/sh\\nCLOUDSDK_PYTHON="\${CLOUDSDK_PYTHON:-%s}" exec "$HOME/google-cloud-sdk/bin/gcloud" "$@"\\n' "$py" > "$HOME/.todoforai/tools/bin/gcloud" && chmod +x "$HOME/.todoforai/tools/bin/gcloud"`,
45010
45208
  statusCmd: `gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | tr -d '\\r' | grep -m1 .`,
45011
45209
  loginCmd: "gcloud auth login --no-launch-browser",
45012
45210
  loginNeedsPty: true,
@@ -45015,8 +45213,7 @@ var tool_catalog_default = {
45015
45213
  credentialPaths: ["~/.config/gcloud"],
45016
45214
  description: "Official Google Cloud CLI. `gcloud auth login --no-launch-browser` prints the OAuth URL and waits for the pasted verification code.",
45017
45215
  versionCmd: "gcloud --version 2>/dev/null | sed -n 1p",
45018
- uninstallCmd: 'rm -rf "$HOME/google-cloud-sdk" "$HOME/.todoforai/tools/bin/gcloud"; hash -r 2>/dev/null; ! command -v gcloud >/dev/null 2>&1 || { echo "gcloud is still on PATH: $(command -v gcloud)"; exit 1; }',
45019
- updateCmd: "gcloud components update --quiet"
45216
+ uninstallCmd: 'rm -rf "$HOME/google-cloud-sdk" "$HOME/.todoforai/tools/bin/gcloud"; hash -r 2>/dev/null; ! command -v gcloud >/dev/null 2>&1 || { echo "gcloud is still on PATH: $(command -v gcloud)"; exit 1; }'
45020
45217
  },
45021
45218
  az: {
45022
45219
  category: "cloud",
@@ -45150,19 +45347,6 @@ var tool_catalog_default = {
45150
45347
  },
45151
45348
  preinstallJarvis: true
45152
45349
  },
45153
- "todoforai-browser": {
45154
- category: "development",
45155
- pkg: "@todoforai/browser",
45156
- aliases: ["tfa-browser"],
45157
- installer: "npm",
45158
- preinstallCloud: true,
45159
- label: "Browser (Extension)",
45160
- summary: "Drive your own browser through the TODOforAI extension.",
45161
- versionCmd: `node -p "require(require('fs').realpathSync(require('child_process').execSync('command -v todoforai-browser',{encoding:'utf8'}).trim()).replace(/\\/dist\\/index\\.js$/, '/package.json')).version" 2>/dev/null`,
45162
- internal: true,
45163
- preinstallJarvis: true,
45164
- description: "Browser automation via extension (non-headless), web scraping, accessibility tree snapshots, screenshots, PDF generation"
45165
- },
45166
45350
  "tfa-explore": {
45167
45351
  preinstallCloud: true,
45168
45352
  category: "development",
@@ -45633,6 +45817,21 @@ var tool_catalog_default = {
45633
45817
  description: "WordPress REST API CLI. `wordpress-api auth --site https://example.com --user <user> --password '<application password>'` (application password, not the login password). Reading published content needs no auth. `posts list|get|create|update|delete`, `pages`, `media`, `categories`. `posts create` saves a draft unless --status publish. On Elementor/JetEngine sites content.rendered comes back empty — fetch the post's public URL for the body.",
45634
45818
  versionCmd: "wordpress-api --version 2>/dev/null | sed -n 1p"
45635
45819
  },
45820
+ "mailchimp-api": {
45821
+ category: "marketing",
45822
+ pkg: "@todoforai/mailchimp-api",
45823
+ installer: "npm",
45824
+ label: "Mailchimp",
45825
+ summary: "Manage Mailchimp audiences, contacts, email campaigns and reports.",
45826
+ loginCmd: "mailchimp-api auth --api-key <api_key>",
45827
+ oauthBroker: true,
45828
+ statusCmd: `mailchimp-api whoami 2>/dev/null | sed -n 's/.*"account_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m1 .`,
45829
+ credentialPaths: [
45830
+ "~/.config/mailchimp-api/credentials.json"
45831
+ ],
45832
+ versionCmd: "mailchimp-api --version 2>/dev/null | sed -n 1p",
45833
+ description: "Independent CLI over the official Mailchimp Marketing API: 155 endpoint commands for lists (email audiences), members (contacts; email or MD5), tags, segments, merge fields, interests, notes/events, campaigns/content/tests/scheduling, templates, reports, assets, landing pages, classic automations, journey triggers, webhooks and batches. JSON output; --data JSON|@file|-; --schema; --all pagination. Every write requires --yes; use --dry-run first. Raw request METHOD /path covers other Marketing endpoints. API-key auth, not OAuth onboarding. Plan/API restrictions apply; no Transactional/Mandrill or Open Commerce. Run --help for limits and consent cautions."
45834
+ },
45636
45835
  "facebook-pages-api": {
45637
45836
  preinstallCloud: true,
45638
45837
  category: "social",
@@ -45647,8 +45846,10 @@ var tool_catalog_default = {
45647
45846
  versionCmd: "facebook-pages-api --version 2>/dev/null | sed -n 1p"
45648
45847
  },
45649
45848
  "ews-api": {
45849
+ preinstallCloud: true,
45650
45850
  category: "email",
45651
45851
  pkg: "ews-api",
45852
+ packages: ["ews-api==0.2.0"],
45652
45853
  installer: "pip",
45653
45854
  label: "Exchange (EWS)",
45654
45855
  summary: "Read and write mail on a company Exchange server.",
@@ -45676,6 +45877,7 @@ var tool_catalog_default = {
45676
45877
  canStreamLogin: true
45677
45878
  },
45678
45879
  neonctl: {
45880
+ preinstallCloud: true,
45679
45881
  category: "database",
45680
45882
  pkg: "neonctl",
45681
45883
  installer: "npm",
@@ -45698,7 +45900,7 @@ var tool_catalog_default = {
45698
45900
  label: "Microsoft 365",
45699
45901
  summary: "Work with Microsoft 365 mail, Teams, SharePoint and users.",
45700
45902
  statusCmd: `m365 status --output text 2>/dev/null | tr -d '"' | grep -vi 'logged out' | grep -m1 .`,
45701
- loginCmd: "m365 login --authType deviceCode --appId 04b07795-8ddb-461a-bbee-02f9e1bf7b46",
45903
+ loginCmd: "m365 login --authType deviceCode --appId 14d82eec-204b-4c2f-b7e8-296a70dab67e",
45702
45904
  deviceCodePattern: "enter the code ([A-Z0-9]{8,}) to authenticate",
45703
45905
  oauthUrlPattern: "open the page (https://\\S+) and enter",
45704
45906
  credentialPaths: ["~/.cli-m365-msal.json", "~/.cli-m365-connection.json"],
@@ -45737,6 +45939,7 @@ var tool_catalog_default = {
45737
45939
  versionCmd: "posthog-cli --version 2>/dev/null | sed -n 1p"
45738
45940
  },
45739
45941
  canva: {
45942
+ preinstallCloud: true,
45740
45943
  category: "design",
45741
45944
  pkg: "@canva/cli",
45742
45945
  installer: "npm",
@@ -45752,6 +45955,7 @@ var tool_catalog_default = {
45752
45955
  oauthUrlPattern: "OPEN_URL: (https://www\\.canva\\.com/api/oauth/authorize\\S+)"
45753
45956
  },
45754
45957
  webflow: {
45958
+ preinstallCloud: true,
45755
45959
  category: "development",
45756
45960
  pkg: "@webflow/webflow-cli",
45757
45961
  installer: "npm",
@@ -45766,6 +45970,7 @@ var tool_catalog_default = {
45766
45970
  versionCmd: "webflow --version 2>/dev/null | sed -n 1p"
45767
45971
  },
45768
45972
  readwise: {
45973
+ preinstallCloud: true,
45769
45974
  category: "productivity",
45770
45975
  pkg: "@readwise/cli",
45771
45976
  installer: "npm",
@@ -45836,6 +46041,7 @@ var tool_catalog_default = {
45836
46041
  versionCmd: "exa --version 2>/dev/null | sed -n 1p"
45837
46042
  },
45838
46043
  mobbin: {
46044
+ preinstallCloud: true,
45839
46045
  category: "design",
45840
46046
  pkg: "mobbin",
45841
46047
  installer: "npm",
@@ -45866,6 +46072,7 @@ var tool_catalog_default = {
45866
46072
  versionCmd: "{ python -c 'import sys; from importlib.metadata import version; print(version(sys.argv[1]))' blender-mcp || python3 -c 'import sys; from importlib.metadata import version; print(version(sys.argv[1]))' blender-mcp; } 2>/dev/null"
45867
46073
  },
45868
46074
  "google-ads": {
46075
+ preinstallCloud: true,
45869
46076
  category: "marketing",
45870
46077
  pkg: "google-ads",
45871
46078
  packages: [
@@ -45935,6 +46142,7 @@ var model_promos_default = {
45935
46142
  { family: "gpt-5-6-sol", rate: 0.85, starts: "2026-08-26", ends: "2026-09-13" },
45936
46143
  { family: "gpt-5-6-sol", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21" },
45937
46144
  { family: "gpt-6-sol", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
46145
+ { family: "gpt-6-1-sol", rate: 0.8, starts: "2026-09-29", ends: "2026-11-21" },
45938
46146
  { family: "gpt-6-luna", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
45939
46147
  { family: "gpt-6-astra", rate: 0.85, starts: "2026-09-05", ends: "2026-09-13", boostable: false },
45940
46148
  { family: "gpt-6-astra", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21", boostable: false },
@@ -46135,7 +46343,13 @@ import { parseArgs } from "util";
46135
46343
  // package.json
46136
46344
  var package_default = {
46137
46345
  name: "@todoforai/cli",
46138
- version: "0.1.61",
46346
+ description: "TODOforAI CLI — create and manage tasks with tfa-cli.",
46347
+ homepage: "https://todofor.ai",
46348
+ repository: {
46349
+ type: "git",
46350
+ url: "git+https://github.com/todoforai/cli.git"
46351
+ },
46352
+ version: "0.1.63",
46139
46353
  type: "module",
46140
46354
  bin: {
46141
46355
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -46155,7 +46369,7 @@ var package_default = {
46155
46369
  },
46156
46370
  dependencies: {
46157
46371
  "@agentclientprotocol/sdk": "^1.4.0",
46158
- "@todoforai/update-notifier": "^0.1.0",
46372
+ "@todoforai/update-notifier": "^0.4.0",
46159
46373
  chalk: "^6.0.0",
46160
46374
  "cli-highlight": "^2.1.11",
46161
46375
  "diff-match-patch": "^1.0.5",
@@ -46197,6 +46411,7 @@ Examples:
46197
46411
  tfa-cli device list|rename|wallpaper … # Paired machines
46198
46412
  tfa-cli list [-n 30] [--cursor N] [--all] [--status S] # List todos (paginated)
46199
46413
  tfa-cli status <todo-id> <STATUS> # Update a todo's status
46414
+ tfa-cli update # Update tfa-cli itself (also auto-updates in background; NO_UPDATE_NOTIFIER=1 disables)
46200
46415
  tfa-cli delete <todo-id> # Permanently delete a todo
46201
46416
  tfa-cli addmessage <todo-id> "text" # Send a message and exit (like -r, no watch/bridge)
46202
46417
  tfa-cli show <file|-> [todo-id] # Render a local file (image/pdf/html…) in the chat; - = stdin
@@ -46208,6 +46423,7 @@ Examples:
46208
46423
  tfa-cli notify [--to <email>] "Title" "message" [--href /t/<id>] # Note to your own user, or --to a project member (Messages tab + phone push)
46209
46424
  tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> links for a project you own
46210
46425
  tfa-cli next [--direction "<text>"] # Analyzer growth recommendation cards
46426
+ tfa-cli import [claude|codex|<file>...] [--dry-run] # Import Claude Code / Codex sessions as todos
46211
46427
  tfa-cli <command> help # Details per command
46212
46428
 
46213
46429
  Options:
@@ -46247,15 +46463,27 @@ Usage:
46247
46463
  tfa-cli show <file|-> [todo-id] Render a file by mimetype (- reads stdin)
46248
46464
  [--title T] [--alias A] [--mime M] [--card <name>] [--link] [--json]
46249
46465
  --link: compact download chip instead of inline render
46250
- Prints "<todoId>:<alias|id> <public url>". Re-showing with the same
46251
- --alias updates the block in place; old versions stay at /<id>/<version>
46252
- tfa-cli show rm <ref|alias> Take it down (block, url, every version)
46466
+ Prints "<todoId>:<alias|id>". Private: visible to whoever can read the todo.
46467
+ Re-showing with the same --alias updates the block in place (new version)
46468
+ --alias <todoId>:<alias> updates that todo's block instead,
46469
+ so a later chat can revise an artifact made elsewhere
46470
+ tfa-cli show share <ref|alias> Make it public; prints its link (serves the latest version)
46471
+ tfa-cli show unshare <ref|alias> Make it private again (the link stops working)
46472
+ tfa-cli show rm <ref|alias> Take it down (block, link, every version)
46253
46473
  tfa-cli show list [todo-id] List show blocks (ref, title, mime/url, card)
46254
46474
  [--project <id>] [--card <name>] [--json]
46255
46475
  no todo-id + --project (or $TODOFORAI_PROJECT_ID) = every todo
46256
46476
  tfa-cli open <url> [todo-id] Live http(s) url as a preview [--title T] [--alias A] [--json]
46257
46477
 
46258
46478
  todo-id defaults to $TODOFORAI_TODO_ID (agent shell) or the last todo this CLI touched.
46479
+
46480
+ When to use what:
46481
+ read once, move on → plain chat reply
46482
+ interactive, this chat only → html_snippet (can exec/connect/chat back)
46483
+ keep / revise later → show <file> --alias <name> (versioned, private)
46484
+ someone outside needs it → show share <alias> (public link, revocable)
46485
+ a live web page → open <url>
46486
+ a format the user asked for → show the file (pptx, docx, xlsx, pdf…)
46259
46487
  `);
46260
46488
  }
46261
46489
  var STATUS_HELP = {
@@ -46286,7 +46514,7 @@ var WORD_FLAGS = {
46286
46514
  agents: "list-agents",
46287
46515
  config: "show-config"
46288
46516
  };
46289
- var HELP_SUBCOMMANDS = ["agent", "todo", "project", "brand", "device", "list", "ls", "status", "show", "open", "inbox", "notify"];
46517
+ var HELP_SUBCOMMANDS = ["agent", "todo", "project", "brand", "device", "list", "ls", "status", "show", "open", "inbox", "notify", "import"];
46290
46518
  function parseCliArgs() {
46291
46519
  const { values, positionals } = parseArgs({
46292
46520
  args: process.argv.slice(2),
@@ -46326,6 +46554,7 @@ function parseCliArgs() {
46326
46554
  to: { type: "string" },
46327
46555
  unread: { type: "boolean", default: false },
46328
46556
  limit: { type: "string" },
46557
+ days: { type: "string" },
46329
46558
  resume: { type: "string", short: "r" },
46330
46559
  continue: { type: "boolean", short: "c", default: false },
46331
46560
  "non-interactive": { type: "boolean", short: "n", default: false },
@@ -46698,7 +46927,7 @@ Cancelled
46698
46927
  import { resolve, basename } from "path";
46699
46928
 
46700
46929
  // src/ensure-bridge.ts
46701
- import { spawn, spawnSync } from "child_process";
46930
+ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
46702
46931
  import fs2 from "fs";
46703
46932
  import path2 from "path";
46704
46933
  import os2 from "os";
@@ -46708,7 +46937,7 @@ var INSTALL_TIMEOUT_MS = 90000;
46708
46937
  var bridgeBin = "todoforai-bridge";
46709
46938
  var bridgeBinary = () => bridgeBin;
46710
46939
  function probeBridge(bin) {
46711
- return spawnSync(bin, ["--version"], { stdio: "ignore" }).status === 0;
46940
+ return spawnSync2(bin, ["--version"], { stdio: "ignore" }).status === 0;
46712
46941
  }
46713
46942
  function hasBridge() {
46714
46943
  if (probeBridge(bridgeBin))
@@ -46723,7 +46952,7 @@ function installBridge() {
46723
46952
  if (process.platform !== "linux" && process.platform !== "darwin")
46724
46953
  return false;
46725
46954
  console.error(`\x1B[2mInstalling TODOforAI Bridge (${INSTALLER_URL})...\x1B[0m`);
46726
- const r = spawnSync("sh", ["-c", `curl -fsSL --connect-timeout 10 --max-time 30 ${INSTALLER_URL} | sh`], { stdio: ["ignore", "ignore", "inherit"], timeout: INSTALL_TIMEOUT_MS, killSignal: "SIGKILL" });
46955
+ const r = spawnSync2("sh", ["-c", `curl -fsSL --connect-timeout 10 --max-time 30 ${INSTALLER_URL} | sh`], { stdio: ["ignore", "ignore", "inherit"], timeout: INSTALL_TIMEOUT_MS, killSignal: "SIGKILL" });
46727
46956
  if (r.error?.code === "ETIMEDOUT")
46728
46957
  console.error("\x1B[33mBridge install timed out\x1B[0m");
46729
46958
  return r.status === 0 && hasBridge();
@@ -46776,7 +47005,7 @@ function bridgeWhoamiArgs(apiUrl) {
46776
47005
  return withProfile(["whoami"], apiUrl);
46777
47006
  }
46778
47007
  function bridgeDeviceId(apiUrl) {
46779
- const r = spawnSync(bridgeBin, bridgeWhoamiArgs(apiUrl), { encoding: "utf-8" });
47008
+ const r = spawnSync2(bridgeBin, bridgeWhoamiArgs(apiUrl), { encoding: "utf-8" });
46780
47009
  if (r.status !== 0)
46781
47010
  return null;
46782
47011
  const m = (r.stdout || "").match(/^Device:.*\(id:\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)/im);
@@ -46802,11 +47031,11 @@ async function enrollBridgeWithApiKey(apiUrl, apiKey) {
46802
47031
  console.error(`\x1B[33mBridge enroll: mint failed (${e?.message || e})\x1B[0m`);
46803
47032
  return false;
46804
47033
  }
46805
- const login = spawnSync(bridgeBin, [...bridgeLoginArgs(apiUrl), "--token", token], { stdio: ["ignore", "ignore", "inherit"], timeout: 30000, killSignal: "SIGKILL" });
47034
+ const login = spawnSync2(bridgeBin, [...bridgeLoginArgs(apiUrl), "--token", token], { stdio: ["ignore", "ignore", "inherit"], timeout: 30000, killSignal: "SIGKILL" });
46806
47035
  return login.status === 0;
46807
47036
  }
46808
47037
  function ensureBridgeCredentials(apiUrl, opts = {}) {
46809
- const whoami = spawnSync(bridgeBin, bridgeWhoamiArgs(apiUrl), { stdio: "ignore" });
47038
+ const whoami = spawnSync2(bridgeBin, bridgeWhoamiArgs(apiUrl), { stdio: "ignore" });
46810
47039
  if (whoami.status === 0)
46811
47040
  return true;
46812
47041
  if (opts.interactive === false) {
@@ -46814,7 +47043,7 @@ function ensureBridgeCredentials(apiUrl, opts = {}) {
46814
47043
  return false;
46815
47044
  }
46816
47045
  console.error("\x1B[2mBridge credentials not found. Starting `todoforai-bridge login`...\x1B[0m");
46817
- const login = spawnSync(bridgeBin, bridgeLoginArgs(apiUrl), { stdio: "inherit" });
47046
+ const login = spawnSync2(bridgeBin, bridgeLoginArgs(apiUrl), { stdio: "inherit" });
46818
47047
  return login.status === 0;
46819
47048
  }
46820
47049
  async function bridgeState(apiUrl, apiKey, deviceId) {
@@ -46840,7 +47069,7 @@ async function ensureBridgeRunning(apiUrl, apiKey, opts = {}) {
46840
47069
  console.error(`\x1B[2mBridge not started: \`todoforai-bridge\` was not found on PATH. Install it (curl -fsSL ${INSTALLER_URL} | sh), or pass --no-bridge (or deprecated --no-edge) to silence this.\x1B[0m`);
46841
47070
  return false;
46842
47071
  }
46843
- const hasCreds = spawnSync(bridgeBin, bridgeWhoamiArgs(apiUrl), { stdio: "ignore" }).status === 0 || await enrollBridgeWithApiKey(apiUrl, apiKey) || ensureBridgeCredentials(apiUrl, opts);
47072
+ const hasCreds = spawnSync2(bridgeBin, bridgeWhoamiArgs(apiUrl), { stdio: "ignore" }).status === 0 || await enrollBridgeWithApiKey(apiUrl, apiKey) || ensureBridgeCredentials(apiUrl, opts);
46844
47073
  if (!hasCreds) {
46845
47074
  console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
46846
47075
  return false;
@@ -46857,7 +47086,7 @@ async function ensureBridgeRunning(apiUrl, apiKey, opts = {}) {
46857
47086
  fs2.mkdirSync(logDir, { recursive: true });
46858
47087
  const logFile = path2.join(logDir, "bridge.log");
46859
47088
  const out = fs2.openSync(logFile, "a");
46860
- const child = spawn(bridgeBin, bridgeRunArgs(apiUrl), {
47089
+ const child = spawn2(bridgeBin, bridgeRunArgs(apiUrl), {
46861
47090
  detached: true,
46862
47091
  stdio: ["ignore", out, out]
46863
47092
  });
@@ -47091,7 +47320,7 @@ class ScopedConfig {
47091
47320
  }
47092
47321
  }
47093
47322
  // src/device-login.ts
47094
- import { spawn as spawn2 } from "child_process";
47323
+ import { spawn as spawn3 } from "child_process";
47095
47324
  class DeviceLoginError extends Error {
47096
47325
  }
47097
47326
  async function runDeviceLogin(apiUrl) {
@@ -47109,7 +47338,7 @@ async function runDeviceLogin(apiUrl) {
47109
47338
  `);
47110
47339
  try {
47111
47340
  const [cmd, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : [process.platform === "darwin" ? "open" : "xdg-open", [url]];
47112
- const child = spawn2(cmd, args, { stdio: "ignore", detached: true });
47341
+ const child = spawn3(cmd, args, { stdio: "ignore", detached: true });
47113
47342
  child.on("error", () => {});
47114
47343
  child.unref();
47115
47344
  } catch {}
@@ -48308,7 +48537,7 @@ var createBuilder = (self, _styler, _isEmpty) => {
48308
48537
  return applyStyle(builder, "" + arguments_[0]);
48309
48538
  }
48310
48539
  if (arguments_.length === 2) {
48311
- return applyStyle(builder, arguments_[0] + " " + arguments_[1]);
48540
+ return applyStyle(builder, `${arguments_[0] ?? ""} ${arguments_[1] ?? ""}`);
48312
48541
  }
48313
48542
  return applyStyle(builder, arguments_.join(" "));
48314
48543
  };
@@ -50707,11 +50936,442 @@ ${message}`).digest("hex").slice(0, 16);
50707
50936
  `);
50708
50937
  }
50709
50938
 
50939
+ // src/import-command.ts
50940
+ import { statSync as statSync3 } from "fs";
50941
+ import { basename as basename3, dirname as dirname3, resolve as resolve4 } from "path";
50942
+
50943
+ // src/import/claude.ts
50944
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3, statSync } from "fs";
50945
+ import { homedir as homedir3 } from "os";
50946
+ import { join as join3 } from "path";
50947
+
50948
+ // src/import/common.ts
50949
+ import { createHash as createHash2 } from "crypto";
50950
+ var NOTE_MAX_LEN = 2000;
50951
+ var TOOL_RESULT_MAX_LEN = 4000;
50952
+ var SESSION_TITLE_MAX_LEN = 120;
50953
+ var clip = (s, max) => s.length > max ? `${s.slice(0, max)}…` : s;
50954
+ var hashContent = (s) => createHash2("sha256").update(s).digest("hex");
50955
+ function noteToolCall(name, input) {
50956
+ const lines = [`[external_agent_tool_call: ${typeof name === "string" ? name : "tool"}]`];
50957
+ const inp = input && typeof input === "object" ? input : null;
50958
+ let shown = false;
50959
+ if (inp) {
50960
+ for (const [label, keys] of [["description", ["description"]], ["command", ["command", "cmd"]], ["file", ["file_path", "path", "notebook_path"]]]) {
50961
+ const v = keys.map((k) => inp[k]).find((x) => typeof x === "string" && x);
50962
+ if (v) {
50963
+ lines.push(`${label}: ${clip(v, NOTE_MAX_LEN)}`);
50964
+ shown = true;
50965
+ }
50966
+ }
50967
+ }
50968
+ if (!shown && input !== undefined)
50969
+ lines.push(`input: ${clip(typeof input === "string" ? input : JSON.stringify(input), NOTE_MAX_LEN)}`);
50970
+ lines.push("[/external_agent_tool_call]");
50971
+ return lines.join(`
50972
+ `);
50973
+ }
50974
+ function noteToolResult(output, isError) {
50975
+ const head = isError ? "[external_agent_tool_result: error]" : "[external_agent_tool_result]";
50976
+ return `${head}
50977
+ ${clip(output.trim(), TOOL_RESULT_MAX_LEN)}
50978
+ [/external_agent_tool_result]`;
50979
+ }
50980
+ function titleFromMessages(messages, fallback = "Imported session") {
50981
+ const first = messages.find((m) => m.role === "user" && !m.content.startsWith("[external_agent_tool"))?.content ?? "";
50982
+ const line = first.split(`
50983
+ `).map((l) => l.trim()).find(Boolean) ?? "";
50984
+ return line ? clip(line, SESSION_TITLE_MAX_LEN) : fallback;
50985
+ }
50986
+
50987
+ // src/import/claude.ts
50988
+ var claudeHome = () => process.env.CLAUDE_CONFIG_DIR || join3(homedir3(), ".claude");
50989
+ function listClaudeSessionFiles(home = claudeHome()) {
50990
+ const root = join3(home, "projects");
50991
+ if (!existsSync2(root))
50992
+ return [];
50993
+ const files = [];
50994
+ for (const slug of readdirSync(root, { withFileTypes: true })) {
50995
+ if (!slug.isDirectory())
50996
+ continue;
50997
+ const dir = join3(root, slug.name);
50998
+ for (const f of readdirSync(dir))
50999
+ if (f.endsWith(".jsonl"))
51000
+ files.push(join3(dir, f));
51001
+ }
51002
+ return files;
51003
+ }
51004
+ var parseTs = (r) => {
51005
+ if (typeof r.timestamp === "string") {
51006
+ const t = Date.parse(r.timestamp);
51007
+ if (!Number.isNaN(t))
51008
+ return t;
51009
+ }
51010
+ if (typeof r.timestamp_ms === "number")
51011
+ return r.timestamp_ms;
51012
+ return;
51013
+ };
51014
+ var stripUserQuery = (s) => {
51015
+ const m = s.match(/^\s*<user_query>\s*([\s\S]*?)\s*<\/user_query>\s*$/);
51016
+ return m ? m[1] : s;
51017
+ };
51018
+ var isHarnessUserText = (s) => /^\s*<(command-name|local-command-caveat|local-command-stdout|local-command-stderr|system-reminder)>/.test(s);
51019
+ function flattenContent(content) {
51020
+ if (typeof content === "string")
51021
+ return { text: content, onlyToolResults: false };
51022
+ if (!Array.isArray(content))
51023
+ return { text: "", onlyToolResults: false };
51024
+ const parts = [];
51025
+ let toolResults = 0;
51026
+ for (const b of content) {
51027
+ if (!b || typeof b !== "object")
51028
+ continue;
51029
+ switch (b.type) {
51030
+ case "text":
51031
+ if (typeof b.text === "string" && b.text.trim())
51032
+ parts.push(b.text);
51033
+ break;
51034
+ case "tool_use":
51035
+ parts.push(noteToolCall(b.name, b.input));
51036
+ break;
51037
+ case "tool_result": {
51038
+ toolResults++;
51039
+ const inner = typeof b.content === "string" ? b.content : Array.isArray(b.content) ? b.content.map((c3) => c3?.type === "text" ? c3.text : `[${c3?.type ?? "block"}]`).join(`
51040
+ `) : "";
51041
+ parts.push(noteToolResult(inner, b.is_error === true));
51042
+ break;
51043
+ }
51044
+ case "thinking":
51045
+ case "redacted_thinking":
51046
+ break;
51047
+ default:
51048
+ parts.push(`[external unsupported block: ${b.type}]`);
51049
+ }
51050
+ }
51051
+ return { text: parts.join(`
51052
+
51053
+ `), onlyToolResults: toolResults > 0 && toolResults === content.length };
51054
+ }
51055
+ function parseClaudeSession(path3) {
51056
+ const raw = readFileSync3(path3, "utf8");
51057
+ const messages = [];
51058
+ let cwd;
51059
+ let customTitle;
51060
+ let aiTitle;
51061
+ for (const line of raw.split(`
51062
+ `)) {
51063
+ if (!line.trim())
51064
+ continue;
51065
+ let r;
51066
+ try {
51067
+ r = JSON.parse(line);
51068
+ } catch {
51069
+ continue;
51070
+ }
51071
+ if (!cwd && typeof r.cwd === "string" && r.cwd)
51072
+ cwd = r.cwd;
51073
+ if (r.type === "custom-title" && typeof r.customTitle === "string")
51074
+ customTitle = r.customTitle;
51075
+ if (r.type === "ai-title" && typeof r.aiTitle === "string")
51076
+ aiTitle = r.aiTitle;
51077
+ if (r.type !== "user" && r.type !== "assistant")
51078
+ continue;
51079
+ if (r.isMeta === true || r.isSidechain === true)
51080
+ continue;
51081
+ const { text, onlyToolResults } = flattenContent(r.message?.content);
51082
+ const role = r.type === "user" && !onlyToolResults ? "user" : "assistant";
51083
+ const content = (role === "user" ? stripUserQuery(text) : text).trim();
51084
+ if (!content || role === "user" && isHarnessUserText(content))
51085
+ continue;
51086
+ messages.push({ role, content, timestamp: parseTs(r) });
51087
+ }
51088
+ if (!cwd || !messages.some((m) => m.role === "user"))
51089
+ return null;
51090
+ const st = statSync(path3);
51091
+ return {
51092
+ kind: "claude",
51093
+ path: path3,
51094
+ cwd,
51095
+ messages,
51096
+ title: customTitle?.trim() || aiTitle?.trim() || titleFromMessages(messages),
51097
+ sha256: hashContent(raw),
51098
+ modifiedAt: st.mtimeMs
51099
+ };
51100
+ }
51101
+
51102
+ // src/import/codex.ts
51103
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
51104
+ import { homedir as homedir4 } from "os";
51105
+ import { join as join4 } from "path";
51106
+ var codexHome = () => process.env.CODEX_HOME || join4(homedir4(), ".codex");
51107
+ function listCodexSessionFiles(home = codexHome()) {
51108
+ const root = join4(home, "sessions");
51109
+ if (!existsSync3(root))
51110
+ return [];
51111
+ const out = [];
51112
+ const walk = (dir) => {
51113
+ for (const e of readdirSync2(dir, { withFileTypes: true })) {
51114
+ const p = join4(dir, e.name);
51115
+ if (e.isDirectory())
51116
+ walk(p);
51117
+ else if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl"))
51118
+ out.push(p);
51119
+ }
51120
+ };
51121
+ walk(root);
51122
+ return out;
51123
+ }
51124
+ function readCodexTitles(home = codexHome()) {
51125
+ const titles = new Map;
51126
+ const idx = join4(home, "session_index.jsonl");
51127
+ if (!existsSync3(idx))
51128
+ return titles;
51129
+ for (const line of readFileSync4(idx, "utf8").split(`
51130
+ `)) {
51131
+ try {
51132
+ const r = JSON.parse(line);
51133
+ if (r?.id && typeof r.thread_name === "string")
51134
+ titles.set(r.id, r.thread_name);
51135
+ } catch {}
51136
+ }
51137
+ return titles;
51138
+ }
51139
+ var HARNESS_TAGS = ["environment_context", "recommended_plugins", "user_instructions", "permissions_instructions", "turn_aborted", "collaboration_mode_instructions"];
51140
+ var isHarnessBlock = (s) => {
51141
+ const m = /^\s*<([a-z_]+)>[\s\S]*<\/\1>\s*$/.exec(s);
51142
+ return !!m && HARNESS_TAGS.includes(m[1]);
51143
+ };
51144
+ var partsText = (content, dropHarness = false) => Array.isArray(content) ? content.map((c3) => typeof c3?.text === "string" ? c3.text : "").filter((t) => t.trim() && !(dropHarness && isHarnessBlock(t))).join(`
51145
+
51146
+ `) : typeof content === "string" ? content : "";
51147
+ var outputText = (output) => {
51148
+ if (typeof output === "string") {
51149
+ try {
51150
+ const parsed = JSON.parse(output);
51151
+ if (Array.isArray(parsed))
51152
+ return partsText(parsed);
51153
+ } catch {}
51154
+ return output;
51155
+ }
51156
+ return partsText(output);
51157
+ };
51158
+ function parseCodexSession(path3, titles = readCodexTitles()) {
51159
+ const raw = readFileSync4(path3, "utf8");
51160
+ const messages = [];
51161
+ let cwd;
51162
+ let sessionId;
51163
+ for (const line of raw.split(`
51164
+ `)) {
51165
+ if (!line.trim())
51166
+ continue;
51167
+ let r;
51168
+ try {
51169
+ r = JSON.parse(line);
51170
+ } catch {
51171
+ continue;
51172
+ }
51173
+ const ts = typeof r.timestamp === "string" ? Date.parse(r.timestamp) : NaN;
51174
+ const timestamp = Number.isNaN(ts) ? undefined : ts;
51175
+ if (r.type === "session_meta") {
51176
+ cwd = cwd || r.payload?.cwd;
51177
+ sessionId = sessionId || r.payload?.session_id || r.payload?.id;
51178
+ continue;
51179
+ }
51180
+ if (r.type !== "response_item")
51181
+ continue;
51182
+ const p = r.payload ?? {};
51183
+ switch (p.type) {
51184
+ case "message": {
51185
+ if (p.role !== "user" && p.role !== "assistant")
51186
+ break;
51187
+ const content = partsText(p.content, p.role === "user").trim();
51188
+ if (content)
51189
+ messages.push({ role: p.role, content, timestamp });
51190
+ break;
51191
+ }
51192
+ case "function_call":
51193
+ case "custom_tool_call": {
51194
+ let input = p.arguments ?? p.input;
51195
+ if (typeof input === "string") {
51196
+ try {
51197
+ input = JSON.parse(input);
51198
+ } catch {}
51199
+ }
51200
+ messages.push({ role: "assistant", content: noteToolCall(p.name, input), timestamp });
51201
+ break;
51202
+ }
51203
+ case "function_call_output":
51204
+ case "custom_tool_call_output":
51205
+ messages.push({ role: "assistant", content: noteToolResult(outputText(p.output), false), timestamp });
51206
+ break;
51207
+ default:
51208
+ break;
51209
+ }
51210
+ }
51211
+ if (!cwd || !messages.some((m) => m.role === "user"))
51212
+ return null;
51213
+ const st = statSync2(path3);
51214
+ return {
51215
+ kind: "codex",
51216
+ path: path3,
51217
+ cwd,
51218
+ messages,
51219
+ title: sessionId && titles.get(sessionId)?.trim() || titleFromMessages(messages),
51220
+ sha256: hashContent(raw),
51221
+ modifiedAt: st.mtimeMs
51222
+ };
51223
+ }
51224
+
51225
+ // src/import-command.ts
51226
+ var DEFAULT_MAX_AGE_DAYS = 30;
51227
+ var DEFAULT_MAX_COUNT = 50;
51228
+ var SOURCES = ["claude", "codex"];
51229
+ function printImportHelp() {
51230
+ process.stderr.write(`
51231
+ tfa-cli import — import Claude Code / Codex sessions as todos
51232
+
51233
+ Usage:
51234
+ tfa-cli import [claude|codex] [flags]
51235
+ tfa-cli import <session.jsonl>... [flags]
51236
+
51237
+ Flags:
51238
+ --project <id> Target project (default: current default project)
51239
+ -a, --agent <id|name> Agent for every imported todo (default: match the session cwd, else your default agent)
51240
+ --days <n> Only sessions modified in the last n days (default: ${DEFAULT_MAX_AGE_DAYS}; 0 = all)
51241
+ --limit <n> Max sessions per source, newest first (default: ${DEFAULT_MAX_COUNT}; 0 = all)
51242
+ --dry-run Parse and list, write nothing (no login needed)
51243
+ --json Machine-readable output
51244
+ -h, --help Show this help
51245
+
51246
+ Sources on this machine:
51247
+ Claude Code ${claudeHome()}/projects/*/*.jsonl
51248
+ Codex ${codexHome()}/sessions/**/rollout-*.jsonl
51249
+ `);
51250
+ }
51251
+ var codexTitles;
51252
+ function safeParse(kind, file) {
51253
+ try {
51254
+ return kind === "claude" ? parseClaudeSession(file) : parseCodexSession(file, codexTitles ??= readCodexTitles());
51255
+ } catch {
51256
+ return null;
51257
+ }
51258
+ }
51259
+ function collect(kinds, days, limit) {
51260
+ const cutoff = days > 0 ? Date.now() - days * 86400000 : 0;
51261
+ return kinds.flatMap((kind) => {
51262
+ const files = kind === "claude" ? listClaudeSessionFiles() : listCodexSessionFiles();
51263
+ const sessions = files.map((f) => ({ f, mtime: statSync3(f).mtimeMs })).filter((x) => x.mtime >= cutoff).sort((a, b) => b.mtime - a.mtime).map((x) => safeParse(kind, x.f)).filter((s) => !!s);
51264
+ return limit > 0 ? sessions.slice(0, limit) : sessions;
51265
+ });
51266
+ }
51267
+ async function agentForCwd(api, cwd, fallback, cache) {
51268
+ for (let dir = cwd;; dir = dirname3(dir)) {
51269
+ const hit = cache.get(dir);
51270
+ if (hit)
51271
+ return hit;
51272
+ const matches = await api.listAgentSettings({ workspacePath: dir }).catch(() => []);
51273
+ if (matches.length) {
51274
+ cache.set(dir, matches[0].id);
51275
+ return matches[0].id;
51276
+ }
51277
+ if (dirname3(dir) === dir)
51278
+ break;
51279
+ }
51280
+ cache.set(cwd, fallback);
51281
+ return fallback;
51282
+ }
51283
+ async function importCommand(api, positionals, args, scopedProject, defaultAgentId) {
51284
+ const days = args.days !== undefined ? Number(args.days) : DEFAULT_MAX_AGE_DAYS;
51285
+ const limit = args.limit !== undefined ? Number(args.limit) : DEFAULT_MAX_COUNT;
51286
+ const json = !!args.json;
51287
+ const ops = positionals.slice(1);
51288
+ const named = ops.filter((o) => SOURCES.includes(o));
51289
+ const files = ops.filter((o) => !SOURCES.includes(o)).map((f) => resolve4(f));
51290
+ const skipped = [];
51291
+ const sessions = files.length ? files.flatMap((f) => {
51292
+ const kind = named[0] ?? (basename3(f).startsWith("rollout-") ? "codex" : "claude");
51293
+ const s = safeParse(kind, f);
51294
+ if (!s)
51295
+ skipped.push(f);
51296
+ return s ? [s] : [];
51297
+ }) : collect(named.length ? named : SOURCES, days, limit);
51298
+ if (!json)
51299
+ for (const f of skipped)
51300
+ process.stderr.write(`${YELLOW}skip ${f}: not a readable session${RESET}
51301
+ `);
51302
+ if (args["dry-run"]) {
51303
+ const rows = sessions.map((s) => ({ kind: s.kind, title: s.title, cwd: s.cwd, messages: s.messages.length, path: s.path, modifiedAt: s.modifiedAt }));
51304
+ if (json) {
51305
+ console.log(JSON.stringify({ sessions: rows, skipped }));
51306
+ return;
51307
+ }
51308
+ for (const r of rows)
51309
+ process.stderr.write(`${CYAN}${r.kind.padEnd(7)}${RESET} ${r.title} ${DIM}${r.messages} msgs · ${r.cwd}${RESET}
51310
+ `);
51311
+ process.stderr.write(`${DIM}${rows.length} session(s) — dry run, nothing written${RESET}
51312
+ `);
51313
+ return;
51314
+ }
51315
+ if (!api)
51316
+ throw new Error("import: not authenticated");
51317
+ const projectId = args.project || scopedProject;
51318
+ if (!projectId) {
51319
+ process.stderr.write(`${RED}No project — pass --project <id>${RESET}
51320
+ `);
51321
+ process.exit(2);
51322
+ }
51323
+ let fallbackAgent = defaultAgentId;
51324
+ if (args.agent || !fallbackAgent) {
51325
+ const agents = await api.listAgentSettings();
51326
+ fallbackAgent = args.agent ? agents.find((a) => a.id === args.agent || a.name === args.agent)?.id : agents[0]?.id;
51327
+ if (!fallbackAgent) {
51328
+ process.stderr.write(`${RED}Agent not found — pass --agent <id>${RESET}
51329
+ `);
51330
+ process.exit(2);
51331
+ }
51332
+ }
51333
+ const agentCache = new Map;
51334
+ const results = [];
51335
+ for (const s of sessions) {
51336
+ const agentSettingsId = args.agent ? fallbackAgent : await agentForCwd(api, s.cwd, fallbackAgent, agentCache);
51337
+ try {
51338
+ const r = await api.importSession({
51339
+ projectId,
51340
+ agentSettingsId,
51341
+ title: s.title,
51342
+ messages: s.messages,
51343
+ source: { kind: s.kind, path: s.path, sha256: s.sha256, cwd: s.cwd }
51344
+ });
51345
+ results.push({ ...r, kind: s.kind, title: s.title, path: s.path });
51346
+ if (!json) {
51347
+ const mark = r.outcome === "unchanged" ? `${DIM}=` : `${GREEN}✓`;
51348
+ process.stderr.write(`${mark} ${r.outcome.padEnd(9)}${RESET} ${s.title} ${DIM}${r.messageCount} msgs · ${r.todoId}${RESET}
51349
+ `);
51350
+ }
51351
+ } catch (e) {
51352
+ results.push({ kind: s.kind, title: s.title, path: s.path, error: e.message });
51353
+ if (!json)
51354
+ process.stderr.write(`${RED}✗ failed ${RESET} ${s.title} ${DIM}${e.message}${RESET}
51355
+ `);
51356
+ }
51357
+ }
51358
+ const failed = results.filter((r) => r.error).length;
51359
+ if (json)
51360
+ console.log(JSON.stringify({ imported: results, total: results.length, skipped }));
51361
+ else {
51362
+ const count = (o) => results.filter((r) => r.outcome === o).length;
51363
+ process.stderr.write(`${GREEN}Imported ${count("created")} new, ${count("updated")} updated${failed ? `, ${RED}${failed} failed` : ""}${RESET}
51364
+ `);
51365
+ }
51366
+ if (failed)
51367
+ process.exitCode = 1;
51368
+ }
51369
+
50710
51370
  // src/index.ts
50711
51371
  import { randomUUID as randomUUID3 } from "crypto";
50712
51372
 
50713
51373
  // src/ensure-bridge.ts
50714
- import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
51374
+ import { spawn as spawn4, spawnSync as spawnSync3 } from "child_process";
50715
51375
  import fs3 from "fs";
50716
51376
  import path3 from "path";
50717
51377
  import os4 from "os";
@@ -50720,7 +51380,7 @@ var INSTALL_PREFIX2 = process.env.TODOFORAI_PREFIX || path3.join(os4.homedir(),
50720
51380
  var INSTALL_TIMEOUT_MS2 = 90000;
50721
51381
  var bridgeBin2 = "todoforai-bridge";
50722
51382
  function probeBridge2(bin) {
50723
- return spawnSync2(bin, ["--version"], { stdio: "ignore" }).status === 0;
51383
+ return spawnSync3(bin, ["--version"], { stdio: "ignore" }).status === 0;
50724
51384
  }
50725
51385
  function hasBridge2() {
50726
51386
  if (probeBridge2(bridgeBin2))
@@ -50735,7 +51395,7 @@ function installBridge2() {
50735
51395
  if (process.platform !== "linux" && process.platform !== "darwin")
50736
51396
  return false;
50737
51397
  console.error(`\x1B[2mInstalling TODOforAI Bridge (${INSTALLER_URL2})...\x1B[0m`);
50738
- const r = spawnSync2("sh", ["-c", `curl -fsSL --connect-timeout 10 --max-time 30 ${INSTALLER_URL2} | sh`], { stdio: ["ignore", "ignore", "inherit"], timeout: INSTALL_TIMEOUT_MS2, killSignal: "SIGKILL" });
51398
+ const r = spawnSync3("sh", ["-c", `curl -fsSL --connect-timeout 10 --max-time 30 ${INSTALLER_URL2} | sh`], { stdio: ["ignore", "ignore", "inherit"], timeout: INSTALL_TIMEOUT_MS2, killSignal: "SIGKILL" });
50739
51399
  if (r.error?.code === "ETIMEDOUT")
50740
51400
  console.error("\x1B[33mBridge install timed out\x1B[0m");
50741
51401
  return r.status === 0 && hasBridge2();
@@ -50788,7 +51448,7 @@ function bridgeWhoamiArgs2(apiUrl) {
50788
51448
  return withProfile2(["whoami"], apiUrl);
50789
51449
  }
50790
51450
  function bridgeDeviceId2(apiUrl) {
50791
- const r = spawnSync2(bridgeBin2, bridgeWhoamiArgs2(apiUrl), { encoding: "utf-8" });
51451
+ const r = spawnSync3(bridgeBin2, bridgeWhoamiArgs2(apiUrl), { encoding: "utf-8" });
50792
51452
  if (r.status !== 0)
50793
51453
  return null;
50794
51454
  const m = (r.stdout || "").match(/^Device:.*\(id:\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)/im);
@@ -50814,11 +51474,11 @@ async function enrollBridgeWithApiKey2(apiUrl, apiKey) {
50814
51474
  console.error(`\x1B[33mBridge enroll: mint failed (${e?.message || e})\x1B[0m`);
50815
51475
  return false;
50816
51476
  }
50817
- const login = spawnSync2(bridgeBin2, [...bridgeLoginArgs2(apiUrl), "--token", token], { stdio: ["ignore", "ignore", "inherit"], timeout: 30000, killSignal: "SIGKILL" });
51477
+ const login = spawnSync3(bridgeBin2, [...bridgeLoginArgs2(apiUrl), "--token", token], { stdio: ["ignore", "ignore", "inherit"], timeout: 30000, killSignal: "SIGKILL" });
50818
51478
  return login.status === 0;
50819
51479
  }
50820
51480
  function ensureBridgeCredentials2(apiUrl, opts = {}) {
50821
- const whoami = spawnSync2(bridgeBin2, bridgeWhoamiArgs2(apiUrl), { stdio: "ignore" });
51481
+ const whoami = spawnSync3(bridgeBin2, bridgeWhoamiArgs2(apiUrl), { stdio: "ignore" });
50822
51482
  if (whoami.status === 0)
50823
51483
  return true;
50824
51484
  if (opts.interactive === false) {
@@ -50826,7 +51486,7 @@ function ensureBridgeCredentials2(apiUrl, opts = {}) {
50826
51486
  return false;
50827
51487
  }
50828
51488
  console.error("\x1B[2mBridge credentials not found. Starting `todoforai-bridge login`...\x1B[0m");
50829
- const login = spawnSync2(bridgeBin2, bridgeLoginArgs2(apiUrl), { stdio: "inherit" });
51489
+ const login = spawnSync3(bridgeBin2, bridgeLoginArgs2(apiUrl), { stdio: "inherit" });
50830
51490
  return login.status === 0;
50831
51491
  }
50832
51492
  async function bridgeState2(apiUrl, apiKey, deviceId) {
@@ -50843,7 +51503,7 @@ async function waitForBridgeOnline2(apiUrl, apiKey, deviceId, timeoutMs = 5000)
50843
51503
  const state = await bridgeState2(apiUrl, apiKey, deviceId);
50844
51504
  if (state !== "offline")
50845
51505
  return state === "online";
50846
- await new Promise((resolve4) => setTimeout(resolve4, 150));
51506
+ await new Promise((resolve5) => setTimeout(resolve5, 150));
50847
51507
  } while (Date.now() < deadline);
50848
51508
  return false;
50849
51509
  }
@@ -50852,7 +51512,7 @@ async function ensureBridgeRunning2(apiUrl, apiKey, opts = {}) {
50852
51512
  console.error(`\x1B[2mBridge not started: \`todoforai-bridge\` was not found on PATH. Install it (curl -fsSL ${INSTALLER_URL2} | sh), or pass --no-bridge (or deprecated --no-edge) to silence this.\x1B[0m`);
50853
51513
  return false;
50854
51514
  }
50855
- const hasCreds = spawnSync2(bridgeBin2, bridgeWhoamiArgs2(apiUrl), { stdio: "ignore" }).status === 0 || await enrollBridgeWithApiKey2(apiUrl, apiKey) || ensureBridgeCredentials2(apiUrl, opts);
51515
+ const hasCreds = spawnSync3(bridgeBin2, bridgeWhoamiArgs2(apiUrl), { stdio: "ignore" }).status === 0 || await enrollBridgeWithApiKey2(apiUrl, apiKey) || ensureBridgeCredentials2(apiUrl, opts);
50856
51516
  if (!hasCreds) {
50857
51517
  console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
50858
51518
  return false;
@@ -50869,7 +51529,7 @@ async function ensureBridgeRunning2(apiUrl, apiKey, opts = {}) {
50869
51529
  fs3.mkdirSync(logDir, { recursive: true });
50870
51530
  const logFile = path3.join(logDir, "bridge.log");
50871
51531
  const out = fs3.openSync(logFile, "a");
50872
- const child = spawn3(bridgeBin2, bridgeRunArgs2(apiUrl), {
51532
+ const child = spawn4(bridgeBin2, bridgeRunArgs2(apiUrl), {
50873
51533
  detached: true,
50874
51534
  stdio: ["ignore", out, out]
50875
51535
  });
@@ -50894,7 +51554,7 @@ async function ensureBridgeRunning2(apiUrl, apiKey, opts = {}) {
50894
51554
  }
50895
51555
 
50896
51556
  // src/isolated.ts
50897
- import { spawn as spawn4 } from "child_process";
51557
+ import { spawn as spawn5 } from "child_process";
50898
51558
  async function mintMayflyToken(apiUrl, apiKey, todoId) {
50899
51559
  const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/cli/mayfly/token`, {
50900
51560
  method: "POST",
@@ -50925,7 +51585,7 @@ async function spawnMayflyBridge(apiUrl, todoId, workspace, opts = {}) {
50925
51585
  delete env2[p + n];
50926
51586
  env2.TODOFORAI_MAYFLY_TOKEN = sessionToken;
50927
51587
  }
50928
- const child = spawn4(bridgeBinary(), args, { stdio: ["ignore", "pipe", "pipe"], env: env2 });
51588
+ const child = spawn5(bridgeBinary(), args, { stdio: ["ignore", "pipe", "pipe"], env: env2 });
50929
51589
  let stopped = false;
50930
51590
  const stop = () => {
50931
51591
  if (stopped)
@@ -51019,9 +51679,7 @@ var CLIENT_METHODS = {
51019
51679
  terminal_release: "terminal/release",
51020
51680
  terminal_wait_for_exit: "terminal/wait_for_exit",
51021
51681
  terminal_kill: "terminal/kill",
51022
- mcp_connect: "mcp/connect",
51023
51682
  mcp_message: "mcp/message",
51024
- mcp_disconnect: "mcp/disconnect",
51025
51683
  elicitation_create: "elicitation/create",
51026
51684
  elicitation_complete: "elicitation/complete"
51027
51685
  };
@@ -51074,7 +51732,7 @@ __export(exports_external, {
51074
51732
  setErrorMap: () => setErrorMap,
51075
51733
  set: () => set,
51076
51734
  safeParseAsync: () => safeParseAsync2,
51077
- safeParse: () => safeParse2,
51735
+ safeParse: () => safeParse3,
51078
51736
  safeEncodeAsync: () => safeEncodeAsync2,
51079
51737
  safeEncode: () => safeEncode2,
51080
51738
  safeDecodeAsync: () => safeDecodeAsync2,
@@ -51311,7 +51969,7 @@ __export(exports_core2, {
51311
51969
  stripTabAndNewline: () => stripTabAndNewline,
51312
51970
  standardProps: () => standardProps,
51313
51971
  safeParseAsync: () => safeParseAsync,
51314
- safeParse: () => safeParse,
51972
+ safeParse: () => safeParse2,
51315
51973
  safeEncodeAsync: () => safeEncodeAsync,
51316
51974
  safeEncode: () => safeEncode,
51317
51975
  safeDecodeAsync: () => safeDecodeAsync,
@@ -52807,7 +53465,7 @@ var _safeParse = (_Err) => (schema, value, _ctx) => {
52807
53465
  }
52808
53466
  return result.issues.length ? failure(_Err, result.issues, ctx) : { success: true, data: result.value };
52809
53467
  };
52810
- var safeParse = /* @__PURE__ */ _safeParse($ZodRealError);
53468
+ var safeParse2 = /* @__PURE__ */ _safeParse($ZodRealError);
52811
53469
  function failure(Err, issues, ctx) {
52812
53470
  let error;
52813
53471
  return {
@@ -54995,7 +55653,7 @@ function handleIntersectionResults(result, left, right) {
54995
55653
  const unrecKeys = new Map;
54996
55654
  let unrecIssue;
54997
55655
  const keyIssues = new Map;
54998
- const collect = (iss, side) => {
55656
+ const collect2 = (iss, side) => {
54999
55657
  let keys;
55000
55658
  if (iss.code === "unrecognized_keys" && !iss.path?.length) {
55001
55659
  unrecIssue ?? (unrecIssue = iss);
@@ -55016,11 +55674,11 @@ function handleIntersectionResults(result, left, right) {
55016
55674
  return true;
55017
55675
  };
55018
55676
  for (const iss of left.issues) {
55019
- if (!collect(iss, "l"))
55677
+ if (!collect2(iss, "l"))
55020
55678
  result.issues.push(iss);
55021
55679
  }
55022
55680
  for (const iss of right.issues) {
55023
- if (!collect(iss, "r"))
55681
+ if (!collect2(iss, "r"))
55024
55682
  result.issues.push(iss);
55025
55683
  }
55026
55684
  const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);
@@ -56024,7 +56682,7 @@ class $ZodCyclicError extends Error {
56024
56682
  this.name = "ZodCyclicError";
56025
56683
  }
56026
56684
  }
56027
- var STATE = "~memo";
56685
+ var STATE2 = "~memo";
56028
56686
  var NO_ISSUES = [];
56029
56687
  function isRef(value) {
56030
56688
  return value !== null && typeof value === "object";
@@ -56036,7 +56694,7 @@ var recursive = /* @__PURE__ */ new WeakMap;
56036
56694
  var NONE = 0;
56037
56695
  var ASSUMED = 1;
56038
56696
  var PROVEN = 2;
56039
- function isRecursive(inst, stack, resolve4) {
56697
+ function isRecursive(inst, stack, resolve5) {
56040
56698
  const cached2 = recursive.get(inst);
56041
56699
  if (cached2 !== undefined)
56042
56700
  return cached2 ? PROVEN : NONE;
@@ -56046,7 +56704,7 @@ function isRecursive(inst, stack, resolve4) {
56046
56704
  let result = NONE;
56047
56705
  const check = (child) => {
56048
56706
  if (result !== PROVEN && child?._zod) {
56049
- const answer = isRecursive(child, stack, resolve4);
56707
+ const answer = isRecursive(child, stack, resolve5);
56050
56708
  if (answer > result)
56051
56709
  result = answer;
56052
56710
  }
@@ -56057,7 +56715,7 @@ function isRecursive(inst, stack, resolve4) {
56057
56715
  const desc = Object.getOwnPropertyDescriptor(sh, key);
56058
56716
  if (spread && !desc.enumerable)
56059
56717
  continue;
56060
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve4) : NONE;
56718
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve5) : NONE;
56061
56719
  if (child > answer)
56062
56720
  answer = child;
56063
56721
  }
@@ -56120,7 +56778,7 @@ function isRecursive(inst, stack, resolve4) {
56120
56778
  check(def.output);
56121
56779
  break;
56122
56780
  case "lazy": {
56123
- const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : undefined);
56781
+ const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : undefined);
56124
56782
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
56125
56783
  break;
56126
56784
  }
@@ -56234,10 +56892,10 @@ var memo = {
56234
56892
  const input = payload.value;
56235
56893
  if (!isRef(input))
56236
56894
  return base(payload, ctx);
56237
- let state = ctx[STATE];
56895
+ let state = ctx[STATE2];
56238
56896
  if (!state) {
56239
56897
  state = { buckets: new WeakMap, backEdges: undefined };
56240
- ctx[STATE] = state;
56898
+ ctx[STATE2] = state;
56241
56899
  }
56242
56900
  let bucket;
56243
56901
  if (lastCtx === ctx) {
@@ -56286,7 +56944,7 @@ function memoizer() {
56286
56944
  return memo;
56287
56945
  }
56288
56946
  function isBackEdge(ctx, value) {
56289
- const backEdges = ctx[STATE]?.backEdges;
56947
+ const backEdges = ctx[STATE2]?.backEdges;
56290
56948
  return backEdges !== undefined && isRef(value) && backEdges.has(value);
56291
56949
  }
56292
56950
  // node_modules/zod/v4/locales/index.js
@@ -67966,7 +68624,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, undefi
67966
68624
  // node_modules/zod/v4/classic/parse.js
67967
68625
  var parse3 = /* @__PURE__ */ _parse(ZodRealError);
67968
68626
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
67969
- var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
68627
+ var safeParse3 = /* @__PURE__ */ _safeParse(ZodRealError);
67970
68628
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
67971
68629
  var encode2 = /* @__PURE__ */ _encode(ZodRealError);
67972
68630
  var decode2 = /* @__PURE__ */ _decode(ZodRealError);
@@ -68106,7 +68764,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
68106
68764
  return await parseAsync2(this, data, params, { callee: _parseAsync2 });
68107
68765
  },
68108
68766
  safeParse(data, params) {
68109
- return safeParse2(this, data, params);
68767
+ return safeParse3(this, data, params);
68110
68768
  },
68111
68769
  async safeParseAsync(data, params) {
68112
68770
  return safeParseAsync2(this, data, params);
@@ -70970,27 +71628,16 @@ var zCreateElicitationRequest = preserveCustomPayload(intersection(union([
70970
71628
  })
70971
71629
  })), "mode", ["form", "url"]);
70972
71630
  var zMcpServerAcpId = string2();
70973
- var zConnectMcpRequest = object({
70974
- serverId: zMcpServerAcpId,
70975
- _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
70976
- return;
70977
- })
70978
- });
70979
- var zMcpConnectionId = string2();
71631
+ var zMcpRequestId = string2();
70980
71632
  var zMessageMcpRequest = object({
70981
- connectionId: zMcpConnectionId,
71633
+ serverId: zMcpServerAcpId,
71634
+ requestId: zMcpRequestId,
70982
71635
  method: string2(),
70983
71636
  params: record(string2(), unknown()).nullish(),
70984
71637
  _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
70985
71638
  return;
70986
71639
  })
70987
71640
  });
70988
- var zDisconnectMcpRequest = object({
70989
- connectionId: zMcpConnectionId,
70990
- _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
70991
- return;
70992
- })
70993
- });
70994
71641
  var zExtRequest = unknown();
70995
71642
  var zAgentRequest = object({
70996
71643
  id: zRequestId,
@@ -71005,9 +71652,7 @@ var zAgentRequest = object({
71005
71652
  zWaitForTerminalExitRequest,
71006
71653
  zKillTerminalRequest,
71007
71654
  zCreateElicitationRequest,
71008
- zConnectMcpRequest,
71009
71655
  zMessageMcpRequest,
71010
- zDisconnectMcpRequest,
71011
71656
  zExtRequest
71012
71657
  ]).nullish()
71013
71658
  });
@@ -71703,7 +72348,6 @@ var zCloseNesResponse = object({
71703
72348
  })
71704
72349
  });
71705
72350
  var zExtResponse = unknown();
71706
- var zMessageMcpResponse = unknown();
71707
72351
  var zErrorCode = union([
71708
72352
  literal(-32700),
71709
72353
  literal(-32600),
@@ -71749,8 +72393,7 @@ var zAgentResponse = union([
71749
72393
  zStartNesResponse,
71750
72394
  zSuggestNesResponse,
71751
72395
  zCloseNesResponse,
71752
- zExtResponse,
71753
- zMessageMcpResponse
72396
+ zExtResponse
71754
72397
  ])
71755
72398
  }),
71756
72399
  object({
@@ -71968,6 +72611,108 @@ var zCompactionSummaryChunk = object({
71968
72611
  return;
71969
72612
  })
71970
72613
  });
72614
+ var zSessionCancelCapabilities = object({
72615
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72616
+ return;
72617
+ })
72618
+ });
72619
+ var zSubagentSessionCapabilities = object({
72620
+ cancel: defaultOnError(zSessionCancelCapabilities.nullish(), () => {
72621
+ return;
72622
+ }),
72623
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72624
+ return;
72625
+ })
72626
+ });
72627
+ var zRunningStateUpdate = object({
72628
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72629
+ return;
72630
+ })
72631
+ });
72632
+ var zIdleStateUpdate = object({
72633
+ stopReason: defaultOnError(zStopReason.nullish(), () => {
72634
+ return;
72635
+ }),
72636
+ usage: defaultOnError(zUsage.nullish(), () => {
72637
+ return;
72638
+ }),
72639
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72640
+ return;
72641
+ })
72642
+ });
72643
+ var zRequiresActionStateUpdate = object({
72644
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72645
+ return;
72646
+ })
72647
+ });
72648
+ var zUnknownStateUpdate = object({
72649
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72650
+ return;
72651
+ })
72652
+ });
72653
+ var zStateUpdate = preserveCustomPayload(union([
72654
+ zRunningStateUpdate.and(object({
72655
+ state: literal("running")
72656
+ })),
72657
+ zIdleStateUpdate.and(object({
72658
+ state: literal("idle")
72659
+ })),
72660
+ zRequiresActionStateUpdate.and(object({
72661
+ state: literal("requires_action")
72662
+ })),
72663
+ zUnknownStateUpdate.and(object({
72664
+ state: literal("unknown")
72665
+ })),
72666
+ excludeKnownTags(object({
72667
+ state: string2()
72668
+ }), "state", ["idle", "requires_action", "running", "unknown"])
72669
+ ]), "state", ["idle", "requires_action", "running", "unknown"]);
72670
+ var zSubagentUpdate = object({
72671
+ sessionId: zSessionId,
72672
+ title: defaultOnError(string2().nullish(), () => {
72673
+ return;
72674
+ }),
72675
+ description: defaultOnError(string2().nullish(), () => {
72676
+ return;
72677
+ }),
72678
+ capabilities: defaultOnError(zSubagentSessionCapabilities.nullish(), () => {
72679
+ return;
72680
+ }),
72681
+ state: defaultOnError(zStateUpdate.nullish(), () => {
72682
+ return;
72683
+ }),
72684
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72685
+ return;
72686
+ })
72687
+ });
72688
+ var zSessionMessage = object({
72689
+ messageId: zMessageId,
72690
+ senderSessionId: defaultOnError(zSessionId.nullish(), () => {
72691
+ return;
72692
+ }),
72693
+ recipientSessionId: defaultOnError(zSessionId.nullish(), () => {
72694
+ return;
72695
+ }),
72696
+ content: defaultOnError(vecSkipError(zContentBlock).nullish(), () => {
72697
+ return;
72698
+ }),
72699
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72700
+ return;
72701
+ })
72702
+ });
72703
+ var zSessionMessageChunk = object({
72704
+ messageId: zMessageId,
72705
+ senderSessionId: defaultOnError(zSessionId.nullish(), () => {
72706
+ return;
72707
+ }),
72708
+ recipientSessionId: defaultOnError(zSessionId.nullish(), () => {
72709
+ return;
72710
+ }),
72711
+ content: zContentBlock,
72712
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72713
+ return;
72714
+ })
72715
+ });
71971
72716
  var zSessionUpdate = union([
71972
72717
  zContentChunk.and(object({
71973
72718
  sessionUpdate: literal("user_message_chunk")
@@ -72016,6 +72761,15 @@ var zSessionUpdate = union([
72016
72761
  })),
72017
72762
  zCompactionSummaryChunk.and(object({
72018
72763
  sessionUpdate: literal("compaction_summary_chunk")
72764
+ })),
72765
+ zSubagentUpdate.and(object({
72766
+ sessionUpdate: literal("subagent_update")
72767
+ })),
72768
+ zSessionMessage.and(object({
72769
+ sessionUpdate: literal("session_message")
72770
+ })),
72771
+ zSessionMessageChunk.and(object({
72772
+ sessionUpdate: literal("session_message_chunk")
72019
72773
  }))
72020
72774
  ]);
72021
72775
  var zSessionNotification = object({
@@ -72031,23 +72785,12 @@ var zCompleteElicitationNotification = object({
72031
72785
  return;
72032
72786
  })
72033
72787
  });
72034
- var zMessageMcpNotification = object({
72035
- connectionId: zMcpConnectionId,
72036
- method: string2(),
72037
- params: defaultOnError(record(string2(), unknown()).nullish(), () => {
72038
- return;
72039
- }),
72040
- _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72041
- return;
72042
- })
72043
- });
72044
72788
  var zExtNotification = unknown();
72045
72789
  var zAgentNotification = object({
72046
72790
  method: string2(),
72047
72791
  params: union([
72048
72792
  zSessionNotification,
72049
72793
  zCompleteElicitationNotification,
72050
- zMessageMcpNotification,
72051
72794
  zExtNotification
72052
72795
  ]).nullish()
72053
72796
  });
@@ -72087,6 +72830,11 @@ var zClientSessionCapabilities = object({
72087
72830
  return;
72088
72831
  })
72089
72832
  });
72833
+ var zSubagentCapabilities = object({
72834
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72835
+ return;
72836
+ })
72837
+ });
72090
72838
  var zPlanCapabilities = object({
72091
72839
  _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72092
72840
  return;
@@ -72154,6 +72902,9 @@ var zClientCapabilities = object({
72154
72902
  session: defaultOnError(zClientSessionCapabilities.nullish(), () => {
72155
72903
  return;
72156
72904
  }),
72905
+ subagents: defaultOnError(zSubagentCapabilities.nullish(), () => {
72906
+ return;
72907
+ }),
72157
72908
  plan: defaultOnError(zPlanCapabilities.nullish(), () => {
72158
72909
  return;
72159
72910
  }),
@@ -72507,7 +73258,6 @@ var zClientRequest = object({
72507
73258
  zStartNesRequest,
72508
73259
  zSuggestNesRequest,
72509
73260
  zCloseNesRequest,
72510
- zMessageMcpRequest,
72511
73261
  zExtRequest
72512
73262
  ]).nullish()
72513
73263
  });
@@ -72622,17 +73372,29 @@ var zCreateElicitationResponse = preserveCustomPayload(intersection(union([
72622
73372
  return;
72623
73373
  })
72624
73374
  })), "action", ["accept", "cancel", "decline"]);
72625
- var zConnectMcpResponse = object({
72626
- connectionId: zMcpConnectionId,
72627
- _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72628
- return;
72629
- })
73375
+ var zMcpError = looseObject({
73376
+ code: int().min(-2147483648, {
73377
+ error: "Invalid value: Expected int32 to be >= -2147483648"
73378
+ }).max(2147483647, {
73379
+ error: "Invalid value: Expected int32 to be <= 2147483647"
73380
+ }),
73381
+ message: string2(),
73382
+ data: unknown().optional()
72630
73383
  });
72631
- var zDisconnectMcpResponse = object({
72632
- _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
72633
- return;
73384
+ var zMessageMcpResponse = union([
73385
+ object({
73386
+ result: unknown(),
73387
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
73388
+ return;
73389
+ })
73390
+ }),
73391
+ object({
73392
+ error: zMcpError,
73393
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
73394
+ return;
73395
+ })
72634
73396
  })
72635
- });
73397
+ ]);
72636
73398
  var zClientResponse = union([
72637
73399
  object({
72638
73400
  id: zRequestId,
@@ -72646,8 +73408,6 @@ var zClientResponse = union([
72646
73408
  zWaitForTerminalExitResponse,
72647
73409
  zKillTerminalResponse,
72648
73410
  zCreateElicitationResponse,
72649
- zConnectMcpResponse,
72650
- zDisconnectMcpResponse,
72651
73411
  zMessageMcpResponse,
72652
73412
  zExtResponse
72653
73413
  ])
@@ -72736,6 +73496,15 @@ var zRejectNesNotification = object({
72736
73496
  return;
72737
73497
  })
72738
73498
  });
73499
+ var zMessageMcpNotification = object({
73500
+ serverId: zMcpServerAcpId,
73501
+ requestId: zMcpRequestId,
73502
+ method: string2(),
73503
+ params: record(string2(), unknown()).nullish(),
73504
+ _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
73505
+ return;
73506
+ })
73507
+ });
72739
73508
  var zClientNotification = object({
72740
73509
  method: string2(),
72741
73510
  params: union([
@@ -73103,11 +73872,11 @@ class Connection {
73103
73872
  prepareRequest(method, params, mapResponse, options = {}) {
73104
73873
  const id = this.nextRequestId++;
73105
73874
  let cancel = () => {};
73106
- const response = new Promise((resolve4, reject) => {
73875
+ const response = new Promise((resolve5, reject) => {
73107
73876
  const pendingResponse = {
73108
73877
  resolve: (value) => {
73109
73878
  try {
73110
- resolve4(mapResponse ? mapResponse(value) : value);
73879
+ resolve5(mapResponse ? mapResponse(value) : value);
73111
73880
  } catch (error62) {
73112
73881
  reject(error62);
73113
73882
  }
@@ -73158,8 +73927,8 @@ class Connection {
73158
73927
  this.stream = stream;
73159
73928
  this.staticHandlers = handlers;
73160
73929
  this.allowBatches = options?.allowBatches ?? true;
73161
- this.closedPromise = new Promise((resolve4) => {
73162
- this.abortController.signal.addEventListener("abort", () => resolve4());
73930
+ this.closedPromise = new Promise((resolve5) => {
73931
+ this.abortController.signal.addEventListener("abort", () => resolve5());
73163
73932
  });
73164
73933
  this.receive();
73165
73934
  }
@@ -73525,53 +74294,124 @@ function protocolErrorResponse(error62) {
73525
74294
  };
73526
74295
  }
73527
74296
 
74297
+ // node_modules/@agentclientprotocol/sdk/dist/stream-limits.js
74298
+ var DEFAULT_MAX_MESSAGE_BYTES = 32 * 1024 * 1024;
74299
+
74300
+ class MessageTooLargeError extends Error {
74301
+ maxMessageBytes;
74302
+ constructor(maxMessageBytes) {
74303
+ super(`Incoming ACP data exceeds the configured ${maxMessageBytes} byte limit`);
74304
+ this.maxMessageBytes = maxMessageBytes;
74305
+ this.name = "MessageTooLargeError";
74306
+ }
74307
+ }
74308
+ function resolveMaxMessageBytes(value) {
74309
+ const maxMessageBytes = value ?? DEFAULT_MAX_MESSAGE_BYTES;
74310
+ if (!Number.isSafeInteger(maxMessageBytes) || maxMessageBytes <= 0) {
74311
+ throw new RangeError("maxMessageBytes must be a positive safe integer");
74312
+ }
74313
+ return maxMessageBytes;
74314
+ }
74315
+
74316
+ class MessageBuffer {
74317
+ #buffer = new Uint8Array(0);
74318
+ #length = 0;
74319
+ #maxMessageBytes;
74320
+ constructor(maxMessageBytes) {
74321
+ this.#maxMessageBytes = resolveMaxMessageBytes(maxMessageBytes);
74322
+ }
74323
+ get byteLength() {
74324
+ return this.#length;
74325
+ }
74326
+ get lastByte() {
74327
+ return this.#buffer[this.#length - 1];
74328
+ }
74329
+ #checkAppend(byteLength) {
74330
+ if (byteLength > this.#maxMessageBytes - this.#length) {
74331
+ this.clear();
74332
+ throw new MessageTooLargeError(this.#maxMessageBytes);
74333
+ }
74334
+ }
74335
+ append(bytes) {
74336
+ this.#checkAppend(bytes.byteLength);
74337
+ const length = this.#length + bytes.byteLength;
74338
+ if (length > this.#buffer.byteLength) {
74339
+ const capacity = Math.min(this.#maxMessageBytes, Math.max(length, this.#buffer.byteLength * 2, 1024));
74340
+ const buffer = new Uint8Array(capacity);
74341
+ buffer.set(this.#buffer.subarray(0, this.#length));
74342
+ this.#buffer = buffer;
74343
+ }
74344
+ this.#buffer.set(bytes, this.#length);
74345
+ this.#length = length;
74346
+ }
74347
+ take() {
74348
+ const bytes = this.#buffer.subarray(0, this.#length);
74349
+ this.clear();
74350
+ return bytes;
74351
+ }
74352
+ clear() {
74353
+ this.#buffer = new Uint8Array(0);
74354
+ this.#length = 0;
74355
+ }
74356
+ }
74357
+
73528
74358
  // node_modules/@agentclientprotocol/sdk/dist/line-buffer.js
73529
74359
  var newline = 10;
73530
74360
 
73531
74361
  class LineBuffer {
73532
- #pending = [];
73533
- push(chunk) {
73534
- const lines = [];
74362
+ #pending;
74363
+ #maxMessageBytes;
74364
+ constructor(maxMessageBytes) {
74365
+ this.#maxMessageBytes = resolveMaxMessageBytes(maxMessageBytes);
74366
+ this.#pending = new MessageBuffer(Math.min(Number.MAX_SAFE_INTEGER, this.#maxMessageBytes + 1));
74367
+ }
74368
+ *push(chunk) {
73535
74369
  let start = 0;
73536
74370
  let newlineIndex = chunk.indexOf(newline, start);
73537
74371
  while (newlineIndex !== -1) {
73538
- lines.push(this.#takeLine(chunk.subarray(start, newlineIndex)));
74372
+ yield this.#takeLine(chunk.subarray(start, newlineIndex));
73539
74373
  start = newlineIndex + 1;
73540
74374
  newlineIndex = chunk.indexOf(newline, start);
73541
74375
  }
73542
74376
  if (start < chunk.byteLength) {
73543
- this.#pending.push(start === 0 ? chunk : new Uint8Array(chunk.subarray(start)));
74377
+ const tail = chunk.subarray(start);
74378
+ this.#checkLine(tail);
74379
+ this.#pending.append(tail);
73544
74380
  }
73545
- return lines;
73546
74381
  }
73547
74382
  flush() {
73548
- if (this.#pending.length === 0) {
74383
+ if (this.#pending.byteLength === 0) {
73549
74384
  return;
73550
74385
  }
73551
- return this.#takeLine(new Uint8Array(0));
74386
+ return stripCarriageReturn(this.#pending.take());
74387
+ }
74388
+ clear() {
74389
+ this.#pending.clear();
73552
74390
  }
73553
74391
  #takeLine(tail) {
73554
- if (this.#pending.length === 0) {
73555
- return tail;
73556
- }
73557
- let total = tail.byteLength;
73558
- for (const part of this.#pending) {
73559
- total += part.byteLength;
74392
+ this.#checkLine(tail);
74393
+ if (this.#pending.byteLength === 0) {
74394
+ return stripCarriageReturn(tail);
73560
74395
  }
73561
- const line = new Uint8Array(total);
73562
- let offset = 0;
73563
- for (const part of this.#pending) {
73564
- line.set(part, offset);
73565
- offset += part.byteLength;
74396
+ this.#pending.append(tail);
74397
+ return stripCarriageReturn(this.#pending.take());
74398
+ }
74399
+ #checkLine(tail) {
74400
+ const lastByte = tail.byteLength > 0 ? tail[tail.byteLength - 1] : this.#pending.lastByte;
74401
+ const byteLength = this.#pending.byteLength + tail.byteLength - (lastByte === 13 ? 1 : 0);
74402
+ if (byteLength > this.#maxMessageBytes) {
74403
+ this.clear();
74404
+ throw new MessageTooLargeError(this.#maxMessageBytes);
73566
74405
  }
73567
- line.set(tail, offset);
73568
- this.#pending = [];
73569
- return line;
73570
74406
  }
73571
74407
  }
74408
+ function stripCarriageReturn(line) {
74409
+ return line[line.byteLength - 1] === 13 ? line.subarray(0, -1) : line;
74410
+ }
73572
74411
 
73573
74412
  // node_modules/@agentclientprotocol/sdk/dist/stream.js
73574
- function ndJsonStream(output2, input2) {
74413
+ function ndJsonStream(output2, input2, options = {}) {
74414
+ const maxMessageBytes = resolveMaxMessageBytes(options.maxMessageBytes);
73575
74415
  const textEncoder = new TextEncoder;
73576
74416
  const textDecoder = new TextDecoder;
73577
74417
  let cancelled = false;
@@ -73593,7 +74433,7 @@ function ndJsonStream(output2, input2) {
73593
74433
  };
73594
74434
  const readable = new ReadableStream({
73595
74435
  async start(controller) {
73596
- const lines = new LineBuffer;
74436
+ const lines = new LineBuffer(maxMessageBytes);
73597
74437
  const enqueueLine = async (lineBytes) => {
73598
74438
  const trimmedLine = textDecoder.decode(lineBytes).trim();
73599
74439
  if (!trimmedLine) {
@@ -73645,8 +74485,10 @@ function ndJsonStream(output2, input2) {
73645
74485
  return;
73646
74486
  }
73647
74487
  controller.error(err);
74488
+ reader.cancel(err).catch(() => {});
73648
74489
  return;
73649
74490
  } finally {
74491
+ lines.clear();
73650
74492
  if (inputReader === reader) {
73651
74493
  inputReader = undefined;
73652
74494
  }
@@ -73680,6 +74522,10 @@ var zGuardElicitationPropertySchemaInteger = zIntegerPropertySchema.and(object({
73680
74522
  var zGuardElicitationPropertySchemaBoolean = zBooleanPropertySchema.and(object({ type: literal("boolean") }));
73681
74523
  var zGuardElicitationPropertySchemaArray = zMultiSelectPropertySchema.and(object({ type: literal("array") }));
73682
74524
  var zGuardMultiSelectItemsString = zStringMultiSelectItems.and(object({ type: literal("string") }));
74525
+ var zGuardStateUpdateRunning = zRunningStateUpdate.and(object({ state: literal("running") }));
74526
+ var zGuardStateUpdateIdle = zIdleStateUpdate.and(object({ state: literal("idle") }));
74527
+ var zGuardStateUpdateRequiresAction = zRequiresActionStateUpdate.and(object({ state: literal("requires_action") }));
74528
+ var zGuardStateUpdateUnknown = zUnknownStateUpdate.and(object({ state: literal("unknown") }));
73683
74529
  var zGuardCreateElicitationResponseAccept = zElicitationAcceptAction.and(object({ action: literal("accept") }));
73684
74530
  var zGuardCreateElicitationResponseDecline = object({
73685
74531
  action: literal("decline")
@@ -73688,8 +74534,8 @@ var zGuardCreateElicitationResponseCancel = object({
73688
74534
  action: literal("cancel")
73689
74535
  });
73690
74536
  // node_modules/@agentclientprotocol/sdk/dist/acp.js
73691
- function ndJsonStream2(output2, input2) {
73692
- return ndJsonStream(output2, input2);
74537
+ function ndJsonStream2(output2, input2, options) {
74538
+ return ndJsonStream(output2, input2, options);
73693
74539
  }
73694
74540
  function emptyObjectResponse(response) {
73695
74541
  return response ?? {};
@@ -73974,8 +74820,8 @@ class AsyncQueue {
73974
74820
  if (this.failed) {
73975
74821
  return Promise.reject(this.failure);
73976
74822
  }
73977
- return new Promise((resolve4, reject) => {
73978
- this.waiters.push({ resolve: resolve4, reject });
74823
+ return new Promise((resolve5, reject) => {
74824
+ this.waiters.push({ resolve: resolve5, reject });
73979
74825
  });
73980
74826
  }
73981
74827
  }
@@ -74703,7 +75549,7 @@ function classifyBlock2(info) {
74703
75549
  }
74704
75550
 
74705
75551
  // src/device-login.ts
74706
- import { spawn as spawn5 } from "child_process";
75552
+ import { spawn as spawn6 } from "child_process";
74707
75553
  class DeviceLoginError2 extends Error {
74708
75554
  }
74709
75555
  async function runDeviceLogin2(apiUrl) {
@@ -74721,7 +75567,7 @@ async function runDeviceLogin2(apiUrl) {
74721
75567
  `);
74722
75568
  try {
74723
75569
  const [cmd, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url2]] : [process.platform === "darwin" ? "open" : "xdg-open", [url2]];
74724
- const child = spawn5(cmd, args, { stdio: "ignore", detached: true });
75570
+ const child = spawn6(cmd, args, { stdio: "ignore", detached: true });
74725
75571
  child.on("error", () => {});
74726
75572
  child.unref();
74727
75573
  } catch {}
@@ -74751,7 +75597,7 @@ async function runDeviceLogin2(apiUrl) {
74751
75597
  }
74752
75598
 
74753
75599
  // src/acp.ts
74754
- var log2 = (...a) => process.stderr.write(`[acp] ${a.join(" ")}
75600
+ var log3 = (...a) => process.stderr.write(`[acp] ${a.join(" ")}
74755
75601
  `);
74756
75602
  var ACP_KIND = { create: "edit", edit: "edit", read: "read", search: "search", shell: "execute" };
74757
75603
  var toolTitle = (info) => {
@@ -74827,7 +75673,7 @@ class TodoforaiAgent {
74827
75673
  }
74828
75674
  this.api = new ApiClient(this.apiUrl, this.apiKey);
74829
75675
  this.ws = ws;
74830
- log2(`ready (${this.apiUrl})`);
75676
+ log3(`ready (${this.apiUrl})`);
74831
75677
  })();
74832
75678
  }
74833
75679
  async requireAuth() {
@@ -74872,7 +75718,7 @@ class TodoforaiAgent {
74872
75718
  const agent2 = await this.resolveAgent(cwd);
74873
75719
  const todoId = randomUUID2();
74874
75720
  this.sessions.set(todoId, { todoId, projectId, agent: agent2 });
74875
- log2(`session ${todoId} agent=${agent2.name} cwd=${cwd}`);
75721
+ log3(`session ${todoId} agent=${agent2.name} cwd=${cwd}`);
74876
75722
  return { sessionId: todoId, modes: await this.modes(agent2) };
74877
75723
  }
74878
75724
  async setSessionMode(p) {
@@ -74883,7 +75729,7 @@ class TodoforaiAgent {
74883
75729
  if (!agent2)
74884
75730
  throw RequestError.invalidParams(`unknown agent ${p.modeId}`);
74885
75731
  s.agent = agent2;
74886
- log2(`session ${s.todoId} agent=${agent2.name}`);
75732
+ log3(`session ${s.todoId} agent=${agent2.name}`);
74887
75733
  this.conn.sessionUpdate({ sessionId: p.sessionId, update: { sessionUpdate: "current_mode_update", currentModeId: agent2.id } });
74888
75734
  return {};
74889
75735
  }
@@ -74893,7 +75739,7 @@ class TodoforaiAgent {
74893
75739
  return;
74894
75740
  s.turn.cancelled = true;
74895
75741
  if (s.turn.started && !await this.ws.sendInterrupt(s.projectId, s.todoId))
74896
- log2("interrupt not delivered (socket down)");
75742
+ log3("interrupt not delivered (socket down)");
74897
75743
  }
74898
75744
  async prompt(p) {
74899
75745
  const s = this.sessions.get(p.sessionId);
@@ -74908,7 +75754,7 @@ ${b.resource.text}
74908
75754
  </file>
74909
75755
  ` : "").join("");
74910
75756
  const sessionId = p.sessionId;
74911
- const update = (u) => this.conn.sessionUpdate({ sessionId, update: u }).catch((e) => log2("update failed", e?.message));
75757
+ const update = (u) => this.conn.sessionUpdate({ sessionId, update: u }).catch((e) => log3("update failed", e?.message));
74912
75758
  const blocks4 = new Map;
74913
75759
  const view = (id) => blocks4.get(id) ?? blocks4.set(id, new BlockView).get(id);
74914
75760
  const announce = (id, b) => {
@@ -74942,9 +75788,9 @@ ${b.resource.text}
74942
75788
  return;
74943
75789
  const ok = choice === "allow_once" || choice === "allow_remember" ? await this.ws.sendBlockApproval(s.todoId, messageId, id, choice, choice === "allow_remember" ? patterns : undefined) : await this.ws.sendBlockDeny(s.todoId, messageId, id);
74944
75790
  if (!ok)
74945
- log2(`approval for ${id} not delivered (socket down)`);
75791
+ log3(`approval for ${id} not delivered (socket down)`);
74946
75792
  } catch (e) {
74947
- log2("permission request failed", e?.message);
75793
+ log3("permission request failed", e?.message);
74948
75794
  }
74949
75795
  };
74950
75796
  const onEvent = (type, payload) => {
@@ -75020,18 +75866,18 @@ ${b.resource.text}
75020
75866
  async function runAcp(apiUrl, apiKey, opts) {
75021
75867
  console.log = (...a) => process.stderr.write(a.map(String).join(" ") + `
75022
75868
  `);
75023
- const eof = new Promise((resolve4) => {
75024
- process.stdin.once("end", resolve4);
75025
- process.stdin.once("close", resolve4);
75026
- process.stdin.once("error", resolve4);
75869
+ const eof = new Promise((resolve5) => {
75870
+ process.stdin.once("end", resolve5);
75871
+ process.stdin.once("close", resolve5);
75872
+ process.stdin.once("error", resolve5);
75027
75873
  });
75028
75874
  const stream = ndJsonStream2(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
75029
75875
  let agent2;
75030
75876
  new AgentSideConnection((conn) => agent2 = new TodoforaiAgent(conn, apiUrl, apiKey, opts), stream);
75031
75877
  if (apiKey)
75032
- agent2.connect().catch((e) => log2("connect failed:", e?.message));
75878
+ agent2.connect().catch((e) => log3("connect failed:", e?.message));
75033
75879
  else
75034
- log2("not logged in — waiting for authenticate");
75880
+ log3("not logged in — waiting for authenticate");
75035
75881
  await eof;
75036
75882
  await agent2.close();
75037
75883
  }
@@ -75124,12 +75970,15 @@ function installSignalHandlers() {
75124
75970
  }
75125
75971
 
75126
75972
  // src/index.ts
75973
+ var ownPkg;
75127
75974
  try {
75128
- const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
75129
- checkForUpdates(JSON.parse(readFileSync3(pkgPath, "utf-8")));
75975
+ ownPkg = JSON.parse(readFileSync5(path4.resolve(fileURLToPath(import.meta.url), "../../package.json"), "utf-8"));
75976
+ if (process.argv[2] !== "update")
75977
+ backgroundUpdate(ownPkg);
75130
75978
  } catch {}
75979
+ var aliasTodoId = (alias) => typeof alias === "string" && alias.includes(":") ? alias.slice(0, alias.indexOf(":")) : undefined;
75131
75980
  function formatPathWithTilde(path5) {
75132
- const home = homedir3();
75981
+ const home = homedir5();
75133
75982
  return path5.startsWith(home) ? path5.replace(home, "~") : path5;
75134
75983
  }
75135
75984
  function promptArg(words) {
@@ -75239,6 +76088,10 @@ async function main() {
75239
76088
  console.log(VERSION);
75240
76089
  process.exit(0);
75241
76090
  }
76091
+ if (positionals[0] === "update") {
76092
+ const r = ownPkg ? await selfUpdate(ownPkg) : "unsupported";
76093
+ process.exit(r === "updated" || r === "current" ? 0 : 1);
76094
+ }
75242
76095
  if (positionals[0] === "status" && args.help) {
75243
76096
  printStatusHelp();
75244
76097
  process.exit(0);
@@ -75275,11 +76128,19 @@ async function main() {
75275
76128
  printNotifyHelp();
75276
76129
  process.exit(0);
75277
76130
  }
76131
+ if (positionals[0] === "import" && args.help) {
76132
+ printImportHelp();
76133
+ process.exit(0);
76134
+ }
76135
+ if (positionals[0] === "import" && args["dry-run"]) {
76136
+ await importCommand(null, positionals, args);
76137
+ return;
76138
+ }
75278
76139
  if ((positionals[0] === "list" || positionals[0] === "ls") && args.help) {
75279
76140
  printListTodosHelp();
75280
76141
  process.exit(0);
75281
76142
  }
75282
- if (args.help && !["list", "ls", "agent", "todo", "project", "brand", "device", "inbox", "notify"].includes(positionals[0])) {
76143
+ if (args.help && !["list", "ls", "agent", "todo", "project", "brand", "device", "inbox", "notify", "import"].includes(positionals[0])) {
75283
76144
  printUsage();
75284
76145
  process.exit(0);
75285
76146
  }
@@ -75308,7 +76169,7 @@ async function main() {
75308
76169
  }
75309
76170
  if (args["reset-config"]) {
75310
76171
  const { unlinkSync } = await import("fs");
75311
- if (existsSync2(cfg.path)) {
76172
+ if (existsSync4(cfg.path)) {
75312
76173
  unlinkSync(cfg.path);
75313
76174
  console.log(`Configuration reset: ${formatPathWithTilde(cfg.path)}`);
75314
76175
  } else
@@ -75418,6 +76279,10 @@ async function main() {
75418
76279
  await notifyCommand(api2, positionals, args, scopedProject);
75419
76280
  return;
75420
76281
  }
76282
+ if (positionals[0] === "import") {
76283
+ await importCommand(api2, positionals, args, scopedProject, cfgScope.data.default_agent_settings?.id);
76284
+ return;
76285
+ }
75421
76286
  if (positionals[0] === "delete") {
75422
76287
  const todoId = positionals[1];
75423
76288
  if (!todoId) {
@@ -75472,14 +76337,16 @@ async function main() {
75472
76337
  const kind = it.url ? `url ${it.url}` : it.mime || "";
75473
76338
  const card = it.cardRef ? ` card=${it.cardRef}` : "";
75474
76339
  const link = it.display === "link" ? " link" : "";
75475
- console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}${link}`);
76340
+ const shared = it.shareUrl ? ` ${it.shareUrl}` : "";
76341
+ console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}${link}${shared}`);
75476
76342
  }
75477
76343
  return;
75478
76344
  }
75479
- if (positionals[0] === "show" && positionals[1] === "rm") {
76345
+ const showSub = positionals[1];
76346
+ if (positionals[0] === "show" && (showSub === "rm" || showSub === "share" || showSub === "unshare")) {
75480
76347
  const arg = positionals[2];
75481
76348
  if (!arg) {
75482
- process.stderr.write(`${RED3}Usage: tfa-cli show rm <ref|alias>${RESET2}
76349
+ process.stderr.write(`${RED3}Usage: tfa-cli show ${showSub} <ref|alias>${RESET2}
75483
76350
  `);
75484
76351
  process.exit(2);
75485
76352
  }
@@ -75487,18 +76354,30 @@ async function main() {
75487
76354
  const todoId = sep >= 0 ? arg.slice(0, sep) : getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75488
76355
  const key = sep >= 0 ? arg.slice(sep + 1) : arg;
75489
76356
  if (!todoId || !key) {
75490
- process.stderr.write(`${RED3}show rm: need <todoId>:<alias> (no current todo)${RESET2}
76357
+ process.stderr.write(`${RED3}show ${showSub}: need <todoId>:<alias> (no current todo)${RESET2}
75491
76358
  `);
75492
76359
  process.exit(2);
75493
76360
  }
75494
- await api2.removeShow(todoId, key);
75495
- process.stderr.write(`${GREEN2}\u2705 removed ${todoId}:${key}${RESET2}
76361
+ if (showSub === "rm") {
76362
+ await api2.removeShow(todoId, key);
76363
+ process.stderr.write(`${GREEN2}\u2705 removed ${todoId}:${key}${RESET2}
75496
76364
  `);
76365
+ } else if (showSub === "share") {
76366
+ const res = await api2.shareShow(todoId, key);
76367
+ if (args.json)
76368
+ console.log(JSON.stringify(res, null, 2));
76369
+ else
76370
+ console.log(res.url);
76371
+ } else {
76372
+ await api2.unshareShow(todoId, key);
76373
+ process.stderr.write(`${GREEN2}\u2705 ${todoId}:${key} is private again${RESET2}
76374
+ `);
76375
+ }
75497
76376
  return;
75498
76377
  }
75499
76378
  if (positionals[0] === "show") {
75500
76379
  const [, filePath, todoArg] = positionals;
75501
- const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
76380
+ const todoId = todoArg || aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75502
76381
  if (!filePath || !todoId) {
75503
76382
  process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link]${RESET2}
75504
76383
  `);
@@ -75517,13 +76396,13 @@ async function main() {
75517
76396
  }
75518
76397
  name = "stdin";
75519
76398
  } else {
75520
- const abs = resolve4(filePath);
75521
- if (!existsSync2(abs)) {
76399
+ const abs = resolve5(filePath);
76400
+ if (!existsSync4(abs)) {
75522
76401
  process.stderr.write(`${RED3}File not found: ${filePath}${RESET2}
75523
76402
  `);
75524
76403
  process.exit(1);
75525
76404
  }
75526
- blob = new Blob([readFileSync3(abs)], { type: getMimeTypeFromFilename(abs) || undefined });
76405
+ blob = new Blob([readFileSync5(abs)], { type: getMimeTypeFromFilename(abs) || undefined });
75527
76406
  name = path4.basename(filePath);
75528
76407
  }
75529
76408
  const res = await api2.showFile(todoId, blob, name, {
@@ -75536,12 +76415,12 @@ async function main() {
75536
76415
  if (args.json)
75537
76416
  console.log(JSON.stringify(res, null, 2));
75538
76417
  else
75539
- console.log(res.url ? `${res.ref} ${res.url}` : res.ref);
76418
+ console.log(res.shareUrl ? `${res.ref} ${res.shareUrl}` : res.ref);
75540
76419
  return;
75541
76420
  }
75542
76421
  if (positionals[0] === "open") {
75543
76422
  const [, url2, todoArg] = positionals;
75544
- const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
76423
+ const todoId = todoArg || aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75545
76424
  if (!url2 || !todoId) {
75546
76425
  process.stderr.write(`${RED3}Usage: tfa-cli open <url> [todo-id]${RESET2}
75547
76426
  `);
@@ -75855,7 +76734,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
75855
76734
  const wsReady = ws ? ws.connect() : null;
75856
76735
  const bridgeReady = !args["no-bridge"] && !args["no-watch"] && !args.isolated ? ensureBridgeRunning2(apiUrl, apiKey) : null;
75857
76736
  const isolatedTodoId = args.isolated ? randomUUID3() : undefined;
75858
- const mayfly = isolatedTodoId ? await spawnMayflyBridge(apiUrl, isolatedTodoId, realpathSync2(resolve4(args.path || ".")), { debug: !!args.debug, apiKey }) : null;
76737
+ const mayfly = isolatedTodoId ? await spawnMayflyBridge(apiUrl, isolatedTodoId, realpathSync2(resolve5(args.path || ".")), { debug: !!args.debug, apiKey }) : null;
75859
76738
  if (mayfly) {
75860
76739
  process.stderr.write(`${DIM2}Isolated bridge:${RESET2} ${CYAN2}mayfly-${isolatedTodoId}${RESET2}
75861
76740
  `);
@@ -75887,7 +76766,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
75887
76766
  cfgScope.setDefaultAgent(getDisplayName(preMatchedAgent), preMatchedAgent);
75888
76767
  } else {
75889
76768
  const pathArg = args.path || ".";
75890
- const resolved = realpathSync2(resolve4(pathArg));
76769
+ const resolved = realpathSync2(resolve5(pathArg));
75891
76770
  const matches = await api2.listAgentSettings({ workspacePath: resolved });
75892
76771
  if (matches.length > 0) {
75893
76772
  preMatchedAgent = matches[0];
@@ -75960,7 +76839,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
75960
76839
  if (args.model)
75961
76840
  agent2 = { ...agent2, model: args.model };
75962
76841
  if (args["raw-sysmsg"]) {
75963
- const sysmsg = readFileSync3(resolve4(args["raw-sysmsg"]), "utf-8");
76842
+ const sysmsg = readFileSync5(resolve5(args["raw-sysmsg"]), "utf-8");
75964
76843
  agent2 = { ...agent2, systemMessage: sysmsg, systemMessageMode: "raw" };
75965
76844
  }
75966
76845
  if (args["allow-all"]) {