@todoforai/cli 0.1.31 → 0.1.33
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 +870 -137
- package/package.json +2 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -42136,7 +42136,7 @@ var require_supports_color = __commonJS((exports, module) => {
|
|
|
42136
42136
|
};
|
|
42137
42137
|
});
|
|
42138
42138
|
|
|
42139
|
-
// node_modules/chalk/source/util.js
|
|
42139
|
+
// node_modules/cli-highlight/node_modules/chalk/source/util.js
|
|
42140
42140
|
var require_util = __commonJS((exports, module) => {
|
|
42141
42141
|
var stringReplaceAll = (string, substring, replacer) => {
|
|
42142
42142
|
let index = string.indexOf(substring);
|
|
@@ -42175,7 +42175,7 @@ var require_util = __commonJS((exports, module) => {
|
|
|
42175
42175
|
};
|
|
42176
42176
|
});
|
|
42177
42177
|
|
|
42178
|
-
// node_modules/chalk/source/templates.js
|
|
42178
|
+
// node_modules/cli-highlight/node_modules/chalk/source/templates.js
|
|
42179
42179
|
var require_templates = __commonJS((exports, module) => {
|
|
42180
42180
|
var TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
|
|
42181
42181
|
var STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
|
|
@@ -42287,7 +42287,7 @@ var require_templates = __commonJS((exports, module) => {
|
|
|
42287
42287
|
};
|
|
42288
42288
|
});
|
|
42289
42289
|
|
|
42290
|
-
// node_modules/chalk/source/index.js
|
|
42290
|
+
// node_modules/cli-highlight/node_modules/chalk/source/index.js
|
|
42291
42291
|
var require_source = __commonJS((exports, module) => {
|
|
42292
42292
|
var ansiStyles = require_ansi_styles();
|
|
42293
42293
|
var { stdout: stdoutColor, stderr: stderrColor } = require_supports_color();
|
|
@@ -42744,7 +42744,14 @@ class ApiClient {
|
|
|
42744
42744
|
this.apiKey = apiKey;
|
|
42745
42745
|
}
|
|
42746
42746
|
get headers() {
|
|
42747
|
-
|
|
42747
|
+
const h = { "content-type": "application/json", "x-api-key": this.apiKey };
|
|
42748
|
+
const extra = typeof process !== "undefined" && process.env?.TODOFORAI_EXTRA_HEADERS;
|
|
42749
|
+
if (extra) {
|
|
42750
|
+
try {
|
|
42751
|
+
Object.assign(h, JSON.parse(extra));
|
|
42752
|
+
} catch {}
|
|
42753
|
+
}
|
|
42754
|
+
return h;
|
|
42748
42755
|
}
|
|
42749
42756
|
async request(method, endpoint, body) {
|
|
42750
42757
|
const url = `${this.apiUrl}${endpoint.replace(/^\/api\/v1/, restBasePath(this.apiKey))}`;
|
|
@@ -42857,12 +42864,27 @@ class ApiClient {
|
|
|
42857
42864
|
payload.todoId = todoId;
|
|
42858
42865
|
if (scheduledTimestamp)
|
|
42859
42866
|
payload.scheduledTimestamp = scheduledTimestamp;
|
|
42860
|
-
if (
|
|
42867
|
+
if (groupTag)
|
|
42861
42868
|
payload.groupTag = groupTag;
|
|
42862
|
-
if (
|
|
42869
|
+
if (groupName)
|
|
42863
42870
|
payload.groupName = groupName;
|
|
42864
42871
|
return this.request("POST", `/api/v1/projects/${projectId}/todos`, payload);
|
|
42865
42872
|
}
|
|
42873
|
+
async registerAttachment(file, filename, opts = {}) {
|
|
42874
|
+
const form = new FormData;
|
|
42875
|
+
form.append("file", file, filename);
|
|
42876
|
+
if (opts.todoId)
|
|
42877
|
+
form.append("todoId", opts.todoId);
|
|
42878
|
+
const res = await fetch(`${this.apiUrl}${restBasePath(this.apiKey)}/resources/register`, {
|
|
42879
|
+
method: "POST",
|
|
42880
|
+
headers: { "x-api-key": this.apiKey },
|
|
42881
|
+
body: form,
|
|
42882
|
+
signal: AbortSignal.timeout(60000)
|
|
42883
|
+
});
|
|
42884
|
+
if (!res.ok)
|
|
42885
|
+
throw new Error(`API POST /resources/register failed: ${res.status} ${await res.text()}`);
|
|
42886
|
+
return res.json();
|
|
42887
|
+
}
|
|
42866
42888
|
async showFile(todoId, file, filename, opts = {}) {
|
|
42867
42889
|
const form = new FormData;
|
|
42868
42890
|
form.append("file", file, filename);
|
|
@@ -43280,7 +43302,7 @@ var TodoStatus;
|
|
|
43280
43302
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43281
43303
|
})(TodoStatus ||= {});
|
|
43282
43304
|
// ../packages/shared-fbe/src/models.ts
|
|
43283
|
-
var DEFAULT_MODEL = "anthropic:anthropic/claude-
|
|
43305
|
+
var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5";
|
|
43284
43306
|
var MODEL_ALIASES = {
|
|
43285
43307
|
claude: DEFAULT_MODEL
|
|
43286
43308
|
};
|
|
@@ -43336,8 +43358,8 @@ function qualifiedModelIds(listing, filter) {
|
|
|
43336
43358
|
var USAGE_WINDOWS = {
|
|
43337
43359
|
SESSION_MS: 5 * 60 * 60 * 1000,
|
|
43338
43360
|
WEEK_MS: 7 * 24 * 60 * 60 * 1000,
|
|
43339
|
-
WEEKLY_FACTOR: 0.
|
|
43340
|
-
SESSION_FACTOR: 1 /
|
|
43361
|
+
WEEKLY_FACTOR: 0.35,
|
|
43362
|
+
SESSION_FACTOR: 1 / 5
|
|
43341
43363
|
};
|
|
43342
43364
|
// ../packages/shared-fbe/src/mimetypes.json
|
|
43343
43365
|
var mimetypes_default = {
|
|
@@ -43517,7 +43539,11 @@ var API_TOOL_TYPES = new Set([
|
|
|
43517
43539
|
"api_list_mcps" /* ApiListMcps */,
|
|
43518
43540
|
"api_get_usage" /* ApiGetUsage */,
|
|
43519
43541
|
"api_get_balance" /* ApiGetBalance */,
|
|
43520
|
-
"api_get_api_schema" /* ApiGetApiSchema
|
|
43542
|
+
"api_get_api_schema" /* ApiGetApiSchema */,
|
|
43543
|
+
"api_list_board" /* ApiListBoard */,
|
|
43544
|
+
"api_upsert_group" /* ApiUpsertGroup */,
|
|
43545
|
+
"api_create_template" /* ApiCreateTemplate */,
|
|
43546
|
+
"api_recommend_template" /* ApiRecommendTemplate */
|
|
43521
43547
|
]);
|
|
43522
43548
|
var FILE_TOOL_TYPES = new Set([
|
|
43523
43549
|
"read" /* Read */,
|
|
@@ -43959,34 +43985,36 @@ var BUSINESS_ANALYZER_AGENT = {
|
|
|
43959
43985
|
ownerId: "SYSTEM",
|
|
43960
43986
|
systemMessage: `You are a business analyst and growth advisor. Beyond analyzing and updating the business context, you provide ongoing, actionable advice for growing the company's digital presence and business.
|
|
43961
43987
|
|
|
43962
|
-
You have no previous context about this business yet
|
|
43988
|
+
You have no previous context about this business yet. Everything you know must come from the current business context markdown and what you research now. Do not assume prior knowledge or earlier conversations.
|
|
43989
|
+
|
|
43990
|
+
Write dense, not long. Use concrete names, numbers and URLs, with no filler or restated context. Cut any sentence that adds no information and keep scannable structure. Use commas, colons and periods only. Never use em-dashes or semicolons anywhere, including in generated templates, notes and group descriptions.
|
|
43963
43991
|
|
|
43964
|
-
This chat lives on the user's Brand page
|
|
43992
|
+
This chat lives on the user's Brand page. The business context markdown you maintain is rendered live right next to this conversation, together with the brand voice answers and the company's online-presence map. The document lives at the \`todoforai:business-context\` resource. Whenever you save to it, the user sees the document change immediately. Refer to it as "your brand page", never as a file, and keep it polished since it doubles as the company's profile.
|
|
43965
43993
|
|
|
43966
43994
|
How to analyze and update the business context:
|
|
43967
43995
|
1. Read the current business context with \`Read todoforai:business-context\`
|
|
43968
|
-
2. Research
|
|
43969
|
-
3. Merge findings into the existing context
|
|
43996
|
+
2. Research the company. Fetch websites with webfetch and follow key subpages like /about, /pricing and /contact when they exist. Use google_search and google_rag to find competitors and fill knowledge gaps
|
|
43997
|
+
3. Merge findings into the existing context and save it with \`Edit todoforai:business-context\`, or \`Write todoforai:business-context\` to create or overwrite it. Preserve valid existing information. Update a conflicting fact only when the new source is clearer or more specific. You MUST persist your findings this way. Analysis that is only spoken in chat and never written to the brand page is lost.
|
|
43970
43998
|
|
|
43971
43999
|
Cover these sections (skip any without data):
|
|
43972
|
-
- **Company Info
|
|
43973
|
-
- **Product & Services
|
|
43974
|
-
- **Value Proposition
|
|
43975
|
-
- **Voice & Tone
|
|
43976
|
-
- **Target Market
|
|
43977
|
-
- **Tech Stack
|
|
43978
|
-
- **Competitors
|
|
43979
|
-
- **Online Presence
|
|
44000
|
+
- **Company Info**: name, founding year, location, team size, stage
|
|
44001
|
+
- **Product & Services**: offerings, key features, pricing model, target audience
|
|
44002
|
+
- **Value Proposition**: core problem solved, unique selling points
|
|
44003
|
+
- **Voice & Tone**: brand personality, communication style (derive from website copy)
|
|
44004
|
+
- **Target Market**: customer segments, industries, geographic focus, B2B vs B2C
|
|
44005
|
+
- **Tech Stack**: technologies, integrations, platforms
|
|
44006
|
+
- **Competitors**: 3-5 direct competitors with one-line comparisons
|
|
44007
|
+
- **Online Presence**: where the company is present. Search for its accounts on X/Twitter, LinkedIn, Facebook, Instagram, YouTube, Reddit, GitHub, blog/newsletter, and any industry-relevant platforms. List each found channel as \`platform: absolute profile URL (one-line activity note such as active, stale or unclaimed)\`. Also note important channels where no presence was found. Always record the full public URL like \`https://x.com/acme\`, never a bare handle. These links are read back to learn the company's writing voice, so an unfetchable \`@acme\` is a dead end
|
|
43980
44008
|
|
|
43981
44009
|
If anything important remains unclear, add an **Open Questions** section at the end.
|
|
43982
44010
|
|
|
43983
|
-
Maintain a single machine-readable **Presence** block so the user's brand page can render a live presence map. Write it as one fenced code block tagged \`growth-plan\` containing JSON
|
|
44011
|
+
Maintain a single machine-readable **Presence** block so the user's brand page can render a live presence map. Write it as one fenced code block tagged \`growth-plan\` containing JSON. The user never sees this raw. It powers the presence hexes on the brand page.
|
|
43984
44012
|
|
|
43985
44013
|
\`\`\`growth-plan
|
|
43986
44014
|
{
|
|
43987
44015
|
"presence": {
|
|
43988
44016
|
"linkedin-networking": { "score": 45, "handle": "https://linkedin.com/company/example", "note": "Company page exists but posts infrequently" },
|
|
43989
|
-
"x-twitter-presence": { "score": 60, "handle": "
|
|
44017
|
+
"x-twitter-presence": { "score": 60, "handle": "https://x.com/example", "note": "Active, regular posts" },
|
|
43990
44018
|
"newsletter": { "score": null, "note": "No newsletter signup found" }
|
|
43991
44019
|
}
|
|
43992
44020
|
}
|
|
@@ -43995,46 +44023,66 @@ Maintain a single machine-readable **Presence** block so the user's brand page c
|
|
|
43995
44023
|
Rules for the Presence block:
|
|
43996
44024
|
- Keys MUST be one of these exact ids: facebook-posting, x-twitter-presence, tiktok-content, instagram-content, linkedin-networking, reddit-engagement, blog-writing, seo-optimization, backlink-building, keyword-research, aso-optimization, llm-optimization, google-ads-management, meta-ads-management, lead-outreach, crm-management, newsletter, affiliate-program, partnership-outreach, analytics-monitoring. Skip channels you did not check.
|
|
43997
44025
|
- \`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.
|
|
43998
|
-
-
|
|
44026
|
+
- \`handle\` is the channel's ABSOLUTE public URL such as \`https://www.linkedin.com/company/acme\` or \`https://x.com/acme\`, never a bare \`@handle\`. The brand page reads these pages back to learn the company's writing voice, so a URL that can't be fetched is worthless. Omit \`handle\` entirely when you found no account.
|
|
44027
|
+
- Keep it consistent with the prose Online Presence section. The JSON is the structured mirror of those findings.
|
|
43999
44028
|
|
|
44000
|
-
Growth recommendations are NOT part of this block
|
|
44029
|
+
Growth recommendations are NOT part of this block. They live on the project board as proposed todos, shown as \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.
|
|
44001
44030
|
|
|
44002
|
-
Document order for the saved brand page: human-readable sections first, then the **Next Steps** section, then Open Questions
|
|
44031
|
+
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.
|
|
44003
44032
|
|
|
44004
|
-
Guide the user one step at a time
|
|
44005
|
-
- When you need input
|
|
44006
|
-
- After completing any analysis or context update, always close with a short **Next Steps** section: 2-4 concrete, prioritized tips tailored to this specific business
|
|
44033
|
+
Guide the user one step at a time. They should always know the single next thing to do:
|
|
44034
|
+
- When you need input on brand voice, tone, target audience or which channel to prioritize, prefer the question tool with short multi-choice options over asking open-ended prose questions.
|
|
44035
|
+
- After completing any analysis or context update, always close with a short **Next Steps** section: 2-4 concrete, prioritized tips tailored to this specific business, such as SEO and content gaps, social channels worth activating, positioning against the competitors you found, or conversion and messaging improvements. Base tips on what you actually observed, keep each to one or two sentences, and lead with the one you'd do first.
|
|
44007
44036
|
|
|
44008
44037
|
In follow-up conversations, act as an ongoing advisor: answer growth and digital-presence questions using the business context, refresh your analysis when asked, and suggest what to tackle next.
|
|
44009
44038
|
|
|
44010
|
-
When the user asks you to recommend how to grow
|
|
44039
|
+
When the user asks you to recommend how to grow, for example "recommend growth TODOs", the brand page's guided suggestion, or the board's Recommend button, read the current business context including the presence block, read the board with \`api_list_board\`, 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. Do not write a separate recommendations list into the markdown.
|
|
44011
44040
|
|
|
44012
44041
|
Strategic workstreams (board groups):
|
|
44013
|
-
The project board is organized into group cards
|
|
44014
|
-
- FIRST fit each recommendation into an existing group when it genuinely advances that workstream
|
|
44015
|
-
- When your analysis surfaces a strategic direction the board doesn't cover yet, introduce it
|
|
44016
|
-
- Keep the map tight
|
|
44042
|
+
The project board is organized into group cards. Each group should be a strategic workstream for THIS company, a front the business is or should be pushing on, such as "SEO & Content", "Outbound to agencies", "Community on Reddit" or "Retention & Email". That board IS the company's current strategic map, so ALWAYS read it with \`api_list_board\` before you propose anything. It returns every live group with its \`slug\`, \`name\` and \`description\`, the workstream's strategic intent, together with the cards already sitting on it. Pass \`includeArchived: "true"\` when you also need the retired ones, for example before restoring one. Recommendation-generation runs also hand you the group list as JSON up front, but that is only a routing hint. It carries no cards, so it can't tell you whether a move is already proposed. Propose blind and you will duplicate what is already on the board.
|
|
44043
|
+
- FIRST fit each recommendation into an existing group when it genuinely advances that workstream, passing its slug as the \`group\` parameter of \`api_recommend_template\`. Match on strategy by reading the descriptions, not on keywords.
|
|
44044
|
+
- 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\` of 2-4 words at strategy level, like "Partner Channel" rather than "Misc" or "Growth". Add a one-sentence \`groupDescription\` stating the strategic bet and its target outcome for THIS company, for example "Win comparison searches against Zapier and n8n to capture switchers". The group is created with the card.
|
|
44045
|
+
- 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. A group is a strategic bet and its cards are the moves.
|
|
44046
|
+
- Curate the map, don't just append to it. \`api_upsert_group\` edits a group in place. Sharpen a vague or missing \`description\` so future todos route correctly, rename a group whose cards have drifted away from its name, reorder with \`order\` to put the most important bet first, and archive a workstream the business has genuinely abandoned with \`archived: "yes"\`, or \`"no"\` to bring one back. Do this when the board tells you it's needed, such as untitled \`c-xxxx\` groups, a description that no longer matches its cards, or more than 7 live groups. Say what you changed and why in your reply, and never archive a group that still has open cards without telling the user.
|
|
44017
44047
|
|
|
44018
44048
|
Recommending actionable TODOs from your recommendations:
|
|
44019
|
-
To materialize growth recommendations, propose todos on the project board. A proposal is a real todo in PROPOSED state
|
|
44049
|
+
To materialize growth recommendations, propose todos on the project board. A proposal is a real todo in PROPOSED state. It sits as a \u2727 row on its group card until the user accepts it by starting it, or dismisses it. You NEVER create running todos. You only propose. One tool does it: \`api_recommend_template\`.
|
|
44050
|
+
|
|
44051
|
+
Exactly ONE \`api_recommend_template\` call per proposal: it carries both what the todo is and where it lands on the board.
|
|
44052
|
+
|
|
44053
|
+
1. WHAT IT IS. Pass \`name\` + \`body\`, written for THIS business, and optionally \`description\`, \`short\` and \`categories\`.
|
|
44054
|
+
- Reuse an existing registry template instead ONLY when it already fits this business closely with no meaningful edits: pass its id as \`templateId\` and omit \`name\`/\`body\`. If it needs tailoring, write yours inline. Do not recommend a generic playbook that ignores this company's specifics.
|
|
44055
|
+
|
|
44056
|
+
Language: match the business. Write user-facing text such as names, bodies and notes in the language the company itself communicates in. When the company is international or unclear, use English. Only the frame stays in English. The heading lines \`Your task:\`, \`Steps:\` and \`Data needed:\` are parsed, so keep them exactly as-is. Everything else follows the business's language.
|
|
44057
|
+
|
|
44058
|
+
The body is read by a PERSON deciding whether to run it, so it must be understandable at a glance. The executing agent is capable and has the business context at run time, so it will elaborate and figure out the details itself. Write a minimal brief, not a playbook. Include ONLY what the agent could not figure out on its own: the business-specific direction such as company, competitor, segment, channel and the metric to move, plus any hard constraint. Cut everything else, including background, caveats, generic how-to and restated context.
|
|
44059
|
+
|
|
44060
|
+
Hard limits: body \u226480 words total. One sentence of persona. One-sentence task. 2-4 steps, each a short fragment of at most 8 words naming WHAT, not how. Data needed is a comma-separated list. If a sentence wouldn't change what the agent does, delete it.
|
|
44020
44061
|
|
|
44021
|
-
|
|
44022
|
-
- Default: CREATE a template tailored to THIS business with the \`api_create_template\` tool. It returns the new template id.
|
|
44023
|
-
- Reuse an existing registry template ONLY when it already fits this business closely with no meaningful edits \u2014 pass its id straight to \`api_recommend_template\`. If it needs tailoring, create a new one instead \u2014 do not recommend a generic playbook that ignores this company's specifics.
|
|
44062
|
+
\`name\`: 3-6 words, action-first like "Win Reddit comparison threads", with no company name prefix. Give \`description\` and \`short\` one tight sentence each.
|
|
44024
44063
|
|
|
44025
|
-
|
|
44064
|
+
Example body at the right density (adapt specifics, keep the brevity):
|
|
44065
|
+
"You are a B2B outbound specialist for dev-tool companies.
|
|
44066
|
+
Your task:
|
|
44067
|
+
Land Acme in 10 agency partner conversations this month.
|
|
44068
|
+
Steps:
|
|
44069
|
+
- Shortlist 30 EU automation agencies
|
|
44070
|
+
- Draft one-line personalized openers
|
|
44071
|
+
- Send, track replies in a sheet
|
|
44072
|
+
Data needed:
|
|
44073
|
+
Acme pricing page, current partner list, founder's LinkedIn"
|
|
44026
44074
|
|
|
44027
|
-
Body structure (REQUIRED
|
|
44028
|
-
- Start with the persona
|
|
44029
|
-
- Then the request
|
|
44030
|
-
- Headings must match exactly
|
|
44075
|
+
Body structure (REQUIRED). The body is split into the agent's system prompt and the first user turn by section headings, so follow this exactly:
|
|
44076
|
+
- Start with the persona, expertise and any Rules as plain prose at the top with no heading. This becomes the agent's system prompt.
|
|
44077
|
+
- Then write the request using these exact standalone heading lines, letters only, each on its own line and ending with a colon: \`Your task:\`, \`Steps:\`, \`Data needed:\`. Everything under these headings becomes the first user turn.
|
|
44078
|
+
- Headings must match exactly or the split breaks. Write \`Your task:\`, not \`Your task (SEO):\`. Put the business-specific detail in the body text under each heading, never in the heading itself.
|
|
44031
44079
|
|
|
44032
|
-
2.
|
|
44033
|
-
|
|
44080
|
+
2. WHERE IT LANDS, in that same call. It becomes a PROPOSED todo on the project and never a running todo:
|
|
44081
|
+
Give it a one-line \`note\` of roughly 15 words at most giving the WHY for THIS business, which 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. Reuse an existing slug when one fits. A new slug plus \`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 \`templateId\` refreshes that open proposal instead of duplicating it; an inline proposal is always a new one, so check the board you just read before repeating a move that is already there. Dismissed proposals since your last run are fed back to you. Don't re-propose those, steer elsewhere.
|
|
44034
44082
|
|
|
44035
|
-
|
|
44083
|
+
It runs in-process and needs no machine or shell. Never shell out to \`todoregistry-cli\` or \`todoforai-cli\` for this.
|
|
44036
44084
|
|
|
44037
|
-
3. Propose 3-6 todos
|
|
44085
|
+
3. Propose 3-6 todos, highest priority first. Make sure a few of them are the trivial, high-probability moves for this company, the strategic directions its owner would nod at instantly, alongside any cleverer bets. Then reply with a one-line summary per proposal grouped by workstream, and tell the user they're waiting on the board for review.`,
|
|
44038
44086
|
mcpConfigs: {},
|
|
44039
44087
|
edgesMcpConfigs: {},
|
|
44040
44088
|
permissions: {
|
|
@@ -44221,7 +44269,7 @@ import { parseArgs } from "util";
|
|
|
44221
44269
|
// package.json
|
|
44222
44270
|
var package_default = {
|
|
44223
44271
|
name: "@todoforai/cli",
|
|
44224
|
-
version: "0.1.
|
|
44272
|
+
version: "0.1.33",
|
|
44225
44273
|
type: "module",
|
|
44226
44274
|
bin: {
|
|
44227
44275
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -44240,6 +44288,7 @@ var package_default = {
|
|
|
44240
44288
|
},
|
|
44241
44289
|
dependencies: {
|
|
44242
44290
|
"@todoforai/update-notifier": "^0.1.0",
|
|
44291
|
+
chalk: "^6.0.0",
|
|
44243
44292
|
"cli-highlight": "^2.1.11",
|
|
44244
44293
|
"diff-match-patch": "^1.0.5",
|
|
44245
44294
|
ws: "^8.18.0"
|
|
@@ -44258,35 +44307,35 @@ function getEnv(name) {
|
|
|
44258
44307
|
}
|
|
44259
44308
|
function printUsage() {
|
|
44260
44309
|
process.stderr.write(`
|
|
44261
|
-
|
|
44310
|
+
tfa-cli \u2014 TODOforAI CLI (Bun). Legacy aliases: todoforai-cli, todoai.
|
|
44262
44311
|
|
|
44263
44312
|
Usage:
|
|
44264
|
-
|
|
44265
|
-
|
|
44266
|
-
|
|
44267
|
-
echo "content" |
|
|
44268
|
-
|
|
44269
|
-
|
|
44270
|
-
|
|
44271
|
-
|
|
44272
|
-
|
|
44273
|
-
|
|
44274
|
-
|
|
44275
|
-
|
|
44276
|
-
|
|
44277
|
-
|
|
44278
|
-
|
|
44279
|
-
|
|
44280
|
-
|
|
44313
|
+
tfa-cli login # Browser-based device auth
|
|
44314
|
+
tfa-cli "prompt text" # Prompt as argument
|
|
44315
|
+
tfa-cli -n "Quick task" # Non-interactive (run and exit)
|
|
44316
|
+
echo "content" | tfa-cli # Pipe from stdin
|
|
44317
|
+
tfa-cli --path /my/project "Fix bug" # Explicit workspace path
|
|
44318
|
+
tfa-cli -c ["prompt"] # Resume last todo (optional prompt sent on attach)
|
|
44319
|
+
tfa-cli --resume <todo-id> ["prompt"] # Resume specific todo (optional prompt sent on attach)
|
|
44320
|
+
tfa-cli --inspect <todo-id>[@<slice>] # Read chat log. <slice> = -3:, :1, 5:10, 7 (Python-style)
|
|
44321
|
+
tfa-cli start <id> # Start a TODO from the registry (todoregistry.com)
|
|
44322
|
+
tfa-cli --list-agents # List available agents and exit
|
|
44323
|
+
tfa-cli --list-models [filter] # List models usable with --model and exit
|
|
44324
|
+
tfa-cli agent update <agent> model=<model> # Update agent settings (see 'agent --help')
|
|
44325
|
+
tfa-cli list [-n 30] [--cursor N] [--all] [--status S] # List todos (paginated); see 'list --help'
|
|
44326
|
+
tfa-cli status <todo-id> <STATUS> # Update a todo's status (run 'status --help' for the full list)
|
|
44327
|
+
tfa-cli delete <todo-id> # Permanently delete a todo
|
|
44328
|
+
tfa-cli addmessage <todo-id> "text" # Add a message to an existing todo
|
|
44329
|
+
tfa-cli show <file|-> [todo-id] # Show a file in the chat (rendered by mimetype; - reads stdin)
|
|
44281
44330
|
# [--title T] [--alias A] [--mime M] [--card <name>] [--json]
|
|
44282
|
-
|
|
44331
|
+
tfa-cli show list [todo-id] # List show blocks (ref, title, mime/url, card)
|
|
44283
44332
|
# [--project <id>] [--card <name>] [--json]
|
|
44284
44333
|
# no todo-id + --project (or $TODOFORAI_PROJECT_ID) = every todo
|
|
44285
|
-
|
|
44334
|
+
tfa-cli open <url> [todo-id] # Show a live http(s) url in the chat as a preview
|
|
44286
44335
|
# [--title T] [--alias A] [--json]
|
|
44287
|
-
|
|
44288
|
-
|
|
44289
|
-
|
|
44336
|
+
tfa-cli recommend --template <id> # Add a template as a recommendation card (see 'todoregistry-cli create')
|
|
44337
|
+
tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> ownership links for a project you own
|
|
44338
|
+
tfa-cli next [--direction "<text>"] # Ask the analyzer for growth recommendation cards (optional free-text steer)
|
|
44290
44339
|
|
|
44291
44340
|
Options:
|
|
44292
44341
|
--path <dir> Workspace path (default: cwd)
|
|
@@ -44310,6 +44359,8 @@ Options:
|
|
|
44310
44359
|
--allow-all Set permissions to allow all tools (no approval needed)
|
|
44311
44360
|
--raw-sysmsg <file> Use file contents verbatim as system prompt (new TODO only)
|
|
44312
44361
|
--no-watch Create todo and exit
|
|
44362
|
+
--isolated Spawn an ephemeral, task-scoped bridge: the agent sees ONLY
|
|
44363
|
+
this machine (workspace = --path/cwd); session dies with the CLI
|
|
44313
44364
|
--no-bridge Do not auto-spawn bridge
|
|
44314
44365
|
--no-edge Deprecated alias for --no-bridge
|
|
44315
44366
|
--json Output as JSON
|
|
@@ -44317,6 +44368,7 @@ Options:
|
|
|
44317
44368
|
--format-anthropic 'inspect --json': Anthropic-style shape (tool_result in next user msg); attachment sources are uri-typed, so not a 1:1 messages.create input
|
|
44318
44369
|
--safe Validate API key upfront
|
|
44319
44370
|
--debug, -d Debug output
|
|
44371
|
+
--debug-dump Attach LLM request debug info per turn (requires server grant)
|
|
44320
44372
|
--show-config Show config
|
|
44321
44373
|
--reset-config Reset config file
|
|
44322
44374
|
--version, -v Print version and exit
|
|
@@ -44334,7 +44386,7 @@ var STATUS_HELP = {
|
|
|
44334
44386
|
};
|
|
44335
44387
|
function printStatusHelp() {
|
|
44336
44388
|
process.stderr.write(`
|
|
44337
|
-
|
|
44389
|
+
tfa-cli status <todo-id> <STATUS>
|
|
44338
44390
|
|
|
44339
44391
|
Common statuses:
|
|
44340
44392
|
${Object.entries(STATUS_HELP).map(([s, d]) => ` ${s.padEnd(18)}${d}`).join(`
|
|
@@ -44380,6 +44432,8 @@ function parseCliArgs() {
|
|
|
44380
44432
|
"allow-all": { type: "boolean", default: false },
|
|
44381
44433
|
"raw-sysmsg": { type: "string" },
|
|
44382
44434
|
"no-watch": { type: "boolean", default: false },
|
|
44435
|
+
isolated: { type: "boolean", default: false },
|
|
44436
|
+
"debug-dump": { type: "boolean", default: false },
|
|
44383
44437
|
"no-bridge": { type: "boolean", default: false },
|
|
44384
44438
|
"no-edge": { type: "boolean", default: false },
|
|
44385
44439
|
json: { type: "boolean", default: false },
|
|
@@ -44770,14 +44824,12 @@ function bridgeRunArgs(apiUrl) {
|
|
|
44770
44824
|
const url = parseApiUrl(apiUrl);
|
|
44771
44825
|
if (!url)
|
|
44772
44826
|
return [];
|
|
44773
|
-
if (
|
|
44827
|
+
if (url.hostname && url.hostname !== "api.todofor.ai") {
|
|
44774
44828
|
const args = ["--host", url.hostname];
|
|
44775
44829
|
if (url.port)
|
|
44776
44830
|
args.push("--port", url.port);
|
|
44777
44831
|
return withProfile(args, apiUrl);
|
|
44778
44832
|
}
|
|
44779
|
-
if (url.hostname && url.hostname !== "api.todofor.ai")
|
|
44780
|
-
return withProfile(["--host", url.hostname], apiUrl);
|
|
44781
44833
|
return [];
|
|
44782
44834
|
}
|
|
44783
44835
|
function bridgeLoginArgs(apiUrl) {
|
|
@@ -45175,7 +45227,9 @@ var DETAILED_ONLY = new Set([
|
|
|
45175
45227
|
"stop_sequence",
|
|
45176
45228
|
"permissions",
|
|
45177
45229
|
"isPublic",
|
|
45178
|
-
"metadata"
|
|
45230
|
+
"metadata",
|
|
45231
|
+
"systemPrompt",
|
|
45232
|
+
"toolSchemas"
|
|
45179
45233
|
]);
|
|
45180
45234
|
var DEBUG_ONLY = new Set([
|
|
45181
45235
|
"runMeta",
|
|
@@ -45303,6 +45357,10 @@ function toAnthropicShape(messages, mode = "default", format = "compact") {
|
|
|
45303
45357
|
msg.id = m.id;
|
|
45304
45358
|
if (m.createdAt)
|
|
45305
45359
|
msg.createdAt = m.createdAt;
|
|
45360
|
+
if (m.systemPrompt !== undefined)
|
|
45361
|
+
msg.systemPrompt = m.systemPrompt;
|
|
45362
|
+
if (m.toolSchemas !== undefined)
|
|
45363
|
+
msg.toolSchemas = m.toolSchemas;
|
|
45306
45364
|
}
|
|
45307
45365
|
if (m.role === "user") {
|
|
45308
45366
|
const atts = (m.attachments ?? []).filter((a) => !prevToolAttachmentIds.has(a.id)).map((a) => {
|
|
@@ -45676,45 +45734,594 @@ Please choose an agent:
|
|
|
45676
45734
|
// src/diff-view.ts
|
|
45677
45735
|
var import_diff_match_patch = __toESM(require_diff_match_patch(), 1);
|
|
45678
45736
|
var import_cli_highlight = __toESM(require_dist(), 1);
|
|
45679
|
-
var import_chalk = __toESM(require_source(), 1);
|
|
45680
45737
|
import { extname } from "path";
|
|
45738
|
+
|
|
45739
|
+
// node_modules/chalk/source/utilities.js
|
|
45740
|
+
function stringReplaceAll(string, substring, postfix) {
|
|
45741
|
+
let index = string.indexOf(substring);
|
|
45742
|
+
if (index === -1) {
|
|
45743
|
+
return string;
|
|
45744
|
+
}
|
|
45745
|
+
const substringLength = substring.length;
|
|
45746
|
+
let endIndex = 0;
|
|
45747
|
+
let returnValue = "";
|
|
45748
|
+
do {
|
|
45749
|
+
returnValue += string.slice(endIndex, index) + substring + postfix;
|
|
45750
|
+
endIndex = index + substringLength;
|
|
45751
|
+
index = string.indexOf(substring, endIndex);
|
|
45752
|
+
} while (index !== -1);
|
|
45753
|
+
returnValue += string.slice(endIndex);
|
|
45754
|
+
return returnValue;
|
|
45755
|
+
}
|
|
45756
|
+
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
45757
|
+
let endIndex = 0;
|
|
45758
|
+
let returnValue = "";
|
|
45759
|
+
do {
|
|
45760
|
+
const isGotCR = string[index - 1] === "\r";
|
|
45761
|
+
returnValue += string.slice(endIndex, isGotCR ? index - 1 : index) + prefix + (isGotCR ? `\r
|
|
45762
|
+
` : `
|
|
45763
|
+
`) + postfix;
|
|
45764
|
+
endIndex = index + 1;
|
|
45765
|
+
index = string.indexOf(`
|
|
45766
|
+
`, endIndex);
|
|
45767
|
+
} while (index !== -1);
|
|
45768
|
+
returnValue += string.slice(endIndex);
|
|
45769
|
+
return returnValue;
|
|
45770
|
+
}
|
|
45771
|
+
|
|
45772
|
+
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
45773
|
+
var ANSI_BACKGROUND_OFFSET = 10;
|
|
45774
|
+
var ANSI_UNDERLINE_OFFSET = 20;
|
|
45775
|
+
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
45776
|
+
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
45777
|
+
var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
|
|
45778
|
+
var wrapUnderlineAnsi = (code) => `\x1B[58;5;${code < 90 ? code - 30 : code - 90 + 8}m`;
|
|
45779
|
+
var styles = {
|
|
45780
|
+
modifier: {
|
|
45781
|
+
reset: [0, 0],
|
|
45782
|
+
bold: [1, 22],
|
|
45783
|
+
dim: [2, 22],
|
|
45784
|
+
italic: [3, 23],
|
|
45785
|
+
underline: [4, 24],
|
|
45786
|
+
underlineDouble: ["4:2", 24],
|
|
45787
|
+
underlineCurly: ["4:3", 24],
|
|
45788
|
+
underlineDotted: ["4:4", 24],
|
|
45789
|
+
underlineDashed: ["4:5", 24],
|
|
45790
|
+
overline: [53, 55],
|
|
45791
|
+
inverse: [7, 27],
|
|
45792
|
+
hidden: [8, 28],
|
|
45793
|
+
strikethrough: [9, 29]
|
|
45794
|
+
},
|
|
45795
|
+
color: {
|
|
45796
|
+
black: [30, 39],
|
|
45797
|
+
red: [31, 39],
|
|
45798
|
+
green: [32, 39],
|
|
45799
|
+
yellow: [33, 39],
|
|
45800
|
+
blue: [34, 39],
|
|
45801
|
+
magenta: [35, 39],
|
|
45802
|
+
cyan: [36, 39],
|
|
45803
|
+
white: [37, 39],
|
|
45804
|
+
blackBright: [90, 39],
|
|
45805
|
+
gray: [90, 39],
|
|
45806
|
+
grey: [90, 39],
|
|
45807
|
+
redBright: [91, 39],
|
|
45808
|
+
greenBright: [92, 39],
|
|
45809
|
+
yellowBright: [93, 39],
|
|
45810
|
+
blueBright: [94, 39],
|
|
45811
|
+
magentaBright: [95, 39],
|
|
45812
|
+
cyanBright: [96, 39],
|
|
45813
|
+
whiteBright: [97, 39]
|
|
45814
|
+
},
|
|
45815
|
+
bgColor: {
|
|
45816
|
+
bgBlack: [40, 49],
|
|
45817
|
+
bgRed: [41, 49],
|
|
45818
|
+
bgGreen: [42, 49],
|
|
45819
|
+
bgYellow: [43, 49],
|
|
45820
|
+
bgBlue: [44, 49],
|
|
45821
|
+
bgMagenta: [45, 49],
|
|
45822
|
+
bgCyan: [46, 49],
|
|
45823
|
+
bgWhite: [47, 49],
|
|
45824
|
+
bgBlackBright: [100, 49],
|
|
45825
|
+
bgGray: [100, 49],
|
|
45826
|
+
bgGrey: [100, 49],
|
|
45827
|
+
bgRedBright: [101, 49],
|
|
45828
|
+
bgGreenBright: [102, 49],
|
|
45829
|
+
bgYellowBright: [103, 49],
|
|
45830
|
+
bgBlueBright: [104, 49],
|
|
45831
|
+
bgMagentaBright: [105, 49],
|
|
45832
|
+
bgCyanBright: [106, 49],
|
|
45833
|
+
bgWhiteBright: [107, 49]
|
|
45834
|
+
},
|
|
45835
|
+
underlineColor: {
|
|
45836
|
+
underlineBlack: ["58;5;0", 59],
|
|
45837
|
+
underlineRed: ["58;5;1", 59],
|
|
45838
|
+
underlineGreen: ["58;5;2", 59],
|
|
45839
|
+
underlineYellow: ["58;5;3", 59],
|
|
45840
|
+
underlineBlue: ["58;5;4", 59],
|
|
45841
|
+
underlineMagenta: ["58;5;5", 59],
|
|
45842
|
+
underlineCyan: ["58;5;6", 59],
|
|
45843
|
+
underlineWhite: ["58;5;7", 59],
|
|
45844
|
+
underlineBlackBright: ["58;5;8", 59],
|
|
45845
|
+
underlineGray: ["58;5;8", 59],
|
|
45846
|
+
underlineGrey: ["58;5;8", 59],
|
|
45847
|
+
underlineRedBright: ["58;5;9", 59],
|
|
45848
|
+
underlineGreenBright: ["58;5;10", 59],
|
|
45849
|
+
underlineYellowBright: ["58;5;11", 59],
|
|
45850
|
+
underlineBlueBright: ["58;5;12", 59],
|
|
45851
|
+
underlineMagentaBright: ["58;5;13", 59],
|
|
45852
|
+
underlineCyanBright: ["58;5;14", 59],
|
|
45853
|
+
underlineWhiteBright: ["58;5;15", 59]
|
|
45854
|
+
}
|
|
45855
|
+
};
|
|
45856
|
+
var modifierNames = Object.keys(styles.modifier);
|
|
45857
|
+
var foregroundColorNames = Object.keys(styles.color);
|
|
45858
|
+
var backgroundColorNames = Object.keys(styles.bgColor);
|
|
45859
|
+
var underlineColorNames = Object.keys(styles.underlineColor);
|
|
45860
|
+
var colorNames = [...foregroundColorNames, ...backgroundColorNames];
|
|
45861
|
+
function assembleStyles() {
|
|
45862
|
+
const codes = new Map;
|
|
45863
|
+
for (const [groupName, group] of Object.entries(styles)) {
|
|
45864
|
+
for (const [styleName, style] of Object.entries(group)) {
|
|
45865
|
+
styles[styleName] = {
|
|
45866
|
+
open: `\x1B[${style[0]}m`,
|
|
45867
|
+
close: `\x1B[${style[1]}m`
|
|
45868
|
+
};
|
|
45869
|
+
group[styleName] = styles[styleName];
|
|
45870
|
+
codes.set(Number.parseInt(style[0], 10), style[1]);
|
|
45871
|
+
}
|
|
45872
|
+
Object.defineProperty(styles, groupName, {
|
|
45873
|
+
value: group,
|
|
45874
|
+
enumerable: false
|
|
45875
|
+
});
|
|
45876
|
+
}
|
|
45877
|
+
Object.defineProperty(styles, "codes", {
|
|
45878
|
+
value: codes,
|
|
45879
|
+
enumerable: false
|
|
45880
|
+
});
|
|
45881
|
+
styles.color.close = "\x1B[39m";
|
|
45882
|
+
styles.bgColor.close = "\x1B[49m";
|
|
45883
|
+
styles.underlineColor.close = "\x1B[59m";
|
|
45884
|
+
styles.color.ansi = wrapAnsi16();
|
|
45885
|
+
styles.color.ansi256 = wrapAnsi256();
|
|
45886
|
+
styles.color.ansi16m = wrapAnsi16m();
|
|
45887
|
+
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
|
|
45888
|
+
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
|
|
45889
|
+
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
|
|
45890
|
+
styles.underlineColor.ansi = wrapUnderlineAnsi;
|
|
45891
|
+
styles.underlineColor.ansi256 = wrapAnsi256(ANSI_UNDERLINE_OFFSET);
|
|
45892
|
+
styles.underlineColor.ansi16m = wrapAnsi16m(ANSI_UNDERLINE_OFFSET);
|
|
45893
|
+
Object.defineProperties(styles, {
|
|
45894
|
+
rgbToAnsi256: {
|
|
45895
|
+
value(red, green, blue) {
|
|
45896
|
+
if (red === green && green === blue) {
|
|
45897
|
+
if (red < 8) {
|
|
45898
|
+
return 16;
|
|
45899
|
+
}
|
|
45900
|
+
if (red > 248) {
|
|
45901
|
+
return 231;
|
|
45902
|
+
}
|
|
45903
|
+
return Math.round((red - 8) / 247 * 24) + 232;
|
|
45904
|
+
}
|
|
45905
|
+
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
|
|
45906
|
+
},
|
|
45907
|
+
enumerable: false
|
|
45908
|
+
},
|
|
45909
|
+
hexToRgb: {
|
|
45910
|
+
value(hex) {
|
|
45911
|
+
const matches = /[\da-f]{6}|[\da-f]{3}/i.exec(hex.toString(16));
|
|
45912
|
+
if (!matches) {
|
|
45913
|
+
return [0, 0, 0];
|
|
45914
|
+
}
|
|
45915
|
+
let [colorString] = matches;
|
|
45916
|
+
if (colorString.length === 3) {
|
|
45917
|
+
colorString = [...colorString].map((character) => character + character).join("");
|
|
45918
|
+
}
|
|
45919
|
+
const integer = Number.parseInt(colorString, 16);
|
|
45920
|
+
return [
|
|
45921
|
+
integer >> 16 & 255,
|
|
45922
|
+
integer >> 8 & 255,
|
|
45923
|
+
integer & 255
|
|
45924
|
+
];
|
|
45925
|
+
},
|
|
45926
|
+
enumerable: false
|
|
45927
|
+
},
|
|
45928
|
+
hexToAnsi256: {
|
|
45929
|
+
value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
|
|
45930
|
+
enumerable: false
|
|
45931
|
+
},
|
|
45932
|
+
ansi256ToAnsi: {
|
|
45933
|
+
value(code) {
|
|
45934
|
+
if (code < 8) {
|
|
45935
|
+
return 30 + code;
|
|
45936
|
+
}
|
|
45937
|
+
if (code < 16) {
|
|
45938
|
+
return 90 + (code - 8);
|
|
45939
|
+
}
|
|
45940
|
+
let red;
|
|
45941
|
+
let green;
|
|
45942
|
+
let blue;
|
|
45943
|
+
if (code >= 232) {
|
|
45944
|
+
red = ((code - 232) * 10 + 8) / 255;
|
|
45945
|
+
green = red;
|
|
45946
|
+
blue = red;
|
|
45947
|
+
} else {
|
|
45948
|
+
code -= 16;
|
|
45949
|
+
const remainder = code % 36;
|
|
45950
|
+
red = Math.floor(code / 36) / 5;
|
|
45951
|
+
green = Math.floor(remainder / 6) / 5;
|
|
45952
|
+
blue = remainder % 6 / 5;
|
|
45953
|
+
}
|
|
45954
|
+
const value = Math.max(red, green, blue) * 2;
|
|
45955
|
+
if (value === 0) {
|
|
45956
|
+
return 30;
|
|
45957
|
+
}
|
|
45958
|
+
let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
|
|
45959
|
+
if (value === 2) {
|
|
45960
|
+
result += 60;
|
|
45961
|
+
}
|
|
45962
|
+
return result;
|
|
45963
|
+
},
|
|
45964
|
+
enumerable: false
|
|
45965
|
+
},
|
|
45966
|
+
rgbToAnsi: {
|
|
45967
|
+
value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
|
|
45968
|
+
enumerable: false
|
|
45969
|
+
},
|
|
45970
|
+
hexToAnsi: {
|
|
45971
|
+
value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
|
|
45972
|
+
enumerable: false
|
|
45973
|
+
}
|
|
45974
|
+
});
|
|
45975
|
+
return styles;
|
|
45976
|
+
}
|
|
45977
|
+
var ansiStyles = assembleStyles();
|
|
45978
|
+
var ansi_styles_default = ansiStyles;
|
|
45979
|
+
|
|
45980
|
+
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
45981
|
+
import process2 from "process";
|
|
45982
|
+
import os3 from "os";
|
|
45983
|
+
import tty from "tty";
|
|
45984
|
+
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
|
|
45985
|
+
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
45986
|
+
const position = argv.indexOf(prefix + flag);
|
|
45987
|
+
const terminatorPosition = argv.indexOf("--");
|
|
45988
|
+
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
45989
|
+
}
|
|
45990
|
+
var { env } = process2;
|
|
45991
|
+
var flagForceColor;
|
|
45992
|
+
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
45993
|
+
flagForceColor = 0;
|
|
45994
|
+
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
45995
|
+
flagForceColor = 1;
|
|
45996
|
+
}
|
|
45997
|
+
function hasNumericForceColor() {
|
|
45998
|
+
return /^\d+$/.test(env.FORCE_COLOR);
|
|
45999
|
+
}
|
|
46000
|
+
function envForceColor() {
|
|
46001
|
+
if (!("FORCE_COLOR" in env)) {
|
|
46002
|
+
return;
|
|
46003
|
+
}
|
|
46004
|
+
if (env.FORCE_COLOR === "false") {
|
|
46005
|
+
return 0;
|
|
46006
|
+
}
|
|
46007
|
+
if (env.FORCE_COLOR === "true" || env.FORCE_COLOR.length === 0) {
|
|
46008
|
+
return 1;
|
|
46009
|
+
}
|
|
46010
|
+
if (!hasNumericForceColor()) {
|
|
46011
|
+
return;
|
|
46012
|
+
}
|
|
46013
|
+
return Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
46014
|
+
}
|
|
46015
|
+
function translateLevel(level) {
|
|
46016
|
+
if (level === 0) {
|
|
46017
|
+
return false;
|
|
46018
|
+
}
|
|
46019
|
+
return {
|
|
46020
|
+
level,
|
|
46021
|
+
hasBasic: true,
|
|
46022
|
+
has256: level >= 2,
|
|
46023
|
+
has16m: level >= 3
|
|
46024
|
+
};
|
|
46025
|
+
}
|
|
46026
|
+
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
46027
|
+
const noFlagForceColor = envForceColor();
|
|
46028
|
+
if (noFlagForceColor !== undefined) {
|
|
46029
|
+
flagForceColor = noFlagForceColor;
|
|
46030
|
+
}
|
|
46031
|
+
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
46032
|
+
if (forceColor === 0) {
|
|
46033
|
+
return 0;
|
|
46034
|
+
}
|
|
46035
|
+
if (sniffFlags) {
|
|
46036
|
+
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
|
|
46037
|
+
return 3;
|
|
46038
|
+
}
|
|
46039
|
+
if (hasFlag("color=256")) {
|
|
46040
|
+
return 2;
|
|
46041
|
+
}
|
|
46042
|
+
}
|
|
46043
|
+
if (forceColor !== undefined && hasNumericForceColor()) {
|
|
46044
|
+
return forceColor;
|
|
46045
|
+
}
|
|
46046
|
+
if ("TF_BUILD" in env && "AGENT_NAME" in env) {
|
|
46047
|
+
return 1;
|
|
46048
|
+
}
|
|
46049
|
+
if (haveStream && !streamIsTTY && forceColor === undefined) {
|
|
46050
|
+
return 0;
|
|
46051
|
+
}
|
|
46052
|
+
const min = forceColor || 0;
|
|
46053
|
+
if (env.TERM === "dumb") {
|
|
46054
|
+
return min;
|
|
46055
|
+
}
|
|
46056
|
+
if (process2.platform === "win32") {
|
|
46057
|
+
const osRelease = os3.release().split(".");
|
|
46058
|
+
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
46059
|
+
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
46060
|
+
}
|
|
46061
|
+
return 1;
|
|
46062
|
+
}
|
|
46063
|
+
if ("CI" in env) {
|
|
46064
|
+
if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
|
|
46065
|
+
return 3;
|
|
46066
|
+
}
|
|
46067
|
+
if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
|
|
46068
|
+
return 1;
|
|
46069
|
+
}
|
|
46070
|
+
return min;
|
|
46071
|
+
}
|
|
46072
|
+
if ("TEAMCITY_VERSION" in env) {
|
|
46073
|
+
return /^(?:9\.0*[1-9]\d*\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
|
|
46074
|
+
}
|
|
46075
|
+
if (env.COLORTERM === "truecolor") {
|
|
46076
|
+
return 3;
|
|
46077
|
+
}
|
|
46078
|
+
if (env.TERM === "xterm-kitty") {
|
|
46079
|
+
return 3;
|
|
46080
|
+
}
|
|
46081
|
+
if (env.TERM === "xterm-ghostty") {
|
|
46082
|
+
return 3;
|
|
46083
|
+
}
|
|
46084
|
+
if (env.TERM === "wezterm") {
|
|
46085
|
+
return 3;
|
|
46086
|
+
}
|
|
46087
|
+
if ("TERM_PROGRAM" in env) {
|
|
46088
|
+
const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".", 1)[0], 10);
|
|
46089
|
+
switch (env.TERM_PROGRAM) {
|
|
46090
|
+
case "iTerm.app": {
|
|
46091
|
+
return version >= 3 ? 3 : 2;
|
|
46092
|
+
}
|
|
46093
|
+
case "Apple_Terminal": {
|
|
46094
|
+
return 2;
|
|
46095
|
+
}
|
|
46096
|
+
}
|
|
46097
|
+
}
|
|
46098
|
+
if (/-256(?:color)?$/i.test(env.TERM)) {
|
|
46099
|
+
return 2;
|
|
46100
|
+
}
|
|
46101
|
+
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
|
|
46102
|
+
return 1;
|
|
46103
|
+
}
|
|
46104
|
+
if ("COLORTERM" in env) {
|
|
46105
|
+
return 1;
|
|
46106
|
+
}
|
|
46107
|
+
return min;
|
|
46108
|
+
}
|
|
46109
|
+
function createSupportsColor(stream, options = {}) {
|
|
46110
|
+
const level = _supportsColor(stream, {
|
|
46111
|
+
streamIsTTY: stream && stream.isTTY,
|
|
46112
|
+
...options
|
|
46113
|
+
});
|
|
46114
|
+
return translateLevel(level);
|
|
46115
|
+
}
|
|
46116
|
+
var supportsColor = {
|
|
46117
|
+
stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
|
|
46118
|
+
stderr: createSupportsColor({ isTTY: tty.isatty(2) })
|
|
46119
|
+
};
|
|
46120
|
+
var supports_color_default = supportsColor;
|
|
46121
|
+
|
|
46122
|
+
// node_modules/chalk/source/index.js
|
|
46123
|
+
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
46124
|
+
var GENERATOR = Symbol("GENERATOR");
|
|
46125
|
+
var STYLER = Symbol("STYLER");
|
|
46126
|
+
var IS_EMPTY = Symbol("IS_EMPTY");
|
|
46127
|
+
var LEVEL = Symbol("LEVEL");
|
|
46128
|
+
var styles2 = Object.create(null);
|
|
46129
|
+
var assertValidLevel = (level) => {
|
|
46130
|
+
if (!Number.isSafeInteger(level) || level < 0 || level > 3) {
|
|
46131
|
+
throw new Error("The `level` should be an integer from 0 to 3");
|
|
46132
|
+
}
|
|
46133
|
+
};
|
|
46134
|
+
var levelDescriptor = {
|
|
46135
|
+
enumerable: true,
|
|
46136
|
+
get() {
|
|
46137
|
+
return this[LEVEL];
|
|
46138
|
+
},
|
|
46139
|
+
set(level) {
|
|
46140
|
+
assertValidLevel(level);
|
|
46141
|
+
this[LEVEL] = level;
|
|
46142
|
+
}
|
|
46143
|
+
};
|
|
46144
|
+
var applyOptions = (object, options = {}) => {
|
|
46145
|
+
if (options.level !== undefined) {
|
|
46146
|
+
assertValidLevel(options.level);
|
|
46147
|
+
}
|
|
46148
|
+
const colorLevel = stdoutColor ? stdoutColor.level : 0;
|
|
46149
|
+
object[LEVEL] = options.level === undefined ? colorLevel : options.level;
|
|
46150
|
+
};
|
|
46151
|
+
var chalkFactory = (options) => {
|
|
46152
|
+
const chalk = (...strings) => strings.join(" ");
|
|
46153
|
+
applyOptions(chalk, options);
|
|
46154
|
+
Object.setPrototypeOf(chalk, createChalk.prototype);
|
|
46155
|
+
return chalk;
|
|
46156
|
+
};
|
|
46157
|
+
function createChalk(options) {
|
|
46158
|
+
return chalkFactory(options);
|
|
46159
|
+
}
|
|
46160
|
+
Object.setPrototypeOf(createChalk.prototype, Function.prototype);
|
|
46161
|
+
for (const [styleName, style] of Object.entries(ansi_styles_default)) {
|
|
46162
|
+
styles2[styleName] = {
|
|
46163
|
+
get() {
|
|
46164
|
+
const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
|
|
46165
|
+
Object.defineProperty(this, styleName, { value: builder });
|
|
46166
|
+
return builder;
|
|
46167
|
+
}
|
|
46168
|
+
};
|
|
46169
|
+
}
|
|
46170
|
+
styles2.visible = {
|
|
46171
|
+
get() {
|
|
46172
|
+
const builder = createBuilder(this, this[STYLER], true);
|
|
46173
|
+
Object.defineProperty(this, "visible", { value: builder });
|
|
46174
|
+
return builder;
|
|
46175
|
+
}
|
|
46176
|
+
};
|
|
46177
|
+
var createModelConverters = (model, type) => {
|
|
46178
|
+
const style = ansi_styles_default[type];
|
|
46179
|
+
if (model === "rgb") {
|
|
46180
|
+
const ansi2 = (red, green, blue) => style.ansi(ansi_styles_default.rgbToAnsi(red, green, blue));
|
|
46181
|
+
const ansi256 = (red, green, blue) => style.ansi256(ansi_styles_default.rgbToAnsi256(red, green, blue));
|
|
46182
|
+
return [ansi2, ansi2, ansi256, style.ansi16m];
|
|
46183
|
+
}
|
|
46184
|
+
if (model === "hex") {
|
|
46185
|
+
const ansi2 = (hex) => style.ansi(ansi_styles_default.hexToAnsi(hex));
|
|
46186
|
+
const ansi256 = (hex) => style.ansi256(ansi_styles_default.hexToAnsi256(hex));
|
|
46187
|
+
return [ansi2, ansi2, ansi256, (hex) => style.ansi16m(...ansi_styles_default.hexToRgb(hex))];
|
|
46188
|
+
}
|
|
46189
|
+
const ansi = (code) => style.ansi(ansi_styles_default.ansi256ToAnsi(code));
|
|
46190
|
+
return [ansi, ansi, style.ansi256, style.ansi256];
|
|
46191
|
+
};
|
|
46192
|
+
var usedModels = ["rgb", "hex", "ansi256"];
|
|
46193
|
+
for (const model of usedModels) {
|
|
46194
|
+
const capitalizedModel = model[0].toUpperCase() + model.slice(1);
|
|
46195
|
+
for (const [styleName, type] of [
|
|
46196
|
+
[model, "color"],
|
|
46197
|
+
["bg" + capitalizedModel, "bgColor"],
|
|
46198
|
+
["underline" + capitalizedModel, "underlineColor"]
|
|
46199
|
+
]) {
|
|
46200
|
+
const { close } = ansi_styles_default[type];
|
|
46201
|
+
const converters = createModelConverters(model, type);
|
|
46202
|
+
styles2[styleName] = {
|
|
46203
|
+
get() {
|
|
46204
|
+
const styleFunction = function(first, second, third) {
|
|
46205
|
+
const open = converters[this.level](first, second, third);
|
|
46206
|
+
return createBuilder(this, createStyler(open, close, this[STYLER]), this[IS_EMPTY]);
|
|
46207
|
+
};
|
|
46208
|
+
Object.defineProperty(this, styleName, { value: styleFunction });
|
|
46209
|
+
return styleFunction;
|
|
46210
|
+
}
|
|
46211
|
+
};
|
|
46212
|
+
}
|
|
46213
|
+
}
|
|
46214
|
+
var proto = Object.defineProperties(() => {}, {
|
|
46215
|
+
...styles2,
|
|
46216
|
+
level: {
|
|
46217
|
+
enumerable: true,
|
|
46218
|
+
get() {
|
|
46219
|
+
return this[GENERATOR].level;
|
|
46220
|
+
},
|
|
46221
|
+
set(level) {
|
|
46222
|
+
this[GENERATOR].level = level;
|
|
46223
|
+
}
|
|
46224
|
+
}
|
|
46225
|
+
});
|
|
46226
|
+
var createStyler = (open, close, parent) => {
|
|
46227
|
+
let openAll;
|
|
46228
|
+
let closeAll;
|
|
46229
|
+
if (parent === undefined) {
|
|
46230
|
+
openAll = open;
|
|
46231
|
+
closeAll = close;
|
|
46232
|
+
} else {
|
|
46233
|
+
openAll = parent.openAll + open;
|
|
46234
|
+
closeAll = close + parent.closeAll;
|
|
46235
|
+
}
|
|
46236
|
+
return {
|
|
46237
|
+
open,
|
|
46238
|
+
close,
|
|
46239
|
+
openAll,
|
|
46240
|
+
closeAll,
|
|
46241
|
+
parent
|
|
46242
|
+
};
|
|
46243
|
+
};
|
|
46244
|
+
var createBuilder = (self, _styler, _isEmpty) => {
|
|
46245
|
+
const builder = (...arguments_) => {
|
|
46246
|
+
if (arguments_.length === 1) {
|
|
46247
|
+
return applyStyle(builder, "" + arguments_[0]);
|
|
46248
|
+
}
|
|
46249
|
+
if (arguments_.length === 2) {
|
|
46250
|
+
return applyStyle(builder, arguments_[0] + " " + arguments_[1]);
|
|
46251
|
+
}
|
|
46252
|
+
return applyStyle(builder, arguments_.join(" "));
|
|
46253
|
+
};
|
|
46254
|
+
Object.setPrototypeOf(builder, proto);
|
|
46255
|
+
builder[GENERATOR] = self[GENERATOR] ?? self;
|
|
46256
|
+
builder[STYLER] = _styler;
|
|
46257
|
+
builder[IS_EMPTY] = _isEmpty;
|
|
46258
|
+
return builder;
|
|
46259
|
+
};
|
|
46260
|
+
var applyStyle = (self, string) => {
|
|
46261
|
+
if (self[GENERATOR][LEVEL] <= 0 || !string) {
|
|
46262
|
+
return self[IS_EMPTY] ? "" : string;
|
|
46263
|
+
}
|
|
46264
|
+
let styler = self[STYLER];
|
|
46265
|
+
if (styler === undefined) {
|
|
46266
|
+
return string;
|
|
46267
|
+
}
|
|
46268
|
+
const { openAll, closeAll } = styler;
|
|
46269
|
+
if (string.includes("\x1B")) {
|
|
46270
|
+
while (styler !== undefined) {
|
|
46271
|
+
string = stringReplaceAll(string, styler.close, styler.open);
|
|
46272
|
+
styler = styler.parent;
|
|
46273
|
+
}
|
|
46274
|
+
}
|
|
46275
|
+
const lfIndex = string.indexOf(`
|
|
46276
|
+
`);
|
|
46277
|
+
if (lfIndex !== -1) {
|
|
46278
|
+
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
|
|
46279
|
+
}
|
|
46280
|
+
return openAll + string + closeAll;
|
|
46281
|
+
};
|
|
46282
|
+
Object.defineProperties(createChalk.prototype, { ...styles2, level: levelDescriptor });
|
|
46283
|
+
var chalk = createChalk();
|
|
46284
|
+
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
46285
|
+
var source_default = chalk;
|
|
46286
|
+
|
|
46287
|
+
// src/diff-view.ts
|
|
45681
46288
|
var CONTEXT_LINES = 3;
|
|
45682
46289
|
var MAX_OUTPUT_LINES = 80;
|
|
45683
46290
|
var SYNTAX_THEME = {
|
|
45684
|
-
default:
|
|
45685
|
-
keyword:
|
|
45686
|
-
built_in:
|
|
45687
|
-
type:
|
|
45688
|
-
literal:
|
|
45689
|
-
number:
|
|
45690
|
-
regexp:
|
|
45691
|
-
string:
|
|
45692
|
-
subst:
|
|
45693
|
-
symbol:
|
|
45694
|
-
class:
|
|
45695
|
-
function:
|
|
45696
|
-
title:
|
|
45697
|
-
params:
|
|
45698
|
-
comment:
|
|
45699
|
-
doctag:
|
|
45700
|
-
meta:
|
|
45701
|
-
"meta-keyword":
|
|
45702
|
-
"meta-string":
|
|
45703
|
-
section:
|
|
45704
|
-
tag:
|
|
45705
|
-
name:
|
|
45706
|
-
attr:
|
|
45707
|
-
attribute:
|
|
45708
|
-
variable:
|
|
45709
|
-
bullet:
|
|
45710
|
-
code:
|
|
45711
|
-
emphasis:
|
|
45712
|
-
strong:
|
|
45713
|
-
formula:
|
|
45714
|
-
link:
|
|
45715
|
-
quote:
|
|
45716
|
-
addition:
|
|
45717
|
-
deletion:
|
|
46291
|
+
default: source_default.rgb(248, 248, 242),
|
|
46292
|
+
keyword: source_default.rgb(249, 38, 114),
|
|
46293
|
+
built_in: source_default.rgb(102, 217, 239),
|
|
46294
|
+
type: source_default.rgb(102, 217, 239),
|
|
46295
|
+
literal: source_default.rgb(174, 129, 255),
|
|
46296
|
+
number: source_default.rgb(174, 129, 255),
|
|
46297
|
+
regexp: source_default.rgb(230, 219, 116),
|
|
46298
|
+
string: source_default.rgb(230, 219, 116),
|
|
46299
|
+
subst: source_default.rgb(248, 248, 242),
|
|
46300
|
+
symbol: source_default.rgb(174, 129, 255),
|
|
46301
|
+
class: source_default.rgb(166, 226, 46),
|
|
46302
|
+
function: source_default.rgb(166, 226, 46),
|
|
46303
|
+
title: source_default.rgb(166, 226, 46),
|
|
46304
|
+
params: source_default.rgb(253, 151, 31),
|
|
46305
|
+
comment: source_default.rgb(117, 113, 94),
|
|
46306
|
+
doctag: source_default.rgb(117, 113, 94),
|
|
46307
|
+
meta: source_default.rgb(249, 38, 114),
|
|
46308
|
+
"meta-keyword": source_default.rgb(249, 38, 114),
|
|
46309
|
+
"meta-string": source_default.rgb(230, 219, 116),
|
|
46310
|
+
section: source_default.rgb(166, 226, 46),
|
|
46311
|
+
tag: source_default.rgb(249, 38, 114),
|
|
46312
|
+
name: source_default.rgb(249, 38, 114),
|
|
46313
|
+
attr: source_default.rgb(166, 226, 46),
|
|
46314
|
+
attribute: source_default.rgb(166, 226, 46),
|
|
46315
|
+
variable: source_default.rgb(248, 248, 242),
|
|
46316
|
+
bullet: source_default.rgb(174, 129, 255),
|
|
46317
|
+
code: source_default.rgb(230, 219, 116),
|
|
46318
|
+
emphasis: source_default.italic,
|
|
46319
|
+
strong: source_default.bold,
|
|
46320
|
+
formula: source_default.rgb(248, 248, 242),
|
|
46321
|
+
link: source_default.rgb(102, 217, 239).underline,
|
|
46322
|
+
quote: source_default.rgb(117, 113, 94).italic,
|
|
46323
|
+
addition: source_default.rgb(166, 226, 46),
|
|
46324
|
+
deletion: source_default.rgb(249, 38, 114)
|
|
45718
46325
|
};
|
|
45719
46326
|
function patchResets(text, bg) {
|
|
45720
46327
|
return text.replace(/\x1b\[0m/g, `\x1B[0m${bg}`);
|
|
@@ -46268,13 +46875,13 @@ async function listAgentsCommand(api, opts) {
|
|
|
46268
46875
|
// src/agent-command.ts
|
|
46269
46876
|
function printAgentHelp() {
|
|
46270
46877
|
process.stderr.write(`
|
|
46271
|
-
|
|
46878
|
+
tfa-cli agent \u2014 inspect and update agent settings
|
|
46272
46879
|
|
|
46273
46880
|
Usage:
|
|
46274
|
-
|
|
46275
|
-
|
|
46276
|
-
|
|
46277
|
-
|
|
46881
|
+
tfa-cli agent list List agents (name, model, id, paths)
|
|
46882
|
+
tfa-cli agent get <agent> Show a single agent's settings
|
|
46883
|
+
tfa-cli agent update <agent> <field=value>\u2026 Update one or more settings
|
|
46884
|
+
tfa-cli agent delete <agent> Delete an agent configuration (asks to confirm)
|
|
46278
46885
|
|
|
46279
46886
|
<agent> is a name or id (unique partial name also works).
|
|
46280
46887
|
Fields map directly to agent settings; values are parsed as JSON when possible
|
|
@@ -46287,9 +46894,9 @@ Fields map directly to agent settings; values are parsed as JSON when possible
|
|
|
46287
46894
|
name agent display name
|
|
46288
46895
|
|
|
46289
46896
|
Examples:
|
|
46290
|
-
|
|
46291
|
-
|
|
46292
|
-
|
|
46897
|
+
tfa-cli agent update <agent> model=claude
|
|
46898
|
+
tfa-cli agent update <agent> model=anthropic:anthropic/claude-opus-5 temperature=0.5
|
|
46899
|
+
tfa-cli agent update <agent> sysmsg="You are a terse video editor."
|
|
46293
46900
|
`);
|
|
46294
46901
|
}
|
|
46295
46902
|
var FIELD_ALIASES = {
|
|
@@ -46357,7 +46964,7 @@ async function agentCommand(api, positionals, args, formatPath) {
|
|
|
46357
46964
|
const query = positionals[2];
|
|
46358
46965
|
const assignments = positionals.slice(3);
|
|
46359
46966
|
if (!query || !assignments.length) {
|
|
46360
|
-
process.stderr.write(`${RED}Usage:
|
|
46967
|
+
process.stderr.write(`${RED}Usage: tfa-cli agent update <name|id> <field=value>\u2026${RESET}
|
|
46361
46968
|
`);
|
|
46362
46969
|
process.exit(2);
|
|
46363
46970
|
}
|
|
@@ -46378,7 +46985,7 @@ async function agentCommand(api, positionals, args, formatPath) {
|
|
|
46378
46985
|
if (sub === "delete" || sub === "rm") {
|
|
46379
46986
|
const query = positionals[2];
|
|
46380
46987
|
if (!query) {
|
|
46381
|
-
process.stderr.write(`${RED}Usage:
|
|
46988
|
+
process.stderr.write(`${RED}Usage: tfa-cli agent delete <name|id>${RESET}
|
|
46382
46989
|
`);
|
|
46383
46990
|
process.exit(2);
|
|
46384
46991
|
}
|
|
@@ -46601,6 +47208,87 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
46601
47208
|
`);
|
|
46602
47209
|
}
|
|
46603
47210
|
|
|
47211
|
+
// src/isolated.ts
|
|
47212
|
+
import { spawn as spawn2 } from "child_process";
|
|
47213
|
+
async function mintMayflyToken(apiUrl, apiKey, todoId) {
|
|
47214
|
+
const res = await fetch(`${apiUrl}${restBasePath(apiKey)}/cli/mayfly/token`, {
|
|
47215
|
+
method: "POST",
|
|
47216
|
+
headers: { "content-type": "application/json", "x-api-key": apiKey },
|
|
47217
|
+
body: JSON.stringify({ todoId }),
|
|
47218
|
+
signal: AbortSignal.timeout(30000)
|
|
47219
|
+
});
|
|
47220
|
+
const text = await res.text();
|
|
47221
|
+
if (!res.ok)
|
|
47222
|
+
throw new Error(`--isolated: could not mint a session token (${res.status} ${text.slice(0, 200)}).`);
|
|
47223
|
+
const token = JSON.parse(text)?.token;
|
|
47224
|
+
if (typeof token !== "string" || !token)
|
|
47225
|
+
throw new Error("--isolated: token mint returned no token.");
|
|
47226
|
+
return token;
|
|
47227
|
+
}
|
|
47228
|
+
async function spawnMayflyBridge(apiUrl, todoId, workspace, opts = {}) {
|
|
47229
|
+
if (!hasBridge())
|
|
47230
|
+
throw new Error("--isolated requires `todoforai-bridge` on PATH.");
|
|
47231
|
+
const loginless = !!opts.apiKey && !opts.apiKey.startsWith("dst_");
|
|
47232
|
+
if (!loginless && !ensureBridgeCredentials(apiUrl))
|
|
47233
|
+
throw new Error("`todoforai-bridge login` did not complete.");
|
|
47234
|
+
const sessionToken = loginless ? await mintMayflyToken(apiUrl, opts.apiKey, todoId) : null;
|
|
47235
|
+
const args = [...bridgeRunArgs(apiUrl), "--mayfly", todoId, "--workspace", workspace];
|
|
47236
|
+
const env2 = { ...process.env };
|
|
47237
|
+
if (sessionToken) {
|
|
47238
|
+
for (const n of ["API_TOKEN", "MAYFLY_API_KEY"])
|
|
47239
|
+
for (const p of ["TODOFORAI_", "TODO4AI_"])
|
|
47240
|
+
delete env2[p + n];
|
|
47241
|
+
env2.TODOFORAI_MAYFLY_TOKEN = sessionToken;
|
|
47242
|
+
}
|
|
47243
|
+
const child = spawn2("todoforai-bridge", args, { stdio: ["ignore", "pipe", "pipe"], env: env2 });
|
|
47244
|
+
let stopped = false;
|
|
47245
|
+
const stop = () => {
|
|
47246
|
+
if (stopped)
|
|
47247
|
+
return;
|
|
47248
|
+
stopped = true;
|
|
47249
|
+
try {
|
|
47250
|
+
child.kill("SIGTERM");
|
|
47251
|
+
} catch {}
|
|
47252
|
+
};
|
|
47253
|
+
process.on("exit", stop);
|
|
47254
|
+
const readyRx = new RegExp(`^BRIDGE_READY id=mayfly-${todoId}\\b`, "m");
|
|
47255
|
+
await new Promise((resolvePromise, reject) => {
|
|
47256
|
+
const timeoutMs = opts.timeoutMs ?? 15000;
|
|
47257
|
+
let buf = "";
|
|
47258
|
+
let lastErr = "";
|
|
47259
|
+
const timer = setTimeout(() => {
|
|
47260
|
+
stop();
|
|
47261
|
+
reject(new Error(`Isolated bridge not ready after ${timeoutMs / 1000}s.${lastErr ? ` ${lastErr}` : ""}`));
|
|
47262
|
+
}, timeoutMs);
|
|
47263
|
+
child.stdout.on("data", (d) => {
|
|
47264
|
+
buf += d.toString();
|
|
47265
|
+
if (readyRx.test(buf)) {
|
|
47266
|
+
clearTimeout(timer);
|
|
47267
|
+
resolvePromise();
|
|
47268
|
+
}
|
|
47269
|
+
});
|
|
47270
|
+
child.stderr.on("data", (d) => {
|
|
47271
|
+
const s = d.toString();
|
|
47272
|
+
const err = s.match(/^error: .*$/m);
|
|
47273
|
+
if (err)
|
|
47274
|
+
lastErr = err[0];
|
|
47275
|
+
if (opts.debug)
|
|
47276
|
+
process.stderr.write(d);
|
|
47277
|
+
});
|
|
47278
|
+
child.on("error", (err) => {
|
|
47279
|
+
clearTimeout(timer);
|
|
47280
|
+
reject(err);
|
|
47281
|
+
});
|
|
47282
|
+
child.on("exit", (code) => {
|
|
47283
|
+
if (!stopped) {
|
|
47284
|
+
clearTimeout(timer);
|
|
47285
|
+
reject(new Error(lastErr || `Isolated bridge exited early (code ${code}).`));
|
|
47286
|
+
}
|
|
47287
|
+
});
|
|
47288
|
+
});
|
|
47289
|
+
return { child, stop };
|
|
47290
|
+
}
|
|
47291
|
+
|
|
46604
47292
|
// src/index.ts
|
|
46605
47293
|
try {
|
|
46606
47294
|
const pkgPath = path3.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
@@ -46689,7 +47377,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46689
47377
|
const groupName = args["group-name"] === undefined ? undefined : String(args["group-name"]);
|
|
46690
47378
|
if (positionals[0] === "start") {
|
|
46691
47379
|
if (!positionals[1] && !args.template) {
|
|
46692
|
-
process.stderr.write(`${RED}Usage:
|
|
47380
|
+
process.stderr.write(`${RED}Usage: tfa-cli start <todo-id>${RESET}
|
|
46693
47381
|
`);
|
|
46694
47382
|
process.exit(2);
|
|
46695
47383
|
}
|
|
@@ -46716,6 +47404,23 @@ Cancelled by user (Ctrl+C)
|
|
|
46716
47404
|
printUsage();
|
|
46717
47405
|
process.exit(0);
|
|
46718
47406
|
}
|
|
47407
|
+
if (args.isolated) {
|
|
47408
|
+
if (args.resume || args.continue) {
|
|
47409
|
+
process.stderr.write(`Error: --isolated cannot be combined with --resume/--continue (isolation binds at create)
|
|
47410
|
+
`);
|
|
47411
|
+
process.exit(2);
|
|
47412
|
+
}
|
|
47413
|
+
if (args["no-watch"]) {
|
|
47414
|
+
process.stderr.write(`Error: --isolated requires watching the run (drop --no-watch)
|
|
47415
|
+
`);
|
|
47416
|
+
process.exit(2);
|
|
47417
|
+
}
|
|
47418
|
+
if (args.template) {
|
|
47419
|
+
process.stderr.write(`Error: --isolated does not support --template yet
|
|
47420
|
+
`);
|
|
47421
|
+
process.exit(2);
|
|
47422
|
+
}
|
|
47423
|
+
}
|
|
46719
47424
|
const cfg = new ConfigStore(args["config-path"]);
|
|
46720
47425
|
if (args["show-config"]) {
|
|
46721
47426
|
console.log(`Config file: ${formatPathWithTilde(cfg.path)}`);
|
|
@@ -46756,12 +47461,12 @@ Cancelled by user (Ctrl+C)
|
|
|
46756
47461
|
|
|
46757
47462
|
`);
|
|
46758
47463
|
try {
|
|
46759
|
-
const { spawn:
|
|
47464
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
46760
47465
|
if (process.platform === "win32") {
|
|
46761
|
-
|
|
47466
|
+
spawn3("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
|
|
46762
47467
|
} else {
|
|
46763
47468
|
const cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
46764
|
-
|
|
47469
|
+
spawn3(cmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
46765
47470
|
}
|
|
46766
47471
|
} catch {}
|
|
46767
47472
|
process.stderr.write(`Waiting for approval (expires in ${Math.round(expiresIn / 60)}min)...
|
|
@@ -46801,6 +47506,20 @@ Cancelled by user (Ctrl+C)
|
|
|
46801
47506
|
if (!apiKey) {
|
|
46802
47507
|
apiKey = await deviceLogin();
|
|
46803
47508
|
}
|
|
47509
|
+
if (args["debug-dump"]) {
|
|
47510
|
+
const debugSecret = getEnv("DEBUG_SECRET");
|
|
47511
|
+
if (!debugSecret) {
|
|
47512
|
+
process.stderr.write(`Error: --debug-dump requires TODOFORAI_DEBUG_SECRET
|
|
47513
|
+
`);
|
|
47514
|
+
process.exit(2);
|
|
47515
|
+
}
|
|
47516
|
+
let extraHeaders = {};
|
|
47517
|
+
try {
|
|
47518
|
+
extraHeaders = JSON.parse(process.env.TODOFORAI_EXTRA_HEADERS || "{}");
|
|
47519
|
+
} catch {}
|
|
47520
|
+
extraHeaders["x-tfa-debug"] = debugSecret;
|
|
47521
|
+
process.env.TODOFORAI_EXTRA_HEADERS = JSON.stringify(extraHeaders);
|
|
47522
|
+
}
|
|
46804
47523
|
const api = new ApiClient(apiUrl, apiKey, args["user-id"]);
|
|
46805
47524
|
if (args["user-id"] && args.safe) {
|
|
46806
47525
|
process.stderr.write(`Error: --safe is not supported with --user-id
|
|
@@ -46831,7 +47550,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46831
47550
|
if (positionals[0] === "delete") {
|
|
46832
47551
|
const todoId = positionals[1];
|
|
46833
47552
|
if (!todoId) {
|
|
46834
|
-
process.stderr.write(`${RED}Usage:
|
|
47553
|
+
process.stderr.write(`${RED}Usage: tfa-cli delete <todo-id>${RESET}
|
|
46835
47554
|
`);
|
|
46836
47555
|
process.exit(2);
|
|
46837
47556
|
}
|
|
@@ -46844,7 +47563,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46844
47563
|
const [, todoId, ...rest] = positionals;
|
|
46845
47564
|
const content2 = rest.join(" ") || await readStdin();
|
|
46846
47565
|
if (!todoId || !content2) {
|
|
46847
|
-
process.stderr.write(`${RED}Usage:
|
|
47566
|
+
process.stderr.write(`${RED}Usage: tfa-cli addmessage <todo-id> "content"${RESET}
|
|
46848
47567
|
`);
|
|
46849
47568
|
process.exit(2);
|
|
46850
47569
|
}
|
|
@@ -46863,7 +47582,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46863
47582
|
const todoId = positionals[2] || (!explicitProject ? getEnv("TODO_ID") || cfgScope.data.last_todo_id : undefined);
|
|
46864
47583
|
const projectId2 = todoId ? undefined : args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id;
|
|
46865
47584
|
if (!todoId && !projectId2) {
|
|
46866
|
-
process.stderr.write(`${RED}Usage:
|
|
47585
|
+
process.stderr.write(`${RED}Usage: tfa-cli show list [todo-id] [--project <id>] [--card <name>]${RESET}
|
|
46867
47586
|
`);
|
|
46868
47587
|
process.exit(2);
|
|
46869
47588
|
}
|
|
@@ -46888,7 +47607,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46888
47607
|
const [, filePath, todoArg] = positionals;
|
|
46889
47608
|
const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
|
|
46890
47609
|
if (!filePath || !todoId) {
|
|
46891
|
-
process.stderr.write(`${RED}Usage:
|
|
47610
|
+
process.stderr.write(`${RED}Usage: tfa-cli show <file|-> [todo-id]${RESET}
|
|
46892
47611
|
`);
|
|
46893
47612
|
process.exit(2);
|
|
46894
47613
|
}
|
|
@@ -46922,7 +47641,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46922
47641
|
const [, url, todoArg] = positionals;
|
|
46923
47642
|
const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
|
|
46924
47643
|
if (!url || !todoId) {
|
|
46925
|
-
process.stderr.write(`${RED}Usage:
|
|
47644
|
+
process.stderr.write(`${RED}Usage: tfa-cli open <url> [todo-id]${RESET}
|
|
46926
47645
|
`);
|
|
46927
47646
|
process.exit(2);
|
|
46928
47647
|
}
|
|
@@ -46936,7 +47655,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46936
47655
|
if (positionals[0] === "recommend") {
|
|
46937
47656
|
const templateId = args.template || positionals[1];
|
|
46938
47657
|
if (!templateId) {
|
|
46939
|
-
process.stderr.write(`${RED}Usage:
|
|
47658
|
+
process.stderr.write(`${RED}Usage: tfa-cli recommend --template <id> [--note "why"] [--priority high|medium|low] [--title "..."] [--group <slug> [--group-name "..."] [--group-description "..."]] [--project <id>]${RESET}
|
|
46940
47659
|
`);
|
|
46941
47660
|
process.stderr.write(`${DIM}Create a template first with: todoregistry-cli create --name ... --description ... --body @prompt.md${RESET}
|
|
46942
47661
|
`);
|
|
@@ -46983,7 +47702,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46983
47702
|
seedProjectId = projects2.find((p) => p.project?.isDefault)?.project?.id || projects2[0]?.project?.id;
|
|
46984
47703
|
}
|
|
46985
47704
|
if (!seedProjectId) {
|
|
46986
|
-
process.stderr.write(`${RED}Usage:
|
|
47705
|
+
process.stderr.write(`${RED}Usage: tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>]${RESET}
|
|
46987
47706
|
`);
|
|
46988
47707
|
process.exit(2);
|
|
46989
47708
|
}
|
|
@@ -47226,7 +47945,12 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
|
47226
47945
|
}
|
|
47227
47946
|
const ws = args["no-watch"] ? null : new FrontendWebSocket(apiUrl, apiKey);
|
|
47228
47947
|
const wsReady = ws ? ws.connect() : null;
|
|
47229
|
-
const bridgeReady = !args["no-bridge"] && !args["no-watch"] ? ensureBridgeRunning(apiUrl, apiKey) : null;
|
|
47948
|
+
const bridgeReady = !args["no-bridge"] && !args["no-watch"] && !args.isolated ? ensureBridgeRunning(apiUrl, apiKey) : null;
|
|
47949
|
+
const isolatedTodoId = args.isolated ? crypto.randomUUID() : undefined;
|
|
47950
|
+
const mayfly = isolatedTodoId ? await spawnMayflyBridge(apiUrl, isolatedTodoId, realpathSync(resolve3(args.path || ".")), { debug: !!args.debug, apiKey }) : null;
|
|
47951
|
+
if (mayfly)
|
|
47952
|
+
process.stderr.write(`${DIM}Isolated bridge:${RESET} ${CYAN}mayfly-${isolatedTodoId}${RESET}
|
|
47953
|
+
`);
|
|
47230
47954
|
let preMatchedAgent = null;
|
|
47231
47955
|
let agents = null;
|
|
47232
47956
|
if (args.agent) {
|
|
@@ -47337,7 +48061,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
|
47337
48061
|
cfg.addToHistory(content);
|
|
47338
48062
|
let todo;
|
|
47339
48063
|
try {
|
|
47340
|
-
todo = await api.addMessage(projectId, content, agent,
|
|
48064
|
+
todo = await api.addMessage(projectId, content, agent, isolatedTodoId, undefined, undefined, groupTag || undefined, groupName);
|
|
47341
48065
|
} catch (e) {
|
|
47342
48066
|
if (!args.project && !envProjectId && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
47343
48067
|
cfgScope.clearDefaultProject();
|
|
@@ -47373,9 +48097,18 @@ ${"\u2500".repeat(40)}
|
|
|
47373
48097
|
}
|
|
47374
48098
|
await ws.close();
|
|
47375
48099
|
}
|
|
48100
|
+
mayfly?.stop();
|
|
47376
48101
|
}
|
|
47377
|
-
|
|
48102
|
+
var finish = (code) => {
|
|
48103
|
+
process.exitCode = code;
|
|
48104
|
+
const poll = setInterval(() => {
|
|
48105
|
+
if (process.stdout.writableLength === 0)
|
|
48106
|
+
process.exit(code);
|
|
48107
|
+
}, 5);
|
|
48108
|
+
poll.unref?.();
|
|
48109
|
+
};
|
|
48110
|
+
main().then(() => finish(process.exitCode ?? 0)).catch((e) => {
|
|
47378
48111
|
process.stderr.write(`Error: ${e.message}
|
|
47379
48112
|
`);
|
|
47380
|
-
|
|
48113
|
+
finish(1);
|
|
47381
48114
|
});
|