@todoforai/cli 0.1.56 → 0.1.58
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.
- package/dist/todoforai-cli.js +56 -22
- package/package.json +1 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -42767,9 +42767,9 @@ class ApiClient {
|
|
|
42767
42767
|
}
|
|
42768
42768
|
return h;
|
|
42769
42769
|
}
|
|
42770
|
-
async request(method, endpoint, body) {
|
|
42770
|
+
async request(method, endpoint, body, timeoutMs = 30000) {
|
|
42771
42771
|
const url = `${this.apiUrl}${endpoint.replace(/^\/api\/v1/, restBasePath(this.apiKey))}`;
|
|
42772
|
-
const opts = { method, headers: this.headers, signal: AbortSignal.timeout(
|
|
42772
|
+
const opts = { method, headers: this.headers, signal: AbortSignal.timeout(timeoutMs) };
|
|
42773
42773
|
if (body)
|
|
42774
42774
|
opts.body = JSON.stringify(body);
|
|
42775
42775
|
const res = await fetch(url, opts);
|
|
@@ -42900,7 +42900,7 @@ class ApiClient {
|
|
|
42900
42900
|
return this.request("GET", `/api/v1/projects/${projectId}/voice-sources`);
|
|
42901
42901
|
}
|
|
42902
42902
|
collectVoiceSource(projectId, channel, opts) {
|
|
42903
|
-
return this.request("POST", "/api/v1/business-context/collect-voice-source", { projectId, channel, ...opts });
|
|
42903
|
+
return this.request("POST", "/api/v1/business-context/collect-voice-source", { projectId, channel, ...opts }, 130000);
|
|
42904
42904
|
}
|
|
42905
42905
|
removeVoiceSource(projectId, channel, opts = {}) {
|
|
42906
42906
|
return this.request("POST", "/api/v1/business-context/remove-voice-source", { projectId, channel, ...opts });
|
|
@@ -42939,6 +42939,12 @@ class ApiClient {
|
|
|
42939
42939
|
sendFeedMessage(msg) {
|
|
42940
42940
|
return this.request("POST", "/api/v1/feed/message", msg);
|
|
42941
42941
|
}
|
|
42942
|
+
getProjectMembers(projectId) {
|
|
42943
|
+
return this.request("GET", `/api/v1/projects/${projectId}/members`);
|
|
42944
|
+
}
|
|
42945
|
+
sendMemberMessage(msg) {
|
|
42946
|
+
return this.request("POST", "/api/v1/feed/message/member", msg);
|
|
42947
|
+
}
|
|
42942
42948
|
listProjectSurfaces(projectId) {
|
|
42943
42949
|
return this.request("GET", `/api/v1/projects/${projectId}/surfaces`);
|
|
42944
42950
|
}
|
|
@@ -43882,7 +43888,7 @@ function manualAnswersOf(sources) {
|
|
|
43882
43888
|
return out;
|
|
43883
43889
|
}
|
|
43884
43890
|
var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
|
|
43885
|
-
var senderLabel = (s) => s.kind === "team" ? `${s.name} · TODO for AI team` : `${s.name} · your agent`;
|
|
43891
|
+
var senderLabel = (s) => s.kind === "team" ? `${s.name} · TODO for AI team` : s.kind === "member" ? `${s.name} · teammate` : `${s.name} · your agent`;
|
|
43886
43892
|
// ../packages/shared-fbe/src/templateBody.ts
|
|
43887
43893
|
var USER_HEADINGS = new Set([
|
|
43888
43894
|
"your task",
|
|
@@ -44861,7 +44867,7 @@ var tool_catalog_default = {
|
|
|
44861
44867
|
installer: "npm",
|
|
44862
44868
|
label: "TODOforAI",
|
|
44863
44869
|
summary: "Create and follow up on your TODOs, and show results in the chat.",
|
|
44864
|
-
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
|
|
44870
|
+
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 `` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows.',
|
|
44865
44871
|
installCmd: "npm install -g @todoforai/cli",
|
|
44866
44872
|
versionCmd: "tfa-cli --version 2>/dev/null | sed -n 1p",
|
|
44867
44873
|
preinstallCloud: true,
|
|
@@ -44987,7 +44993,8 @@ var tool_catalog_default = {
|
|
|
44987
44993
|
installer: "system",
|
|
44988
44994
|
label: "Google Cloud",
|
|
44989
44995
|
summary: "Manage your Google Cloud projects and services.",
|
|
44990
|
-
|
|
44996
|
+
needsPython: true,
|
|
44997
|
+
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"`,
|
|
44991
44998
|
statusCmd: `gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | tr -d '\\r' | grep -m1 .`,
|
|
44992
44999
|
loginCmd: "gcloud auth login --no-launch-browser",
|
|
44993
45000
|
loginNeedsPty: true,
|
|
@@ -45270,7 +45277,7 @@ var tool_catalog_default = {
|
|
|
45270
45277
|
label: "Surface",
|
|
45271
45278
|
summary: "See and control what is on your TODOforAI screen.",
|
|
45272
45279
|
platformAuth: true,
|
|
45273
|
-
description: "The user's live UI from the shell. Everything on screen is a SURFACE holding one ref (a todo id, `<todoId>:<alias>` = a shown artifact, or a url). `describe` lists them (kind, id, ref, focus, embedded-browser CDP URL; `--json`). `open overlay <todoId>` (full page), `open window <ref
|
|
45280
|
+
description: "The user's live UI from the shell. Everything on screen is a SURFACE holding one ref (a todo id, `<todoId>:<alias>` = a shown artifact, or a url). `describe` lists them (kind, id, ref, focus, embedded-browser CDP URL; `--json`). `open overlay <todoId>` (full page), `open window <ref> [--detached]` (floating; --detached = out of the grid, own size, resizable), `open panel changes|preview|browser|terminal [--url]` (a todo's side panel), `open card <ref> [--title] [--group <slug>]` (a persisted board card every member sees); `close|focus <kind> <id>`. Persistent state (plans, reports, live status) → `show chat.panel <todoId> \"...md\"` (stdin ok; re-show updates in place), `clear` removes; events go in chat. `card add|list|get|rm <name>` = project HTML renderers. Exit 2 = no frontend connected.",
|
|
45274
45281
|
versionCmd: "tfa-surface --version 2>/dev/null | sed -n 1p",
|
|
45275
45282
|
installCmd: "npm install -g @todoforai/tfa-surface",
|
|
45276
45283
|
internal: true
|
|
@@ -45844,7 +45851,7 @@ var tool_catalog_default = {
|
|
|
45844
45851
|
logoutCmd: "cd ~ && mcporter config remove blender",
|
|
45845
45852
|
credentialPaths: ["~/.mcporter/mcporter.json"],
|
|
45846
45853
|
description: "Blender 3D via the ahujasid/blender-mcp server through mcporter: `mcporter list blender --schema`, `mcporter call blender.<tool> key=value` (`code=@file.py`; every tool needs `user_prompt`). Workhorse: execute_blender_code (any bpy Python); also scene/object info, viewport screenshot (`--save-images <dir>`), PolyHaven/Sketchfab import. Needs a GUI Blender with the addon server started (socket :9876; headless has none). 'Could not connect' → `blender-mcp install-addon`, enable in Preferences → Add-ons, N-panel → Start MCP Server (scripted: `blender --python s.py`, s.py registers a `bpy.app.timers` calling `bpy.ops.blendermcp.start_server()`). Code runs unguarded: save first (`bpy.ops.wm.save_mainfile()`), approve destructive ops.",
|
|
45847
|
-
versionCmd:
|
|
45854
|
+
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"
|
|
45848
45855
|
},
|
|
45849
45856
|
"google-ads": {
|
|
45850
45857
|
category: "marketing",
|
|
@@ -46116,7 +46123,7 @@ import { parseArgs } from "util";
|
|
|
46116
46123
|
// package.json
|
|
46117
46124
|
var package_default = {
|
|
46118
46125
|
name: "@todoforai/cli",
|
|
46119
|
-
version: "0.1.
|
|
46126
|
+
version: "0.1.58",
|
|
46120
46127
|
type: "module",
|
|
46121
46128
|
bin: {
|
|
46122
46129
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -46186,7 +46193,7 @@ Examples:
|
|
|
46186
46193
|
tfa-cli recommend --template <id> # Add a template as a recommendation card
|
|
46187
46194
|
tfa-cli inbox [needs-you|messages|activity] [--unread] # Read the notification feed (the bell)
|
|
46188
46195
|
tfa-cli inbox seen [tab] # Mark notifications read
|
|
46189
|
-
tfa-cli notify "Title" "message" [--href /t/<id>] # Note to
|
|
46196
|
+
tfa-cli notify [--to <email>] "Title" "message" [--href /t/<id>] # Note to your own user, or --to a project member (Messages tab + phone push)
|
|
46190
46197
|
tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> links for a project you own
|
|
46191
46198
|
tfa-cli next [--direction "<text>"] # Analyzer growth recommendation cards
|
|
46192
46199
|
tfa-cli <command> help # Details per command
|
|
@@ -46304,6 +46311,7 @@ function parseCliArgs() {
|
|
|
46304
46311
|
ttl: { type: "string" },
|
|
46305
46312
|
subject: { type: "string" },
|
|
46306
46313
|
href: { type: "string" },
|
|
46314
|
+
to: { type: "string" },
|
|
46307
46315
|
unread: { type: "boolean", default: false },
|
|
46308
46316
|
limit: { type: "string" },
|
|
46309
46317
|
resume: { type: "string", short: "r" },
|
|
@@ -49524,6 +49532,7 @@ tfa-cli project — edit the current project
|
|
|
49524
49532
|
|
|
49525
49533
|
Usage:
|
|
49526
49534
|
tfa-cli project list Projects you can access (* = default)
|
|
49535
|
+
tfa-cli project members Who is on it (notify --to <email> reaches them)
|
|
49527
49536
|
tfa-cli project set <field=value>… name=… description=…
|
|
49528
49537
|
(isPublic refused from an agent shell)
|
|
49529
49538
|
tfa-cli project default Make it the project you land on at /
|
|
@@ -49625,6 +49634,17 @@ async function projectCommand(api, positionals, args, projectId) {
|
|
|
49625
49634
|
}
|
|
49626
49635
|
if (!projectId)
|
|
49627
49636
|
fail(NO_PROJECT);
|
|
49637
|
+
if (sub === "members") {
|
|
49638
|
+
const members = await api.getProjectMembers(projectId);
|
|
49639
|
+
if (args.json) {
|
|
49640
|
+
console.log(JSON.stringify(members, null, 2));
|
|
49641
|
+
return;
|
|
49642
|
+
}
|
|
49643
|
+
for (const m of members)
|
|
49644
|
+
process.stderr.write(` ${m.name ?? ""} ${m.email ?? ""} ${DIM}${m.id}${RESET}
|
|
49645
|
+
`);
|
|
49646
|
+
return;
|
|
49647
|
+
}
|
|
49628
49648
|
if (sub === "set") {
|
|
49629
49649
|
if (!rest.length)
|
|
49630
49650
|
fail("Usage: tfa-cli project set <field=value>…");
|
|
@@ -50557,16 +50577,19 @@ Filters (same as the bell's chips):
|
|
|
50557
50577
|
}
|
|
50558
50578
|
function printNotifyHelp() {
|
|
50559
50579
|
process.stderr.write(`
|
|
50560
|
-
tfa-cli notify — leave a note
|
|
50580
|
+
tfa-cli notify — leave a note in someone's Messages inbox (+ phone push)
|
|
50561
50581
|
|
|
50562
50582
|
Usage:
|
|
50563
50583
|
tfa-cli notify "Title" "message" [--href /t/<todo-id>] [--subject <key>] [--json]
|
|
50584
|
+
tfa-cli notify --to <email> "Title" "message" [--project <id>]
|
|
50564
50585
|
echo "message" | tfa-cli notify "Title"
|
|
50565
50586
|
|
|
50566
|
-
Recipient:
|
|
50567
|
-
|
|
50587
|
+
Recipient: your own user by default. --to <email> reaches a TEAMMATE: someone who is a member
|
|
50588
|
+
of the current project (--project, $TODOFORAI_PROJECT_ID or your default), shown as from you.
|
|
50589
|
+
Anyone else is refused. List them with: tfa-cli project members
|
|
50568
50590
|
--subject dedupes: the same subject is delivered once (default: hash of title+message,
|
|
50569
|
-
so a retry never double-notifies). --href must be an in-app path.
|
|
50591
|
+
so a retry never double-notifies). --href must be an in-app path.
|
|
50592
|
+
Limits: 10 notes/day to yourself, 20/day to teammates.
|
|
50570
50593
|
`);
|
|
50571
50594
|
}
|
|
50572
50595
|
function tierArg(name) {
|
|
@@ -50618,12 +50641,22 @@ async function inboxCommand(api, positionals, args) {
|
|
|
50618
50641
|
console.log(` ${DIM}${e.href}${RESET}`);
|
|
50619
50642
|
}
|
|
50620
50643
|
}
|
|
50621
|
-
var
|
|
50622
|
-
async function notifyCommand(api, positionals, args) {
|
|
50623
|
-
const
|
|
50624
|
-
const bad = argv.find((a) => RECIPIENT_FLAGS.some((f) => a === f || a.startsWith(`${f}=`)));
|
|
50644
|
+
var OTHER_RECIPIENT_FLAGS = ["--email", "--user", "--recipient", "--cc"];
|
|
50645
|
+
async function notifyCommand(api, positionals, args, projectId) {
|
|
50646
|
+
const bad = process.argv.slice(2).find((a) => OTHER_RECIPIENT_FLAGS.some((f) => a === f || a.startsWith(`${f}=`)));
|
|
50625
50647
|
if (bad) {
|
|
50626
|
-
process.stderr.write(`${RED}
|
|
50648
|
+
process.stderr.write(`${RED}Unknown option ${bad.split("=")[0]}: use --to <email> for a project teammate${RESET}
|
|
50649
|
+
`);
|
|
50650
|
+
process.exit(2);
|
|
50651
|
+
}
|
|
50652
|
+
const to = args.to === undefined ? undefined : String(args.to).trim();
|
|
50653
|
+
if (to === "") {
|
|
50654
|
+
process.stderr.write(`${RED}--to is empty: pass a teammate's email, or drop --to to notify your own user${RESET}
|
|
50655
|
+
`);
|
|
50656
|
+
process.exit(2);
|
|
50657
|
+
}
|
|
50658
|
+
if (to && !projectId) {
|
|
50659
|
+
process.stderr.write(`${RED}--to needs a project: pass --project <id> (the recipient must be a member of it)${RESET}
|
|
50627
50660
|
`);
|
|
50628
50661
|
process.exit(2);
|
|
50629
50662
|
}
|
|
@@ -50635,12 +50668,13 @@ async function notifyCommand(api, positionals, args) {
|
|
|
50635
50668
|
}
|
|
50636
50669
|
const subject = args.subject || createHash("sha256").update(`${title}
|
|
50637
50670
|
${message}`).digest("hex").slice(0, 16);
|
|
50638
|
-
const
|
|
50671
|
+
const msg = { subject, title, message, ...args.href ? { href: String(args.href) } : {} };
|
|
50672
|
+
const row = await (to ? api.sendMemberMessage({ ...msg, projectId, to }) : api.sendFeedMessage(msg));
|
|
50639
50673
|
if (args.json) {
|
|
50640
50674
|
console.log(JSON.stringify(row));
|
|
50641
50675
|
return;
|
|
50642
50676
|
}
|
|
50643
|
-
process.stderr.write(row.created !== false ? `${GREEN}✅ Delivered to your own user's Messages inbox
|
|
50677
|
+
process.stderr.write(row.created !== false ? `${GREEN}✅ Delivered to ${to ?? "your own user"}'s Messages inbox${RESET} ${DIM}(subject ${subject})${RESET}
|
|
50644
50678
|
` : `${DIM}Already sent (subject ${subject}) — nothing new delivered${RESET}
|
|
50645
50679
|
`);
|
|
50646
50680
|
}
|
|
@@ -75353,7 +75387,7 @@ async function main() {
|
|
|
75353
75387
|
return;
|
|
75354
75388
|
}
|
|
75355
75389
|
if (positionals[0] === "notify") {
|
|
75356
|
-
await notifyCommand(api2, positionals, args);
|
|
75390
|
+
await notifyCommand(api2, positionals, args, scopedProject);
|
|
75357
75391
|
return;
|
|
75358
75392
|
}
|
|
75359
75393
|
if (positionals[0] === "delete") {
|