@todoforai/cli 0.1.20 → 0.1.21
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/todoai.js +207 -4
- package/package.json +1 -1
package/dist/todoai.js
CHANGED
|
@@ -42825,6 +42825,9 @@ class ApiClient {
|
|
|
42825
42825
|
deleteTodo(todoId) {
|
|
42826
42826
|
return this.request("DELETE", `/api/v1/todos/${todoId}`);
|
|
42827
42827
|
}
|
|
42828
|
+
listModels() {
|
|
42829
|
+
return this.request("GET", "/api/v1/models");
|
|
42830
|
+
}
|
|
42828
42831
|
listAgentSettings(filters) {
|
|
42829
42832
|
const params = new URLSearchParams;
|
|
42830
42833
|
if (filters?.workspacePath)
|
|
@@ -42923,7 +42926,7 @@ function normalizeApiUrl(url) {
|
|
|
42923
42926
|
return `https://${url}`;
|
|
42924
42927
|
return url;
|
|
42925
42928
|
}
|
|
42926
|
-
var SUBCOMMANDS = new Set(["login", "logout"]);
|
|
42929
|
+
var SUBCOMMANDS = new Set(["login", "logout", "update"]);
|
|
42927
42930
|
function credentialsPath() {
|
|
42928
42931
|
const sys = os2.platform();
|
|
42929
42932
|
if (sys === "win32") {
|
|
@@ -44095,18 +44098,27 @@ Usage:
|
|
|
44095
44098
|
todoforai-cli --inspect <todo-id>[@<slice>] # Read chat log. <slice> = -3:, :1, 5:10, 7 (Python-style)
|
|
44096
44099
|
todoforai-cli --template <id> [--input k=v] # Start from a registry template
|
|
44097
44100
|
todoforai-cli --list-agents # List available agents and exit
|
|
44101
|
+
todoforai-cli --list-models [filter] # List models usable with --model and exit
|
|
44098
44102
|
todoforai-cli agent update <agent> model=<model> # Update agent settings (see 'agent --help')
|
|
44099
44103
|
todoforai-cli list [-n 30] [--cursor N] [--all] [--status S] # List todos (paginated); see 'list --help'
|
|
44100
44104
|
todoforai-cli status <todo-id> <STATUS> # Update a todo's status (run 'status --help' for the full list)
|
|
44101
44105
|
todoforai-cli delete <todo-id> # Permanently delete a todo
|
|
44102
44106
|
todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
|
|
44103
44107
|
todoforai-cli recommend --template <id> # Add a template as a NEXT-column card (see 'todoregistry-cli create')
|
|
44108
|
+
todoforai-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> ownership links for a project you own
|
|
44109
|
+
todoforai-cli steering get # Show recommendation steering (direction + boosted/muted categories)
|
|
44110
|
+
todoforai-cli steering direction "<text>" # Set the direction the generator should follow
|
|
44111
|
+
todoforai-cli steering boost|mute <cat> # Favour / avoid a category
|
|
44112
|
+
todoforai-cli next # Generate steered NEXT-column recommendations
|
|
44104
44113
|
|
|
44105
44114
|
Options:
|
|
44106
44115
|
--path <dir> Workspace path (default: cwd)
|
|
44107
44116
|
--project <id> Project ID
|
|
44108
44117
|
--agent, -a <name> Agent name (partial match)
|
|
44118
|
+
--model <model> Override the agent's model for this todo
|
|
44119
|
+
(e.g. anthropic:anthropic/claude-opus-5, openai:openai/gpt-5.6-sol)
|
|
44109
44120
|
--list-agents List available agents (name, id, workspace paths) and exit
|
|
44121
|
+
--list-models List models usable with --model (optional substring filter) and exit
|
|
44110
44122
|
--api-url <url> API URL
|
|
44111
44123
|
--api-key <key> API key
|
|
44112
44124
|
--user-id <id> Admin HTTP impersonation; requires --no-watch
|
|
@@ -44163,6 +44175,7 @@ function parseCliArgs() {
|
|
|
44163
44175
|
agent: { type: "string", short: "a" },
|
|
44164
44176
|
model: { type: "string" },
|
|
44165
44177
|
"list-agents": { type: "boolean", default: false },
|
|
44178
|
+
"list-models": { type: "boolean", default: false },
|
|
44166
44179
|
"api-url": { type: "string" },
|
|
44167
44180
|
"api-key": { type: "string" },
|
|
44168
44181
|
"user-id": { type: "string" },
|
|
@@ -44173,6 +44186,9 @@ function parseCliArgs() {
|
|
|
44173
44186
|
priority: { type: "string" },
|
|
44174
44187
|
title: { type: "string" },
|
|
44175
44188
|
"business-context": { type: "string" },
|
|
44189
|
+
seed: { type: "string" },
|
|
44190
|
+
emails: { type: "string" },
|
|
44191
|
+
ttl: { type: "string" },
|
|
44176
44192
|
resume: { type: "string", short: "r" },
|
|
44177
44193
|
continue: { type: "boolean", short: "c", default: false },
|
|
44178
44194
|
"non-interactive": { type: "boolean", short: "n", default: false },
|
|
@@ -46065,6 +46081,20 @@ async function listAgentsCommand(api, opts) {
|
|
|
46065
46081
|
}
|
|
46066
46082
|
}
|
|
46067
46083
|
|
|
46084
|
+
// src/list-models.ts
|
|
46085
|
+
async function listModelsCommand(api, opts) {
|
|
46086
|
+
const res = await api.listModels();
|
|
46087
|
+
const q = opts.filter?.toLowerCase();
|
|
46088
|
+
const ids = (res?.data || []).map((m) => String(m.id).includes("/") ? `${String(m.id).split("/")[0]}:${m.id}` : String(m.id)).filter((id) => !q || id.toLowerCase().includes(q));
|
|
46089
|
+
if (opts.json) {
|
|
46090
|
+
console.log(JSON.stringify(ids, null, 2));
|
|
46091
|
+
return;
|
|
46092
|
+
}
|
|
46093
|
+
for (const id of ids)
|
|
46094
|
+
process.stderr.write(`${BRAND}${id}${RESET}
|
|
46095
|
+
`);
|
|
46096
|
+
}
|
|
46097
|
+
|
|
46068
46098
|
// src/agent-command.ts
|
|
46069
46099
|
function printAgentHelp() {
|
|
46070
46100
|
process.stderr.write(`
|
|
@@ -46374,6 +46404,92 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
46374
46404
|
`);
|
|
46375
46405
|
}
|
|
46376
46406
|
|
|
46407
|
+
// src/steering-command.ts
|
|
46408
|
+
var RED2 = "\x1B[31m";
|
|
46409
|
+
var GREEN2 = "\x1B[32m";
|
|
46410
|
+
var DIM2 = "\x1B[2m";
|
|
46411
|
+
var RESET2 = "\x1B[0m";
|
|
46412
|
+
async function rest(ctx, method, path4, body) {
|
|
46413
|
+
const res = await fetch(`${ctx.apiUrl}/api/v1${path4}`, {
|
|
46414
|
+
method,
|
|
46415
|
+
headers: { "content-type": "application/json", "x-api-key": ctx.apiKey },
|
|
46416
|
+
...body ? { body: JSON.stringify(body) } : {},
|
|
46417
|
+
signal: AbortSignal.timeout(30000)
|
|
46418
|
+
});
|
|
46419
|
+
const text = await res.text();
|
|
46420
|
+
if (!res.ok)
|
|
46421
|
+
throw new Error(`${method} ${path4} failed: ${res.status} ${text}`);
|
|
46422
|
+
return text ? JSON.parse(text) : null;
|
|
46423
|
+
}
|
|
46424
|
+
function printSteering(s) {
|
|
46425
|
+
process.stderr.write(`${GREEN2}Direction:${RESET2} ${s.direction || DIM2 + "(none)" + RESET2}
|
|
46426
|
+
`);
|
|
46427
|
+
process.stderr.write(`${GREEN2}Boosted:${RESET2} ${s.boostedCategoryIds?.join(", ") || DIM2 + "(none)" + RESET2}
|
|
46428
|
+
`);
|
|
46429
|
+
process.stderr.write(`${GREEN2}Muted:${RESET2} ${s.mutedCategoryIds?.join(", ") || DIM2 + "(none)" + RESET2}
|
|
46430
|
+
`);
|
|
46431
|
+
}
|
|
46432
|
+
var USAGE = `${RED2}Usage:
|
|
46433
|
+
todoforai-cli steering get [--project <id>]
|
|
46434
|
+
todoforai-cli steering direction "<text>" [--project <id>] (empty text clears it)
|
|
46435
|
+
todoforai-cli steering boost <categoryId> [--project <id>]
|
|
46436
|
+
todoforai-cli steering mute <categoryId> [--project <id>]
|
|
46437
|
+
todoforai-cli steering clear [--project <id>] (clears all steering)${RESET2}`;
|
|
46438
|
+
async function steeringCommand(ctx, argv) {
|
|
46439
|
+
const sub = argv[0];
|
|
46440
|
+
const path4 = `/projects/${ctx.projectId}/recommendation-steering`;
|
|
46441
|
+
if (!sub || sub === "get") {
|
|
46442
|
+
const s = await rest(ctx, "GET", path4);
|
|
46443
|
+
if (ctx.json)
|
|
46444
|
+
console.log(JSON.stringify(s, null, 2));
|
|
46445
|
+
else
|
|
46446
|
+
printSteering(s);
|
|
46447
|
+
return;
|
|
46448
|
+
}
|
|
46449
|
+
if (sub === "direction") {
|
|
46450
|
+
const text = argv.slice(1).join(" ").trim();
|
|
46451
|
+
const s = await rest(ctx, "PATCH", path4, { projectId: ctx.projectId, direction: text || null });
|
|
46452
|
+
if (ctx.json)
|
|
46453
|
+
console.log(JSON.stringify(s, null, 2));
|
|
46454
|
+
else
|
|
46455
|
+
process.stderr.write(`${GREEN2}\u2705 ${text ? `Direction set: ${text}` : "Direction cleared"}${RESET2}
|
|
46456
|
+
`);
|
|
46457
|
+
return;
|
|
46458
|
+
}
|
|
46459
|
+
if (sub === "boost" || sub === "mute") {
|
|
46460
|
+
const categoryId = argv[1];
|
|
46461
|
+
if (!categoryId) {
|
|
46462
|
+
process.stderr.write(USAGE + `
|
|
46463
|
+
`);
|
|
46464
|
+
process.exit(2);
|
|
46465
|
+
}
|
|
46466
|
+
const current = await rest(ctx, "GET", path4);
|
|
46467
|
+
const key = sub === "boost" ? "boostedCategoryIds" : "mutedCategoryIds";
|
|
46468
|
+
const other = sub === "boost" ? "mutedCategoryIds" : "boostedCategoryIds";
|
|
46469
|
+
const next = Array.from(new Set([...current[key] ?? [], categoryId]));
|
|
46470
|
+
const nextOther = (current[other] ?? []).filter((c2) => c2 !== categoryId);
|
|
46471
|
+
const s = await rest(ctx, "PATCH", path4, { projectId: ctx.projectId, [key]: next, [other]: nextOther });
|
|
46472
|
+
if (ctx.json)
|
|
46473
|
+
console.log(JSON.stringify(s, null, 2));
|
|
46474
|
+
else
|
|
46475
|
+
process.stderr.write(`${GREEN2}\u2705 ${sub === "boost" ? "Boosted" : "Muted"} ${categoryId}${RESET2}
|
|
46476
|
+
`);
|
|
46477
|
+
return;
|
|
46478
|
+
}
|
|
46479
|
+
if (sub === "clear") {
|
|
46480
|
+
const s = await rest(ctx, "PATCH", path4, { projectId: ctx.projectId, direction: null, boostedCategoryIds: [], mutedCategoryIds: [] });
|
|
46481
|
+
if (ctx.json)
|
|
46482
|
+
console.log(JSON.stringify(s, null, 2));
|
|
46483
|
+
else
|
|
46484
|
+
process.stderr.write(`${GREEN2}\u2705 Steering cleared${RESET2}
|
|
46485
|
+
`);
|
|
46486
|
+
return;
|
|
46487
|
+
}
|
|
46488
|
+
process.stderr.write(USAGE + `
|
|
46489
|
+
`);
|
|
46490
|
+
process.exit(2);
|
|
46491
|
+
}
|
|
46492
|
+
|
|
46377
46493
|
// src/index.ts
|
|
46378
46494
|
try {
|
|
46379
46495
|
const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
@@ -46565,7 +46681,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46565
46681
|
await deviceLogin();
|
|
46566
46682
|
return;
|
|
46567
46683
|
}
|
|
46568
|
-
|
|
46684
|
+
const notDst = (t) => t.startsWith("dst_") ? "" : t;
|
|
46685
|
+
let apiKey = args["api-key"] || notDst(readCredential(apiUrl)) || notDst(getEnv("API_TOKEN")) || "";
|
|
46569
46686
|
if (!apiKey) {
|
|
46570
46687
|
apiKey = await deviceLogin();
|
|
46571
46688
|
}
|
|
@@ -46609,8 +46726,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46609
46726
|
return;
|
|
46610
46727
|
}
|
|
46611
46728
|
if (positionals[0] === "addmessage") {
|
|
46612
|
-
const [, todoId, ...
|
|
46613
|
-
const content2 =
|
|
46729
|
+
const [, todoId, ...rest2] = positionals;
|
|
46730
|
+
const content2 = rest2.join(" ") || await readStdin();
|
|
46614
46731
|
if (!todoId || !content2) {
|
|
46615
46732
|
process.stderr.write(`${RED}Usage: todoforai-cli addmessage <todo-id> "content"${RESET}
|
|
46616
46733
|
`);
|
|
@@ -46665,10 +46782,92 @@ Cancelled by user (Ctrl+C)
|
|
|
46665
46782
|
`);
|
|
46666
46783
|
return;
|
|
46667
46784
|
}
|
|
46785
|
+
if (positionals[0] === "claim" && positionals[1] === "mint") {
|
|
46786
|
+
let seedProjectId = args.seed || args.project || cfgScope.data.default_project_id;
|
|
46787
|
+
if (!seedProjectId) {
|
|
46788
|
+
const projects2 = await api.listProjects();
|
|
46789
|
+
seedProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
46790
|
+
}
|
|
46791
|
+
if (!seedProjectId) {
|
|
46792
|
+
process.stderr.write(`${RED}Usage: todoforai-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>]${RESET}
|
|
46793
|
+
`);
|
|
46794
|
+
process.exit(2);
|
|
46795
|
+
}
|
|
46796
|
+
const emails = args.emails?.split(",").map((e) => e.trim()).filter(Boolean);
|
|
46797
|
+
let ttlSec;
|
|
46798
|
+
if (args.ttl !== undefined) {
|
|
46799
|
+
ttlSec = Number(args.ttl);
|
|
46800
|
+
if (!Number.isFinite(ttlSec)) {
|
|
46801
|
+
process.stderr.write(`${RED}--ttl must be a number (seconds)${RESET}
|
|
46802
|
+
`);
|
|
46803
|
+
process.exit(2);
|
|
46804
|
+
}
|
|
46805
|
+
}
|
|
46806
|
+
const res = await fetch(`${apiUrl}/api/v1/claims/mint`, {
|
|
46807
|
+
method: "POST",
|
|
46808
|
+
headers: { "content-type": "application/json", "x-api-key": apiKey },
|
|
46809
|
+
body: JSON.stringify({ seedProjectId, ...emails?.length ? { emails } : {}, ...ttlSec !== undefined ? { ttlSec } : {} }),
|
|
46810
|
+
signal: AbortSignal.timeout(30000)
|
|
46811
|
+
});
|
|
46812
|
+
const text = await res.text();
|
|
46813
|
+
if (!res.ok) {
|
|
46814
|
+
process.stderr.write(`${RED}Mint failed: ${res.status} ${text}${RESET}
|
|
46815
|
+
`);
|
|
46816
|
+
process.exit(1);
|
|
46817
|
+
}
|
|
46818
|
+
const out = JSON.parse(text);
|
|
46819
|
+
if (args.json)
|
|
46820
|
+
console.log(JSON.stringify(out, null, 2));
|
|
46821
|
+
else
|
|
46822
|
+
for (const l of out.links)
|
|
46823
|
+
process.stdout.write(`${l.email ? `${l.email} ` : ""}${CYAN}${l.url}${RESET}
|
|
46824
|
+
`);
|
|
46825
|
+
return;
|
|
46826
|
+
}
|
|
46827
|
+
if (positionals[0] === "steering" || positionals[0] === "next") {
|
|
46828
|
+
let projectId2 = args.project || cfgScope.data.default_project_id;
|
|
46829
|
+
if (!projectId2) {
|
|
46830
|
+
const projects2 = await api.listProjects();
|
|
46831
|
+
projectId2 = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
46832
|
+
}
|
|
46833
|
+
if (!projectId2) {
|
|
46834
|
+
process.stderr.write(`${RED}No project found \u2014 pass --project <id>${RESET}
|
|
46835
|
+
`);
|
|
46836
|
+
process.exit(2);
|
|
46837
|
+
}
|
|
46838
|
+
const sctx = { apiUrl, apiKey, projectId: projectId2, json: !!args.json };
|
|
46839
|
+
if (positionals[0] === "next") {
|
|
46840
|
+
const res = await fetch(`${apiUrl}/api/v1/projects/${projectId2}/recommendations/generate`, {
|
|
46841
|
+
method: "POST",
|
|
46842
|
+
headers: { "content-type": "application/json", "x-api-key": apiKey },
|
|
46843
|
+
body: JSON.stringify({ projectId: projectId2, ...args["business-context"] ? { businessContextId: args["business-context"] } : {} }),
|
|
46844
|
+
signal: AbortSignal.timeout(30000)
|
|
46845
|
+
});
|
|
46846
|
+
const text = await res.text();
|
|
46847
|
+
if (!res.ok) {
|
|
46848
|
+
process.stderr.write(`${RED}Generate failed: ${res.status} ${text}${RESET}
|
|
46849
|
+
`);
|
|
46850
|
+
process.exit(1);
|
|
46851
|
+
}
|
|
46852
|
+
const out = JSON.parse(text);
|
|
46853
|
+
if (args.json)
|
|
46854
|
+
console.log(JSON.stringify(out, null, 2));
|
|
46855
|
+
else
|
|
46856
|
+
process.stderr.write(`${GREEN}\u2705 Generating recommendations \u2014 analyzer todo ${out.todoId}${RESET}
|
|
46857
|
+
`);
|
|
46858
|
+
return;
|
|
46859
|
+
}
|
|
46860
|
+
await steeringCommand(sctx, positionals.slice(1));
|
|
46861
|
+
return;
|
|
46862
|
+
}
|
|
46668
46863
|
if (args["list-agents"]) {
|
|
46669
46864
|
await listAgentsCommand(api, { json: !!args.json, formatPath: formatPathWithTilde });
|
|
46670
46865
|
return;
|
|
46671
46866
|
}
|
|
46867
|
+
if (args["list-models"]) {
|
|
46868
|
+
await listModelsCommand(api, { json: !!args.json, filter: positionals[0] });
|
|
46869
|
+
return;
|
|
46870
|
+
}
|
|
46672
46871
|
if (positionals[0] === "agent") {
|
|
46673
46872
|
await agentCommand(api, positionals, args, formatPathWithTilde);
|
|
46674
46873
|
return;
|
|
@@ -46768,6 +46967,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46768
46967
|
const frontendUrl2 = getFrontendUrl(apiUrl, projectId2, todoId);
|
|
46769
46968
|
if (args.json) {
|
|
46770
46969
|
console.log(JSON.stringify({ ...todo2, frontend_url: frontendUrl2 }, null, 2));
|
|
46970
|
+
} else if (args["no-watch"]) {
|
|
46971
|
+
console.log(`created ${todoId}`);
|
|
46771
46972
|
} else {
|
|
46772
46973
|
process.stderr.write(`${DIM}TODO:${RESET} ${CYAN}${frontendUrl2}${RESET}
|
|
46773
46974
|
`);
|
|
@@ -46962,6 +47163,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
46962
47163
|
const frontendUrl = getFrontendUrl(apiUrl, projectId, actualTodoId);
|
|
46963
47164
|
if (args.json) {
|
|
46964
47165
|
console.log(JSON.stringify({ ...todo, frontend_url: frontendUrl }, null, 2));
|
|
47166
|
+
} else if (!ws) {
|
|
47167
|
+
console.log(`created ${actualTodoId}`);
|
|
46965
47168
|
} else {
|
|
46966
47169
|
process.stderr.write(`${DIM}TODO:${RESET} ${CYAN}${frontendUrl}${RESET}
|
|
46967
47170
|
`);
|