@todoforai/cli 0.1.28 → 0.1.29
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 -40
- package/package.json +3 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -42893,6 +42893,65 @@ class ApiClient {
|
|
|
42893
42893
|
throw new Error(`API POST /resources/open failed: ${res.status} ${await res.text()}`);
|
|
42894
42894
|
return res.json();
|
|
42895
42895
|
}
|
|
42896
|
+
async listShows(opts = {}) {
|
|
42897
|
+
const qs = new URLSearchParams;
|
|
42898
|
+
if (opts.todoId)
|
|
42899
|
+
qs.set("todoId", opts.todoId);
|
|
42900
|
+
if (opts.projectId)
|
|
42901
|
+
qs.set("projectId", opts.projectId);
|
|
42902
|
+
if (opts.card)
|
|
42903
|
+
qs.set("card", opts.card);
|
|
42904
|
+
const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show?${qs}`;
|
|
42905
|
+
const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
|
|
42906
|
+
if (!res.ok)
|
|
42907
|
+
throw new Error(`API GET /resources/show failed: ${res.status} ${await res.text()}`);
|
|
42908
|
+
return res.json();
|
|
42909
|
+
}
|
|
42910
|
+
async upsertCard(projectId, name, html, opts = {}) {
|
|
42911
|
+
const form = new FormData;
|
|
42912
|
+
form.append("file", html, `${name}.html`);
|
|
42913
|
+
for (const [k, v] of Object.entries(opts))
|
|
42914
|
+
if (v)
|
|
42915
|
+
form.append(k, v);
|
|
42916
|
+
const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}`;
|
|
42917
|
+
const res = await fetch(url, {
|
|
42918
|
+
method: "PUT",
|
|
42919
|
+
headers: { "x-api-key": this.apiKey },
|
|
42920
|
+
body: form,
|
|
42921
|
+
signal: AbortSignal.timeout(60000)
|
|
42922
|
+
});
|
|
42923
|
+
if (!res.ok)
|
|
42924
|
+
throw new Error(`API PUT cards failed: ${res.status} ${await res.text()}`);
|
|
42925
|
+
return res.json();
|
|
42926
|
+
}
|
|
42927
|
+
async listCards(projectId) {
|
|
42928
|
+
const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}`;
|
|
42929
|
+
const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
|
|
42930
|
+
if (!res.ok)
|
|
42931
|
+
throw new Error(`API GET cards failed: ${res.status} ${await res.text()}`);
|
|
42932
|
+
return res.json();
|
|
42933
|
+
}
|
|
42934
|
+
async getCard(projectId, name) {
|
|
42935
|
+
const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}`;
|
|
42936
|
+
const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
|
|
42937
|
+
if (!res.ok)
|
|
42938
|
+
throw new Error(`API GET card failed: ${res.status} ${await res.text()}`);
|
|
42939
|
+
return res.json();
|
|
42940
|
+
}
|
|
42941
|
+
async getCardBody(projectId, name) {
|
|
42942
|
+
const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}?body=1`;
|
|
42943
|
+
const res = await fetch(url, { headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
|
|
42944
|
+
if (!res.ok)
|
|
42945
|
+
throw new Error(`API GET card body failed: ${res.status} ${await res.text()}`);
|
|
42946
|
+
return res.text();
|
|
42947
|
+
}
|
|
42948
|
+
async deleteCard(projectId, name) {
|
|
42949
|
+
const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/cards/${projectId}/${name}`;
|
|
42950
|
+
const res = await fetch(url, { method: "DELETE", headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
|
|
42951
|
+
if (!res.ok)
|
|
42952
|
+
throw new Error(`API DELETE card failed: ${res.status} ${await res.text()}`);
|
|
42953
|
+
return res.json();
|
|
42954
|
+
}
|
|
42896
42955
|
patchEdgeConfig(edgeId, updates) {
|
|
42897
42956
|
return this.request("PATCH", `/api/v1/edges/${edgeId}`, { updates });
|
|
42898
42957
|
}
|
|
@@ -43199,6 +43258,8 @@ class FrontendWebSocket {
|
|
|
43199
43258
|
// ../packages/shared-fbe/src/enums.ts
|
|
43200
43259
|
var TodoStatus;
|
|
43201
43260
|
((TodoStatus2) => {
|
|
43261
|
+
TodoStatus2["PROPOSED"] = "PROPOSED";
|
|
43262
|
+
TodoStatus2["DISMISSED"] = "DISMISSED";
|
|
43202
43263
|
TodoStatus2["TODO"] = "TODO";
|
|
43203
43264
|
TodoStatus2["SCHEDULED"] = "SCHEDULED";
|
|
43204
43265
|
TodoStatus2["PAUSED"] = "PAUSED";
|
|
@@ -43479,9 +43540,6 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43479
43540
|
hexGrid: {
|
|
43480
43541
|
updated: (projectId) => `${"hexgrid:updated" /* HEXGRID_UPDATED */}:${projectId}`
|
|
43481
43542
|
},
|
|
43482
|
-
placement: {
|
|
43483
|
-
updated: (projectId) => `${"placement:updated" /* PLACEMENT_UPDATED */}:${projectId}`
|
|
43484
|
-
},
|
|
43485
43543
|
sandbox: {
|
|
43486
43544
|
state: (userId) => `${"sandbox:state" /* SANDBOX_STATE */}:${userId}`
|
|
43487
43545
|
},
|
|
@@ -43939,7 +43997,7 @@ Rules for the Presence block:
|
|
|
43939
43997
|
- \`score\` is an integer 0-100, or \`null\` when you checked but found no presence. Base scores on real findings, not guesses. Include every channel you actually checked.
|
|
43940
43998
|
- Keep it consistent with the prose Online Presence section \u2014 the JSON is the structured mirror of those findings.
|
|
43941
43999
|
|
|
43942
|
-
Growth recommendations are NOT part of this block \u2014 they live
|
|
44000
|
+
Growth recommendations are NOT part of this block \u2014 they live on the project board as proposed todos (\u2727 rows on their group cards; see "Recommending actionable TODOs" below). After any analysis that surfaces growth opportunities, materialize your top recommendations as proposals there; the prose Next Steps section stays human-readable advice.
|
|
43943
44001
|
|
|
43944
44002
|
Document order for the saved brand page: human-readable sections first, then the **Next Steps** section, then Open Questions (if any), and finally exactly one \`growth-plan\` block as the very last element. (Your chat reply can still end with a short spoken summary; only the saved markdown must end with the machine block.)
|
|
43945
44003
|
|
|
@@ -43951,8 +44009,14 @@ In follow-up conversations, act as an ongoing advisor: answer growth and digital
|
|
|
43951
44009
|
|
|
43952
44010
|
When the user asks you to recommend how to grow (e.g. "recommend growth TODOs", the brand page's guided suggestion, the board's Recommend button), read the current business context (including the presence block), research only where information is missing or stale, refresh the presence block if findings changed, then materialize your top growth actions as recommendation cards (see below). The cards ARE the growth plan \u2014 do not write a separate recommendations list into the markdown.
|
|
43953
44011
|
|
|
44012
|
+
Strategic workstreams (board groups):
|
|
44013
|
+
The project board is organized into group cards, and each group should be a strategic workstream for THIS company \u2014 a front the business is (or should be) pushing on, e.g. "SEO & Content", "Outbound to agencies", "Community on Reddit", "Retention & Email". Your recommendation-generation prompt includes the board's existing groups as JSON (\`slug\`, \`name\`, and \`description\` when set \u2014 the description states the workstream's strategic intent). Treat them as the company's current strategic map:
|
|
44014
|
+
- FIRST fit each recommendation into an existing group when it genuinely advances that workstream \u2014 pass its slug as the \`group\` parameter of \`api_recommend_template\`. Match on strategy (read the descriptions), not keywords.
|
|
44015
|
+
- When your analysis surfaces a strategic direction the board doesn't cover yet, introduce it: pass a new short kebab-case \`group\` slug plus a human \`groupName\` (2-4 words, strategy-level \u2014 "Partner Channel", not "Misc" or "Growth") and a one-sentence \`groupDescription\` stating the strategic bet and its target outcome for THIS company (e.g. "Win comparison searches against Zapier and n8n to capture switchers"). The group is created with the card.
|
|
44016
|
+
- Keep the map tight: 3-7 workstreams total is healthy. Never invent a new group when an existing one fits, never create catch-all groups, and never mirror priority or channel-lists as groups \u2014 a group is a strategic bet, its cards are the moves.
|
|
44017
|
+
|
|
43954
44018
|
Recommending actionable TODOs from your recommendations:
|
|
43955
|
-
To materialize growth recommendations,
|
|
44019
|
+
To materialize growth recommendations, propose todos on the project board. A proposal is a real todo in PROPOSED state \u2014 it sits as a \u2727 row on its group card until the user starts (accepts) or dismisses it. You NEVER create running todos \u2014 you only propose. Two tools, strict roles:
|
|
43956
44020
|
|
|
43957
44021
|
1. GET A TEMPLATE (api_create_template) \u2014 generate-first, reuse only on a close fit:
|
|
43958
44022
|
- Default: CREATE a template tailored to THIS business with the \`api_create_template\` tool. It returns the new template id.
|
|
@@ -43965,12 +44029,12 @@ To materialize growth recommendations, create recommendation cards in the projec
|
|
|
43965
44029
|
- Then the request, using these exact standalone heading lines (letters only, each on its own line, ending with a colon): \`Your task:\`, \`Steps:\`, \`Data needed:\`. Everything under these headings becomes the first user turn.
|
|
43966
44030
|
- Headings must match exactly (e.g. \`Your task:\`, not \`Your task (SEO):\`) or the split breaks. Put the business-specific detail in the body text under each heading, never in the heading itself.
|
|
43967
44031
|
|
|
43968
|
-
2. RECOMMEND (api_recommend_template \u2014
|
|
43969
|
-
Call \`api_recommend_template\` with the template id, a one-line \`note\` (the WHY for THIS business \u2014 it becomes the
|
|
44032
|
+
2. RECOMMEND (api_recommend_template \u2014 proposes the template as a PROPOSED todo on the project; never creates a running todo):
|
|
44033
|
+
Call \`api_recommend_template\` with the template id, a one-line \`note\` (the WHY for THIS business \u2014 it becomes the proposal's tooltip), a \`priority\` of high, medium or low, and the \`group\` slug of the strategic workstream it belongs to (see "Strategic workstreams" above \u2014 reuse an existing slug when one fits; a new slug + \`groupName\` creates the workstream). Project and business context default to the current session, so pass them only when targeting a different one. The proposal appears as a \u2727 row on that group's board card for the user to review before any credits are spent. Re-proposing the same template refreshes the open proposal instead of duplicating it; dismissed proposals since your last run are fed back to you \u2014 don't re-propose those, steer elsewhere.
|
|
43970
44034
|
|
|
43971
44035
|
Both tools run in-process and need no machine or shell \u2014 never shell out to \`todoregistry-cli\` / \`todoforai-cli\` for this.
|
|
43972
44036
|
|
|
43973
|
-
3.
|
|
44037
|
+
3. Propose 3-6 todos (highest priority first). Then reply with a one-line summary per proposal grouped by workstream, and tell the user they're waiting on the board for review.`,
|
|
43974
44038
|
mcpConfigs: {},
|
|
43975
44039
|
edgesMcpConfigs: {},
|
|
43976
44040
|
permissions: {
|
|
@@ -44014,6 +44078,9 @@ var OUTPUT_POLICIES = {
|
|
|
44014
44078
|
full: { firstLimit: Infinity, lastLimit: 0, hardCap: RUN_OUTPUT_CAP, lineLimit: MAX_LINE_LEN },
|
|
44015
44079
|
raw: { firstLimit: Infinity, lastLimit: 0, hardCap: Infinity, lineLimit: Infinity }
|
|
44016
44080
|
};
|
|
44081
|
+
// ../packages/shared-fbe/src/toolInstallCommand.ts
|
|
44082
|
+
var PYTHON_MIN_MINOR = 10;
|
|
44083
|
+
var PYTHON_VERSION_CHECK = `-c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3, ${PYTHON_MIN_MINOR}) else 1)'`;
|
|
44017
44084
|
// ../packages/shared-fbe/src/realtime/topics.ts
|
|
44018
44085
|
var TOPICS = {
|
|
44019
44086
|
["todo:new" /* NEW_TODO */]: {
|
|
@@ -44052,10 +44119,6 @@ var TOPICS = {
|
|
|
44052
44119
|
channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
|
|
44053
44120
|
audience: "frontend"
|
|
44054
44121
|
},
|
|
44055
|
-
["placement:updated" /* PLACEMENT_UPDATED */]: {
|
|
44056
|
-
channel: (p) => SERVER_TO_FRONTENDS.placement.updated(p.projectId),
|
|
44057
|
-
audience: "frontend"
|
|
44058
|
-
},
|
|
44059
44122
|
["sandbox:state" /* SANDBOX_STATE */]: {
|
|
44060
44123
|
channel: (p) => SERVER_TO_FRONTENDS.sandbox.state(p.userId),
|
|
44061
44124
|
audience: "frontend"
|
|
@@ -44158,7 +44221,7 @@ import { parseArgs } from "util";
|
|
|
44158
44221
|
// package.json
|
|
44159
44222
|
var package_default = {
|
|
44160
44223
|
name: "@todoforai/cli",
|
|
44161
|
-
version: "0.1.
|
|
44224
|
+
version: "0.1.29",
|
|
44162
44225
|
type: "module",
|
|
44163
44226
|
bin: {
|
|
44164
44227
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -44176,6 +44239,8 @@ var package_default = {
|
|
|
44176
44239
|
dev: "bun run src/index.ts"
|
|
44177
44240
|
},
|
|
44178
44241
|
dependencies: {
|
|
44242
|
+
"@shared/api": "file:../packages/shared-api",
|
|
44243
|
+
"@shared/credentials": "file:../packages/shared-credentials",
|
|
44179
44244
|
"@todoforai/update-notifier": "^0.1.0",
|
|
44180
44245
|
"cli-highlight": "^2.1.11",
|
|
44181
44246
|
"diff-match-patch": "^1.0.5",
|
|
@@ -44191,7 +44256,7 @@ var package_default = {
|
|
|
44191
44256
|
var DEFAULT_API_URL = "https://api.todofor.ai";
|
|
44192
44257
|
var VERSION = package_default.version;
|
|
44193
44258
|
function getEnv(name) {
|
|
44194
|
-
return process.env[`TODOFORAI_${name}`] || process.env[`TODO4AI_${name}`] || "";
|
|
44259
|
+
return (process.env[`TODOFORAI_${name}`] || process.env[`TODO4AI_${name}`] || "").trim();
|
|
44195
44260
|
}
|
|
44196
44261
|
function printUsage() {
|
|
44197
44262
|
process.stderr.write(`
|
|
@@ -44215,7 +44280,10 @@ Usage:
|
|
|
44215
44280
|
todoforai-cli delete <todo-id> # Permanently delete a todo
|
|
44216
44281
|
todoforai-cli addmessage <todo-id> "text" # Add a message to an existing todo
|
|
44217
44282
|
todoforai-cli show <file|-> [todo-id] # Show a file in the chat (rendered by mimetype; - reads stdin)
|
|
44218
|
-
# [--title T] [--alias A] [--mime M] [--json]
|
|
44283
|
+
# [--title T] [--alias A] [--mime M] [--card <name>] [--json]
|
|
44284
|
+
todoforai-cli show list [todo-id] # List show blocks (ref, title, mime/url, card)
|
|
44285
|
+
# [--project <id>] [--card <name>] [--json]
|
|
44286
|
+
# no todo-id + --project (or $TODOFORAI_PROJECT_ID) = every todo
|
|
44219
44287
|
todoforai-cli open <url> [todo-id] # Show a live http(s) url in the chat as a preview
|
|
44220
44288
|
# [--title T] [--alias A] [--json]
|
|
44221
44289
|
todoforai-cli recommend --template <id> # Add a template as a recommendation card (see 'todoregistry-cli create')
|
|
@@ -44288,6 +44356,7 @@ function parseCliArgs() {
|
|
|
44288
44356
|
model: { type: "string" },
|
|
44289
44357
|
group: { type: "string" },
|
|
44290
44358
|
"group-name": { type: "string" },
|
|
44359
|
+
"group-description": { type: "string" },
|
|
44291
44360
|
"list-agents": { type: "boolean", default: false },
|
|
44292
44361
|
"list-models": { type: "boolean", default: false },
|
|
44293
44362
|
"api-url": { type: "string" },
|
|
@@ -44300,6 +44369,7 @@ function parseCliArgs() {
|
|
|
44300
44369
|
title: { type: "string" },
|
|
44301
44370
|
alias: { type: "string" },
|
|
44302
44371
|
mime: { type: "string" },
|
|
44372
|
+
card: { type: "string" },
|
|
44303
44373
|
direction: { type: "string" },
|
|
44304
44374
|
"business-context": { type: "string" },
|
|
44305
44375
|
seed: { type: "string" },
|
|
@@ -44849,20 +44919,29 @@ async function autoCreateAgent(api, resolvedPath) {
|
|
|
44849
44919
|
}
|
|
44850
44920
|
|
|
44851
44921
|
// src/config.ts
|
|
44852
|
-
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
44922
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
44853
44923
|
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
44854
44924
|
import { homedir as homedir2, platform as platform2 } from "os";
|
|
44855
|
-
function
|
|
44925
|
+
function getConfigBase() {
|
|
44856
44926
|
const sys = platform2();
|
|
44857
|
-
if (sys === "win32")
|
|
44858
|
-
|
|
44859
|
-
|
|
44860
|
-
|
|
44861
|
-
|
|
44862
|
-
|
|
44927
|
+
if (sys === "win32")
|
|
44928
|
+
return process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
|
|
44929
|
+
if (sys === "darwin")
|
|
44930
|
+
return join2(homedir2(), "Library", "Application Support");
|
|
44931
|
+
return process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
44932
|
+
}
|
|
44933
|
+
function getConfigDir() {
|
|
44934
|
+
const base = getConfigBase();
|
|
44935
|
+
const dir = join2(base, "todoforai-cli");
|
|
44936
|
+
const legacy = join2(base, "todoai-cli");
|
|
44937
|
+
if (!existsSync(dir) && existsSync(legacy)) {
|
|
44938
|
+
try {
|
|
44939
|
+
renameSync(legacy, dir);
|
|
44940
|
+
} catch {
|
|
44941
|
+
return legacy;
|
|
44942
|
+
}
|
|
44863
44943
|
}
|
|
44864
|
-
|
|
44865
|
-
return join2(xdg, "todoai-cli");
|
|
44944
|
+
return dir;
|
|
44866
44945
|
}
|
|
44867
44946
|
function defaultScope() {
|
|
44868
44947
|
return {
|
|
@@ -45505,7 +45584,16 @@ function selectProject(projects, defaultId, setDefault) {
|
|
|
45505
45584
|
return Promise.resolve({ id: defaultId, name });
|
|
45506
45585
|
}
|
|
45507
45586
|
}
|
|
45587
|
+
const accountDefault = projects.find((p) => p?.project?.isDefault) || projects[0];
|
|
45588
|
+
if (!process.stdin.isTTY) {
|
|
45589
|
+
const id = getItemId(accountDefault);
|
|
45590
|
+
const name = getDisplayName(accountDefault);
|
|
45591
|
+
process.stderr.write(`Using account default project: ${name}
|
|
45592
|
+
`);
|
|
45593
|
+
return Promise.resolve({ id, name });
|
|
45594
|
+
}
|
|
45508
45595
|
return (async () => {
|
|
45596
|
+
const defIdx = projects.indexOf(accountDefault);
|
|
45509
45597
|
process.stderr.write(`
|
|
45510
45598
|
Please choose a project:
|
|
45511
45599
|
|
|
@@ -45513,7 +45601,7 @@ Please choose a project:
|
|
|
45513
45601
|
for (let i = 0;i < projects.length; i++) {
|
|
45514
45602
|
const name = getDisplayName(projects[i]);
|
|
45515
45603
|
const id = getItemId(projects[i]);
|
|
45516
|
-
process.stderr.write(` [${i + 1}] ${name}
|
|
45604
|
+
process.stderr.write(` [${i + 1}] ${name}${i === defIdx ? " (default)" : ""}
|
|
45517
45605
|
`);
|
|
45518
45606
|
if (id && id !== name)
|
|
45519
45607
|
process.stderr.write(` ${id}
|
|
@@ -45522,8 +45610,8 @@ Please choose a project:
|
|
|
45522
45610
|
process.stderr.write(`
|
|
45523
45611
|
`);
|
|
45524
45612
|
while (true) {
|
|
45525
|
-
const choice = await terminalLine(
|
|
45526
|
-
const idx = parseInt(choice, 10) - 1;
|
|
45613
|
+
const choice = await terminalLine(`Please enter your numeric choice [${defIdx + 1}]: `);
|
|
45614
|
+
const idx = choice === "" ? defIdx : parseInt(choice, 10) - 1;
|
|
45527
45615
|
if (idx >= 0 && idx < projects.length) {
|
|
45528
45616
|
const id = getItemId(projects[idx]);
|
|
45529
45617
|
const name = getDisplayName(projects[idx]);
|
|
@@ -46772,6 +46860,32 @@ Cancelled by user (Ctrl+C)
|
|
|
46772
46860
|
`);
|
|
46773
46861
|
return;
|
|
46774
46862
|
}
|
|
46863
|
+
if (positionals[0] === "show" && positionals[1] === "list") {
|
|
46864
|
+
const explicitProject = !!args.project;
|
|
46865
|
+
const todoId = positionals[2] || (!explicitProject ? getEnv("TODO_ID") || cfgScope.data.last_todo_id : undefined);
|
|
46866
|
+
const projectId2 = todoId ? undefined : args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
46867
|
+
if (!todoId && !projectId2) {
|
|
46868
|
+
process.stderr.write(`${RED}Usage: todoforai-cli show list [todo-id] [--project <id>] [--card <name>]${RESET}
|
|
46869
|
+
`);
|
|
46870
|
+
process.exit(2);
|
|
46871
|
+
}
|
|
46872
|
+
const { items } = await api.listShows({ todoId, projectId: projectId2, card: args.card });
|
|
46873
|
+
if (args.json) {
|
|
46874
|
+
console.log(JSON.stringify(items, null, 2));
|
|
46875
|
+
return;
|
|
46876
|
+
}
|
|
46877
|
+
if (items.length === 0) {
|
|
46878
|
+
process.stderr.write(`${DIM}No show blocks in ${todoId || `project ${projectId2}`}${RESET}
|
|
46879
|
+
`);
|
|
46880
|
+
return;
|
|
46881
|
+
}
|
|
46882
|
+
for (const it of items) {
|
|
46883
|
+
const kind = it.url ? `url ${it.url}` : it.mime || "";
|
|
46884
|
+
const card = it.cardRef ? ` card=${it.cardRef}` : "";
|
|
46885
|
+
console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}`);
|
|
46886
|
+
}
|
|
46887
|
+
return;
|
|
46888
|
+
}
|
|
46775
46889
|
if (positionals[0] === "show") {
|
|
46776
46890
|
const [, filePath, todoArg] = positionals;
|
|
46777
46891
|
const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
|
|
@@ -46799,7 +46913,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46799
46913
|
blob = file;
|
|
46800
46914
|
name = path3.basename(filePath);
|
|
46801
46915
|
}
|
|
46802
|
-
const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
|
|
46916
|
+
const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime, card: args.card });
|
|
46803
46917
|
if (args.json)
|
|
46804
46918
|
console.log(JSON.stringify(res, null, 2));
|
|
46805
46919
|
else
|
|
@@ -46824,13 +46938,13 @@ Cancelled by user (Ctrl+C)
|
|
|
46824
46938
|
if (positionals[0] === "recommend") {
|
|
46825
46939
|
const templateId = args.template || positionals[1];
|
|
46826
46940
|
if (!templateId) {
|
|
46827
|
-
process.stderr.write(`${RED}Usage: todoforai-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--project <id>]${RESET}
|
|
46941
|
+
process.stderr.write(`${RED}Usage: todoforai-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--group <slug> [--group-name "..."] [--group-description "..."]] [--project <id>]${RESET}
|
|
46828
46942
|
`);
|
|
46829
46943
|
process.stderr.write(`${DIM}Create a template first with: todoregistry-cli create --name ... --description ... --body @prompt.md${RESET}
|
|
46830
46944
|
`);
|
|
46831
46945
|
process.exit(2);
|
|
46832
46946
|
}
|
|
46833
|
-
let projectId2 = args.project || cfgScope.data.default_project_id;
|
|
46947
|
+
let projectId2 = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
46834
46948
|
if (!projectId2) {
|
|
46835
46949
|
const projects2 = await api.listProjects();
|
|
46836
46950
|
projectId2 = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
@@ -46852,7 +46966,10 @@ Cancelled by user (Ctrl+C)
|
|
|
46852
46966
|
...args.title ? { title: args.title } : {},
|
|
46853
46967
|
...args.note ? { note: args.note } : {},
|
|
46854
46968
|
...priority ? { priority } : {},
|
|
46855
|
-
...args["business-context"] ? { businessContextId: args["business-context"] } : {}
|
|
46969
|
+
...args["business-context"] ? { businessContextId: args["business-context"] } : {},
|
|
46970
|
+
...args.group ? { group: args.group } : {},
|
|
46971
|
+
...args["group-name"] ? { groupName: args["group-name"] } : {},
|
|
46972
|
+
...args["group-description"] ? { groupDescription: args["group-description"] } : {}
|
|
46856
46973
|
});
|
|
46857
46974
|
if (args.json)
|
|
46858
46975
|
console.log(JSON.stringify(rec, null, 2));
|
|
@@ -46862,7 +46979,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46862
46979
|
return;
|
|
46863
46980
|
}
|
|
46864
46981
|
if (positionals[0] === "claim" && positionals[1] === "mint") {
|
|
46865
|
-
let seedProjectId = args.seed || args.project || cfgScope.data.default_project_id;
|
|
46982
|
+
let seedProjectId = args.seed || args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
46866
46983
|
if (!seedProjectId) {
|
|
46867
46984
|
const projects2 = await api.listProjects();
|
|
46868
46985
|
seedProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
@@ -46904,7 +47021,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46904
47021
|
return;
|
|
46905
47022
|
}
|
|
46906
47023
|
if (positionals[0] === "next") {
|
|
46907
|
-
let projectId2 = args.project || cfgScope.data.default_project_id;
|
|
47024
|
+
let projectId2 = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
46908
47025
|
if (!projectId2) {
|
|
46909
47026
|
const projects2 = await api.listProjects();
|
|
46910
47027
|
projectId2 = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
@@ -46954,7 +47071,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46954
47071
|
return;
|
|
46955
47072
|
}
|
|
46956
47073
|
if (positionals[0] === "list" || positionals[0] === "ls") {
|
|
46957
|
-
let defaultProjectId = args.project || cfgScope.data.default_project_id;
|
|
47074
|
+
let defaultProjectId = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
46958
47075
|
if (!defaultProjectId) {
|
|
46959
47076
|
const projects2 = await api.listProjects();
|
|
46960
47077
|
defaultProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
@@ -47009,7 +47126,7 @@ Cancelled by user (Ctrl+C)
|
|
|
47009
47126
|
process.stderr.write(`${DIM}${spec.description}${RESET}
|
|
47010
47127
|
`);
|
|
47011
47128
|
const projects2 = await api.listProjects();
|
|
47012
|
-
let projectId2 = args.project;
|
|
47129
|
+
let projectId2 = args.project || getEnv("PROJECT_ID");
|
|
47013
47130
|
if (!projectId2) {
|
|
47014
47131
|
const cached = cfgScope.data.default_project_id;
|
|
47015
47132
|
projectId2 = (cached && projects2.some((p) => getItemId(p) === cached) ? cached : null) || projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
@@ -47169,7 +47286,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
|
47169
47286
|
} else {
|
|
47170
47287
|
content = await readStdin();
|
|
47171
47288
|
}
|
|
47172
|
-
const
|
|
47289
|
+
const envProjectId = getEnv("PROJECT_ID");
|
|
47290
|
+
const hasProject = args.project || envProjectId || cfgScope.data.default_project_id;
|
|
47173
47291
|
const storedAgent = cfgScope.data.default_agent_settings;
|
|
47174
47292
|
const hasAgent = preMatchedAgent || storedAgent?.id && !args.agent;
|
|
47175
47293
|
let projects = null;
|
|
@@ -47180,8 +47298,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
|
47180
47298
|
}
|
|
47181
47299
|
let projectId;
|
|
47182
47300
|
let projectName;
|
|
47183
|
-
if (args.project) {
|
|
47184
|
-
projectId = args.project;
|
|
47301
|
+
if (args.project || envProjectId) {
|
|
47302
|
+
projectId = args.project || envProjectId;
|
|
47185
47303
|
projectName = projectId;
|
|
47186
47304
|
if (projects) {
|
|
47187
47305
|
const match = projects.find((p) => getItemId(p) === projectId);
|
|
@@ -47223,7 +47341,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
|
47223
47341
|
try {
|
|
47224
47342
|
todo = await api.addMessage(projectId, content, agent, undefined, undefined, undefined, groupTag || undefined, groupName);
|
|
47225
47343
|
} catch (e) {
|
|
47226
|
-
if (!args.project && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
47344
|
+
if (!args.project && !envProjectId && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
47227
47345
|
cfgScope.clearDefaultProject();
|
|
47228
47346
|
process.stderr.write(`${RED}Not authorized for cached default project ${projectName} (${projectId}) \u2014 cleared it. Re-run to pick a project.${RESET}
|
|
47229
47347
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@todoforai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"dev": "bun run src/index.ts"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@shared/api": "file:../packages/shared-api",
|
|
22
|
+
"@shared/credentials": "file:../packages/shared-credentials",
|
|
21
23
|
"@todoforai/update-notifier": "^0.1.0",
|
|
22
24
|
"cli-highlight": "^2.1.11",
|
|
23
25
|
"diff-match-patch": "^1.0.5",
|