UNPKG

task-master-ai

Version:

A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.

123 lines (103 loc) 94.9 kB
import"./ai-services-unified-DNZsHZ1J.js";import{An as e,B as t,Bt as n,E as r,Ht as i,Kt as a,Lt as o,Nt as s,Q as c,R as l,Rt as u,Tt as d,U as f,Ut as p,Vt as m,dt as h,f as g,hn as _,ln as ee,m as v,mn as te,p as y,t as b,ut as ne,yt as x}from"./config-manager-DsRE1v6M.js";import"./git-utils-DllbRE35.js";import"./sentry-Cd6-vFUd.js";import{$ as S,C,D as w,F as T,I as E,J as re,L as ie,M as ae,N as oe,O as se,P as ce,Q as le,S as ue,St as de,X as D,Y as fe,Z as pe,_ as me,_t as he,a as ge,at as O,b as _e,bt as ve,c as ye,ct as be,d as k,dt as xe,et as Se,f as Ce,g as we,h as Te,ht as Ee,i as De,it as Oe,j as ke,k as A,l as Ae,m as je,n as Me,nt as Ne,o as Pe,ot as Fe,p as j,r as Ie,rt as Le,s as Re,st as ze,t as Be,tt as Ve,u as He,ut as Ue,v as M,vt as We,w as Ge,wt as Ke,xt as qe,y as Je,yt as Ye}from"./dependency-manager-Cr2QBH2y.js";import{t as Xe}from"./response-language-DJHLmtoP.js";import{_ as Ze,a as Qe,c as $e,d as et,f as tt,g as nt,h as rt,i as it,l as N,m as at,n as ot,o as P,p as st,r as ct,s as F,t as I,u as L,v as R}from"./profiles-GRADHxlk.js";import z from"chalk";import B from"fs";import V from"path";import H from"boxen";import{Command as lt}from"commander";import U from"inquirer";const W={AUTHENTICATION:`authentication`,VALIDATION:`validation`,NETWORK:`network`,API:`api`,FILE_SYSTEM:`file_system`,TASK:`task`,PERMISSION:`permission`,TIMEOUT:`timeout`,GENERIC:`generic`},ut=[/\b[A-Za-z0-9_-]{20,}\b/g,/sk-[A-Za-z0-9]{32,}/g,/api[_-]?key[:\s=]+[^\s]+/gi,/bearer\s+[^\s]+/gi,/token[:\s=]+[^\s]+/gi,/\/Users\/[^/]+/g,/C:\\Users\\[^\\]+/g,/\/home\/[^/]+/g,/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,/https?:\/\/[^:]+:[^@]+@/g];function G(e){if(!e||typeof e!=`string`)return e;let t=e;for(let e of ut)t=t.replace(e,`***REDACTED***`);return t}function dt(e){if(!e)return W.GENERIC;let t=(e.message||``).toLowerCase(),n=(e.code||``).toLowerCase();return t.includes(`auth`)||t.includes(`unauthorized`)||t.includes(`forbidden`)||t.includes(`api key`)||t.includes(`token`)||n.includes(`auth`)?W.AUTHENTICATION:t.includes(`invalid`)||t.includes(`validation`)||t.includes(`required`)||t.includes(`must be`)||n.includes(`validation`)?W.VALIDATION:t.includes(`network`)||t.includes(`connection`)||t.includes(`econnrefused`)||t.includes(`enotfound`)||n.includes(`network`)||n.includes(`econnrefused`)||n.includes(`enotfound`)?W.NETWORK:t.includes(`timeout`)||t.includes(`timed out`)||n.includes(`timeout`)?W.TIMEOUT:t.includes(`api`)||t.includes(`rate limit`)||t.includes(`quota`)||n.includes(`api`)?W.API:t.includes(`enoent`)||t.includes(`eacces`)||t.includes(`file`)||t.includes(`directory`)||n.includes(`enoent`)||n.includes(`eacces`)?W.FILE_SYSTEM:t.includes(`permission`)||t.includes(`access denied`)||n.includes(`eperm`)?W.PERMISSION:t.includes(`task`)||t.includes(`subtask`)?W.TASK:W.GENERIC}function ft(e,t,n){let r=[],i=(t.message||``).toLowerCase();switch(e){case W.AUTHENTICATION:i.includes(`api key`)?(r.push(`Check that your API key is correctly set in the .env file`),r.push(`Verify the API key has not expired or been revoked`)):i.includes(`token`)?(r.push(`Your authentication token may have expired`),r.push(`Try running: tm auth refresh`)):(r.push(`Verify your credentials are correctly configured`),r.push(`Check the authentication status with: tm auth status`));break;case W.VALIDATION:i.includes(`brief id`)?(r.push(`Brief IDs are case-insensitive (e.g., "ham32" = "HAM-32")`),r.push(`Check the brief ID format: usually LETTERS-NUMBERS`)):i.includes(`task id`)||i.includes(`invalid id`)?(r.push(`Task IDs should be numbers (e.g., 1, 2, 3)`),r.push(`Subtask IDs use dot notation (e.g., 1.1, 2.3)`)):(r.push(`Check that all required parameters are provided`),r.push(`Verify parameter values match expected formats`));break;case W.NETWORK:i.includes(`econnrefused`)?(r.push(`Could not connect to the server`),r.push(`Check your internet connection`),r.push(`Verify the API endpoint URL is correct`)):i.includes(`enotfound`)?(r.push(`Could not resolve the server hostname`),r.push(`Check your internet connection`)):(r.push(`Check your network connection`),r.push(`Verify firewall settings are not blocking the request`));break;case W.TIMEOUT:r.push(`The operation took too long to complete`),r.push(`Try again with a simpler request`),r.push(`Check your network speed and stability`);break;case W.API:i.includes(`rate limit`)?(r.push(`You have exceeded the API rate limit`),r.push(`Wait a few minutes before trying again`)):i.includes(`quota`)?(r.push(`You have reached your API quota`),r.push(`Check your account usage and limits`)):(r.push(`The API returned an error`),r.push(`Try again in a few moments`));break;case W.FILE_SYSTEM:i.includes(`enoent`)?(r.push(`The specified file or directory does not exist`),r.push(`Check the file path and ensure it is correct`),n.includes(`tasks.json`)&&r.push(`Initialize the project with: tm init`)):i.includes(`eacces`)?(r.push(`Permission denied to access the file`),r.push(`Check file permissions or run with appropriate privileges`)):r.push(`Check that the file or directory exists and is accessible`);break;case W.PERMISSION:r.push(`You do not have permission to perform this operation`),r.push(`Check file/directory permissions`),r.push(`You may need elevated privileges (sudo)`);break;case W.TASK:i.includes(`not found`)?(r.push(`The specified task does not exist`),r.push(`Use: tm list to see all available tasks`)):i.includes(`dependency`)||i.includes(`circular`)?(r.push(`Task dependencies form a circular reference`),r.push(`Use: tm validate-dependencies to identify issues`)):(r.push(`Check that the task ID is correct`),r.push(`Use: tm show <id> to view task details`));break;default:r.push(`Check the error message for specific details`),n&&r.push(`Operation failed while: ${n}`)}return r.slice(0,2)}function pt(e,t={}){let{context:n=``,debug:r=!1,command:i=``}=t;typeof e==`string`&&(e=Error(e)),(!e||typeof e!=`object`)&&(e=Error(`An unknown error occurred`));let a=G(e.message||`Unknown error`),o=dt(e),s=ft(o,e,n);return{type:o,message:a,context:n||`Unknown operation`,hints:s,command:i||null,code:e.code||null,stack:r?G(e.stack):null}}function K(e,t={}){let n=pt(e,t),r=z.red.bold(`✗ Error `);r+=z.white(n.message)+` `,n.context&&n.context!==`Unknown operation`&&(r+=z.gray(`Context: `)+z.white(n.context)+` `),n.command&&(r+=z.gray(`Command: `)+z.cyan(n.command)+` `),n.hints&&n.hints.length>0&&(r+=z.yellow.bold(`Suggestions: `),n.hints.forEach((e,t)=>{r+=z.yellow(` ${t+1}. ${e}\n`)})),n.code&&(r+=` `+z.gray(`Error Code: ${n.code}`)),console.log(` `+H(r.trim(),{padding:{top:1,bottom:1,left:2,right:2},borderStyle:`round`,borderColor:`red`})+` `),t.debug&&n.stack&&(console.log(z.gray(`Stack Trace:`)),console.log(z.dim(n.stack)),console.log())}function mt(e,t=`Info`){let n=z.blue.bold(`ℹ ${t}\n\n`);n+=z.white(e),console.log(` `+H(n.trim(),{padding:{top:1,bottom:1,left:2,right:2},borderStyle:`round`,borderColor:`blue`})+` `)}var ht=class{#e;#t;constructor(e,t){this.#e=Object.freeze({...e}),this.#t=t}getProjectRoot(){return this.#e.projectRoot}getTaskMasterDir(){return this.#e.taskMasterDir}getTasksPath(){return this.#e.tasksPath}getPrdPath(){return this.#e.prdPath}getComplexityReportPath(){if(this.#e.complexityReportPath)return this.#e.complexityReportPath;let e=this.getCurrentTag()===`master`?s:s.replace(`.json`,`_${this.getCurrentTag()}.json`);return V.join(this.#e.projectRoot,e)}getConfigPath(){return this.#e.configPath}getStatePath(){return this.#e.statePath}getAllPaths(){return this.#e}getCurrentTag(){if(this.#t)return this.#t;try{if(B.existsSync(this.#e.statePath)){let e=B.readFileSync(this.#e.statePath,`utf8`),t=JSON.parse(e);if(t&&t.currentTag)return t.currentTag}}catch{}try{if(B.existsSync(this.#e.configPath)){let e=B.readFileSync(this.#e.configPath,`utf8`),t=JSON.parse(e);if(t&&t.global&&t.global.defaultTag)return t.global.defaultTag}}catch{}return`master`}};function q(e={}){let t=(e,t,n=[],r=null,i=!1)=>{if(typeof t==`string`){let n=V.isAbsolute(t)?t:V.resolve(r||process.cwd(),t);if(i){let t=V.dirname(n);if(!B.existsSync(t))try{B.mkdirSync(t,{recursive:!0})}catch(n){throw Error(`Could not create directory for ${e}: ${t}. Error: ${n.message}`)}}else if(!B.existsSync(n))throw Error(`${e} override path does not exist: ${n}`);return n}if(t===!0){for(let e of n){let t=V.isAbsolute(e)?e:V.join(r||process.cwd(),e);if(B.existsSync(t))return t}throw Error(`Required ${e} not found. Searched: ${n.join(`, `)}`)}for(let e of n){let t=V.isAbsolute(e)?e:V.join(r||process.cwd(),e);if(B.existsSync(t))return t}return null},r={};if(e.projectRoot){let t=V.resolve(e.projectRoot);if(!B.existsSync(t))throw Error(`Project root override path does not exist: ${t}`);let n=B.existsSync(V.join(t,m)),i=B.existsSync(V.join(t,o));if(!n&&!i)throw Error(`Project root override is not a valid taskmaster project: ${t}`);r.projectRoot=t}else r.projectRoot=c();return`taskMasterDir`in e?r.taskMasterDir=t(`taskmaster directory`,e.taskMasterDir,[m],r.projectRoot):r.taskMasterDir=t(`taskmaster directory`,!1,[m],r.projectRoot),r.configPath=V.join(r.projectRoot,n),r.statePath=V.join(r.taskMasterDir||V.join(r.projectRoot,m),`state.json`),r.tasksPath=V.join(r.projectRoot,a),`configPath`in e&&(r.configPath=t(`config file`,e.configPath,[n,o],r.projectRoot)),`statePath`in e&&(r.statePath=t(`state file`,e.statePath,[`state.json`],r.taskMasterDir)),`tasksPath`in e&&(r.tasksPath=t(`tasks file`,e.tasksPath,[a,u],r.projectRoot)),`prdPath`in e&&(r.prdPath=t(`PRD file`,e.prdPath,[V.join(i,`PRD.md`),V.join(i,`prd.md`),V.join(i,`PRD.txt`),V.join(i,`prd.txt`),V.join(`scripts`,`PRD.md`),V.join(`scripts`,`prd.md`),V.join(`scripts`,`PRD.txt`),V.join(`scripts`,`prd.txt`),`PRD.md`,`prd.md`,`PRD.txt`,`prd.txt`],r.projectRoot)),`complexityReportPath`in e&&(r.complexityReportPath=t(`complexity report`,e.complexityReportPath,[V.join(p,`task-complexity-report.json`),V.join(p,`complexity-report.json`),V.join(`scripts`,`task-complexity-report.json`),V.join(`scripts`,`complexity-report.json`),`task-complexity-report.json`,`complexity-report.json`],r.projectRoot,!0)),new ht(r,e.tag)}async function gt(e){let t=e.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`, `);console.log(H(z.yellow(`WARNING: This will selectively remove Task Master components for: ${t}. What will be removed: • Task Master specific rule files (e.g., cursor_rules.mdc, taskmaster.mdc, etc.) • Task Master MCP server configuration (if no other MCP servers exist) What will be preserved: • Your existing custom rule files • Other MCP server configurations • The profile directory itself (unless completely empty after removal) The .[profile] directory will only be removed if ALL of the following are true: • All rules in the directory were Task Master rules (no custom rules) • No other files or folders exist in the profile directory • The MCP configuration was completely removed (no other servers) Are you sure you want to proceed?`),{padding:1,borderColor:`yellow`,borderStyle:`round`}));let{confirm:n}=await(await import(`inquirer`)).default.prompt([{type:`confirm`,name:`confirm`,message:`Type y to confirm selective removal, or n to abort:`,default:!1}]);return n}async function _t(e,t){let n=e.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`, `);console.log(H(z.red.bold(`⚠️ CRITICAL WARNING: REMOVING ALL TASK MASTER RULE PROFILES ⚠️\n\nYou are about to remove Task Master components for: ${n}\nThis will leave your project with NO Task Master rule profiles remaining!\n\nWhat will be removed:\n• All Task Master specific rule files\n• Task Master MCP server configurations\n• Profile directories (only if completely empty after removal)\n\nWhat will be preserved:\n• Your existing custom rule files\n• Other MCP server configurations\n• Profile directories with custom content\n\nThis could impact Task Master functionality but will preserve your custom configurations.\n\nAre you absolutely sure you want to proceed?`),{padding:1,borderColor:`red`,borderStyle:`double`,title:`🚨 CRITICAL OPERATION`,titleAlignment:`center`}));let{confirm:r}=await(await import(`inquirer`)).default.prompt([{type:`confirm`,name:`confirm`,message:`Type y to confirm removing ALL Task Master rule profiles, or n to abort:`,default:!1}]);return r}function J(){try{let e=_.getInstance().getContext();if(e&&e.briefId)return!0;f(!0);try{if(g(null,!1,{storageType:`api`})?.storage?.type===`api`)return!0}catch{}finally{f(!1)}return!1}catch{return!1}}async function vt(){if(!process.stdin.isTTY)return`local`;console.log(` `+z.bold.white(`Your tasks are only as good as the context behind them.`)+` `+z.dim(`Parse locally and tasks will be stored in a JSON file. Bring it to Hamster and your brief becomes part of a living system connected to your team, your codebase and your agents. Now your entire team can go as fast as you can with Taskmaster.`)+` `);let{choice:e}=await U.prompt([{type:`list`,name:`choice`,message:z.cyan(`How would you like to parse your PRD? `),choices:[{name:[z.bold(`Parse locally`),``,z.white(` • Your PRD becomes a task list in a local JSON file`),z.white(` • Great for quick prototyping and for vibing on your own`),z.white(` • You can always export to Hamster later`),``].join(` `),value:`local`,short:`Parse locally`},{name:[z.bold(`Bring it to Hamster`),``,z.white(` • Your PRD will become a living brief you can refine with your team`),z.white(` • Hamster will generate tasks automatically, ready to execute in Taskmaster`),z.white(` • Hamster will automatically analyze complexity and expand tasks as needed`),z.white(` • Invite your teammates to collaborate on a single source of truth`),z.white(` • AI inference handled by Hamster, no API keys needed - just a Hamster account!`),``].join(` `),value:`hamster`,short:`Bring it to Hamster`}],default:`local`,pageSize:20}]);return e}async function yt(e){let t=(await import(`ora`)).default,n=(await import(`open`)).default,r,i;try{let a=new te;if(!await a.hasValidSession()){console.log(``),console.log(z.yellow(`🔒 Authentication Required`)),console.log(``);let{shouldLogin:e}=await U.prompt([{type:`confirm`,name:`shouldLogin`,message:`You're not logged in. Log in to create a brief on Hamster?`,default:!0}]);if(!e){console.log(z.gray(` Cancelled. `));return}let r=600*1e3,o=null,s=e=>{let n=Date.now()+e;i=t({text:`Waiting for authentication... ${z.cyan(`10:00`)} remaining`,spinner:`dots`}).start(),o=setInterval(()=>{let e=Math.max(0,n-Date.now()),t=`${Math.floor(e/6e4)}:${Math.floor(e%6e4/1e3).toString().padStart(2,`0`)}`;i&&(i.text=`Waiting for authentication... ${z.cyan(t)} remaining`),e<=0&&o&&clearInterval(o)},1e3)},c=e=>{o&&=(clearInterval(o),null),i&&=(e?i.succeed(`Authentication successful!`):i.fail(`Authentication failed`),null)};try{await a.authenticateWithOAuth({openBrowser:async e=>{await n(e)},timeout:r,onAuthUrl:e=>{console.log(z.blue.bold(` [auth] Browser Authentication `)),console.log(z.white(` Opening your browser to authenticate...`)),console.log(z.gray(` If the browser doesn't open, visit:`)),console.log(z.cyan.underline(` ${e}\n`))},onWaitingForAuth:()=>{console.log(z.dim(` If you signed up, check your email to confirm your account.`)),console.log(z.dim(` The CLI will automatically detect when you log in. `)),s(r)},onSuccess:()=>{c(!0)},onError:()=>{c(!1)}})}catch(e){c(!1),console.error(z.red(`\n Authentication failed: ${e.message||`Unknown error`}\n`));return}}let o=_.getInstance(),s=await de(o,{promptMessage:`Select an organization to create the brief in:`,forceSelection:!0});if(!s.success){console.error(z.red(`\n ${s.message||`Organization selection cancelled.`}\n`));return}let c=B.readFileSync(e,`utf-8`);if(!c.trim()){console.error(z.red(` PRD file is empty. `));return}let l=await ee({projectPath:ne()||process.cwd()}),u=[],{wantsToInvite:d}=await U.prompt([{type:`confirm`,name:`wantsToInvite`,message:`Want to invite teammates to collaborate on this brief?`,default:!1}]);if(d){let{emails:e}=await U.prompt([{type:`input`,name:`emails`,message:`Enter email addresses to invite (comma-separated, max 10):`,validate:e=>e.trim()&&e.split(`,`).map(e=>e.trim()).filter(Boolean).length>10?`Maximum 10 email addresses allowed`:!0}]);u=e.split(`,`).map(e=>e.trim()).filter(Boolean).slice(0,10)}r=t(`Creating brief from your PRD...`).start();let f=await l.integration.generateBriefFromPrd({prdContent:c,options:{generateTitle:!0,generateDescription:!0}});if(!f.success||!f.brief){r.fail(`Failed to create brief`);let e=f.error?.message||`Unknown error occurred`;console.error(z.red(`\n ${e}\n`));return}r.succeed(`Brief created!`),console.log(``),console.log(z.green(` ✓ `)+z.white.bold(f.brief.title||`New Brief`)),console.log(``);let p=f.brief.url,m=`\x1b]8;;${p}\x07${z.cyan.underline(p)}\x1b]8;;\x07`;console.log(` ${m}`),console.log(``);let h=f.brief.url.match(/^(https?:\/\/[^/]+)\/home\/([^/]+)\/briefs\//),g=h?h[2]:null;if(u.length>0&&g){let e=t(`Sending invitations...`).start();try{let t=await l.integration.sendTeamInvitations(g,u,`member`);if(t.success&&t.invitations){e.succeed(`Invitations sent!`),console.log(``),console.log(z.cyan(` Team Invitations:`));for(let e of t.invitations)e.status===`sent`?console.log(z.green(` ${e.email}: Invitation sent`)):e.status===`already_member`?console.log(z.gray(` ${e.email}: Already a team member`)):e.status===`failed`?console.log(z.red(` ${e.email}: Failed to send`)):e.status===`already_invited`&&console.log(z.gray(` ${e.email}: Already invited`));console.log(``)}else{e.fail(`Failed to send invitations`);let n=t.error?.message||`Unknown error occurred`;console.error(z.red(` ${n}`)),console.log(``)}}catch(t){e.fail(`Failed to send invitations`),console.error(z.red(` ${t.message}`)),console.log(``)}}r=t(`Generating tasks from your PRD...`).start();let v=f.brief.id,y=Date.now(),b=0,S=f.brief.status,C=e=>{if(!e)return 0;let t=e.phase||e.currentPhase||``,n=e.parentTasksGenerated||e.progress?.parentTasksGenerated||0,r=e.parentTasksProcessed||e.progress?.parentTasksProcessed||0,i=e.totalParentTasks||e.progress?.totalParentTasks||0;return t===`queued`?0:t===`analyzing`?5:t===`generating_tasks`&&i>0?10+Math.floor(n/i*40):t===`processing_tasks`&&i>0?50+Math.floor(r/i*40):t===`generating_subtasks`?90:t===`complete`?100:0},w=(e,t=30)=>{let n=Math.floor(e/100*t),r=t-n;return z.cyan(`█`.repeat(n))+z.gray(`░`.repeat(r))},T=e=>e===`generating`||e===`pending`||e===`pending_plan`;for(;T(S)&&Date.now()-y<18e4;){await new Promise(e=>setTimeout(e,3e3));try{let e=await l.integration.getBriefStatus(v);if(e.success&&e.status){let t=e.status;if(S=t.status,t.progress){let e=t.progress,n=e.parentTasksGenerated||e.progress?.parentTasksGenerated||0,i=e.parentTasksProcessed||e.progress?.parentTasksProcessed||0,a=e.totalParentTasks||e.progress?.totalParentTasks||0,o=e.subtasksGenerated||e.progress?.subtasksGenerated||0;b=n+o;let s=C(e),c=w(s),l=e.phase||e.currentPhase||`generating`,u=`${c} ${s}%`;l===`generating_tasks`&&a>0?u+=` • Generating tasks (${n}/${a})`:l===`processing_tasks`&&a>0?(u+=` • Processing (${i}/${a})`,o>0&&(u+=` • ${o} subtasks`)):l===`generating_subtasks`?u+=` • ${o} subtasks generated`:e.message&&(u+=` • ${e.message}`),r.text=u}if(t.status===`ready`||t.status===`completed`)break;if(t.status===`failed`){r.fail(`Task generation failed`);let e=t.error||`Task generation failed on Hamster.`;console.error(z.red(`\n ${e}\n`));return}}}catch{}}if(T(S)?(r.warn(`Task generation is still in progress`),console.log(``),console.log(z.yellow(` Tasks are still being generated in the background.`)),console.log(z.white(` Check the brief URL above for progress.`))):r.succeed(b>0?`Done! ${b} tasks generated`:`Task generation complete`),console.log(``),g){let e=f.brief.url.match(/^(https?:\/\/[^/]+)/),t=`${e?e[1]:``}/home/${g}/members`,n=`\x1b]8;;${t}\x07${z.cyan.underline(t)}\x1b]8;;\x07`;console.log(z.gray(` Invite more teammates: `)+n),console.log(``)}try{let e=await l.tasks.resolveBrief(f.brief.url),t=e.document?.title||`Brief ${e.id.slice(0,8)}`,n;try{n=(await o.getOrganization(e.accountId))?.name}catch{}await o.updateContext({orgId:e.accountId,orgName:n,orgSlug:g,briefId:e.id,briefName:t,briefStatus:e.status,briefUpdatedAt:e.updatedAt}),console.log(z.green(` ✓ `)+z.white(`Context set! Run `)+z.cyan(`tm list`)+z.white(` to see your tasks.`))}catch(e){x(`debug`,`Context auto-set failed: ${e.message}`),console.log(z.yellow(` Could not auto-set context. Run `)+z.cyan(`tm context ${f.brief.url}`)+z.yellow(` to connect.`))}console.log(``)}catch(e){r?.isSpinning&&r.fail(`Failed`),console.error(z.red(`\n Error: ${e.message}\n`))}}function Y(e,t,n=` `){let r=Math.max(1,47-n.length-e.length);return z.cyan(n+e)+` `.repeat(r)+z.gray(t)}function X(){let e=process.stdout.columns||80,t=Math.min(120,Math.max(80,Math.floor(e*.9)));console.log(H(z.cyan.bold(`Taskmaster CLI - Connected to Hamster `)+z.white(`Taskmaster syncs tasks from your Hamster brief and provides a CLI `)+z.white(`interface to execute the plan. Commands can be used by humans or AI agents. `)+z.dim(`Tasks are managed in Hamster Studio. Changes sync automatically. `)+z.dim(`Use these commands to view tasks and update their status: `)+H(` Task Management `,{padding:0,borderStyle:`round`,borderColor:`yellow`})+` `+Y(`list`,`View all tasks from the brief `)+Y(`list <status>`,`Filter by status (e.g., pending, done, in-progress) `)+Y(`list all`,`View all tasks with subtasks expanded `)+Y(`show <id>`,`Show detailed task/subtask info `)+Y(`next`,`See the next task to work on `)+Y(`set-status|status <id> <status>`,`Update task status (pending, in-progress, done) `)+Y(`update-task <id> <prompt>`,`Add information to a task `)+` `+H(` Authentication & Context `,{padding:0,borderStyle:`round`,borderColor:`yellow`})+` `+Y(`auth login`,`Log in to Hamster `)+Y(`auth logout`,`Log out from Hamster `)+Y(`auth refresh`,`Refresh authentication token `)+Y(`auth status`,`Check authentication status `)+Y(`briefs`,`View and select from your briefs `)+Y(`context`,`Show current brief context `)+Y(`context org`,`Switch organization `)+Y(`context brief <url>`,`Switch to a different brief `)+` `+H(` Configuration `,{padding:0,borderStyle:`round`,borderColor:`yellow`})+` `+Y(`rules --setup`,`Configure AI IDE rules for better integration `)+H(` Examples `,{padding:0,borderStyle:`round`,borderColor:`yellow`})+` `+Y(`tm list`,`See all tasks `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm list done`,`See completed tasks `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm list in-progress`,`See tasks in progress `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm list all`,`View with all subtasks `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm show HAM-1,HAM-2`,`View multiple tasks `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm status HAM-1,HAM-2 in-progress`,`Start tasks `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm status HAM-1 done`,`Mark task complete `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm update-task HAM-1 <content>`,`Add info/context/breadcrumbs to task `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+Y(`tm briefs`,`View briefs and select one `,` `).replace(z.cyan(` tm`),z.dim(` tm`))+z.white.bold(`» Need more commands? `)+z.gray(`Advanced features (models, tags, PRD parsing) are managed in Hamster Studio.`),{padding:1,margin:{top:1},borderStyle:`round`,borderColor:`cyan`,width:t}))}function Z(n){n.on(`option:unknown`,function(e){let t=this._name||`unknown`;K(Error(`Unknown option '${e}'`),{context:`Running command: ${t}`,command:`task-master ${t}`,debug:y()}),process.exit(1)}),n.command(`help`).description(`Show help information (Hamster-aware)`).action(()=>{J()?X():n.help()}),n.configureHelp({helpWidth:120,sortSubcommands:!1});let i=n.help.bind(n);n.help=function(){J()?X():i()},n.hook(`preAction`,async(t,n)=>{let r=n.name();e.includes(r)&&await qe(r,q(n.opts()).getProjectRoot())&&process.exit(1)}),n.command(`parse-prd`).description(`Parse a PRD file and generate tasks`).argument(`[file]`,`Path to the PRD file`).option(`-i, --input <file>`,`Path to the PRD file (alternative to positional argument)`).option(`-o, --output <file>`,`Output file path`).option(`-n, --num-tasks <number>`,`Number of tasks to generate`,v()).option(`-f, --force`,`Skip confirmation when overwriting existing tasks`).option(`--append`,`Append new tasks to existing tasks.json instead of overwriting`).option(`-r, --research`,`Use Perplexity AI for research-backed task generation, providing more comprehensive and accurate task breakdown`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async(e,t)=>{let n=t.input||e,r;try{let e={prdPath:n||!0,tag:t.tag};t.output&&(e.tasksPath=t.output),r=q(e)}catch(e){K(e,{context:`Initializing Task Master for PRD parsing`,command:`task-master parse-prd`,debug:y()}),mt(`${z.cyan(`Usage:`)}\n task-master parse-prd <prd-file.txt> [options]\n\n${z.cyan(`Options:`)}\n -i, --input <file> Path to the PRD file\n -o, --output <file> Output file path\n -n, --num-tasks <number> Number of tasks to generate\n -f, --force Skip confirmation\n --append Append to existing tasks\n -r, --research Use Perplexity AI\n\n${z.cyan(`Examples:`)}\n task-master parse-prd requirements.txt --num-tasks 15\n task-master parse-prd --input=requirements.txt\n task-master parse-prd requirements.txt --research`,`Parse PRD Help`),process.exit(1)}let i=parseInt(t.numTasks,10),o=t.force||!1,s=t.append||!1,c=t.research||!1,l=o,u=s,d=r.getCurrentTag();if(await k(d),await vt()===`hamster`){await yt(r.getPrdPath());return}async function f(){let e=!1,t=r.getTasksPath();if(B.existsSync(t))try{let n=B.readFileSync(t,`utf8`),r=JSON.parse(n);r[d]&&Array.isArray(r[d].tasks)&&r[d].tasks.length>0&&(e=!0)}catch{e=!1}if(e&&!l&&!u){if(!await ge(t))return x(`info`,`Operation cancelled.`),!1;l=!0}return!0}try{if(!await f())return;console.log(z.blue(`Parsing PRD file: ${r.getPrdPath()}`)),console.log(z.blue(`Generating ${i} tasks...`)),s&&console.log(z.blue(`Appending to existing tasks...`)),c&&console.log(z.blue(`Using Perplexity AI for research-backed task generation`));let e=r.getTasksPath()||V.join(r.getProjectRoot(),a);await ce(r.getPrdPath(),e,i,{append:u,force:l,research:c,projectRoot:r.getProjectRoot(),tag:d})}catch(e){console.error(z.red(`Error parsing PRD: ${e.message}`)),process.exit(1)}}),n.command(`update`).description(`Update multiple tasks with ID >= "from" based on new information or implementation changes`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`--from <id>`,`Task ID to start updating from (tasks with ID >= this value will be updated)`,`1`).option(`-p, --prompt <text>`,`Prompt explaining the changes or new context (required)`).option(`-r, --research`,`Use Perplexity AI for research-backed task updates`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=q({tasksPath:e.file||!0,tag:e.tag}),n=parseInt(e.from,10),r=e.prompt,i=e.research||!1,a=t.getTasksPath(),o=t.getCurrentTag();await k(o),(process.argv.includes(`--id`)||process.argv.some(e=>e.startsWith(`--id=`)))&&(console.error(z.red(`Error: The update command uses --from=<id>, not --id=<id>`)),console.log(z.yellow(` To update multiple tasks:`)),console.log(` task-master update --from=${n} --prompt="Your prompt here"`),console.log(z.yellow(` To update a single specific task, use the update-task command instead:`)),console.log(` task-master update-task --id=<id> --prompt="Your prompt here"`),process.exit(1)),r||(console.error(z.red(`Error: --prompt parameter is required. Please provide information about the changes.`)),process.exit(1)),console.log(z.blue(`Updating tasks from ID >= ${n} with prompt: "${r}"`)),J()||console.log(z.blue(`Tasks file: ${a}`)),i&&console.log(z.blue(`Using Perplexity AI for research-backed task updates`)),await ue(t.getTasksPath(),n,r,i,{projectRoot:t.getProjectRoot(),tag:o})}),n.command(`update-task`).description(`Update a single specific task by ID with new information`).argument(`[id]`,`Task ID to update (e.g., 1, 1.1, TAS-123)`).argument(`[prompt...]`,`Update prompt - multiple words, no quotes needed`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-i, --id <id>`,`Task ID to update (fallback if not using positional)`).option(`-p, --prompt <text>`,`Prompt (fallback if not using positional)`).option(`-r, --research`,`Use Perplexity AI for research-backed task updates`).option(`--append`,`Append timestamped information to task details instead of full update`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async(e,t,n)=>{try{let r=q({tasksPath:n.file||!0,tag:n.tag}),i=r.getTasksPath(),o=r.getCurrentTag();await k(o);let s=e||n.id,c=t.length>0?t.join(` `):n.prompt;s||(console.error(z.red(`Error: Task ID is required`)),console.log(z.yellow(`Usage examples: tm update-task 1 Added implementation details tm update-task TAS-123 Fixed the auth bug tm update-task --id=23 --prompt="Update with new information"`)),process.exit(1)),/^\d+$/.test(s)||/^[a-z]+-\d+$/i.test(s)||(console.error(z.red(`Error: Invalid task ID: ${s}. Task ID must be a positive integer or in the form "ham-123".`)),console.log(z.yellow(`Usage examples: tm update-task 1 Added implementation details tm update-task TAS-123 Fixed the auth bug`)),process.exit(1)),c||(console.error(z.red(`Error: Prompt is required. Please provide information about the changes.`)),console.log(z.yellow(`Usage examples: tm update-task 1 Added implementation details tm update-task 23 "Update with new information"`)),process.exit(1));let u=n.research||!1;B.existsSync(i)||(console.error(z.red(`Error: Tasks file not found at path: ${i}`)),i===a?console.log(z.yellow(`Hint: Run task-master init or task-master parse-prd to create tasks.json first`)):console.log(z.yellow(`Hint: Check if the file path is correct: ${i}`)),process.exit(1)),console.log(z.blue(`Updating task ${s} with prompt: "${c}"`)),J()||console.log(z.blue(`Tasks file: ${i}`)),u&&(l(`perplexity`)?console.log(z.blue(`Using Perplexity AI for research-backed task update`)):(console.log(z.yellow(`Warning: PERPLEXITY_API_KEY environment variable is missing. Research-backed updates will not be available.`)),console.log(z.yellow(`Falling back to Claude AI for task update.`))));let d=J()?!0:n.append||!1;await C(r.getTasksPath(),s,c,u,{projectRoot:r.getProjectRoot(),tag:o},`text`,d)||console.log(z.yellow(` Task update was not completed. Review the messages above for details.`))}catch(e){console.error(z.red(`Error: ${e.message}`)),e.message.includes(`task`)&&e.message.includes(`not found`)?(console.log(z.yellow(` To fix this issue:`)),console.log(` 1. Run task-master list to see all available task IDs`),console.log(` 2. Use a valid task ID with the --id parameter`)):e.message.includes(`API key`)&&console.log(z.yellow(` This error is related to API keys. Check your environment variables.`)),y()&&console.error(e),process.exit(1)}}),n.command(`update-subtask`).description(`Update a subtask by appending additional timestamped information`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-i, --id <id>`,`Subtask ID to update (required)`).option(`-p, --prompt <text>`,`Prompt explaining what information to add (required)`).option(`-r, --research`,`Use Perplexity AI for research-backed updates`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{try{let t=q({tasksPath:e.file||!0,tag:e.tag}),n=t.getTasksPath(),r=t.getCurrentTag();await k(r),e.id||(console.error(z.red(`Error: --id parameter is required`)),console.log(z.yellow(`Usage example: task-master update-subtask --id=5.2 --prompt="Add more details about the API endpoint"`)),process.exit(1));let i=e.id;e.prompt||(console.error(z.red(`Error: --prompt parameter is required. Please provide information to add to the subtask.`)),console.log(z.yellow(`Usage example: task-master update-subtask --id=5.2 --prompt="Add more details about the API endpoint"`)),process.exit(1));let o=e.prompt,s=e.research||!1;B.existsSync(n)||(console.error(z.red(`Error: Tasks file not found at path: ${n}`)),n===a?console.log(z.yellow(`Hint: Run task-master init or task-master parse-prd to create tasks.json first`)):console.log(z.yellow(`Hint: Check if the file path is correct: ${n}`)),process.exit(1)),console.log(z.blue(`Updating subtask ${i} with prompt: "${o}"`)),J()||console.log(z.blue(`Tasks file: ${n}`)),s&&(l(`perplexity`)?console.log(z.blue(`Using Perplexity AI for research-backed subtask update`)):(console.log(z.yellow(`Warning: PERPLEXITY_API_KEY environment variable is missing. Research-backed updates will not be available.`)),console.log(z.yellow(`Falling back to Claude AI for subtask update.`)))),await Ge(t.getTasksPath(),i,o,s,{projectRoot:t.getProjectRoot(),tag:r})||console.log(z.yellow(` Subtask update was not completed. Review the messages above for details.`))}catch(e){console.error(z.red(`Error: ${e.message}`)),e.message.includes(`subtask`)&&e.message.includes(`not found`)?(console.log(z.yellow(` To fix this issue:`)),console.log(` 1. Run task-master list --with-subtasks to see all available subtask IDs`),console.log(` 2. Use a valid subtask ID with the --id parameter in format "parentId.subtaskId"`)):e.message.includes(`API key`)&&console.log(z.yellow(` This error is related to API keys. Check your environment variables.`)),y()&&console.error(e),process.exit(1)}}),n.command(`scope-up`).description(`Increase task complexity with AI assistance`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-i, --id <ids>`,`Comma-separated task/subtask IDs to scope up (required)`).option(`-s, --strength <level>`,`Complexity increase strength: light, regular, heavy`,`regular`).option(`-p, --prompt <text>`,`Custom instructions for targeted scope adjustments`).option(`-r, --research`,`Use research AI for more informed adjustments`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{try{let t=q({tasksPath:e.file||!0,tag:e.tag}),n=t.getTasksPath(),r=t.getCurrentTag();await k(r),e.id||(console.error(z.red(`Error: --id parameter is required`)),console.log(z.yellow(`Usage example: task-master scope-up --id=1,2,3 --strength=regular`)),process.exit(1));let i=e.id.split(`,`).map(e=>{let t=parseInt(e.trim(),10);return(Number.isNaN(t)||t<=0)&&(console.error(z.red(`Error: Invalid task ID: ${e.trim()}`)),process.exit(1)),t});A(e.strength)||(console.error(z.red(`Error: Invalid strength level: ${e.strength}. Must be one of: light, regular, heavy`)),process.exit(1)),B.existsSync(n)||(console.error(z.red(`Error: Tasks file not found at path: ${n}`)),process.exit(1)),console.log(z.blue(`Scoping up ${i.length} task(s): ${i.join(`, `)}`)),console.log(z.blue(`Strength level: ${e.strength}`)),e.prompt&&console.log(z.blue(`Custom instructions: ${e.prompt}`));let a={projectRoot:t.getProjectRoot(),tag:r,commandName:`scope-up`,outputType:`cli`,research:e.research||!1},o=await se(n,i,e.strength,e.prompt||null,a,`text`);console.log(z.green(`✅ Successfully scoped up ${o.updatedTasks.length} task(s)`))}catch(e){console.error(z.red(`Error: ${e.message}`)),e.message.includes(`not found`)&&(console.log(z.yellow(` To fix this issue:`)),console.log(` 1. Run task-master list to see all available task IDs`),console.log(` 2. Use valid task IDs with the --id parameter`)),y()&&console.error(e),process.exit(1)}}),n.command(`scope-down`).description(`Decrease task complexity with AI assistance`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-i, --id <ids>`,`Comma-separated task/subtask IDs to scope down (required)`).option(`-s, --strength <level>`,`Complexity decrease strength: light, regular, heavy`,`regular`).option(`-p, --prompt <text>`,`Custom instructions for targeted scope adjustments`).option(`-r, --research`,`Use research AI for more informed adjustments`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{try{let t=q({tasksPath:e.file||!0,tag:e.tag}),n=t.getTasksPath(),r=t.getCurrentTag();await k(r),e.id||(console.error(z.red(`Error: --id parameter is required`)),console.log(z.yellow(`Usage example: task-master scope-down --id=1,2,3 --strength=regular`)),process.exit(1));let i=e.id.split(`,`).map(e=>{let t=parseInt(e.trim(),10);return(Number.isNaN(t)||t<=0)&&(console.error(z.red(`Error: Invalid task ID: ${e.trim()}`)),process.exit(1)),t});A(e.strength)||(console.error(z.red(`Error: Invalid strength level: ${e.strength}. Must be one of: light, regular, heavy`)),process.exit(1)),B.existsSync(n)||(console.error(z.red(`Error: Tasks file not found at path: ${n}`)),process.exit(1)),console.log(z.blue(`Scoping down ${i.length} task(s): ${i.join(`, `)}`)),console.log(z.blue(`Strength level: ${e.strength}`)),e.prompt&&console.log(z.blue(`Custom instructions: ${e.prompt}`));let a={projectRoot:t.getProjectRoot(),tag:r,commandName:`scope-down`,outputType:`cli`,research:e.research||!1},o=await w(n,i,e.strength,e.prompt||null,a,`text`);console.log(z.green(`✅ Successfully scoped down ${o.updatedTasks.length} task(s)`))}catch(e){console.error(z.red(`Error: ${e.message}`)),e.message.includes(`not found`)&&(console.log(z.yellow(` To fix this issue:`)),console.log(` 1. Run task-master list to see all available task IDs`),console.log(` 2. Use valid task IDs with the --id parameter`)),y()&&console.error(e),process.exit(1)}}),Fe(n),n.command(`expand`).description(`Expand a task into subtasks using AI`).option(`-i, --id <id>`,`ID of the task to expand`).option(`-a, --all`,`Expand all pending tasks based on complexity analysis`).option(`-n, --num <number>`,`Number of subtasks to generate (uses complexity analysis by default if available)`).option(`-r, --research`,`Enable research-backed generation (e.g., using Perplexity)`,!1).option(`-p, --prompt <text>`,`Additional context for subtask generation`).option(`-f, --force`,`Force expansion even if subtasks exist`,!1).option(`--file <file>`,`Path to the tasks file (relative to project root)`,a).option(`-cr, --complexity-report <file>`,`Path to the complexity report file (use this to specify the complexity report, not --file)`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t={tasksPath:e.file||!0,tag:e.tag};e.complexityReport&&(t.complexityReportPath=e.complexityReport);let r=q(t),i=r.getCurrentTag();if(await k(i),e.all){console.log(z.blue(`Expanding all pending tasks...`));try{await re(r.getTasksPath(),e.num,e.research,e.prompt,e.force,{projectRoot:r.getProjectRoot(),tag:i,complexityReportPath:r.getComplexityReportPath()})}catch(e){console.error(z.red(`Error expanding all tasks: ${e.message}`)),process.exit(1)}}else if(e.id){e.id||(console.error(z.red(`Error: Task ID is required unless using --all.`)),process.exit(1)),console.log(z.blue(`Expanding task ${e.id}...`));try{await fe(r.getTasksPath(),e.id,e.num,e.research,e.prompt,{projectRoot:r.getProjectRoot(),tag:i,complexityReportPath:r.getComplexityReportPath()},e.force)}catch(t){console.error(z.red(`Error expanding task ${e.id}: ${t.message}`)),process.exit(1)}}else console.error(z.red(`Error: You must specify either a task ID (--id) or --all.`)),n.help()}),n.command(`analyze-complexity`).description(`Analyze tasks and generate expansion recommendations${z.reset(``)}`).option(`-o, --output <file>`,`Output file path for the report`).option(`-m, --model <model>`,`LLM model to use for analysis (defaults to configured model)`).option(`-t, --threshold <number>`,`Minimum complexity score to recommend expansion (1-10)`,`5`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-r, --research`,`Use configured research model for research-backed complexity analysis`).option(`-i, --id <ids>`,`Comma-separated list of specific task IDs to analyze (e.g., "1,3,5")`).option(`--from <id>`,`Starting task ID in a range to analyze`).option(`--to <id>`,`Ending task ID in a range to analyze`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t={tasksPath:e.file||!0,tag:e.tag};e.output&&(t.complexityReportPath=e.output);let n=q(t);e.model,parseFloat(e.threshold);let r=e.research||!1,i=n.getCurrentTag();await k(i);let a=n.getComplexityReportPath();if(console.log(z.blue(`Analyzing task complexity from: ${n.getTasksPath()}`)),console.log(z.blue(`Output report will be saved to: ${a}`)),e.id)console.log(z.blue(`Analyzing specific task IDs: ${e.id}`));else if(e.from||e.to){let t=e.from?e.from:`first`,n=e.to?e.to:`last`;console.log(z.blue(`Analyzing tasks in range: ${t} to ${n}`))}r&&console.log(z.blue(`Using Perplexity AI for research-backed complexity analysis`)),await pe({...e,output:a,tag:i,projectRoot:n.getProjectRoot(),file:n.getTasksPath()})}),n.command(`research`).description(`Perform AI-powered research queries with project context`).argument(`[prompt]`,`Research prompt to investigate`).option(`--file <file>`,`Path to the tasks file`).option(`-i, --id <ids>`,`Comma-separated task/subtask IDs to include as context (e.g., "15,16.2")`).option(`-f, --files <paths>`,`Comma-separated file paths to include as context`).option(`-c, --context <text>`,`Additional custom context to include in the research prompt`).option(`-t, --tree`,`Include project file tree structure in the research context`).option(`-s, --save <file>`,`Save research results to the specified task/subtask(s)`).option(`-d, --detail <level>`,`Output detail level: low, medium, high`,`medium`).option(`--save-to <id>`,`Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")`).option(`--save-file`,`Save research results to .taskmaster/docs/research/ directory`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async(e,t)=>{let n=q({tasksPath:t.file||!0,tag:t.tag});(!e||typeof e!=`string`||e.trim().length===0)&&(console.error(z.red(`Error: Research prompt is required and cannot be empty`)),m(),process.exit(1));let r=[`low`,`medium`,`high`];t.detail&&!r.includes(t.detail.toLowerCase())&&(console.error(z.red(`Error: Detail level must be one of: ${r.join(`, `)}`)),process.exit(1));let i=[];if(t.id)try{i=t.id.split(`,`).map(e=>{let t=e.trim();if(!/^\d+(\.\d+)?$/.test(t))throw Error(`Invalid task ID format: "${t}". Expected format: "15" or "15.2"`);return t})}catch(e){console.error(z.red(`Error parsing task IDs: ${e.message}`)),process.exit(1)}let a=[];if(t.files)try{a=t.files.split(`,`).map(e=>{let t=e.trim();if(t.length===0)throw Error(`Empty file path provided`);return t})}catch(e){console.error(z.red(`Error parsing file paths: ${e.message}`)),process.exit(1)}if(t.saveTo){let e=t.saveTo.trim();e.length===0&&(console.error(z.red(`Error: Save-to ID cannot be empty`)),process.exit(1)),/^\d+(\.\d+)?$/.test(e)||(console.error(z.red(`Error: Save-to ID must be in format "15" for task or "15.2" for subtask`)),process.exit(1))}if(t.save){let e=t.save.trim();e.length===0&&(console.error(z.red(`Error: Save target cannot be empty`)),process.exit(1)),(e.includes(`..`)||e.startsWith(`/`))&&(console.error(z.red(`Error: Save path must be relative and cannot contain ".."`)),process.exit(1))}let o=n.getCurrentTag();if(await k(o),i.length>0)try{let e=d(n.getTasksPath(),n.getProjectRoot(),o);(!e||!e.tasks)&&(console.error(z.red(`Error: No valid tasks found in ${n.getTasksPath()} for tag '${o}'`)),process.exit(1))}catch(e){console.error(z.red(`Error reading tasks file: ${e.message}`)),process.exit(1)}if(a.length>0)for(let e of a){let t=V.isAbsolute(e)?e:V.join(n.getProjectRoot(),e);B.existsSync(t)||(console.error(z.red(`Error: File not found: ${e}`)),process.exit(1))}let s={prompt:e.trim(),taskIds:i,filePaths:a,customContext:t.context?t.context.trim():null,includeProjectTree:!!t.tree,saveTarget:t.save?t.save.trim():null,saveToId:t.saveTo?t.saveTo.trim():null,allowFollowUp:!0,detailLevel:t.detail?t.detail.toLowerCase():`medium`,tasksPath:n.getTasksPath(),projectRoot:n.getProjectRoot()};console.log(z.blue(`Researching: "${s.prompt}"`)),s.taskIds.length>0&&console.log(z.gray(`Task context: ${s.taskIds.join(`, `)}`)),s.filePaths.length>0&&console.log(z.gray(`File context: ${s.filePaths.join(`, `)}`)),s.customContext&&console.log(z.gray(`Custom context: ${s.customContext.substring(0,50)}${s.customContext.length>50?`...`:``}`)),s.includeProjectTree&&console.log(z.gray(`Including project file tree`)),console.log(z.gray(`Detail level: ${s.detailLevel}`));try{let{performResearch:e}=await import(`./research-CnhTKWc9.js`),n={taskIds:s.taskIds,filePaths:s.filePaths,customContext:s.customContext||``,includeProjectTree:s.includeProjectTree,detailLevel:s.detailLevel,projectRoot:s.projectRoot,saveToFile:!!t.saveFile,tag:o},r=await e(s.prompt,n,{commandName:`research`,outputType:`cli`,tag:o},`text`,s.allowFollowUp);if(s.saveToId&&!r.interactiveSaveOccurred)try{let e=s.saveToId.includes(`.`),t=`## Research Query: ${s.prompt} **Detail Level:** ${r.detailLevel} **Context Size:** ${r.contextSize} characters **Timestamp:** ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()} ### Results ${r.result}`;if(e){let{updateSubtaskById:e}=await import(`./update-subtask-by-id-4npFkBrK.js`);await e(s.tasksPath,s.saveToId,t,!1,{commandName:`research-save`,outputType:`cli`,projectRoot:s.projectRoot,tag:o},`text`),console.log(z.green(`✅ Research saved to subtask ${s.saveToId}`))}else{let e=(await import(`./update-task-by-id-CLnUVpCX.js`)).default,n=parseInt(s.saveToId,10);await e(s.tasksPath,n,t,!1,{commandName:`research-save`,outputType:`cli`,projectRoot:s.projectRoot,tag:o},`text`,!0),console.log(z.green(`✅ Research saved to task ${s.saveToId}`))}}catch(e){console.log(z.red(`❌ Error saving to task/subtask: ${e.message}`))}if(s.saveTarget){let e=`# Research Query: ${s.prompt} **Detail Level:** ${r.detailLevel} **Context Size:** ${r.contextSize} characters **Timestamp:** ${new Date().toISOString()} ## Results ${r.result} `;B.writeFileSync(s.saveTarget,e,`utf-8`),console.log(z.green(`\n💾 Results saved to: ${s.saveTarget}`))}}catch(e){console.error(z.red(`\n❌ Research failed: ${e.message}`)),process.exit(1)}}),n.command(`clear-subtasks`).description(`Clear subtasks from specified tasks`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-i, --id <ids>`,`Task IDs (comma-separated) to clear subtasks from`).option(`--all`,`Clear subtasks from all tasks`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=e.id,n=e.all,r=q({tasksPath:e.file||!0,tag:e.tag}),i=r.getCurrentTag();if(await k(i),!t&&!n&&(console.error(z.red(`Error: Please specify task IDs with --id=<ids> or use --all to clear all tasks`)),process.exit(1)),n){let e=d(r.getTasksPath(),r.getProjectRoot(),i);(!e||!e.tasks)&&(console.error(z.red(`Error: No valid tasks found`)),process.exit(1));let t=e.tasks.map(e=>e.id).join(`,`);D(r.getTasksPath(),t,{projectRoot:r.getProjectRoot(),tag:i})}else D(r.getTasksPath(),t,{projectRoot:r.getProjectRoot(),tag:i})}),n.command(`add-task`).description(`Add a new task using AI, optionally providing manual details`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`-p, --prompt <prompt>`,`Description of the task to add (required if not using manual fields)`).option(`-t, --title <title>`,`Task title (for manual task creation)`).option(`-d, --description <description>`,`Task description (for manual task creation)`).option(`--details <details>`,`Implementation details (for manual task creation)`).option(`--dependencies <dependencies>`,`Comma-separated list of task IDs this task depends on`).option(`--priority <priority>`,`Task priority (high, medium, low)`,`medium`).option(`-r, --research`,`Whether to use research capabilities for task creation`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=e.title&&e.description;!e.prompt&&!t&&(console.error(z.red(`Error: Either --prompt or both --title and --description must be provided`)),process.exit(1));let n=e.file||a;B.existsSync(n)||(console.error(`❌ No tasks.json file found. Please run "task-master init" or create a tasks.json file at ${a}`),process.exit(1));let r=q({tasksPath:e.file||!0,tag:e.tag}),i=r.getProjectRoot(),o=r.getCurrentTag();await k(o);let s=null;t?(s={title:e.title,description:e.description,details:e.details||``,testStrategy:e.testStrategy||``},console.log(z.blue(`Creating task manually with title: "${e.title}"`))):console.log(z.blue(`Creating task with AI using prompt: "${e.prompt}"`));let c=e.dependencies?e.dependencies.split(`,`).map(e=>e.trim()):[];c.length>0&&console.log(z.blue(`Dependencies: [${c.join(`, `)}]`)),e.priority&&console.log(z.blue(`Priority: ${e.priority}`));let l={projectRoot:i,tag:o,commandName:`add-task`,outputType:`cli`};try{let{newTaskId:t,telemetryData:n}=await le(r.getTasksPath(),e.prompt,c,e.priority,l,`text`,s,e.research)}catch(e){console.error(z.red(`Error adding task: ${e.message}`)),e.details&&console.error(z.red(e.details)),process.exit(1)}}),n.command(`add-dependency`).description(`Add a dependency to a task`).option(`-i, --id <id>`,`Task ID to add dependency to`).option(`-d, --depends-on <id>`,`Task ID that will become a dependency`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=q({tasksPath:e.file||!0,tag:e.tag}),n=e.id,r=e.dependsOn,i=t.getCurrentTag();await k(i),(!n||!r)&&(console.error(z.red(`Error: Both --id and --depends-on are required`)),process.exit(1));let a=n.includes(`.`)?n:parseInt(n,10),o=r.includes(`.`)?r:parseInt(r,10);await Be(t.getTasksPath(),a,o,{projectRoot:t.getProjectRoot(),tag:i})}),n.command(`remove-dependency`).description(`Remove a dependency from a task`).option(`-i, --id <id>`,`Task ID to remove dependency from`).option(`-d, --depends-on <id>`,`Task ID to remove as a dependency`).option(`-f, --file <file>`,`Path to the tasks file`,a).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=q({tasksPath:e.file||!0,tag:e