@todoforai/cli 0.1.65 → 0.1.66
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 +158 -77
- package/package.json +1 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -37682,19 +37682,19 @@ var require_doctype = __commonJS((exports) => {
|
|
|
37682
37682
|
return DOCUMENT_MODE.NO_QUIRKS;
|
|
37683
37683
|
};
|
|
37684
37684
|
exports.serializeContent = function(name, publicId, systemId) {
|
|
37685
|
-
let
|
|
37685
|
+
let str2 = "!DOCTYPE ";
|
|
37686
37686
|
if (name) {
|
|
37687
|
-
|
|
37687
|
+
str2 += name;
|
|
37688
37688
|
}
|
|
37689
37689
|
if (publicId) {
|
|
37690
|
-
|
|
37690
|
+
str2 += " PUBLIC " + enquoteDoctypeId(publicId);
|
|
37691
37691
|
} else if (systemId) {
|
|
37692
|
-
|
|
37692
|
+
str2 += " SYSTEM";
|
|
37693
37693
|
}
|
|
37694
37694
|
if (systemId !== null) {
|
|
37695
|
-
|
|
37695
|
+
str2 += " " + enquoteDoctypeId(systemId);
|
|
37696
37696
|
}
|
|
37697
|
-
return
|
|
37697
|
+
return str2;
|
|
37698
37698
|
};
|
|
37699
37699
|
});
|
|
37700
37700
|
|
|
@@ -40266,14 +40266,14 @@ var require_serializer = __commonJS((exports, module) => {
|
|
|
40266
40266
|
this.html += "<" + doctype.serializeContent(name, null, null) + ">";
|
|
40267
40267
|
}
|
|
40268
40268
|
}
|
|
40269
|
-
Serializer.escapeString = function(
|
|
40270
|
-
|
|
40269
|
+
Serializer.escapeString = function(str2, attrMode) {
|
|
40270
|
+
str2 = str2.replace(AMP_REGEX, "&").replace(NBSP_REGEX, " ");
|
|
40271
40271
|
if (attrMode) {
|
|
40272
|
-
|
|
40272
|
+
str2 = str2.replace(DOUBLE_QUOTE_REGEX, """);
|
|
40273
40273
|
} else {
|
|
40274
|
-
|
|
40274
|
+
str2 = str2.replace(LT_REGEX, "<").replace(GT_REGEX, ">");
|
|
40275
40275
|
}
|
|
40276
|
-
return
|
|
40276
|
+
return str2;
|
|
40277
40277
|
};
|
|
40278
40278
|
module.exports = Serializer;
|
|
40279
40279
|
});
|
|
@@ -40664,19 +40664,19 @@ var require_doctype2 = __commonJS((exports) => {
|
|
|
40664
40664
|
return DOCUMENT_MODE.NO_QUIRKS;
|
|
40665
40665
|
};
|
|
40666
40666
|
exports.serializeContent = function(name, publicId, systemId) {
|
|
40667
|
-
let
|
|
40667
|
+
let str2 = "!DOCTYPE ";
|
|
40668
40668
|
if (name) {
|
|
40669
|
-
|
|
40669
|
+
str2 += name;
|
|
40670
40670
|
}
|
|
40671
40671
|
if (publicId) {
|
|
40672
|
-
|
|
40672
|
+
str2 += " PUBLIC " + enquoteDoctypeId(publicId);
|
|
40673
40673
|
} else if (systemId) {
|
|
40674
|
-
|
|
40674
|
+
str2 += " SYSTEM";
|
|
40675
40675
|
}
|
|
40676
40676
|
if (systemId !== null) {
|
|
40677
|
-
|
|
40677
|
+
str2 += " " + enquoteDoctypeId(systemId);
|
|
40678
40678
|
}
|
|
40679
|
-
return
|
|
40679
|
+
return str2;
|
|
40680
40680
|
};
|
|
40681
40681
|
});
|
|
40682
40682
|
|
|
@@ -42981,8 +42981,8 @@ class ApiClient {
|
|
|
42981
42981
|
renameBusinessContext(projectId, name) {
|
|
42982
42982
|
return this.request("PUT", `/api/v1/projects/${projectId}/business-context`, { projectId, name });
|
|
42983
42983
|
}
|
|
42984
|
-
listVoiceSources(projectId) {
|
|
42985
|
-
return this.request("GET", `/api/v1/projects/${projectId}/voice-sources`);
|
|
42984
|
+
listVoiceSources(projectId, scope) {
|
|
42985
|
+
return this.request("GET", `/api/v1/projects/${projectId}/voice-sources${scope ? `?scope=${scope}` : ""}`);
|
|
42986
42986
|
}
|
|
42987
42987
|
collectVoiceSource(projectId, channel, opts) {
|
|
42988
42988
|
return this.request("POST", "/api/v1/business-context/collect-voice-source", { projectId, channel, ...opts }, 130000);
|
|
@@ -42990,8 +42990,8 @@ class ApiClient {
|
|
|
42990
42990
|
removeVoiceSource(projectId, channel, opts = {}) {
|
|
42991
42991
|
return this.request("POST", "/api/v1/business-context/remove-voice-source", { projectId, channel, ...opts });
|
|
42992
42992
|
}
|
|
42993
|
-
refineBrandVoice(projectId, source, correction) {
|
|
42994
|
-
return this.request("POST", "/api/v1/business-context/refine-brand-voice", { projectId, ...source && { source }, ...correction && { correction } });
|
|
42993
|
+
refineBrandVoice(projectId, source, correction, scope) {
|
|
42994
|
+
return this.request("POST", "/api/v1/business-context/refine-brand-voice", { projectId, ...source && { source }, ...correction && { correction }, ...scope && { scope } });
|
|
42995
42995
|
}
|
|
42996
42996
|
getProfile() {
|
|
42997
42997
|
return this.request("GET", "/api/v1/users/profile");
|
|
@@ -43701,6 +43701,7 @@ function qualifiedModelIds(listing, filter) {
|
|
|
43701
43701
|
var HOBBY_ALLOWED = new Set([
|
|
43702
43702
|
"anthropic/claude-sonnet-5.5",
|
|
43703
43703
|
"anthropic/claude-sonnet-5",
|
|
43704
|
+
"anthropic/claude-haiku-5.5",
|
|
43704
43705
|
"anthropic/claude-haiku-4.5"
|
|
43705
43706
|
]);
|
|
43706
43707
|
var USAGE_WINDOWS = {
|
|
@@ -43836,6 +43837,53 @@ function getMimeTypeFromFilename(filename) {
|
|
|
43836
43837
|
const ext = filename.toLowerCase().split(".").pop();
|
|
43837
43838
|
return EXT_TO_MIME[ext || ""] || "";
|
|
43838
43839
|
}
|
|
43840
|
+
function truncatedImageReason(data) {
|
|
43841
|
+
const n = data.length;
|
|
43842
|
+
const ascii = (i, len) => String.fromCharCode(...data.subarray(i, i + len));
|
|
43843
|
+
if (n >= 4 && data[0] === 255 && data[1] === 216 && data[2] === 255) {
|
|
43844
|
+
let i = 2;
|
|
43845
|
+
while (i + 1 < n) {
|
|
43846
|
+
if (data[i] !== 255)
|
|
43847
|
+
return;
|
|
43848
|
+
const marker = data[i + 1];
|
|
43849
|
+
if (marker === 217)
|
|
43850
|
+
return;
|
|
43851
|
+
if (marker === 255) {
|
|
43852
|
+
i++;
|
|
43853
|
+
continue;
|
|
43854
|
+
}
|
|
43855
|
+
if (marker === 1 || marker >= 208 && marker <= 216) {
|
|
43856
|
+
i += 2;
|
|
43857
|
+
continue;
|
|
43858
|
+
}
|
|
43859
|
+
if (i + 3 >= n)
|
|
43860
|
+
break;
|
|
43861
|
+
i += 2 + (data[i + 2] << 8 | data[i + 3]);
|
|
43862
|
+
if (marker === 218) {
|
|
43863
|
+
while (i + 1 < n && !(data[i] === 255 && data[i + 1] !== 0 && !(data[i + 1] >= 208 && data[i + 1] <= 215)))
|
|
43864
|
+
i++;
|
|
43865
|
+
}
|
|
43866
|
+
}
|
|
43867
|
+
return "JPEG ends before its end-of-image marker (FF D9)";
|
|
43868
|
+
}
|
|
43869
|
+
if (n >= 8 && ascii(0, 8) === `PNG\r
|
|
43870
|
+
\x1A
|
|
43871
|
+
`) {
|
|
43872
|
+
let i = 8;
|
|
43873
|
+
while (i + 8 <= n) {
|
|
43874
|
+
const len = (data[i] << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[i + 3]) >>> 0;
|
|
43875
|
+
if (ascii(i + 4, 4) === "IEND")
|
|
43876
|
+
return i + 12 <= n ? undefined : "PNG ends inside its IEND chunk";
|
|
43877
|
+
i += 12 + len;
|
|
43878
|
+
}
|
|
43879
|
+
return "PNG ends before its IEND chunk";
|
|
43880
|
+
}
|
|
43881
|
+
if (n >= 12 && ascii(0, 4) === "RIFF" && ascii(8, 4) === "WEBP") {
|
|
43882
|
+
const riff = (data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24) >>> 0;
|
|
43883
|
+
return n >= riff + 8 ? undefined : `WebP RIFF header declares ${riff + 8} bytes, file has ${n}`;
|
|
43884
|
+
}
|
|
43885
|
+
return;
|
|
43886
|
+
}
|
|
43839
43887
|
var NEEDS_OBJECT_URL = new Set(Object.entries(mimetypes_default.types).filter(([, meta]) => meta.needsObjectUrl).map(([type]) => type));
|
|
43840
43888
|
var MIME_TO_EXT = {};
|
|
43841
43889
|
for (const [ext, entry] of Object.entries(mimetypes_default.extensions)) {
|
|
@@ -45358,7 +45406,7 @@ var tool_catalog_default = {
|
|
|
45358
45406
|
preinstallCloud: true,
|
|
45359
45407
|
label: "Browser",
|
|
45360
45408
|
summary: "Browse websites automatically: click, type, read and screenshot.",
|
|
45361
|
-
description: "Browser automation over CDP (`open`/`snapshot`/`click`/`type`/`fill`/`read`/`screenshot`/`eval`). `connect '<url>'` to a listed live browser (prefer the user's real Chrome), verify with `snapshot`; if empty, `close` (own session only) and reconnect once. NEVER `close --all`. Shared browser: `tab new` for your own tab, `tab <id>` before each command. No URL listed: cloud → browser-manager-cli; PC with display → Chrome `--remote-debugging-port=9222`, attach `--cdp 9222`.",
|
|
45409
|
+
description: "Browser automation over CDP (`open`/`snapshot`/`click`/`type`/`fill`/`read`/`screenshot`/`eval`). `connect '<url>'` to a listed live browser (prefer the user's real Chrome), verify with `snapshot`; if empty, `close` (own session only) and reconnect once. NEVER `close --all`. Shared browser: `tab new` for your own tab, `tab <id>` before each command. No URL listed: cloud → browser-manager-cli; PC with display → Chrome `--remote-debugging-port=9222`, attach `--cdp 9222`. Never `sleep`: `open` returns after load, but `click`/`fill` fail at once if the element isn't there yet — `wait '<sel>'`, `wait --text 'Done'`, `wait --url '**/dash'`, `wait --load networkidle` or `wait '#spinner' --state hidden` first.",
|
|
45362
45410
|
versionCmd: "agent-browser --version 2>/dev/null | sed -n 1p",
|
|
45363
45411
|
preinstallJarvis: true
|
|
45364
45412
|
},
|
|
@@ -45903,6 +45951,7 @@ var tool_catalog_default = {
|
|
|
45903
45951
|
summary: "Post to your Facebook Page and its linked Instagram account, and handle comments.",
|
|
45904
45952
|
statusCmd: `facebook-pages-api whoami 2>/dev/null | sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m10 .`,
|
|
45905
45953
|
loginCmd: "facebook-pages-api auth --page-id <id> --page-token <token>",
|
|
45954
|
+
oauthBroker: true,
|
|
45906
45955
|
credentialPaths: ["~/.config/facebook-pages-api/credentials.json"],
|
|
45907
45956
|
description: "Facebook Pages CLI (Meta Graph API). `auth --page-id <id> --page-token <token>` (or env FB_PAGE_ID/FB_PAGE_TOKEN). `feed`, `comments`, `insights`, `conversations`, `media`, and `request <GET|POST|DELETE> <path> [--data <json>]` = any Graph edge with the page token attached (use instead of curl). Writes are live: `feed create --published false` for tests; `comments reply` posts publicly — get human approval. Follower totals: `whoami --fields followers_count,fan_count` (many classic insights metrics are retired). Instagram (linked professional account, same page token): `ig whoami|media|get|stories|tags|limit`, `ig publish --image_url U | --video_url U [--media_type STORIES] | --carousel U1,U2 [--caption …]` (public URLs; container → wait → publish; `--create-only` = dry run; 100 posts/24h), `ig comments list|replies|create|reply|hide|delete|toggle`, `ig insights [media_id]` (needs instagram_manage_insights). `--help` for subcommands.",
|
|
45908
45957
|
versionCmd: "facebook-pages-api --version 2>/dev/null | sed -n 1p"
|
|
@@ -50151,7 +50200,7 @@ var package_default = {
|
|
|
50151
50200
|
type: "git",
|
|
50152
50201
|
url: "git+https://github.com/todoforai/cli.git"
|
|
50153
50202
|
},
|
|
50154
|
-
version: "0.1.
|
|
50203
|
+
version: "0.1.66",
|
|
50155
50204
|
type: "module",
|
|
50156
50205
|
bin: {
|
|
50157
50206
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -50251,6 +50300,7 @@ Options:
|
|
|
50251
50300
|
--no-bridge Do not auto-spawn bridge
|
|
50252
50301
|
--json
|
|
50253
50302
|
--detailed | --format-anthropic inspect --json: keep ids/timestamps | Anthropic messages shape
|
|
50303
|
+
--full inspect: no truncation of text, thinking, tool calls and results
|
|
50254
50304
|
--safe Validate API key upfront
|
|
50255
50305
|
--debug, -d
|
|
50256
50306
|
--debug-dump Attach LLM request debug info per turn (requires server grant)
|
|
@@ -50263,8 +50313,9 @@ tfa-cli show — put a file or url into the chat
|
|
|
50263
50313
|
|
|
50264
50314
|
Usage:
|
|
50265
50315
|
tfa-cli show <file|-> [todo-id] Render a file by mimetype (- reads stdin)
|
|
50266
|
-
[--title T] [--alias A] [--mime M] [--card <name>] [--link] [--json]
|
|
50316
|
+
[--title T] [--alias A] [--mime M] [--card <name>] [--link] [--force] [--json]
|
|
50267
50317
|
--link: compact download chip instead of inline render
|
|
50318
|
+
Refuses a likely-truncated image (JPEG/PNG cut before the end marker, WebP shorter than its RIFF size); --force shows it anyway
|
|
50268
50319
|
Prints "<todoId>:<alias|id>". Private: visible to whoever can read the todo.
|
|
50269
50320
|
Re-showing with the same --alias updates the block in place (new version)
|
|
50270
50321
|
--alias <todoId>:<alias> updates that todo's block instead,
|
|
@@ -50342,10 +50393,12 @@ function parseCliArgs() {
|
|
|
50342
50393
|
mime: { type: "string" },
|
|
50343
50394
|
card: { type: "string" },
|
|
50344
50395
|
link: { type: "boolean", default: false },
|
|
50396
|
+
force: { type: "boolean", default: false },
|
|
50345
50397
|
direction: { type: "string" },
|
|
50346
50398
|
url: { type: "string" },
|
|
50347
50399
|
account: { type: "string" },
|
|
50348
50400
|
"from-company": { type: "boolean", default: false },
|
|
50401
|
+
scope: { type: "string" },
|
|
50349
50402
|
"dry-run": { type: "boolean", default: false },
|
|
50350
50403
|
max: { type: "string" },
|
|
50351
50404
|
seed: { type: "string" },
|
|
@@ -50370,6 +50423,7 @@ function parseCliArgs() {
|
|
|
50370
50423
|
"no-edge": { type: "boolean", default: false },
|
|
50371
50424
|
json: { type: "boolean", default: false },
|
|
50372
50425
|
detailed: { type: "boolean", default: false },
|
|
50426
|
+
full: { type: "boolean", default: false },
|
|
50373
50427
|
"format-anthropic": { type: "boolean", default: false },
|
|
50374
50428
|
safe: { type: "boolean", default: false },
|
|
50375
50429
|
debug: { type: "boolean", short: "d", default: false },
|
|
@@ -51268,7 +51322,8 @@ function getFrontendUrl(apiUrl, todoId) {
|
|
|
51268
51322
|
}
|
|
51269
51323
|
|
|
51270
51324
|
// src/inspect.ts
|
|
51271
|
-
var
|
|
51325
|
+
var str = (v) => typeof v === "string" ? v : JSON.stringify(v);
|
|
51326
|
+
var xmlAttr = (v) => JSON.stringify(str(v));
|
|
51272
51327
|
var DROP_IF_EMPTY = new Set(["runMeta", "meta", "attachments", "blocks"]);
|
|
51273
51328
|
var ALWAYS_DROP = new Set(["messageIds", "data"]);
|
|
51274
51329
|
var DETAILED_ONLY = new Set([
|
|
@@ -51494,12 +51549,14 @@ function applySlice(arr, spec) {
|
|
|
51494
51549
|
throw new Error(`Bad slice: '${spec}'`);
|
|
51495
51550
|
return arr.slice(start, end);
|
|
51496
51551
|
}
|
|
51497
|
-
var
|
|
51498
|
-
${DIM}... (${s.length} chars)${RESET}` : s;
|
|
51552
|
+
var truncAt = (s, n) => s.length > n ? s.slice(0, n) + `
|
|
51553
|
+
${DIM}... (${s.length} chars, --full for all)${RESET}` : s;
|
|
51499
51554
|
var indent = (s, pre) => s.split(`
|
|
51500
51555
|
`).join(`
|
|
51501
51556
|
${pre}`);
|
|
51502
|
-
function printFullChat(todo, frontendUrl, slice, mode = "default", format = "compact") {
|
|
51557
|
+
function printFullChat(todo, frontendUrl, slice, mode = "default", format = "compact", full = false) {
|
|
51558
|
+
const out = (s) => process.stdout.write(s);
|
|
51559
|
+
const trunc = (s, n) => full ? s : truncAt(s, n);
|
|
51503
51560
|
const statusColors = {
|
|
51504
51561
|
DONE: GREEN,
|
|
51505
51562
|
READY: GREEN,
|
|
@@ -51513,34 +51570,34 @@ function printFullChat(todo, frontendUrl, slice, mode = "default", format = "com
|
|
|
51513
51570
|
TODO: CYAN
|
|
51514
51571
|
};
|
|
51515
51572
|
const statusColor = statusColors[todo.status] || DIM;
|
|
51516
|
-
|
|
51573
|
+
out(`${BOLD}TODO${RESET} ${todo.id}
|
|
51517
51574
|
`);
|
|
51518
|
-
|
|
51575
|
+
out(`${DIM}Status:${RESET} ${statusColor}${todo.status}${RESET}
|
|
51519
51576
|
`);
|
|
51520
|
-
|
|
51577
|
+
out(`${DIM}URL:${RESET} ${CYAN}${frontendUrl}${RESET}
|
|
51521
51578
|
`);
|
|
51522
|
-
|
|
51579
|
+
out(`${DIM}Created:${RESET} ${new Date(todo.createdAt).toLocaleString()}
|
|
51523
51580
|
`);
|
|
51524
51581
|
if (todo.agentSettingsId)
|
|
51525
|
-
|
|
51582
|
+
out(`${DIM}Agent:${RESET} ${todo.agentSettingsId}
|
|
51526
51583
|
`);
|
|
51527
51584
|
if (slice)
|
|
51528
|
-
|
|
51585
|
+
out(`${DIM}Slice:${RESET} [${slice}]
|
|
51529
51586
|
`);
|
|
51530
|
-
|
|
51587
|
+
out("─".repeat(60) + `
|
|
51531
51588
|
`);
|
|
51532
51589
|
let messages = todo.messages || [];
|
|
51533
51590
|
if (slice) {
|
|
51534
51591
|
try {
|
|
51535
51592
|
messages = applySlice(messages, slice);
|
|
51536
51593
|
} catch (e) {
|
|
51537
|
-
|
|
51594
|
+
out(`${RED}${e.message}${RESET}
|
|
51538
51595
|
`);
|
|
51539
51596
|
process.exit(2);
|
|
51540
51597
|
}
|
|
51541
51598
|
}
|
|
51542
51599
|
if (!messages.length) {
|
|
51543
|
-
|
|
51600
|
+
out(`${DIM}(no messages)${RESET}
|
|
51544
51601
|
`);
|
|
51545
51602
|
return;
|
|
51546
51603
|
}
|
|
@@ -51551,33 +51608,33 @@ function printFullChat(todo, frontendUrl, slice, mode = "default", format = "com
|
|
|
51551
51608
|
const orig = messages[i];
|
|
51552
51609
|
const ts = mode !== "default" && orig?.createdAt ? ` ${DIM}${new Date(orig.createdAt).toLocaleTimeString()}${RESET}` : "";
|
|
51553
51610
|
const label = msg.role === "user" ? `${CYAN}▶ USER${RESET}` : `${GREEN}◀ ASSISTANT${RESET}`;
|
|
51554
|
-
|
|
51611
|
+
out(`
|
|
51555
51612
|
${label}${ts}
|
|
51556
51613
|
`);
|
|
51557
51614
|
const items = Array.isArray(msg.content) ? msg.content : [{ type: "text", text: msg.content }];
|
|
51558
51615
|
for (const it of items) {
|
|
51559
51616
|
if (it.type === "text") {
|
|
51560
51617
|
if (it.text)
|
|
51561
|
-
|
|
51618
|
+
out(trunc(it.text, 2000) + `
|
|
51562
51619
|
`);
|
|
51563
51620
|
} else if (it.type === "image" || it.type === "document") {
|
|
51564
51621
|
const s = it.source ?? {};
|
|
51565
|
-
|
|
51622
|
+
out(` ${YELLOW}[${it.type}]${RESET} ${DIM}${s.mimeType ?? ""}${RESET} ${s.name ?? ""} ${DIM}(${s.size ?? "?"} bytes)${RESET}
|
|
51566
51623
|
${DIM}${s.uri ?? ""}${RESET}
|
|
51567
51624
|
`);
|
|
51568
51625
|
} else if (it.type === "thinking") {
|
|
51569
51626
|
if (it.thinking)
|
|
51570
|
-
|
|
51627
|
+
out(`${DIM}[thinking]${RESET} ${trunc(it.thinking, 500)}
|
|
51571
51628
|
`);
|
|
51572
51629
|
} else if (it.type === "tool_use") {
|
|
51573
51630
|
toolUseCount++;
|
|
51574
51631
|
if (it.content) {
|
|
51575
|
-
|
|
51632
|
+
out(` ${YELLOW}${trunc(it.content, 200)}${RESET}
|
|
51576
51633
|
`);
|
|
51577
51634
|
} else {
|
|
51578
|
-
const argStr = Object.entries(it.input || {}).map(([k, v]) => `${DIM}${k}=${RESET}${
|
|
51635
|
+
const argStr = Object.entries(it.input || {}).map(([k, v]) => `${DIM}${k}=${RESET}${full ? str(v) : str(v).split(`
|
|
51579
51636
|
`)[0].slice(0, 80)}`).join(" ");
|
|
51580
|
-
|
|
51637
|
+
out(` ${YELLOW}[${it.name}]${RESET} ${argStr}
|
|
51581
51638
|
`);
|
|
51582
51639
|
}
|
|
51583
51640
|
} else if (it.type === "tool_result") {
|
|
@@ -51600,15 +51657,15 @@ ${label}${ts}
|
|
|
51600
51657
|
}).join(`
|
|
51601
51658
|
`);
|
|
51602
51659
|
}
|
|
51603
|
-
|
|
51660
|
+
out(` ${DIM}└─ result:${RESET}${status} ${indent(trunc(bodyStr, 500), ` ${DIM}│${RESET} `)}
|
|
51604
51661
|
`);
|
|
51605
51662
|
}
|
|
51606
51663
|
}
|
|
51607
51664
|
}
|
|
51608
|
-
|
|
51665
|
+
out(`
|
|
51609
51666
|
` + "─".repeat(60) + `
|
|
51610
51667
|
`);
|
|
51611
|
-
|
|
51668
|
+
out(`${DIM}Messages: ${shaped.length} | Tool calls: ${toolUseCount} | Tool errors: ${errorCount}${RESET}
|
|
51612
51669
|
`);
|
|
51613
51670
|
}
|
|
51614
51671
|
|
|
@@ -53823,6 +53880,12 @@ Usage:
|
|
|
53823
53880
|
tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
|
|
53824
53881
|
in one pass and the correction is kept for every re-learn
|
|
53825
53882
|
|
|
53883
|
+
Every voice verb takes --scope project|mine|personal — which voice to teach:
|
|
53884
|
+
project the project's, shared with its members (default; needs write access)
|
|
53885
|
+
mine yours, only in this project
|
|
53886
|
+
personal yours, in every project (e.g. your own Gmail)
|
|
53887
|
+
Agents read all three; on the same voice name the narrower wins: mine > project > personal.
|
|
53888
|
+
|
|
53826
53889
|
|
|
53827
53890
|
Questions the UI asks (keys for 'answers'):
|
|
53828
53891
|
"What makes you different from competitors?"
|
|
@@ -53921,10 +53984,13 @@ async function voiceDeviceCommand(rest, args) {
|
|
|
53921
53984
|
}
|
|
53922
53985
|
async function voiceCommand(api, projectId, rest, args) {
|
|
53923
53986
|
const [verb, ...vargs] = rest;
|
|
53987
|
+
const scope = args.scope;
|
|
53988
|
+
if (scope && !["project", "mine", "personal"].includes(scope))
|
|
53989
|
+
fail(`--scope must be project, mine or personal, got '${scope}'`);
|
|
53924
53990
|
if (verb === "collect" && !vargs[0])
|
|
53925
53991
|
fail("Usage: tfa-cli brand voice collect <channel> [--url U | --account <page-id> | --max N] [--dry-run]");
|
|
53926
53992
|
if (verb === "answers") {
|
|
53927
|
-
const answers = manualAnswersOf((await api.listVoiceSources(projectId)).sources);
|
|
53993
|
+
const answers = manualAnswersOf((await api.listVoiceSources(projectId, scope)).sources);
|
|
53928
53994
|
if (vargs.length) {
|
|
53929
53995
|
for (const a of vargs) {
|
|
53930
53996
|
const i = a.indexOf("=");
|
|
@@ -53932,7 +53998,7 @@ async function voiceCommand(api, projectId, rest, args) {
|
|
|
53932
53998
|
fail(`Expected question=answer, got '${a}'`);
|
|
53933
53999
|
answers[a.slice(0, i)] = a.slice(i + 1);
|
|
53934
54000
|
}
|
|
53935
|
-
await api.collectVoiceSource(projectId, MANUAL_CHANNEL, { samples: manualSamples(answers) });
|
|
54001
|
+
await api.collectVoiceSource(projectId, MANUAL_CHANNEL, { samples: manualSamples(answers), scope });
|
|
53936
54002
|
process.stderr.write(`${GREEN}✅ ${vargs.length} answer(s) saved${RESET}
|
|
53937
54003
|
`);
|
|
53938
54004
|
return;
|
|
@@ -53948,7 +54014,7 @@ async function voiceCommand(api, projectId, rest, args) {
|
|
|
53948
54014
|
return;
|
|
53949
54015
|
}
|
|
53950
54016
|
if (!verb || verb === "show") {
|
|
53951
|
-
const [{ sources, profile }, brand] = await Promise.all([api.listVoiceSources(projectId), api.getBusinessContext(projectId)]);
|
|
54017
|
+
const [{ sources, profile }, brand] = await Promise.all([api.listVoiceSources(projectId, scope), api.getBusinessContext(projectId)]);
|
|
53952
54018
|
if (args.json) {
|
|
53953
54019
|
console.log(JSON.stringify({ brand, profile, sources }, null, 2));
|
|
53954
54020
|
return;
|
|
@@ -53979,12 +54045,12 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
53979
54045
|
const { samples, note } = collectChannel(channel, Number(args.max ?? 40));
|
|
53980
54046
|
if (!samples.length)
|
|
53981
54047
|
fail(`${channel}: nothing usable came back${note ? ` (${note})` : ""}`);
|
|
53982
|
-
const { sources: sources2 } = await api.collectVoiceSource(projectId, channel, { samples });
|
|
54048
|
+
const { sources: sources2 } = await api.collectVoiceSource(projectId, channel, { samples, scope });
|
|
53983
54049
|
process.stderr.write(`${GREEN}✅ ${channel}: ${samples.length} reply pairs stored (${sources2.length} source(s))${RESET}
|
|
53984
54050
|
`);
|
|
53985
54051
|
return;
|
|
53986
54052
|
}
|
|
53987
|
-
const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, account: args.account });
|
|
54053
|
+
const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, account: args.account, scope });
|
|
53988
54054
|
process.stderr.write(`${GREEN}✅ ${channel} collected (${sources.length} source(s))${RESET}
|
|
53989
54055
|
`);
|
|
53990
54056
|
return;
|
|
@@ -53992,7 +54058,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
53992
54058
|
if (verb === "remove") {
|
|
53993
54059
|
if (!vargs[0])
|
|
53994
54060
|
fail("Usage: tfa-cli brand voice remove <channel> [--account <id>] (no --account: every source of the channel)");
|
|
53995
|
-
await api.removeVoiceSource(projectId, vargs[0], args.account ? { account: args.account } : { all: true });
|
|
54061
|
+
await api.removeVoiceSource(projectId, vargs[0], { ...args.account ? { account: args.account } : { all: true }, scope });
|
|
53996
54062
|
process.stderr.write(`${GREEN}✅ ${vargs[0]} removed${RESET}
|
|
53997
54063
|
`);
|
|
53998
54064
|
return;
|
|
@@ -54000,7 +54066,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
54000
54066
|
if (verb === "learn") {
|
|
54001
54067
|
process.stderr.write(`${DIM}learning voice…${RESET}
|
|
54002
54068
|
`);
|
|
54003
|
-
const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined);
|
|
54069
|
+
const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined, undefined, scope);
|
|
54004
54070
|
if (!res.profile)
|
|
54005
54071
|
fail(args["from-company"] ? "No brand page to learn from — 'tfa-cli brand create <name>' and fill it first" : "Nothing to learn from — add answers, collect a source, or fill the brand page first");
|
|
54006
54072
|
if (args.json) {
|
|
@@ -54016,11 +54082,11 @@ ${res.profile}
|
|
|
54016
54082
|
const text = vargs.join(" ").trim();
|
|
54017
54083
|
if (!text)
|
|
54018
54084
|
fail('Usage: tfa-cli brand voice correct "<what is off>"');
|
|
54019
|
-
if (!(await api.listVoiceSources(projectId)).profile?.profile)
|
|
54085
|
+
if (!(await api.listVoiceSources(projectId, scope)).profile?.profile)
|
|
54020
54086
|
fail("No voice learned yet — 'tfa-cli brand voice learn' first");
|
|
54021
54087
|
process.stderr.write(`${DIM}applying correction…${RESET}
|
|
54022
54088
|
`);
|
|
54023
|
-
const res = await api.refineBrandVoice(projectId, undefined, text);
|
|
54089
|
+
const res = await api.refineBrandVoice(projectId, undefined, text, scope);
|
|
54024
54090
|
if (!res.profile)
|
|
54025
54091
|
fail("The correction pass returned nothing — try rewording it");
|
|
54026
54092
|
if (args.json) {
|
|
@@ -55182,21 +55248,24 @@ tfa-cli memory — memory and context folders as git checkouts
|
|
|
55182
55248
|
|
|
55183
55249
|
Usage:
|
|
55184
55250
|
tfa-cli memory sync [--project <id>] Clone, or pull, into:
|
|
55185
|
-
~/.todoforai/{memory,context}
|
|
55186
|
-
~/.todoforai/projects/<id>/{memory,context}
|
|
55251
|
+
~/.todoforai/{memory,context} yours (todoforai:memory, todoforai:context)
|
|
55252
|
+
~/.todoforai/projects/<id>/{memory,context} the project's (todoforai:projects/<id>/…)
|
|
55253
|
+
~/.todoforai/projects/<id>/me/{memory,context} yours in it (todoforai:projects/<id>/me/…)
|
|
55187
55254
|
tfa-cli memory path [--project <id>] Print the checkout paths
|
|
55188
55255
|
|
|
55189
55256
|
Every change to these folders is committed on the server, from any client. In a checkout
|
|
55190
55257
|
use plain git: log, diff, show, revert, commit, then \`git push\` — the push updates the live
|
|
55191
55258
|
folder. A push behind the server is refused: \`git pull\` first. Files over 5 MB live outside
|
|
55192
|
-
history (upload them instead). Project folders: members with write access push
|
|
55259
|
+
history (upload them instead). Project folders: members with write access push; your own in
|
|
55260
|
+
a project (me/) is private to you. The project
|
|
55193
55261
|
defaults to $TODOFORAI_PROJECT_ID.
|
|
55194
55262
|
`);
|
|
55195
55263
|
}
|
|
55196
55264
|
var FOLDERS = ["memory", "context"];
|
|
55197
55265
|
function checkouts(projectId) {
|
|
55198
55266
|
const root = join5(homedir5(), ".todoforai");
|
|
55199
|
-
const
|
|
55267
|
+
const project = projectId && join5(root, "projects", projectId);
|
|
55268
|
+
const scopes = [[root, ""], ...project ? [[project, `projects/${projectId}/`], [join5(project, "me"), `projects/${projectId}/me/`]] : []];
|
|
55200
55269
|
return scopes.flatMap(([dir, remote]) => FOLDERS.map((f) => [join5(dir, f), `${remote}${f}.git`]));
|
|
55201
55270
|
}
|
|
55202
55271
|
function git(args, cwd) {
|
|
@@ -56399,14 +56468,14 @@ function promiseAllObject(promisesObj) {
|
|
|
56399
56468
|
}
|
|
56400
56469
|
function randomString(length = 10) {
|
|
56401
56470
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
56402
|
-
let
|
|
56471
|
+
let str2 = "";
|
|
56403
56472
|
for (let i = 0;i < length; i++) {
|
|
56404
|
-
|
|
56473
|
+
str2 += chars[Math.floor(Math.random() * chars.length)];
|
|
56405
56474
|
}
|
|
56406
|
-
return
|
|
56475
|
+
return str2;
|
|
56407
56476
|
}
|
|
56408
|
-
function esc(
|
|
56409
|
-
return JSON.stringify(
|
|
56477
|
+
function esc(str2) {
|
|
56478
|
+
return JSON.stringify(str2);
|
|
56410
56479
|
}
|
|
56411
56480
|
function slugify(input) {
|
|
56412
56481
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -56519,8 +56588,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
56519
56588
|
"symbol",
|
|
56520
56589
|
"undefined"
|
|
56521
56590
|
]);
|
|
56522
|
-
function escapeRegex(
|
|
56523
|
-
return
|
|
56591
|
+
function escapeRegex(str2) {
|
|
56592
|
+
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
56524
56593
|
}
|
|
56525
56594
|
function clone(inst, def, params) {
|
|
56526
56595
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -56771,13 +56840,13 @@ function getSizableOrigin(input) {
|
|
|
56771
56840
|
return "unknown";
|
|
56772
56841
|
}
|
|
56773
56842
|
var highSurrogate = /[\uD800-\uDBFF]/;
|
|
56774
|
-
function codePointLength(
|
|
56775
|
-
const units =
|
|
56776
|
-
if (!highSurrogate.test(
|
|
56843
|
+
function codePointLength(str2) {
|
|
56844
|
+
const units = str2.length;
|
|
56845
|
+
if (!highSurrogate.test(str2))
|
|
56777
56846
|
return units;
|
|
56778
56847
|
let count = units;
|
|
56779
56848
|
for (let i = 0;i < units - 1; i++) {
|
|
56780
|
-
if ((
|
|
56849
|
+
if ((str2.charCodeAt(i) & 64512) === 55296 && (str2.charCodeAt(i + 1) & 64512) === 56320) {
|
|
56781
56850
|
count--;
|
|
56782
56851
|
i++;
|
|
56783
56852
|
}
|
|
@@ -80266,17 +80335,17 @@ async function main() {
|
|
|
80266
80335
|
const [, filePath, todoArg] = positionals;
|
|
80267
80336
|
const todoId = todoArg || aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
|
|
80268
80337
|
if (!filePath || !todoId) {
|
|
80269
|
-
process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link]${RESET2}
|
|
80338
|
+
process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link] [--force]${RESET2}
|
|
80270
80339
|
`);
|
|
80271
80340
|
process.exit(2);
|
|
80272
80341
|
}
|
|
80273
|
-
let
|
|
80342
|
+
let bytes, name, mime;
|
|
80274
80343
|
if (filePath === "-") {
|
|
80275
80344
|
const chunks = [];
|
|
80276
80345
|
for await (const c3 of process.stdin)
|
|
80277
80346
|
chunks.push(c3);
|
|
80278
|
-
|
|
80279
|
-
if (
|
|
80347
|
+
bytes = Buffer.concat(chunks);
|
|
80348
|
+
if (bytes.length === 0) {
|
|
80280
80349
|
process.stderr.write(`${RED3}No data on stdin${RESET2}
|
|
80281
80350
|
`);
|
|
80282
80351
|
process.exit(1);
|
|
@@ -80289,9 +80358,21 @@ async function main() {
|
|
|
80289
80358
|
`);
|
|
80290
80359
|
process.exit(1);
|
|
80291
80360
|
}
|
|
80292
|
-
|
|
80361
|
+
bytes = readFileSync5(abs);
|
|
80362
|
+
mime = getMimeTypeFromFilename(abs) || undefined;
|
|
80293
80363
|
name = path4.basename(filePath);
|
|
80294
80364
|
}
|
|
80365
|
+
const truncated = truncatedImageReason(bytes);
|
|
80366
|
+
if (truncated) {
|
|
80367
|
+
if (!args.force) {
|
|
80368
|
+
process.stderr.write(`${RED3}${name} looks truncated: ${truncated} (${bytes.length} bytes). Re-export it, or pass --force to show it anyway.${RESET2}
|
|
80369
|
+
`);
|
|
80370
|
+
process.exit(1);
|
|
80371
|
+
}
|
|
80372
|
+
process.stderr.write(`${YELLOW2}\u26A0 ${name} looks truncated: ${truncated} \u2014 showing anyway (--force)${RESET2}
|
|
80373
|
+
`);
|
|
80374
|
+
}
|
|
80375
|
+
const blob = new Blob([bytes], { type: mime });
|
|
80295
80376
|
const res = await api2.showFile(todoId, blob, name, {
|
|
80296
80377
|
title: args.title,
|
|
80297
80378
|
alias: args.alias,
|
|
@@ -80494,7 +80575,7 @@ async function main() {
|
|
|
80494
80575
|
}
|
|
80495
80576
|
const mode = args.debug ? "debug" : args.detailed ? "detailed" : "default";
|
|
80496
80577
|
const format = args["format-anthropic"] ? "anthropic" : "compact";
|
|
80497
|
-
printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format);
|
|
80578
|
+
printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format, args.full);
|
|
80498
80579
|
return;
|
|
80499
80580
|
}
|
|
80500
80581
|
if (process.stderr.isTTY)
|