@todoforai/cli 0.1.71 → 0.1.72
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 +91 -20
- package/package.json +1 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -44079,7 +44079,9 @@ var SERVER_TO_FRONTENDS = {
|
|
|
44079
44079
|
updated: (userId) => `${"business_context:updated" /* BUSINESS_CONTEXT_UPDATED */}:${userId}`
|
|
44080
44080
|
},
|
|
44081
44081
|
resource: {
|
|
44082
|
-
contentUpdated: (userId) => `${"resource:content_updated" /* RESOURCE_CONTENT_UPDATED */}:${userId}
|
|
44082
|
+
contentUpdated: (userId) => `${"resource:content_updated" /* RESOURCE_CONTENT_UPDATED */}:${userId}`,
|
|
44083
|
+
contentUpdatedInTodo: (todoId) => `${"resource:content_updated" /* RESOURCE_CONTENT_UPDATED */}:todo:${todoId}`,
|
|
44084
|
+
contentUpdatedInProject: (projectId) => `${"resource:content_updated" /* RESOURCE_CONTENT_UPDATED */}:project:${projectId}`
|
|
44083
44085
|
},
|
|
44084
44086
|
hexGrid: {
|
|
44085
44087
|
updated: (projectId) => `${"hexgrid:updated" /* HEXGRID_UPDATED */}:${projectId}`
|
|
@@ -44132,6 +44134,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
44132
44134
|
start_universal: (todoId) => `${"block:start_universal" /* BLOCK_START_UNIVERSAL */}:${todoId}`,
|
|
44133
44135
|
message: (todoId) => `${"block:message" /* BLOCK_MESSAGE */}:${todoId}`,
|
|
44134
44136
|
end: (todoId) => `${"block:end" /* BLOCK_END */}:${todoId}`,
|
|
44137
|
+
toolDelta: (todoId) => `${"block:tool_delta" /* BLOCK_TOOL_DELTA */}:${todoId}`,
|
|
44135
44138
|
new_message: (todoId) => `${"todo:new_message" /* TODO_NEW_MESSAGE */}:${todoId}`,
|
|
44136
44139
|
sh_msg_result: (todoId) => `${"block:sh_msg_result" /* BLOCK_SH_MSG_RESULT */}:${todoId}`,
|
|
44137
44140
|
sh_msg_start_result: (todoId) => `${"block:sh_msg_start" /* BLOCK_SH_MSG_START */}:${todoId}`,
|
|
@@ -44218,6 +44221,7 @@ var USER_HEADINGS = new Set([
|
|
|
44218
44221
|
]);
|
|
44219
44222
|
// ../packages/shared-fbe/src/bridgeProtocol.ts
|
|
44220
44223
|
var ADB_DATA_CHUNK = 40 * 1024;
|
|
44224
|
+
var SHELL_DATA_CHUNK = 40 * 1024;
|
|
44221
44225
|
// ../packages/shared-fbe/src/bashPatterns.ts
|
|
44222
44226
|
function splitShellCommands(input) {
|
|
44223
44227
|
const parts = [];
|
|
@@ -45607,6 +45611,16 @@ var tool_catalog_default = {
|
|
|
45607
45611
|
installCmd: "npm install -g @todoforai/tfa-summary",
|
|
45608
45612
|
internal: true
|
|
45609
45613
|
},
|
|
45614
|
+
"tfa-cat": {
|
|
45615
|
+
category: "todoforai",
|
|
45616
|
+
pkg: "tfa-cat",
|
|
45617
|
+
installer: "system",
|
|
45618
|
+
label: "Cat",
|
|
45619
|
+
summary: "Show images and PDFs from the shell to the agent.",
|
|
45620
|
+
description: "`cat` that lets you SEE media: `tfa-cat shot.png` (or `… | tfa-cat`) attaches images/PDFs to this bash result; text prints like cat; its stdout redirected/piped → raw bytes. Also `todoforai:<path>` files (`tfa-cat todoforai:todos/<id>/data.csv > data.csv`; binaries like docx/xlsx: redirect, then edit with your tools). Big text costs context: read what you need (`| head`, `| sed -n 40,80p`, `| grep -n`).",
|
|
45621
|
+
versionCmd: "tfa-cat --version 2>/dev/null | sed -n 1p",
|
|
45622
|
+
internal: true
|
|
45623
|
+
},
|
|
45610
45624
|
"tfa-handoff": {
|
|
45611
45625
|
preinstallCloud: true,
|
|
45612
45626
|
category: "todoforai",
|
|
@@ -50352,6 +50366,10 @@ var TOPICS = {
|
|
|
50352
50366
|
channel: (p) => SERVER_TO_FRONTENDS.todo.msgStopSequence(p.todoId),
|
|
50353
50367
|
audience: "frontend"
|
|
50354
50368
|
},
|
|
50369
|
+
["block:tool_delta" /* BLOCK_TOOL_DELTA */]: {
|
|
50370
|
+
channel: (p) => SERVER_TO_FRONTENDS.todo.toolDelta(p.todoId),
|
|
50371
|
+
audience: "frontend"
|
|
50372
|
+
},
|
|
50355
50373
|
["block:diff_result" /* BLOCK_DIFF_RESULT */]: {
|
|
50356
50374
|
channel: (p) => SERVER_TO_FRONTENDS.todo.diff_result(p.todoId),
|
|
50357
50375
|
audience: "frontend"
|
|
@@ -50427,7 +50445,7 @@ var package_default = {
|
|
|
50427
50445
|
type: "git",
|
|
50428
50446
|
url: "git+https://github.com/todoforai/cli.git"
|
|
50429
50447
|
},
|
|
50430
|
-
version: "0.1.
|
|
50448
|
+
version: "0.1.72",
|
|
50431
50449
|
type: "module",
|
|
50432
50450
|
bin: {
|
|
50433
50451
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -50496,7 +50514,7 @@ Examples:
|
|
|
50496
50514
|
tfa-cli show list|ref|rm … # List / re-surface / take down shown files
|
|
50497
50515
|
tfa-cli open <url> [todo-id] # Live url preview in the chat
|
|
50498
50516
|
tfa-cli connect <key>… [--reason "why"] # Sign-in card for connectors on this machine (several keys = one choice)
|
|
50499
|
-
tfa-cli recommend --template <id>
|
|
50517
|
+
tfa-cli recommend --template <id> | --title "…" [--info] [--source "label|ref|url"] # ✧ proposal card, or an ⓘ info row
|
|
50500
50518
|
tfa-cli inbox [needs-you|messages|activity] [--unread] # Read the notification feed (the bell)
|
|
50501
50519
|
tfa-cli inbox seen [tab] # Mark notifications read
|
|
50502
50520
|
tfa-cli notify [--to <email>] "Title" "message" [--href /t/<id>] [--url <link>] # Note to your own user, or --to a project member (Messages tab + phone push)
|
|
@@ -50624,6 +50642,8 @@ function parseCliArgs() {
|
|
|
50624
50642
|
inspect: { type: "string", short: "i" },
|
|
50625
50643
|
template: { type: "string", short: "t" },
|
|
50626
50644
|
note: { type: "string" },
|
|
50645
|
+
info: { type: "boolean", default: false },
|
|
50646
|
+
source: { type: "string" },
|
|
50627
50647
|
priority: { type: "string" },
|
|
50628
50648
|
title: { type: "string" },
|
|
50629
50649
|
alias: { type: "string" },
|
|
@@ -51400,6 +51420,16 @@ class ScopedConfig {
|
|
|
51400
51420
|
s.recent_projects = [{ id, name: name || id }, ...s.recent_projects.filter((p) => p.id !== id)].slice(0, 10);
|
|
51401
51421
|
this.store.save();
|
|
51402
51422
|
}
|
|
51423
|
+
cachedUserId(tokenFp) {
|
|
51424
|
+
const s = this.data;
|
|
51425
|
+
return s.user_id && s.user_id_token_fp === tokenFp ? s.user_id : null;
|
|
51426
|
+
}
|
|
51427
|
+
setUserId(id, tokenFp) {
|
|
51428
|
+
const s = this.data;
|
|
51429
|
+
s.user_id = id;
|
|
51430
|
+
s.user_id_token_fp = tokenFp;
|
|
51431
|
+
this.store.save();
|
|
51432
|
+
}
|
|
51403
51433
|
setDefaultAgent(name, settings) {
|
|
51404
51434
|
const s = this.data;
|
|
51405
51435
|
s.default_agent_name = name;
|
|
@@ -53447,10 +53477,9 @@ Examples:
|
|
|
53447
53477
|
tfa-cli agent update <agent> model=anthropic:anthropic/claude-opus-5 temperature=0.5
|
|
53448
53478
|
tfa-cli agent update <agent> sysmsg="You are a terse video editor."
|
|
53449
53479
|
tfa-cli agent create name=Reviewer model=claude sysmsg="Review diffs only."
|
|
53480
|
+
tfa-cli agent update <agent> judgeSysmsg="Ask before sending email." (approval checker; ""=off, null=inherit global)
|
|
53450
53481
|
|
|
53451
|
-
|
|
53452
|
-
|
|
53453
|
-
From an agent shell (dst_ token) permissions, judgeSysmsg, edgesMcpConfigs, devicesConfig and delete are refused by the backend.
|
|
53482
|
+
From an agent shell (dst_ token) permissions, edgesMcpConfigs, devicesConfig and delete are refused by the backend.
|
|
53454
53483
|
`);
|
|
53455
53484
|
}
|
|
53456
53485
|
var FIELD_ALIASES = {
|
|
@@ -53852,6 +53881,7 @@ Fields (PUT /todos/{id}):
|
|
|
53852
53881
|
schedule unix ms timestamp, 0 = immediate (alias: scheduledTimestamp)
|
|
53853
53882
|
agent agentSettingsId (alias: agentSettingsId)
|
|
53854
53883
|
public true|false — refused from an agent shell (isPublic)
|
|
53884
|
+
kind task|info — promote an ⓘ info row to a task (or back)
|
|
53855
53885
|
|
|
53856
53886
|
tfa-cli todo message <todo-id|-> <message-id> <field=value>…
|
|
53857
53887
|
Edit a SCHEDULED (not yet run) message: content, schedule (unix ms;
|
|
@@ -54469,19 +54499,22 @@ var VALID_STATUS = new Set(Object.values(TodoStatus));
|
|
|
54469
54499
|
var isOpen = (s) => !CLOSED.has(s);
|
|
54470
54500
|
function printListTodosHelp() {
|
|
54471
54501
|
process.stderr.write(`
|
|
54472
|
-
todoforai-cli list — list todos in a project (recent first)
|
|
54502
|
+
todoforai-cli list — list YOUR todos in a project (recent first)
|
|
54503
|
+
|
|
54504
|
+
Default: only todos you opened or wrote in. --project-wide shows everyone's.
|
|
54473
54505
|
|
|
54474
54506
|
Usage:
|
|
54475
54507
|
todoforai-cli list [flags]
|
|
54476
54508
|
|
|
54477
54509
|
Flags:
|
|
54510
|
+
--project-wide Every member's todos, not just yours
|
|
54478
54511
|
-n, --limit <n> Max rows to show (default: 30)
|
|
54479
54512
|
--cursor <n> Fetch todos older than this cursor (sort field, epoch ms)
|
|
54480
54513
|
--since <t> Only todos with sort field >= t (inclusive)
|
|
54481
54514
|
--until <t> Only todos with sort field < t (exclusive)
|
|
54482
54515
|
t = epoch ms | YYYY-MM-DD (local midnight) | ISO datetime | 12h / 3d ago
|
|
54483
54516
|
--sort <key> activity (lastActivityAt, default) | created (createdAt)
|
|
54484
|
-
--contributor <u> Only todos a member opened/wrote in (id or email; comma = any)
|
|
54517
|
+
--contributor <u> Only todos a member opened/wrote in (id or email; comma = any; replaces the "mine" default)
|
|
54485
54518
|
--page-size <n> Backend page size per request (default: min(limit, 400), max: 400)
|
|
54486
54519
|
--search <text> Search content/status on the backend
|
|
54487
54520
|
-s, --status <S[,S2]> Filter by status (comma-separated, union).
|
|
@@ -54491,7 +54524,8 @@ Flags:
|
|
|
54491
54524
|
-h, --help Show this help
|
|
54492
54525
|
|
|
54493
54526
|
Examples:
|
|
54494
|
-
todoforai-cli list # 30 most recent open todos
|
|
54527
|
+
todoforai-cli list # 30 most recent open todos of yours
|
|
54528
|
+
todoforai-cli list --project-wide # … of everyone in the project
|
|
54495
54529
|
todoforai-cli list -n 50 # last 50 open
|
|
54496
54530
|
todoforai-cli list --cursor 1719234567890 # next page
|
|
54497
54531
|
todoforai-cli list --all # include DONE
|
|
@@ -54583,7 +54617,7 @@ function matchesRequestedStatus(t, requested, wanted, wantOpen) {
|
|
|
54583
54617
|
const s = String(t.status).toUpperCase();
|
|
54584
54618
|
return wantOpen && isOpen(s) || (wanted?.has(s) ?? false);
|
|
54585
54619
|
}
|
|
54586
|
-
async function listTodosCommand(api, defaultProjectId, argv) {
|
|
54620
|
+
async function listTodosCommand(api, defaultProjectId, argv, resolveUserId) {
|
|
54587
54621
|
const { values } = parseArgs2({
|
|
54588
54622
|
args: argv,
|
|
54589
54623
|
options: {
|
|
@@ -54595,6 +54629,7 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54595
54629
|
until: { type: "string" },
|
|
54596
54630
|
sort: { type: "string" },
|
|
54597
54631
|
contributor: { type: "string" },
|
|
54632
|
+
"project-wide": { type: "boolean", default: false },
|
|
54598
54633
|
status: { type: "string", short: "s" },
|
|
54599
54634
|
all: { type: "boolean", short: "A", default: false },
|
|
54600
54635
|
project: { type: "string" },
|
|
@@ -54627,7 +54662,15 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54627
54662
|
if (until !== undefined)
|
|
54628
54663
|
cursor = cursor === undefined ? until : Math.min(cursor, until);
|
|
54629
54664
|
const search = values.search ? String(values.search) : undefined;
|
|
54630
|
-
|
|
54665
|
+
let contributorIds = values.contributor ? await resolveContributors(api, projectId, String(values.contributor)) : undefined;
|
|
54666
|
+
let mineOnly = false;
|
|
54667
|
+
if (!contributorIds && !values["project-wide"] && resolveUserId) {
|
|
54668
|
+
const me = await resolveUserId();
|
|
54669
|
+
if (me) {
|
|
54670
|
+
contributorIds = [me];
|
|
54671
|
+
mineOnly = true;
|
|
54672
|
+
}
|
|
54673
|
+
}
|
|
54631
54674
|
const requested = values.status ? String(values.status).split(",").map((s) => s.trim().toUpperCase()).filter(Boolean) : null;
|
|
54632
54675
|
const unknown = requested?.filter((s) => s !== "OPEN" && !VALID_STATUS.has(s)) ?? [];
|
|
54633
54676
|
if (unknown.length)
|
|
@@ -54683,8 +54726,9 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54683
54726
|
`);
|
|
54684
54727
|
return;
|
|
54685
54728
|
}
|
|
54729
|
+
const scopeHint = mineOnly ? " of yours (--project-wide for everyone's)" : "";
|
|
54686
54730
|
if (!visible.length) {
|
|
54687
|
-
process.stderr.write(`${DIM}(no todos)${RESET}
|
|
54731
|
+
process.stderr.write(`${DIM}(no todos${scopeHint})${RESET}
|
|
54688
54732
|
`);
|
|
54689
54733
|
if (nextCursor !== undefined)
|
|
54690
54734
|
process.stderr.write(`${DIM}Next cursor: ${nextCursor} (re-run with the same filters plus --cursor ${nextCursor})${RESET}
|
|
@@ -54701,7 +54745,7 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54701
54745
|
process.stdout.write(`${sc}${status.padEnd(statusW)}${RESET} ${DIM}${ts}${RESET} ${t.id} ${title}
|
|
54702
54746
|
`);
|
|
54703
54747
|
}
|
|
54704
|
-
process.stderr.write(`${DIM}${visible.length} todo(s)${RESET}
|
|
54748
|
+
process.stderr.write(`${DIM}${visible.length} todo(s)${scopeHint}${RESET}
|
|
54705
54749
|
`);
|
|
54706
54750
|
if (nextCursor !== undefined)
|
|
54707
54751
|
process.stderr.write(`${DIM}Next cursor: ${nextCursor} (re-run with the same filters plus --cursor ${nextCursor})${RESET}
|
|
@@ -55656,7 +55700,7 @@ password=${apiKey}
|
|
|
55656
55700
|
}
|
|
55657
55701
|
|
|
55658
55702
|
// src/index.ts
|
|
55659
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
55703
|
+
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
55660
55704
|
|
|
55661
55705
|
// src/ensure-bridge.ts
|
|
55662
55706
|
import { spawn as spawn4, spawnSync as spawnSync4 } from "child_process";
|
|
@@ -80376,13 +80420,25 @@ async function main() {
|
|
|
80376
80420
|
}
|
|
80377
80421
|
if (positionals[0] === "recommend") {
|
|
80378
80422
|
const templateId = args.template || positionals[1];
|
|
80379
|
-
|
|
80380
|
-
|
|
80423
|
+
const inlineTitle = !templateId && args.title?.trim();
|
|
80424
|
+
if (!templateId && !inlineTitle) {
|
|
80425
|
+
process.stderr.write(`${RED3}Usage: tfa-cli recommend --template <id> | --title "..." [--info] [--source "label|ref|url|from"] [--note "why"] [--priority high|medium|low] [--group <slug> [--group-name "..."] [--group-description "..."]] [--project <id>]${RESET2}
|
|
80381
80426
|
`);
|
|
80382
|
-
process.stderr.write(`${DIM2}
|
|
80427
|
+
process.stderr.write(`${DIM2}--info: an information row (no \u2727, nothing to accept). --source: provenance; the same label|ref never creates a second row.${RESET2}
|
|
80383
80428
|
`);
|
|
80384
80429
|
process.exit(2);
|
|
80385
80430
|
}
|
|
80431
|
+
const source = (() => {
|
|
80432
|
+
if (!args.source)
|
|
80433
|
+
return;
|
|
80434
|
+
const [label, ref, url2, from] = String(args.source).split("|");
|
|
80435
|
+
if (!label || !ref) {
|
|
80436
|
+
process.stderr.write(`${RED3}--source needs at least "label|ref"${RESET2}
|
|
80437
|
+
`);
|
|
80438
|
+
process.exit(2);
|
|
80439
|
+
}
|
|
80440
|
+
return { label, ref, ...url2 ? { url: url2 } : {}, ...from ? { from } : {} };
|
|
80441
|
+
})();
|
|
80386
80442
|
let projectId2 = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
80387
80443
|
if (!projectId2) {
|
|
80388
80444
|
const projects2 = await api2.listProjects();
|
|
@@ -80401,7 +80457,9 @@ async function main() {
|
|
|
80401
80457
|
}
|
|
80402
80458
|
const rec = await api2.recommend({
|
|
80403
80459
|
projectId: projectId2,
|
|
80404
|
-
specId: templateId,
|
|
80460
|
+
...templateId ? { specId: templateId } : { template: { name: inlineTitle, body: args.note || inlineTitle } },
|
|
80461
|
+
...args.info ? { todoKind: "info" } : {},
|
|
80462
|
+
...source ? { source } : {},
|
|
80405
80463
|
...args.title ? { title: args.title } : {},
|
|
80406
80464
|
...args.note ? { note: args.note } : {},
|
|
80407
80465
|
...priority ? { priority } : {},
|
|
@@ -80412,7 +80470,7 @@ async function main() {
|
|
|
80412
80470
|
if (args.json)
|
|
80413
80471
|
console.log(JSON.stringify(rec, null, 2));
|
|
80414
80472
|
else
|
|
80415
|
-
process.stderr.write(`${GREEN2}\u2705
|
|
80473
|
+
process.stderr.write(`${GREEN2}\u2705 ${args.info ? "\u24D8 info" : "\u2727 proposal"} ${rec?.id ?? ""} on project ${projectId2}${RESET2}
|
|
80416
80474
|
`);
|
|
80417
80475
|
return;
|
|
80418
80476
|
}
|
|
@@ -80515,7 +80573,20 @@ async function main() {
|
|
|
80515
80573
|
defaultProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
80516
80574
|
}
|
|
80517
80575
|
const sub = process.argv.slice(process.argv.indexOf(positionals[0]) + 1);
|
|
80518
|
-
|
|
80576
|
+
const tokenFp = createHash3("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
80577
|
+
const resolveUserId = async () => {
|
|
80578
|
+
const envId = getEnv("USER_ID");
|
|
80579
|
+
if (envId)
|
|
80580
|
+
return envId;
|
|
80581
|
+
const cached2 = cfgScope.cachedUserId(tokenFp);
|
|
80582
|
+
if (cached2)
|
|
80583
|
+
return cached2;
|
|
80584
|
+
const v = await api2.validateApiKey();
|
|
80585
|
+
if (v.userId)
|
|
80586
|
+
cfgScope.setUserId(v.userId, tokenFp);
|
|
80587
|
+
return v.userId;
|
|
80588
|
+
};
|
|
80589
|
+
await listTodosCommand(api2, defaultProjectId ?? undefined, sub, resolveUserId);
|
|
80519
80590
|
return;
|
|
80520
80591
|
}
|
|
80521
80592
|
if (args.inspect !== undefined) {
|