warp-task-master
Version:
BETA: Experimental Task Master fork with Warp AI integration and human-readable profile names. For production use, see task-master-ai.
3 lines (2 loc) • 14.3 kB
JavaScript
import{getCurrentTag as e,getTasksForTag as t,log as n,readJSON as r,truncate as i,writeJSON as a}from"./utils-Can7ymw4.js";import{find_next_task_default as o}from"./task-manager-Cy89-0WA.js";import s from"path";import c from"chalk";import l from"boxen";import u from"fs";import d from"inquirer";import f from"cli-table3";async function p(i,o,s={},u={},d=`text`){let{mcpLog:f,projectRoot:p}=u,{copyFromCurrent:m=!1,copyFromTag:h,description:g}=s,_=f||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{if(!o||typeof o!=`string`)throw Error(`Tag name is required and must be a string`);if(!/^[a-zA-Z0-9_-]+$/.test(o))throw Error(`Tag name can only contain letters, numbers, hyphens, and underscores`);if([`master`,`main`,`default`].includes(o.toLowerCase()))throw Error(`"${o}" is a reserved tag name`);_.info(`Creating new tag: ${o}`);let n=r(i,p);if(!n)throw Error(`Could not read tasks file at ${i}`);let s;if(n._rawTaggedData)s=n._rawTaggedData;else if(n.tasks&&!n.master)s={master:{tasks:n.tasks,metadata:n.metadata||{created:new Date().toISOString(),updated:new Date().toISOString(),description:`Tasks live here by default`}}};else{s={};for(let[e,t]of Object.entries(n))e!==`_rawTaggedData`&&e!==`tag`&&(s[e]=t)}if(s[o])throw Error(`Tag "${o}" already exists`);let u=[];if(m||h){let n=h||e(p);u=t(s,n),h&&u.length===0&&_.warn(`Source tag "${h}" not found or has no tasks`),_.info(`Copying ${u.length} tasks from tag "${n}"`)}else _.info(`Creating empty tag (no tasks copied)`);s[o]={tasks:[...u],metadata:{created:new Date().toISOString(),updated:new Date().toISOString(),description:g||`Tag created on ${new Date().toLocaleDateString()}`}};let f={};for(let[e,t]of Object.entries(s))e!==`_rawTaggedData`&&(f[e]=t);return a(i,f,p),_.success(`Successfully created tag "${o}"`),d===`json`||d===`text`&&console.log(l(c.green.bold(`✓ Tag Created Successfully`)+`\n\nTag Name: ${c.cyan(o)}\nTasks Copied: ${c.yellow(u.length)}`+(m||h?`\nSource Tag: ${c.cyan(h||e(p))}`:``)+(g?`\nDescription: ${c.gray(g)}`:``),{padding:1,borderColor:`green`,borderStyle:`round`,margin:{top:1,bottom:1}})),{tagName:o,created:!0,tasksCopied:u.length,sourceTag:m||h?h||e(p):null,description:g||`Tag created on ${new Date().toLocaleDateString()}`}}catch(e){throw _.error(`Error creating tag: ${e.message}`),e}}async function m(i,o,s={},u={},f=`text`){let{mcpLog:p,projectRoot:m}=u,{yes:h=!1}=s,g=p||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{if(!o||typeof o!=`string`)throw Error(`Tag name is required and must be a string`);if(o===`master`)throw Error(`Cannot delete the "master" tag`);g.info(`Deleting tag: ${o}`);let n=r(i,m);if(!n)throw Error(`Could not read tasks file at ${i}`);let s;if(n._rawTaggedData)s=n._rawTaggedData;else if(n.tasks&&!n.master)s={master:{tasks:n.tasks,metadata:n.metadata||{created:new Date().toISOString(),updated:new Date().toISOString(),description:`Tasks live here by default`}}};else{s={};for(let[e,t]of Object.entries(n))e!==`_rawTaggedData`&&e!==`tag`&&(s[e]=t)}if(!s[o])throw Error(`Tag "${o}" does not exist`);let u=e(m)===o,p=t(s,o).length;if(!h&&p>0&&f===`text`){if(console.log(l(c.yellow.bold(`⚠ WARNING: Tag Deletion`)+`\n\nYou are about to delete tag "${c.cyan(o)}"\nThis will permanently delete ${c.red.bold(p)} tasks
This action cannot be undone!`,{padding:1,borderColor:`yellow`,borderStyle:`round`,margin:{top:1,bottom:1}})),!(await d.prompt([{type:`confirm`,name:`proceed`,message:`Are you sure you want to delete tag "${o}" and its ${p} tasks?`,default:!1}])).proceed)throw g.info(`Tag deletion cancelled by user`),Error(`Tag deletion cancelled`);if((await d.prompt([{type:`input`,name:`tagNameConfirm`,message:`To confirm deletion, please type the tag name "${o}":`,validate:e=>e===o?!0:`Please type exactly "${o}" to confirm deletion`}])).tagNameConfirm!==o)throw g.info(`Tag deletion cancelled - incorrect tag name confirmation`),Error(`Tag deletion cancelled`);g.info(`Double confirmation received, proceeding with deletion...`)}delete s[o],u&&(await b(m,`master`),g.info(`Switched current tag to "master"`));let _={};for(let[e,t]of Object.entries(s))e!==`_rawTaggedData`&&(_[e]=t);return a(i,_,m),g.success(`Successfully deleted tag "${o}"`),f===`json`||f===`text`&&console.log(l(c.red.bold(`✓ Tag Deleted Successfully`)+`\n\nTag Name: ${c.cyan(o)}\nTasks Deleted: ${c.yellow(p)}`+(u?`\n${c.yellow(`⚠ Switched current tag to "master"`)}`:``),{padding:1,borderColor:`red`,borderStyle:`round`,margin:{top:1,bottom:1}})),{tagName:o,deleted:!0,tasksDeleted:p,wasCurrentTag:u,switchedToMaster:u}}catch(e){throw g.error(`Error deleting tag: ${e.message}`),e}}async function h(e,t,r={}){let i=!1;try{let n;try{let t=u.statSync(e);n=t.birthtime<t.mtime?t.birthtime:t.mtime}catch(e){n=new Date}for(let[e,r]of Object.entries(t)){if(e===`tasks`||e===`tag`||e===`_rawTaggedData`||!r||typeof r!=`object`||!Array.isArray(r.tasks))continue;r.metadata||(r.metadata={},i=!0),r.metadata.created||(r.metadata.created=n.toISOString(),i=!0),r.metadata.description||(e===`master`?r.metadata.description=`Tasks live here by default`:r.metadata.description=`Tag created on ${new Date(r.metadata.created).toLocaleDateString()}`,i=!0),r.metadata.updated||(r.metadata.updated=r.metadata.created,i=!0)}if(i){let n={};for(let[e,r]of Object.entries(t))e!==`_rawTaggedData`&&(n[e]=r);a(e,n,r.projectRoot)}}catch(e){(r.mcpLog||{warn:(...e)=>n(`warn`,...e)}).warn(`Could not enhance tag metadata: ${e.message}`)}return i}async function g(t,a={},o={},s=`text`){let{mcpLog:u,projectRoot:d}=o,{showTaskCounts:p=!0,showMetadata:m=!1}=a,g=u||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{g.info(`Listing available tags`);let n=r(t,d);if(!n)throw Error(`Could not read tasks file at ${t}`);let a=e(d),u=n._rawTaggedData||n;await h(t,u,o);let _=[];for(let[e,t]of Object.entries(u)){if(e===`tasks`||e===`tag`||e===`_rawTaggedData`||!t||typeof t!=`object`||!Array.isArray(t.tasks))continue;let n=t.tasks||[],r=t.metadata||{};_.push({name:e,isCurrent:e===a,completedTasks:n.filter(e=>e.status===`done`||e.status===`completed`).length,tasks:n||[],created:r.created||`Unknown`,description:r.description||`No description`})}if(_.sort((e,t)=>e.isCurrent?-1:t.isCurrent?1:e.name.localeCompare(t.name)),g.success(`Found ${_.length} tags`),s===`json`)return{tags:_,currentTag:a,totalTags:_.length};if(s===`text`){if(_.length===0)return console.log(l(c.yellow(`No tags found`),{padding:1,borderColor:`yellow`,borderStyle:`round`,margin:{top:1,bottom:1}})),{tags:[],currentTag:a,totalTags:0};let e=[c.cyan.bold(`Tag Name`)];p&&(e.push(c.cyan.bold(`Tasks`)),e.push(c.cyan.bold(`Completed`))),m&&(e.push(c.cyan.bold(`Created`)),e.push(c.cyan.bold(`Description`)));let t=new f({head:e,colWidths:m?[20,10,12,15,50]:[25,10,12]});_.forEach(e=>{let n=[],r=e.isCurrent?`${c.green(`●`)} ${c.green.bold(e.name)} ${c.gray(`(current)`)}`:` ${e.name}`;if(n.push(r),p&&(n.push(c.white(e.tasks.length.toString())),n.push(c.green(e.completedTasks.toString()))),m){let t=e.created===`Unknown`?`Unknown`:new Date(e.created).toLocaleDateString();n.push(c.gray(t)),n.push(c.gray(i(e.description,50)))}t.push(n)}),console.log(t.toString())}return{tags:_,currentTag:a,totalTags:_.length}}catch(e){throw g.error(`Error listing tags: ${e.message}`),e}}async function _(t,i,a={},s={},u=`text`){let{mcpLog:d,projectRoot:f}=s,p=d||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{if(!i||typeof i!=`string`)throw Error(`Tag name is required and must be a string`);p.info(`Switching to tag: ${i}`);let n=r(t,f);if(!n)throw Error(`Could not read tasks file at ${t}`);if(!(n._rawTaggedData||n)[i])throw Error(`Tag "${i}" does not exist`);let a=e(f);await b(f,i);let s=r(t,f,i),d=s&&s.tasks||[],m=d.length,h=o(d);if(p.success(`Successfully switched to tag "${i}"`),u===`json`)return{previousTag:a,currentTag:i,switched:!0,taskCount:m,nextTask:h};if(u===`text`){let e=``;e=h?`\nNext Task: ${c.cyan(`#${h.id}`)} - ${c.white(h.title)}`:`\nNext Task: ${c.gray(`No eligible tasks available`)}`,console.log(l(c.green.bold(`✓ Tag Switched Successfully`)+`\n\nPrevious Tag: ${c.cyan(a)}\nCurrent Tag: ${c.green.bold(i)}\nAvailable Tasks: ${c.yellow(m)}`+e,{padding:1,borderColor:`green`,borderStyle:`round`,margin:{top:1,bottom:1}}))}return{previousTag:a,currentTag:i,switched:!0,taskCount:m,nextTask:h}}catch(e){throw p.error(`Error switching tag: ${e.message}`),e}}async function v(i,o,s,u={},d={},f=`text`){let{mcpLog:p,projectRoot:m}=d,h=p||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{if(!o||typeof o!=`string`)throw Error(`Old tag name is required and must be a string`);if(!s||typeof s!=`string`)throw Error(`New tag name is required and must be a string`);if(!/^[a-zA-Z0-9_-]+$/.test(s))throw Error(`New tag name can only contain letters, numbers, hyphens, and underscores`);if(o===`master`)throw Error(`Cannot rename the "master" tag`);if([`master`,`main`,`default`].includes(s.toLowerCase()))throw Error(`"${s}" is a reserved tag name`);h.info(`Renaming tag from "${o}" to "${s}"`);let n=r(i,m);if(!n)throw Error(`Could not read tasks file at ${i}`);let u=n._rawTaggedData||n;if(!u[o])throw Error(`Tag "${o}" does not exist`);if(u[s])throw Error(`Tag "${s}" already exists`);let d=e(m)===o;u[s]={...u[o]},u[s].metadata&&(u[s].metadata.renamed={from:o,date:new Date().toISOString()}),delete u[o],d&&(await b(m,s),h.info(`Updated current tag reference to "${s}"`));let p={};for(let[e,t]of Object.entries(u))e!==`_rawTaggedData`&&(p[e]=t);a(i,p,m);let g=t(u,s).length;return h.success(`Successfully renamed tag from "${o}" to "${s}"`),f===`json`||f===`text`&&console.log(l(c.green.bold(`✓ Tag Renamed Successfully`)+`\n\nOld Name: ${c.cyan(o)}\nNew Name: ${c.green.bold(s)}\nTasks: ${c.yellow(g)}`+(d?`\n${c.green(`✓ Current tag updated`)}`:``),{padding:1,borderColor:`green`,borderStyle:`round`,margin:{top:1,bottom:1}})),{oldName:o,newName:s,renamed:!0,taskCount:g,wasCurrentTag:d,isCurrentTag:d}}catch(e){throw h.error(`Error renaming tag: ${e.message}`),e}}async function y(e,i,o,s={},u={},d=`text`){let{mcpLog:f,projectRoot:p}=u,{description:m}=s,h=f||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{if(!i||typeof i!=`string`)throw Error(`Source tag name is required and must be a string`);if(!o||typeof o!=`string`)throw Error(`Target tag name is required and must be a string`);if(!/^[a-zA-Z0-9_-]+$/.test(o))throw Error(`Target tag name can only contain letters, numbers, hyphens, and underscores`);if([`master`,`main`,`default`].includes(o.toLowerCase()))throw Error(`"${o}" is a reserved tag name`);h.info(`Copying tag from "${i}" to "${o}"`);let n=r(e,p);if(!n)throw Error(`Could not read tasks file at ${e}`);let s=n._rawTaggedData||n;if(!s[i])throw Error(`Source tag "${i}" does not exist`);if(s[o])throw Error(`Target tag "${o}" already exists`);let u=t(s,i);s[o]={tasks:JSON.parse(JSON.stringify(u)),metadata:{created:new Date().toISOString(),updated:new Date().toISOString(),description:m||`Copy of "${i}" created on ${new Date().toLocaleDateString()}`,copiedFrom:{tag:i,date:new Date().toISOString()}}};let f={};for(let[e,t]of Object.entries(s))e!==`_rawTaggedData`&&(f[e]=t);return a(e,f,p),h.success(`Successfully copied tag from "${i}" to "${o}"`),d===`json`||d===`text`&&console.log(l(c.green.bold(`✓ Tag Copied Successfully`)+`\n\nSource Tag: ${c.cyan(i)}\nTarget Tag: ${c.green.bold(o)}\nTasks Copied: ${c.yellow(u.length)}`+(m?`\nDescription: ${c.gray(m)}`:``),{padding:1,borderColor:`green`,borderStyle:`round`,margin:{top:1,bottom:1}})),{sourceName:i,targetName:o,copied:!0,description:m||`Copy of "${i}" created on ${new Date().toLocaleDateString()}`}}catch(e){throw h.error(`Error copying tag: ${e.message}`),e}}async function b(e,t){try{let n=s.join(e,`.taskmaster`,`state.json`),r={};if(u.existsSync(n)){let e=u.readFileSync(n,`utf8`);r=JSON.parse(e)}r.currentTag=t,r.lastSwitched=new Date().toISOString(),r.branchTagMapping||(r.branchTagMapping={}),r.migrationNoticeShown===void 0&&(r.migrationNoticeShown=!1),u.writeFileSync(n,JSON.stringify(r,null,2),`utf8`)}catch(e){n(`warn`,`Could not update current tag in state.json: ${e.message}`)}}async function x(e,t,r){try{let n=s.join(e,`.taskmaster`,`state.json`),i={};if(u.existsSync(n)){let e=u.readFileSync(n,`utf8`);i=JSON.parse(e)}i.branchTagMapping||(i.branchTagMapping={}),i.branchTagMapping[t]=r,u.writeFileSync(n,JSON.stringify(i,null,2),`utf8`)}catch(e){n(`warn`,`Could not update branch-tag mapping: ${e.message}`)}}async function S(e,t,r={},i={},a=`text`){let{mcpLog:o,projectRoot:s}=i,{copyFromCurrent:c,copyFromTag:l,description:u,autoSwitch:d}=r,{sanitizeBranchNameForTag:f,isValidBranchForTag:m}=await import(`./git-utils-DxggEZwm.js`),h=o||{info:(...e)=>n(`info`,...e),warn:(...e)=>n(`warn`,...e),error:(...e)=>n(`error`,...e),debug:(...e)=>n(`debug`,...e),success:(...e)=>n(`success`,...e)};try{if(!t||typeof t!=`string`)throw Error(`Branch name is required and must be a string`);if(!m(t))throw Error(`Branch "${t}" cannot be converted to a valid tag name`);let n=f(t);h.info(`Creating tag "${n}" from git branch "${t}"`);let r=await p(e,n,{copyFromCurrent:c,copyFromTag:l,description:u||`Tag created from git branch "${t}"`},i,a);return await x(s,t,n),h.info(`Updated branch-tag mapping: ${t} -> ${n}`),d&&(await b(s,n),h.info(`Automatically switched to tag "${n}"`)),a===`json`?{...r,branchName:t,tagName:n,mappingUpdated:!0,autoSwitched:d||!1}:{branchName:t,tagName:n,created:!0,mappingUpdated:!0,autoSwitched:d||!1}}catch(e){throw h.error(`Error creating tag from branch: ${e.message}`),e}}export{y as copyTag,p as createTag,S as createTagFromBranch,m as deleteTag,v as renameTag,b as switchCurrentTag,g as tags,x as updateBranchTagMapping,_ as useTag};