taskgraph-ai 0.52.23 → 0.54.12
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/README.md +38 -34
- package/dist/ai-services-unified-BPPVDTpt.js +3 -0
- package/dist/ai-services-unified-Dskm2lPO.js +1 -0
- package/dist/assets/AGENTS.md +12 -13
- package/dist/commands-DJc5m0Gi.js +126 -0
- package/dist/config-manager-CKwBfpAp.js +1 -0
- package/dist/config-manager-Db2R8Tgw.js +1 -0
- package/dist/git-utils-CRR3iPaC.js +1 -0
- package/dist/{git-utils--i2q96hv.js → git-utils-NnZtE3nY.js} +1 -1
- package/dist/init-CK_i6ZcN.js +24 -0
- package/dist/mcp-server.js +2 -2
- package/dist/research-BKsPc_oX.js +1 -0
- package/dist/research-C8rAJdp0.js +585 -0
- package/dist/{response-language-ip84ug-k.js → response-language-C8euRV2w.js} +1 -1
- package/dist/response-language-DiIdcmnK.js +1 -0
- package/dist/rolldown-runtime-BxhE39jg.js +1 -0
- package/dist/src-CK3olqVP.js +468 -0
- package/dist/src-DRpuDe-k.js +207 -0
- package/dist/tag-management-BL0w3Tk3.js +1 -0
- package/dist/task-manager-B_UFNQnE.js +1 -0
- package/dist/task-manager-BipYRXFW.js +167 -0
- package/dist/taskgraph.js +1 -1
- package/package.json +3 -8
- package/dist/ai-services-unified-CigO4oTy.js +0 -1
- package/dist/ai-services-unified-kmQRPU0g.js +0 -3
- package/dist/assets/claude/TM_COMMANDS_GUIDE.md +0 -147
- package/dist/assets/scripts_README.md +0 -443
- package/dist/commands-dFNugMWF.js +0 -116
- package/dist/config-manager-C5Ih13nA.js +0 -1
- package/dist/config-manager-DhVH8AQp.js +0 -1
- package/dist/dependency-manager-CCREovZA.js +0 -758
- package/dist/git-utils-BezpEZd4.js +0 -1
- package/dist/init-p_pWjmVE.js +0 -3139
- package/dist/research-blv_5JOT.js +0 -1
- package/dist/response-language-D9UxmXBp.js +0 -1
- package/dist/src-TZm8h0X-.js +0 -207
- package/dist/src-yE1DeXKG.js +0 -468
- package/dist/tag-management-Br2FX-YC.js +0 -1
- package/dist/task-manager-DWPIhAB7.js +0 -1
- package/index.js +0 -157
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import e from"chalk";import t,{join as n,resolve as r}from"node:path";import i,{appendFile as a,mkdir as o,readFile as s}from"node:fs/promises";import c,{constants as l,existsSync as u,readFileSync as d,readdirSync as f}from"node:fs";import{parseEnv as p}from"node:util";import m,{join as h}from"path";import g from"fs-extra";import{simpleGit as _}from"simple-git";import{exec as ee,execSync as v}from"child_process";import{promisify as te}from"util";import{z as y}from"zod";import{execFile as ne,spawn as re,spawnSync as ie}from"node:child_process";import{createHash as ae}from"node:crypto";import oe from"node:os";import se from"proper-lockfile";import{Writer as ce}from"steno";import{format as le}from"date-fns";var ue=class{configManager;constructor(e){this.configManager=e}getConfig(){return this.configManager.getConfig()}getStorageConfig(){return this.configManager.getStorageConfig()}getModelConfig(){return this.configManager.getModelConfig()}getResponseLanguage(){return this.configManager.getResponseLanguage()}getProjectRoot(){return this.configManager.getProjectRoot()}getActiveTag(){return this.configManager.getActiveTag()}async setActiveTag(e){return this.configManager.setActiveTag(e)}async updateConfig(e){return this.configManager.updateConfig(e)}async setResponseLanguage(e){return this.configManager.setResponseLanguage(e)}async saveConfig(){return this.configManager.saveConfig()}async reset(){return this.configManager.reset()}getConfigSources(){return this.configManager.getConfigSources()}};const b={MODELS:{MAIN:`claude-sonnet-5-5`,FALLBACK:`claude-sonnet-5-5`},TASKS:{DEFAULT_PRIORITY:`medium`,DEFAULT_COMPLEXITY:`moderate`,MAX_SUBTASKS:20,MAX_CONCURRENT:5,TASK_ID_PREFIX:`TASK-`},TAGS:{DEFAULT_TAG:`master`,MAX_TAGS_PER_TASK:10,NAMING_CONVENTION:`kebab-case`},WORKFLOW:{ENABLE_AUTOPILOT:!0,MAX_PHASE_ATTEMPTS:3,BRANCH_PATTERN:`task-{taskId}`,REQUIRE_CLEAN_WORKING_TREE:!0,AUTO_STAGE_CHANGES:!0,INCLUDE_CO_AUTHOR:!0,CO_AUTHOR_NAME:`TaskGraph`,CO_AUTHOR_EMAIL:`noreply@github.com`,MIN_TESTS:1,MAX_FAILURES_IN_GREEN:0,COMMIT_MESSAGE_TEMPLATE:`{type}({scope}): {description} (Task {taskId}.{subtaskIndex})`,ALLOWED_COMMIT_TYPES:[`feat`,`fix`,`refactor`,`test`,`docs`,`chore`],DEFAULT_COMMIT_TYPE:`feat`,OPERATION_TIMEOUT:6e4,ENABLE_ACTIVITY_LOGGING:!0,ACTIVITY_LOG_PATH:`.taskgraph/logs/workflow-activity.log`,ENABLE_STATE_BACKUP:!0,MAX_STATE_BACKUPS:5,ABORT_ON_MAX_ATTEMPTS:!1},STORAGE:{TYPE:`file`,ENCODING:`utf8`,MAX_BACKUPS:5},RETRY:{ATTEMPTS:3,DELAY:1e3,MAX_DELAY:3e4,BACKOFF_MULTIPLIER:2,TIMEOUT:3e4},LOGGING:{LEVEL:`info`,MAX_FILE_SIZE:10,MAX_FILES:5},SECURITY:{MAX_REQUESTS_PER_MINUTE:60,MAX_PROMPT_LENGTH:1e5,ALLOWED_EXTENSIONS:[`.txt`,`.md`,`.json`]},VERSION:`1.0.0`};let de=/* @__PURE__ */ function(e){return e[e.SILENT=0]=`SILENT`,e[e.ERROR=1]=`ERROR`,e[e.WARN=2]=`WARN`,e[e.INFO=3]=`INFO`,e[e.DEBUG=4]=`DEBUG`,e}({});var fe=class t{config;static DEFAULT_CONFIG={level:0,silent:!1,prefix:``,timestamp:!1,colors:!0,mcpMode:!1,logCallback:void 0};constructor(e={}){let n={};if((process.env.MCP_MODE===`true`||process.env.TASKGRAPH_MCP===`true`)&&(n.mcpMode=!0),(process.env.TASKGRAPH_SILENT===`true`||process.env.TM_SILENT===`true`)&&(n.silent=!0),process.env.TASKGRAPH_LOG_LEVEL||process.env.TM_LOG_LEVEL){let e=(process.env.TASKGRAPH_LOG_LEVEL||process.env.TM_LOG_LEVEL||``).toUpperCase();e in de&&(n.level=de[e])}(process.env.NO_COLOR===`true`||process.env.TASKGRAPH_NO_COLOR===`true`)&&(n.colors=!1),this.config={...t.DEFAULT_CONFIG,...e,...n},this.config.mcpMode&&!this.config.logCallback&&(this.config.silent=!0)}shouldLog(e){return this.config.logCallback?e<=this.config.level:this.config.silent||this.config.mcpMode?!1:e<=this.config.level}formatMessage(t,n,...r){let i=``;if(this.config.timestamp){let t=(/* @__PURE__ */ new Date()).toISOString();i+=this.config.colors?e.dim(`[${t}] `):`[${t}] `}if(this.config.prefix&&(i+=this.config.colors?e.cyan(`[${this.config.prefix}] `):`[${this.config.prefix}] `),this.config.colors)switch(t){case 1:n=e.red(n);break;case 2:n=e.yellow(n);break;case 3:break;case 4:n=e.dim(n)}return i+=n,r.length>0&&(i+=` `+r.map(e=>typeof e==`object`?JSON.stringify(e,null,2):String(e)).join(` `)),i}isLogObject(e){return typeof e==`object`&&!!e&&`info`in e&&`warn`in e&&`error`in e&&`debug`in e}output(e,t,n,...r){let i=this.formatMessage(e,n,...r);if(this.config.logCallback){if(this.isLogObject(this.config.logCallback)){let e=t.toLowerCase();e in this.config.logCallback&&this.config.logCallback[e](i)}else this.config.logCallback(t.toLowerCase(),i)}else switch(e){case 1:console.error(i);break;case 2:console.warn(i);break;default:console.log(i)}}error(e,...t){this.shouldLog(1)&&this.output(1,`ERROR`,e,...t)}warn(e,...t){this.shouldLog(2)&&this.output(2,`WARN`,e,...t)}info(e,...t){this.shouldLog(3)&&this.output(3,`INFO`,e,...t)}debug(e,...t){this.shouldLog(4)&&this.output(4,`DEBUG`,e,...t)}log(e,...t){if(this.config.logCallback){let n=t.length>0?[e,...t].join(` `):e;this.isLogObject(this.config.logCallback)?this.config.logCallback.info(n):this.config.logCallback(`log`,n)}else this.config.silent||this.config.mcpMode||(t.length>0?console.log(e,...t):console.log(e))}setConfig(e){this.config={...this.config,...e},this.config.mcpMode&&!this.config.logCallback&&(this.config.silent=!0)}getConfig(){return{...this.config}}child(e,n){let r=this.config.prefix?`${this.config.prefix}:${e}`:e;return new t({...this.config,...n,prefix:r})}};let pe=null;const me=/* @__PURE__ */ new Map;function he(e){return new fe(e)}function x(e,t){return e?(me.has(e)||me.set(e,he({prefix:e,...t})),me.get(e)):(pe||=he(t),pe)}const S={FILE_NOT_FOUND:`FILE_NOT_FOUND`,FILE_READ_ERROR:`FILE_READ_ERROR`,FILE_WRITE_ERROR:`FILE_WRITE_ERROR`,PARSE_ERROR:`PARSE_ERROR`,JSON_PARSE_ERROR:`JSON_PARSE_ERROR`,YAML_PARSE_ERROR:`YAML_PARSE_ERROR`,VALIDATION_ERROR:`VALIDATION_ERROR`,SCHEMA_VALIDATION_ERROR:`SCHEMA_VALIDATION_ERROR`,TYPE_VALIDATION_ERROR:`TYPE_VALIDATION_ERROR`,API_ERROR:`API_ERROR`,NETWORK_ERROR:`NETWORK_ERROR`,AUTHENTICATION_ERROR:`AUTHENTICATION_ERROR`,AUTHORIZATION_ERROR:`AUTHORIZATION_ERROR`,TASK_NOT_FOUND:`TASK_NOT_FOUND`,TASK_DEPENDENCY_ERROR:`TASK_DEPENDENCY_ERROR`,TASK_STATUS_ERROR:`TASK_STATUS_ERROR`,STORAGE_ERROR:`STORAGE_ERROR`,DATABASE_ERROR:`DATABASE_ERROR`,CONFIG_ERROR:`CONFIG_ERROR`,MISSING_CONFIGURATION:`MISSING_CONFIGURATION`,INVALID_CONFIGURATION:`INVALID_CONFIGURATION`,PROVIDER_ERROR:`PROVIDER_ERROR`,PROVIDER_NOT_FOUND:`PROVIDER_NOT_FOUND`,PROVIDER_INITIALIZATION_ERROR:`PROVIDER_INITIALIZATION_ERROR`,INTERNAL_ERROR:`INTERNAL_ERROR`,INVALID_INPUT:`INVALID_INPUT`,NOT_IMPLEMENTED:`NOT_IMPLEMENTED`,UNKNOWN_ERROR:`UNKNOWN_ERROR`,NOT_FOUND:`NOT_FOUND`};var C=class e extends Error{code;context;cause;timestamp;constructor(t,n=S.UNKNOWN_ERROR,r={},i){super(t),this.name=`TaskGraphError`,this.code=n,this.cause=i,this.timestamp=/* @__PURE__ */ new Date,this.context={timestamp:this.timestamp,...r},Object.setPrototypeOf(this,e.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,e),i?.stack&&(this.stack=`${this.stack}\nCaused by: ${i.stack}`)}getUserMessage(){return this.context.userMessage||this.message}getSanitizedDetails(){let{details:e,resource:t,operation:n}=this.context;return{code:this.code,message:this.getUserMessage(),...t&&{resource:t},...n&&{operation:n},...e&&typeof e==`object`&&!this.containsSensitiveInfo(e)&&{details:e}}}containsSensitiveInfo(e){if(typeof e!=`object`||!e)return!1;let t=[`password`,`token`,`key`,`secret`,`auth`,`credential`],n=JSON.stringify(e).toLowerCase();return t.some(e=>n.includes(e))}toJSON(){let t={name:this.name,message:this.message,code:this.code,context:this.context,stack:this.stack};return this.cause&&(t.cause=this.cause instanceof e?this.cause.toJSON():{name:this.cause.name,message:this.cause.message,code:S.UNKNOWN_ERROR,context:{},stack:this.cause.stack}),t}toString(){let e=`${this.name}[${this.code}]: ${this.message}`;return this.context.operation&&(e+=` (operation: ${this.context.operation})`),this.context.resource&&(e+=` (resource: ${this.context.resource})`),this.cause&&(e+=`\nCaused by: ${this.cause.toString()}`),e}is(e){return this.code===e}hasCode(t){return this.is(t)?!0:this.cause instanceof e&&this.cause.hasCode(t)}withContext(t){return new e(this.message,this.code,{...this.context,...t},this.cause)}wrap(t,n=S.INTERNAL_ERROR,r={}){return new e(t,n,r,this)}},ge=class{localConfigPath;globalConfigPath;logger=x(`ConfigLoader`);constructor(e){this.localConfigPath=t.join(e,`.taskgraph`,`config.json`),this.globalConfigPath=t.join(process.env.HOME||``,`.taskgraph`,`config.json`)}getDefaultConfig(){return{models:{main:b.MODELS.MAIN,fallback:b.MODELS.FALLBACK},workflow:{enableAutopilot:b.WORKFLOW.ENABLE_AUTOPILOT,maxPhaseAttempts:b.WORKFLOW.MAX_PHASE_ATTEMPTS,branchPattern:b.WORKFLOW.BRANCH_PATTERN,requireCleanWorkingTree:b.WORKFLOW.REQUIRE_CLEAN_WORKING_TREE,autoStageChanges:b.WORKFLOW.AUTO_STAGE_CHANGES,includeCoAuthor:b.WORKFLOW.INCLUDE_CO_AUTHOR,coAuthorName:b.WORKFLOW.CO_AUTHOR_NAME,coAuthorEmail:b.WORKFLOW.CO_AUTHOR_EMAIL,testThresholds:{minTests:b.WORKFLOW.MIN_TESTS,maxFailuresInGreen:b.WORKFLOW.MAX_FAILURES_IN_GREEN},commitMessageTemplate:b.WORKFLOW.COMMIT_MESSAGE_TEMPLATE,allowedCommitTypes:[...b.WORKFLOW.ALLOWED_COMMIT_TYPES],defaultCommitType:b.WORKFLOW.DEFAULT_COMMIT_TYPE,operationTimeout:b.WORKFLOW.OPERATION_TIMEOUT,enableActivityLogging:b.WORKFLOW.ENABLE_ACTIVITY_LOGGING,activityLogPath:b.WORKFLOW.ACTIVITY_LOG_PATH,enableStateBackup:b.WORKFLOW.ENABLE_STATE_BACKUP,maxStateBackups:b.WORKFLOW.MAX_STATE_BACKUPS,abortOnMaxAttempts:b.WORKFLOW.ABORT_ON_MAX_ATTEMPTS},storage:{type:b.STORAGE.TYPE,encoding:b.STORAGE.ENCODING,enableBackup:!1,maxBackups:b.STORAGE.MAX_BACKUPS,enableCompression:!1,atomicOperations:!0},version:b.VERSION}}async loadLocalConfig(){try{let e=await i.readFile(this.localConfigPath,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return this.logger.debug(`No local config.json found, using defaults`),null;throw new C(`Failed to load local configuration`,S.CONFIG_ERROR,{configPath:this.localConfigPath},e)}}async loadGlobalConfig(){return null}async hasLocalConfig(){try{return await i.access(this.localConfigPath),!0}catch{return!1}}async hasGlobalConfig(){try{return await i.access(this.globalConfigPath),!0}catch{return!1}}};const w={DEFAULTS:0,GLOBAL:1,LOCAL:2,ENVIRONMENT:3};var _e=class{configSources=[];addSource(e){this.configSources.push(e)}clearSources(){this.configSources=[]}merge(){let e=[...this.configSources].sort((e,t)=>e.precedence-t.precedence),t={};for(let n of e)t=this.deepMerge(t,n.config);return t}deepMerge(e,t){if(!t)return e;if(!e)return t;let n={...e};for(let e in t)t[e]!==null&&t[e]!==void 0&&(n[e]=typeof t[e]==`object`&&!Array.isArray(t[e])?this.deepMerge(n[e]||{},t[e]):t[e]);return n}getSources(){return[...this.configSources].sort((e,t)=>t.precedence-e.precedence)}hasSource(e){return this.configSources.some(t=>t.name===e)}removeSource(e){let t=this.configSources.length;return this.configSources=this.configSources.filter(t=>t.name!==e),this.configSources.length<t}},ve=class{localConfigPath;backupDir;logger=x(`ConfigPersistence`);constructor(e){this.localConfigPath=t.join(e,`.taskgraph`,`config.json`),this.backupDir=t.join(e,`.taskgraph`,`backups`)}async saveConfig(e,n={}){let{createBackup:r=!1,atomic:a=!0}=n;try{r&&await this.configExists()&&await this.createBackup();let n=t.dirname(this.localConfigPath);await i.mkdir(n,{recursive:!0});let o=JSON.stringify(e,null,2);if(a){let e=`${this.localConfigPath}.tmp`;await i.writeFile(e,o,`utf-8`),await i.rename(e,this.localConfigPath)}else await i.writeFile(this.localConfigPath,o,`utf-8`)}catch(e){throw new C(`Failed to save configuration`,S.CONFIG_ERROR,{configPath:this.localConfigPath},e)}}async createBackup(){try{await i.mkdir(this.backupDir,{recursive:!0});let e=(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g,`-`),n=t.join(this.backupDir,`config-${e}.json`),r=await i.readFile(this.localConfigPath,`utf-8`);return await i.writeFile(n,r,`utf-8`),await this.cleanOldBackups(),n}catch(e){throw this.logger.warn(`Failed to create backup:`,e),e}}async cleanOldBackups(e=5){try{let n=(await i.readdir(this.backupDir)).filter(e=>e.startsWith(`config-`)&&e.endsWith(`.json`)).sort().reverse().slice(e);for(let e of n)await i.unlink(t.join(this.backupDir,e))}catch(e){this.logger.warn(`Failed to clean old backups:`,e)}}async configExists(){try{return await i.access(this.localConfigPath),!0}catch{return!1}}async deleteConfig(){try{await i.unlink(this.localConfigPath)}catch(e){if(e.code!==`ENOENT`)throw new C(`Failed to delete configuration`,S.CONFIG_ERROR,{configPath:this.localConfigPath},e)}}async getBackups(){try{return(await i.readdir(this.backupDir)).filter(e=>e.startsWith(`config-`)&&e.endsWith(`.json`)).sort().reverse()}catch{return[]}}async restoreFromBackup(e){let n=t.join(this.backupDir,e);try{let e=await i.readFile(n,`utf-8`);await i.writeFile(this.localConfigPath,e,`utf-8`)}catch(e){throw new C(`Failed to restore from backup`,S.CONFIG_ERROR,{backupPath:n},e)}}},ye=class e{env;logger=x(`EnvironmentConfigProvider`);static DEFAULT_MAPPINGS=[{env:`TASKGRAPH_MODEL_MAIN`,path:[`models`,`main`]},{env:`TASKGRAPH_MODEL_RESEARCH`,path:[`models`,`research`]},{env:`TASKGRAPH_MODEL_FALLBACK`,path:[`models`,`fallback`]},{env:`TASKGRAPH_RESPONSE_LANGUAGE`,path:[`custom`,`responseLanguage`]}];static RUNTIME_STATE_MAPPINGS=[{env:`TASKGRAPH_TAG`,path:[`activeTag`],isRuntimeState:!0}];mappings;constructor(t,n=process.env){this.env=n,this.mappings=t||[...e.DEFAULT_MAPPINGS,...e.RUNTIME_STATE_MAPPINGS]}loadConfig(){let e={};for(let t of this.mappings){if(t.isRuntimeState)continue;let n=this.env[t.env];n&&(t.validate&&!t.validate(n)?this.logger.warn(`Invalid value for ${t.env}: ${n}`):this.setNestedProperty(e,t.path,n))}return e}getRuntimeState(){let e={};for(let t of this.mappings){if(!t.isRuntimeState)continue;let n=this.env[t.env];if(n){let r=t.path[t.path.length-1];e[r]=n}}return e}setNestedProperty(e,t,n){let r=t[t.length-1],i=t.slice(0,-1),a=e;for(let e of i)a[e]||(a[e]={}),a=a[e];a[r]=n}hasEnvVar(e){return this.env[e]!==void 0}getAllTaskGraphEnvVars(){let e={};for(let[t,n]of Object.entries(this.env))t.startsWith(`TASKGRAPH_`)&&n!==void 0&&(e[t]=n);return e}addMapping(e){this.mappings.push(e)}getMappings(){return[...this.mappings]}};function be(e){let n={};try{n=p(c.readFileSync(t.join(e,`.env`),`utf-8`))}catch(n){n.code!==`ENOENT`&&x(`project-env`).warn(`Could not read ${t.join(e,`.env`)}: ${n.message}`)}return{...n,...process.env}}var xe=class{env;stateFilePath;currentState;logger=x(`RuntimeStateManager`);constructor(e,n=process.env){this.env=n,this.stateFilePath=t.join(e,`.taskgraph`,`state.json`),this.currentState={currentTag:b.TAGS.DEFAULT_TAG}}async loadState(){try{let e=await i.readFile(this.stateFilePath,`utf-8`),t=JSON.parse(e),n={currentTag:t.currentTag||t.activeTag||b.TAGS.DEFAULT_TAG,lastUpdated:t.lastUpdated,metadata:t.metadata};return this.env.TASKGRAPH_TAG&&(n.currentTag=this.env.TASKGRAPH_TAG),this.currentState=n,n}catch(e){return e.code===`ENOENT`?(this.logger.debug(`No state.json found, using default state`),this.env.TASKGRAPH_TAG&&(this.currentState.currentTag=this.env.TASKGRAPH_TAG),this.currentState):(this.logger.warn(`Failed to load state file:`,e.message),this.currentState)}}async saveState(){let e=t.dirname(this.stateFilePath);try{await i.mkdir(e,{recursive:!0});let t={...this.currentState,lastUpdated:(/* @__PURE__ */ new Date()).toISOString()};await i.writeFile(this.stateFilePath,JSON.stringify(t,null,2),`utf-8`)}catch(e){throw new C(`Failed to save runtime state`,S.CONFIG_ERROR,{statePath:this.stateFilePath},e)}}getCurrentTag(){return this.currentState.currentTag}async setCurrentTag(e){this.currentState.currentTag=e,await this.saveState()}getState(){return{...this.currentState}}async updateMetadata(e){this.currentState.metadata={...this.currentState.metadata,...e},await this.saveState()}async clearState(){try{await i.unlink(this.stateFilePath)}catch(e){if(e.code!==`ENOENT`)throw e}this.currentState={currentTag:b.TAGS.DEFAULT_TAG}}},Se=class e{projectRoot;config={};initialized=!1;loader;merger;stateManager;persistence;envProvider;static async create(t){let n=new e(t);return await n.initialize(),n}constructor(e){this.projectRoot=e,this.loader=new ge(e),this.merger=new _e;let t=be(e);this.stateManager=new xe(e,t),this.persistence=new ve(e),this.envProvider=new ye(void 0,t)}async initialize(){if(this.initialized)return;this.merger.clearSources(),this.merger.addSource({name:`defaults`,config:this.loader.getDefaultConfig(),precedence:w.DEFAULTS});let e=await this.loader.loadGlobalConfig();e&&this.merger.addSource({name:`global`,config:e,precedence:w.GLOBAL});let t=await this.loader.loadLocalConfig();t&&this.merger.addSource({name:`local`,config:t,precedence:w.LOCAL});let n=this.envProvider.loadConfig();Object.keys(n).length>0&&this.merger.addSource({name:`environment`,config:n,precedence:w.ENVIRONMENT}),this.config=this.merger.merge(),await this.stateManager.loadState(),this.initialized=!0}getConfig(){return this.config}getStorageConfig(){let e=this.config.storage,t=e?.type;return t&&t!==`file`&&x(`ConfigManager`).warn(`Storage type "${t}" is no longer supported; using local file storage (.taskgraph/tasks/tasks.json).`),{type:`file`,basePath:e?.basePath??this.projectRoot}}getModelConfig(){return this.config.models||{main:b.MODELS.MAIN,fallback:b.MODELS.FALLBACK}}getResponseLanguage(){return this.config.custom?.responseLanguage||`English`}getProjectRoot(){return this.projectRoot}getActiveTag(){return this.stateManager.getCurrentTag()}async setActiveTag(e){await this.stateManager.setCurrentTag(e)}async updateConfig(e){Object.assign(this.config,e),await this.persistence.saveConfig(this.config),this.initialized=!1,await this.initialize()}async setResponseLanguage(e){this.config.custom||(this.config.custom={}),this.config.custom.responseLanguage=e,await this.persistence.saveConfig(this.config)}async saveConfig(){await this.persistence.saveConfig(this.config,{createBackup:!0,atomic:!0})}async reset(){await this.persistence.deleteConfig(),await this.stateManager.clearState(),this.initialized=!1,this.config={},await this.initialize()}getConfigSources(){return this.merger.getSources()}},T=class{projectPath;git;constructor(e){if(!e)throw Error(`Project path is required`);if(!m.isAbsolute(e))throw Error(`Project path must be an absolute path`);this.projectPath=m.normalize(e),this.git=_(this.projectPath)}async isGitRepository(){try{let e=m.join(this.projectPath,`.git`);if(await g.pathExists(e))return!0;try{return await this.git.revparse([`--git-dir`]),!0}catch{return!1}}catch{return!1}}async validateGitInstallation(){try{await this.git.version()}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Git is not installed or not accessible: ${t}`)}}async getGitVersion(){let e=await this.git.version();return{major:e.major,minor:e.minor,patch:typeof e.patch==`string`?parseInt(e.patch):e.patch||0,agent:e.agent}}async getRepositoryRoot(){try{let e=await this.git.revparse([`--show-toplevel`]);return m.normalize(e.trim())}catch{throw Error(`not a git repository: ${this.projectPath}`)}}async validateRepository(){if(!await this.isGitRepository())throw Error(`not a git repository: ${this.projectPath}`);try{await this.git.status()}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Repository validation failed: ${t}`)}}async ensureGitRepository(){if(!await this.isGitRepository())throw Error(`not a git repository: ${this.projectPath}\nPlease run this command from within a git repository, or initialize one with 'git init'.`)}async isWorkingTreeClean(){return(await this.git.status()).isClean()}async getStatus(){return await this.git.status()}async hasUncommittedChanges(){return!(await this.git.status()).isClean()}async hasStagedChanges(){return(await this.git.status()).staged.length>0}async hasUntrackedFiles(){return(await this.git.status()).not_added.length>0}async getStatusSummary(){let e=await this.git.status(),t=e.staged.length,n=e.modified.length,r=e.deleted.length,i=e.not_added.length,a=t+n+r+i;return{isClean:e.isClean(),staged:t,modified:n,deleted:r,untracked:i,totalChanges:a}}async ensureCleanWorkingTree(){if(!(await this.git.status()).isClean()){let e=await this.getStatusSummary();throw Error(`working tree is not clean: ${this.projectPath}\nStaged: ${e.staged}, Modified: ${e.modified}, Deleted: ${e.deleted}, Untracked: ${e.untracked}\nPlease commit or stash your changes before proceeding.`)}}async getCurrentBranch(){return(await this.git.status()).current||`HEAD`}async listBranches(){let e=await this.git.branchLocal();return Object.keys(e.branches)}async branchExists(e){return(await this.listBranches()).includes(e)}async createBranch(e,t={}){if(await this.branchExists(e))throw Error(`branch already exists: ${e}`);t.checkout&&await this.ensureCleanWorkingTree(),await this.git.branch([e]),t.checkout&&await this.git.checkout(e)}async checkoutBranch(e,t={}){if(!await this.branchExists(e))throw Error(`branch does not exist: ${e}`);t.force||await this.ensureCleanWorkingTree();let n=t.force?[`-f`,e]:[e];await this.git.checkout(n)}async createAndCheckoutBranch(e){if(await this.ensureCleanWorkingTree(),await this.branchExists(e))throw Error(`branch already exists: ${e}`);await this.git.checkoutLocalBranch(e)}async deleteBranch(e,t={}){if(!await this.branchExists(e))throw Error(`branch does not exist: ${e}`);if(await this.getCurrentBranch()===e)throw Error(`cannot delete current branch: ${e}`);let n=t.force?[`-D`,e]:[`-d`,e];await this.git.branch(n)}async stageFiles(e){await this.git.add(e)}async unstageFiles(e){await this.git.reset([`HEAD`,`--`,...e])}async createCommit(e,t={}){if(t.enforceNonDefaultBranch&&!t.force){let e=await this.getCurrentBranch();if([`main`,`master`,`develop`].includes(e))throw Error(`cannot commit to default branch: ${e}\nPlease create a feature branch or use force option.`)}if(!t.allowEmpty&&!await this.hasStagedChanges())throw Error(`no staged changes to commit`);let n=[`commit`];if(n.push(`-m`,e),t.metadata){n.push(`-m`,``);for(let[e,r]of Object.entries(t.metadata))n.push(`-m`,`[${e}:${r}]`)}n.push(`--no-gpg-sign`),t.allowEmpty&&n.push(`--allow-empty`),await this.git.raw(n)}async getCommitLog(e={}){let t={format:{hash:`%H`,date:`%ai`,message:`%B`,author_name:`%an`,author_email:`%ae`}};return e.maxCount&&(t.maxCount=e.maxCount),[...(await this.git.log(t)).all]}async getLastCommit(){return(await this.git.log({maxCount:1,format:{hash:`%H`,date:`%ai`,message:`%B`,author_name:`%an`,author_email:`%ae`}})).latest}async getDefaultBranch(){let e=await this.getCurrentBranch(),t=[`main`,`master`,`develop`];if(t.includes(e))return e;let n=await this.listBranches();for(let e of t)if(n.includes(e))return e;return`main`}async isDefaultBranch(e){return[`main`,`master`,`develop`].includes(e)}async isOnDefaultBranch(){let e=await this.getCurrentBranch();return await this.isDefaultBranch(e)}async ensureNotOnDefaultBranch(){if(await this.isOnDefaultBranch()){let e=await this.getCurrentBranch();throw Error(`currently on default branch: ${e}\nPlease create a feature branch before proceeding.`)}}async hasRemote(){return(await this.git.getRemotes()).length>0}async getRemotes(){return await this.git.getRemotes(!0)}};const Ce=[[`**/*.test.*`,`test`],[`**/*.spec.*`,`test`],[`**/test/**`,`test`],[`**/tests/**`,`test`],[`**/__tests__/**`,`test`],[`**/package-lock.json`,`deps`],[`package-lock.json`,`deps`],[`**/pnpm-lock.yaml`,`deps`],[`pnpm-lock.yaml`,`deps`],[`**/yarn.lock`,`deps`],[`yarn.lock`,`deps`],[`**/package.json`,`config`],[`package.json`,`config`],[`**/tsconfig*.json`,`config`],[`tsconfig*.json`,`config`],[`**/.eslintrc*`,`config`],[`.eslintrc*`,`config`],[`**/vite.config.*`,`config`],[`vite.config.*`,`config`],[`**/vitest.config.*`,`config`],[`vitest.config.*`,`config`],[`packages/cli/**`,`cli`],[`packages/tm-core/**`,`core`],[`packages/mcp-server/**`,`mcp`],[`**/workflow/**`,`workflow`],[`**/git/**`,`git`],[`**/storage/**`,`storage`],[`**/auth/**`,`auth`],[`**/config/**`,`config`],[`**/*.md`,`docs`],[`**/docs/**`,`docs`],[`README*`,`docs`],[`CHANGELOG*`,`docs`]],we={core:100,cli:90,mcp:85,workflow:80,git:75,storage:70,auth:65,config:60,test:50,docs:30,deps:20,repo:10};var Te=class{scopeMappings;scopePriorities;constructor(e,t){if(this.scopeMappings=[...Ce],e){let t=Object.entries(e);this.scopeMappings=[...t,...this.scopeMappings]}this.scopePriorities={...we,...t}}detectScope(e){if(e.length===0)return`repo`;let t=/* @__PURE__ */ new Map;for(let n of e){let e=this.getMatchingScope(n);e&&t.set(e,(t.get(e)||0)+1)}if(t.size===0)return`repo`;let n=`repo`,r=0;for(let[e,i]of t){let t=this.getScopePriority(e)*i;t>r&&(r=t,n=e)}return n}getAllMatchingScopes(e){let t=/* @__PURE__ */ new Set;for(let n of e){let e=this.getMatchingScope(n);e&&t.add(e)}return Array.from(t)}getMatchingScope(e){let t=e.replace(/\\/g,`/`);for(let[e,n]of this.scopeMappings)if(this.matchesPattern(t,e))return n;return null}getScopePriority(e){return this.scopePriorities[e]||0}matchesPattern(e,t){let n=t.replace(/\*\*/g,`§GLOBSTAR§`);return n=n.replace(/[.+^${}()|[\]\\]/g,`\\$&`),n=n.replace(/\*/g,`[^/]*`),n=n.replace(/§GLOBSTAR§/g,`.*`),RegExp(`^${n}$`).test(e)}};const Ee={commitMessage:`{{type}}{{#scope}}({{scope}}){{/scope}}{{#breaking}}!{{/breaking}}: {{description}}
|
|
2
|
+
|
|
3
|
+
{{#body}}{{body}}
|
|
4
|
+
|
|
5
|
+
{{/body}}{{#taskId}}Task: {{taskId}}{{/taskId}}{{#phase}}
|
|
6
|
+
Phase: {{phase}}{{/phase}}{{#testsPassing}}
|
|
7
|
+
Tests: {{testsPassing}} passing{{#testsFailing}}, {{testsFailing}} failing{{/testsFailing}}{{/testsPassing}}`};var De=class{templates;preservePlaceholders;constructor(e={}){let t=`customTemplates`in e||`preservePlaceholders`in e?e:{customTemplates:e};this.templates={...Ee,...t.customTemplates||{}},this.preservePlaceholders=t.preservePlaceholders??!1}render(e,t,n){let r=n===void 0?this.templates[e]:n;if(r===void 0)throw Error(`Template "${e}" not found`);return this.substituteVariables(r,t)}setTemplate(e,t){this.templates[e]=t}getTemplate(e){return this.templates[e]}hasTemplate(e){return e in this.templates}validateTemplate(e,t){let n=this.extractVariables(e),r=t.filter(e=>!n.includes(e));return{isValid:r.length===0,missingVars:r}}extractVariables(e){let t=e.matchAll(/\{\{\s*([^}#/\s]+)\s*\}\}/g),n=/* @__PURE__ */ new Set;for(let e of t)n.add(e[1]);return Array.from(n)}substituteVariables(e,t){let n=e;return n=this.processConditionalBlocks(n,t),n=n.replace(/\{\{\s*([^}#/\s]+)\s*\}\}/g,(e,n)=>{let r=t[n];return r==null?this.preservePlaceholders?`{{${n}}}`:``:String(r)}),n}processConditionalBlocks(e,t){let n=e,r=!0;for(;r;){let e=n;n=n.replace(/\{\{#([^}]+)\}\}((?:(?!\{\{#).)*?)\{\{\/\1\}\}/gs,(e,n,r)=>{let i=t[n.trim()];return i!=null&&i!==!1&&i!==``?r:``}),r=n!==e}return n}};const Oe=[`feat`,`fix`,`docs`,`style`,`refactor`,`perf`,`test`,`build`,`ci`,`chore`,`revert`];var ke=class{templateEngine;scopeDetector;constructor(e,t,n){this.templateEngine=new De(e),this.scopeDetector=new Te(t,n)}generateMessage(e){let{type:t,description:n,changedFiles:r,scope:i,body:a,breaking:o=!1,taskId:s,phase:c,tag:l,testsPassing:u,testsFailing:d,coveragePercent:f}=e,p={type:t,scope:i??this.scopeDetector.detectScope(r),breaking:o?`!`:``,description:n,body:a,taskId:s,phase:c,tag:l,testsPassing:u,testsFailing:d,coveragePercent:f};return this.templateEngine.render(`commitMessage`,p)}validateConventionalCommit(e){let t=[],n=e.split(`
|
|
8
|
+
`)[0];if(!n)return t.push(`Missing commit message`),{isValid:!1,errors:t};let r=n.match(/^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/);if(!r)return t.push(`Invalid conventional commit format. Expected: type(scope): description`),{isValid:!1,errors:t};let[,i,,,a]=r;return Oe.includes(i)||t.push(`Invalid commit type "${i}". Must be one of: ${Oe.join(`, `)}`),(!a||a.trim().length===0)&&t.push(`Missing description`),{isValid:t.length===0,errors:t}}parseCommitMessage(e){let t=e.split(`
|
|
9
|
+
`),n=t[0].match(/^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/);if(!n)throw Error(`Invalid conventional commit format`);let[,r,i,a,o]=n,s=t.findIndex((e,t)=>t>0&&e===``),c=s===-1?void 0:t.slice(s+1).join(`
|
|
10
|
+
`).trim();return{type:r,scope:i,breaking:a===`!`,description:o,body:c}}getScopeDetector(){return this.scopeDetector}getTemplateEngine(){return this.templateEngine}},Ae=class{gitAdapter;commitGenerator;constructor(e){this.gitAdapter=new T(e),this.commitGenerator=new ke}async isGitRepository(){return this.gitAdapter.isGitRepository()}async ensureGitRepository(){return this.gitAdapter.ensureGitRepository()}async getRepositoryRoot(){return this.gitAdapter.getRepositoryRoot()}async isWorkingTreeClean(){return this.gitAdapter.isWorkingTreeClean()}async getStatus(){return this.gitAdapter.getStatus()}async getStatusSummary(){return this.gitAdapter.getStatusSummary()}async hasUncommittedChanges(){return this.gitAdapter.hasUncommittedChanges()}async hasStagedChanges(){return this.gitAdapter.hasStagedChanges()}async getCurrentBranch(){return this.gitAdapter.getCurrentBranch()}async listBranches(){return this.gitAdapter.listBranches()}async branchExists(e){return this.gitAdapter.branchExists(e)}async createBranch(e,t){return this.gitAdapter.createBranch(e,t)}async checkoutBranch(e,t){return this.gitAdapter.checkoutBranch(e,t)}async createAndCheckoutBranch(e){return this.gitAdapter.createAndCheckoutBranch(e)}async deleteBranch(e,t){return this.gitAdapter.deleteBranch(e,t)}async getDefaultBranch(){return this.gitAdapter.getDefaultBranch()}async isOnDefaultBranch(){return this.gitAdapter.isOnDefaultBranch()}async stageFiles(e){return this.gitAdapter.stageFiles(e)}async unstageFiles(e){return this.gitAdapter.unstageFiles(e)}async createCommit(e,t){return this.gitAdapter.createCommit(e,t)}async getCommitLog(e){return this.gitAdapter.getCommitLog(e)}async getLastCommit(){return this.gitAdapter.getLastCommit()}async hasRemote(){return this.gitAdapter.hasRemote()}async getRemotes(){return this.gitAdapter.getRemotes()}generateCommitMessage(e){return this.commitGenerator.generateMessage(e)}validateCommitMessage(e){return this.commitGenerator.validateConventionalCommit(e)}parseCommitMessage(e){return this.commitGenerator.parseCommitMessage(e)}};const E=te(ee);async function je(e){if(!e)throw Error(`projectRoot is required for isGitRepository`);try{return await E(`git rev-parse --git-dir`,{cwd:e}),!0}catch{return!1}}async function Me(e){if(!e)throw Error(`projectRoot is required for getCurrentBranch`);try{let{stdout:t}=await E(`git rev-parse --abbrev-ref HEAD`,{cwd:e});return t.trim()}catch{return null}}async function Ne(e){if(!e)throw Error(`projectRoot is required for getLocalBranches`);try{let{stdout:t}=await E(`git branch --format="%(refname:short)"`,{cwd:e,maxBuffer:10485760});return t.trim().split(`
|
|
11
|
+
`).filter(e=>e.length>0).map(e=>e.trim())}catch{return[]}}async function Pe(e){if(!e)throw Error(`projectRoot is required for getRemoteBranches`);try{let{stdout:t}=await E(`git branch -r --format="%(refname:short)"`,{cwd:e,maxBuffer:10485760}),n=t.trim().split(`
|
|
12
|
+
`).filter(e=>e.length>0&&!e.includes(`HEAD`)).map(e=>e.replace(/^[^/]+\//,``).trim());return Array.from(new Set(n))}catch{return[]}}async function D(e){try{return await E(`gh auth status`,e?{cwd:e}:{}),!0}catch{return!1}}async function Fe(e){if(!e)throw Error(`projectRoot is required for getGitHubRepoInfo`);try{let{stdout:t}=await E(`gh repo view --json name,owner,defaultBranchRef`,{cwd:e});return JSON.parse(t)}catch{return null}}async function Ie(e){if(!e)throw Error(`projectRoot is required for getDefaultBranch`);try{if(await D(e)){let t=await Fe(e);if(t&&t.defaultBranchRef)return t.defaultBranchRef.name}let t=(await E(`git remote`,{cwd:e})).stdout.trim().split(`
|
|
13
|
+
`).filter(Boolean);if(t.length>0){let n=t.includes(`origin`)?`origin`:t[0];try{let{stdout:t}=await E(`git remote show ${n}`,{cwd:e,maxBuffer:10485760}),r=t.match(/HEAD branch:\s+([^\s]+)/);if(r)return r[1].trim()}catch{}try{let{stdout:t}=await E(`git symbolic-ref refs/remotes/${n}/HEAD`,{cwd:e});return t.replace(`refs/remotes/${n}/`,``).trim()}catch{}}throw Error(`default-branch-not-found`)}catch{let t=[`main`,`master`],n=await Ne(e),r=await Pe(e);for(let e of t)if(n.includes(e)||r.includes(e))return e;return null}}const Le=/^\d+(\.\d+)*$/;function O(e){return Le.test(e)}const Re=y.string().min(1,`Task ID cannot be empty`).refine(O,{message:`Invalid task ID format. Expected numeric (e.g., '15') or subtask (e.g., '15.2')`}),ze=y.string().min(1,`Task ID(s) cannot be empty`).refine(e=>{let t=e.split(`,`).map(e=>e.trim()).filter(e=>e.length>0);return t.length>0&&t.every(O)},{message:`Invalid task ID format. Expected numeric (e.g., '15') or subtask (e.g., '15.2'). Multiple IDs should be comma-separated.`});function Be(e){let t=e.split(`,`).map(e=>e.trim()).filter(e=>e.length>0);if(t.length===0)throw Error(`No valid task IDs provided`);let n=t.filter(e=>!O(e));if(n.length>0)throw Error(`Invalid task ID format: ${n.join(`, `)}. Expected numeric (e.g., '15') or subtask (e.g., '15.2')`);return t}const Ve=/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/,He=e=>new Promise(t=>setTimeout(t,e));function k(e){return{number:e.number,url:e.html_url,title:e.title,body:e.body??``,labels:e.labels.map(e=>typeof e==`string`?e:e.name),state:e.state,stateReason:e.state_reason===`completed`||e.state_reason===`not_planned`?e.state_reason:void 0}}function Ue(e){let t=e,n=0,r={};for(;t.startsWith(`HTTP/`);){let e=t.search(/\r?\n\r?\n/),[i,...a]=(e===-1?t:t.slice(0,e)).split(/\r?\n/);n=Number(i.split(` `)[1]),r={};for(let e of a){let t=e.indexOf(`:`);t>0&&(r[e.slice(0,t).trim().toLowerCase()]=e.slice(t+1).trim())}t=e===-1?``:t.slice(e).replace(/^\r?\n\r?\n/,``)}let i=null;if(t.trim())try{i=JSON.parse(t)}catch{i=t}return{status:n,headers:r,data:i}}var We=class{repo;options;sleep;constructor(e,t={}){if(this.repo=e,this.options=t,!Ve.test(e))throw new C(`Invalid repository "${e}", expected owner/name`,S.INVALID_INPUT);this.sleep=t.sleep??He}async getRepo(){let{data:e}=await this.request(`GET`,`repos/${this.repo}`);return{fullName:e.full_name,url:e.html_url}}async listIssues(e){let t=[];for(let n=1;;n++){let r=`labels=${encodeURIComponent(e)}&state=all&per_page=100&page=${n}`,{data:i}=await this.request(`GET`,`repos/${this.repo}/issues?${r}`),a=i;if(t.push(...a.filter(e=>!e.pull_request).map(k)),a.length<100)return t}}async createIssue(e){let{data:t}=await this.request(`POST`,`repos/${this.repo}/issues`,e);return k(t)}async updateIssue(e,t){let{stateReason:n,...r}=t,i=n?{...r,state_reason:n}:r,{data:a}=await this.request(`PATCH`,`repos/${this.repo}/issues/${e}`,i);return k(a)}async ensureLabels(e){let t=/* @__PURE__ */ new Set;for(let e=1;;e++){let{data:n}=await this.request(`GET`,`repos/${this.repo}/labels?per_page=100&page=${e}`);for(let e of n)t.add(e.name.toLowerCase());if(n.length<100)break}for(let n of new Set(e))t.has(n.toLowerCase())||await this.request(`POST`,`repos/${this.repo}/labels`,{name:n,color:`5319e7`})}async request(e,t,n){let r=[`api`,`-i`,`-X`,e,t];n!==void 0&&r.push(`--input`,`-`);let i=n===void 0?void 0:JSON.stringify(n);for(let n=0;;n++){let{stdout:a,stderr:o}=await this.run(r,i),s=Ue(a);if(s.status>0&&s.status<400)return s;let c=Number(s.headers[`retry-after`]);if((s.status===403||s.status===429)&&c>0&&n<3){await this.sleep(Math.min(c*1e3,12e4));continue}let l=s.data&&s.data.message||o.trim()||`gh failed`;throw new C(`GitHub request ${e} ${t} failed (${s.status||`no response`}): ${l}`,S.API_ERROR,{operation:`gh-api`,resource:t})}}run(e,t){return new Promise(n=>{ne(`gh`,e,{cwd:this.options.cwd,maxBuffer:67108864},(e,t,r)=>{n({stdout:String(t??``),stderr:String(r??(e?e.message:``))})}).stdin?.end(t)})}};const A=`taskgraph`,Ge=/<!-- taskgraph:v1 tag=(\S*) id=(\S*) -->/;function Ke(e,t){return`<!-- taskgraph:v1 tag=${encodeURIComponent(e)} id=${encodeURIComponent(t)} -->`}function qe(e){let t=Ge.exec(e??``);if(!t)return null;try{return{tag:decodeURIComponent(t[1]),id:decodeURIComponent(t[2])}}catch{return null}}function j(e){return`${A}:${e}`.slice(0,50)}const Je=e=>e===`done`||e===`completed`;function Ye(e){return Je(e)?{state:`closed`,stateReason:`completed`}:e===`cancelled`?{state:`closed`,stateReason:`not_planned`}:{state:`open`}}function Xe(e,t){let n=String(e.id),r=Ye(e.status),i=[A];e.priority&&i.push(j(`priority/${e.priority}`)),r.state===`open`&&i.push(j(`status/${e.status}`)),i.push(j(`tag/${t.tag}`));let a=[Ke(t.tag,n)];e.description&&a.push(e.description);let o=(e.dependencies??[]).map(e=>t.issueNumbers?.get(String(e))).filter(e=>e!==void 0).map(e=>`Blocked by #${e}`);if(o.length&&a.push(o.join(`
|
|
14
|
+
`)),e.subtasks?.length){let t=e.subtasks.map(e=>`- [${Je(e.status)?`x`:` `}] ${n}.${e.id} ${e.title}`);a.push(`### Subtasks\n${t.join(`
|
|
15
|
+
`)}`)}return t.includeDetails&&(e.details&&a.push(`### Details\n${e.details}`),e.testStrategy&&a.push(`### Test strategy\n${e.testStrategy}`)),{title:`[${n}] ${e.title}`,body:a.join(`
|
|
16
|
+
|
|
17
|
+
`),labels:i.sort(),...r}}function M(e){let t=JSON.stringify([e.title,e.body,[...e.labels].sort(),e.state,e.stateReason??null]);return ae(`sha256`).update(t).digest(`hex`)}const N=(e,t)=>`${e}\u0000${t}`;function P(e,t){let n=e.metadata?.github;if(n&&typeof n.issueNumber==`number`)return n.repo===t?n:void 0}function Ze(e){let{repo:t,tasks:n,remote:r}=e,i=/* @__PURE__ */ new Map;for(let e of r){let t=qe(e.body);t&&i.set(N(t.tag,t.id),e)}let a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Set,s=n.map(({tag:e,task:n})=>{let r=String(n.id),s=P(n,t),c=s?void 0:i.get(N(e,r)),l=s?.issueNumber??c?.number;return l!==void 0&&(o.add(l),a.has(e)||a.set(e,/* @__PURE__ */ new Map),a.get(e)?.set(r,l)),{tag:e,task:n,link:s,recovered:c,number:l}}),c=new Set(s.filter(e=>e.number===void 0).map(e=>N(e.tag,String(e.task.id)))),l=s.map(({tag:t,task:n,link:r,recovered:i,number:o})=>{let s={tag:t,taskId:String(n.id),title:n.title};return o===void 0?{...s,action:`create`}:r?M(Xe(n,{tag:t,includeDetails:e.includeDetails,issueNumbers:a.get(t)}))===r.bodyHash?(n.dependencies??[]).some(e=>c.has(N(t,String(e))))?{...s,action:`update`,issueNumber:o,reason:`dependency issue not created yet`}:{...s,action:`skip`,issueNumber:o}:{...s,action:`update`,issueNumber:o,reason:`content changed`}:{...s,action:`update`,issueNumber:o,recovered:!0,url:i?.url,reason:`link recovered from issue marker`}}),u=e.scopeTags?new Set(e.scopeTags):void 0,d=[];for(let e of r){if(e.state!==`open`||o.has(e.number))continue;let t=qe(e.body);!t||u&&!u.has(t.tag)||d.push({issueNumber:e.number,url:e.url,title:e.title,tag:t.tag,taskId:t.id})}if(e.closeOrphans)for(let e of d)l.push({action:`close`,tag:e.tag??``,taskId:e.taskId??``,title:e.title,issueNumber:e.issueNumber,reason:`task no longer exists locally`});return{repo:t,steps:l,orphans:d}}const Qe=e=>new Promise(t=>setTimeout(t,e)),$e=e=>({...e,state:`open`,stateReason:void 0});async function et(e,t,n){let{client:r}=n,i=e.repo,a=n.sleep??Qe,o=n.now??(()=>/* @__PURE__ */ new Date),s=n.delayMs??400,c={created:0,updated:0,skipped:0,closed:0},l=new Map(t.map(e=>[`${e.tag}\u0000${e.task.id}`,e])),u=e=>l.get(`${e.tag}\u0000${e.taskId}`),d=/* @__PURE__ */ new Map,f=(e,t,n)=>{d.has(e)||d.set(e,/* @__PURE__ */ new Map),d.get(e)?.set(t,n)};for(let{tag:e,task:n}of t){let t=P(n,i);t&&f(e,String(n.id),t.issueNumber)}let p=({tag:e,task:t})=>Xe(t,{tag:e,includeDetails:n.includeDetails,issueNumbers:d.get(e)}),m=async({tag:e,task:t},r,a,s)=>{let c={repo:i,issueNumber:r,url:a,syncedAt:o().toISOString(),lastPushedStatus:t.status,bodyHash:s},l={...t.metadata,github:c};await n.tasks.update(t.id,{metadata:l},e),t.metadata=l},h=!1,g=async()=>{h&&s>0&&await a(s),h=!0},_=e.steps.filter(e=>e.action===`create`||e.action===`update`);for(let t of e.steps)t.action===`skip`&&c.skipped++;for(let e of _.filter(e=>e.recovered)){let t=u(e);t&&e.issueNumber!==void 0&&(f(e.tag,e.taskId,e.issueNumber),await m(t,e.issueNumber,e.url??``,``))}let ee=/* @__PURE__ */ new Set([A]);for(let e of _){let t=u(e);if(t)for(let e of p(t).labels)ee.add(e)}_.length>0&&await r.ensureLabels([...ee]);for(let e of _.filter(e=>e.action===`create`)){let t=u(e);if(!t)continue;let n=p(t);await g();let i=await r.createIssue({title:n.title,body:n.body,labels:n.labels});f(e.tag,e.taskId,i.number),await m(t,i.number,i.url,M($e(n))),c.created++}for(let e of _){let t=u(e);if(!t)continue;let n=P(t.task,i);if(!n)continue;let a=p(t),o=M(a);o!==n.bodyHash&&(await g(),await r.updateIssue(n.issueNumber,{title:a.title,body:a.body,labels:a.labels,state:a.state,stateReason:a.stateReason}),await m(t,n.issueNumber,n.url,o),e.action===`update`&&c.updated++)}for(let t of e.steps.filter(e=>e.action===`close`))t.issueNumber!==void 0&&(await g(),await r.updateIssue(t.issueNumber,{state:`closed`,stateReason:`not_planned`}),c.closed++);return c}const tt=[`pending`,`in-progress`,`deferred`,`blocked`,`review`],nt=e=>e===`completed`?`done`:e;function F(e,t){return nt(e)===nt(t)}function rt(e,t){if(e.state===`closed`)return e.stateReason===`not_planned`?`cancelled`:`done`;let n=e.labels.filter(e=>e.startsWith(`taskgraph:status/`)).map(e=>e.slice(17)).filter(e=>tt.includes(e));if(n.length===0)return`pending`;if(n.length>1&&t){let e=n.find(e=>!F(e,t));if(e)return e}return n[0]}function it(e){let t=new Map(e.remote.map(e=>[e.number,e])),n=e.tasks.map(({tag:n,task:r})=>{let i={tag:n,taskId:String(r.id),title:r.title},a=P(r,e.repo);if(!a)return{...i,action:`skip`,reason:`not linked, push first`};let o=t.get(a.issueNumber);if(!o)return{...i,action:`skip`,issueNumber:a.issueNumber,reason:`issue not found on GitHub`};let s=r.status,c=rt(o,a.lastPushedStatus),l={...i,issueNumber:a.issueNumber,from:s,to:c},u=!F(s,a.lastPushedStatus);return F(c,a.lastPushedStatus)?{...l,action:`skip`,reason:u?`changed locally, push to update GitHub`:`unchanged`}:u?F(s,c)?{...l,action:`sync`,reason:`already in sync`}:{...l,action:`conflict`,reason:`changed on both sides, the local status is kept`}:{...l,action:`apply`}});return{repo:e.repo,steps:n}}var at=class{projectRoot;options;tasksDomain;constructor(e,t={}){this.projectRoot=e,this.options=t}setTasksDomain(e){this.tasksDomain=e}async status(e={}){let t=await D(this.projectRoot),n=e.repo??null;!n&&t&&(n=await this.detectRepo());let r=await this.loadTasks(),i=r.filter(e=>e.task.metadata?.github).length;return{ghAvailable:t,repo:n,linkedTasks:i,unlinkedTasks:r.length-i}}async planPush(e={}){let{plan:t}=await this.prepare(e);return t}async push(e={}){let{plan:t,client:n,tasks:r}=await this.prepare(e),i={created:0,updated:0,skipped:0,closed:0};if(e.dryRun){for(let e of t.steps){let t={create:`created`,update:`updated`,skip:`skipped`,close:`closed`}[e.action];i[t]++}return{dryRun:!0,plan:t,...i,orphans:t.orphans}}return{dryRun:!1,plan:t,...await et(t,r,{client:n,tasks:this.requireTasks(),includeDetails:e.includeDetails,delayMs:e.delayMs,sleep:this.options.sleep,now:this.options.now}),orphans:t.orphans}}async planPull(e={}){let{plan:t}=await this.preparePull(e);return t}async pull(e={}){let{plan:t,tasks:n}=await this.preparePull(e),r={applied:0,synced:0,conflicts:0,skipped:0},i=new Map(n.map(e=>[`${e.tag}\u0000${e.task.id}`,e]));for(let n of t.steps)if(n.action===`conflict`)r.conflicts++;else if(n.action===`skip`)r.skipped++;else{let a=i.get(`${n.tag}\u0000${n.taskId}`);!e.dryRun&&a&&await this.recordPull(t.repo,n,a),n.action===`apply`?r.applied++:r.synced++}return{dryRun:!!e.dryRun,plan:t,...r}}async unlink(e,t={}){let n=String(e),r=(await this.loadTasks(t.tag)).filter(e=>String(e.task.id)===n);if(r.length===0)throw new C(`Task ${n} not found${t.tag?` in tag ${t.tag}`:``}`,S.TASK_NOT_FOUND);let i=r.filter(e=>e.task.metadata?.github);if(i.length===0)throw new C(`Task ${n} is not linked to a GitHub issue`,S.VALIDATION_ERROR);if(i.length>1)throw new C(`Task ${n} is linked in several tags (${i.map(e=>e.tag).join(`, `)}); pass --tag to choose one`,S.VALIDATION_ERROR);let{tag:a,task:o}=i[0],{github:s,...c}=o.metadata,l=s;return await this.requireTasks().update(o.id,{metadata:c},a),{tag:a,taskId:n,issueNumber:l.issueNumber,url:l.url}}async scope(e){let t=e.repo??await this.detectRepo();if(!t)throw new C("Could not determine the GitHub repository. Pass a repo as owner/name or run `gh auth login` in a GitHub checkout.",S.MISSING_CONFIGURATION);let n=this.options.clientFactory?this.options.clientFactory(t):new We(t,{cwd:this.projectRoot}),r=this.resolveTag(e),i=await this.loadTasks(r),a=await n.listIssues(A),o=this.resolveTaskIds(e.taskIds);if(o){let e=new Set(i.map(e=>String(e.task.id))),t=o.filter(t=>!e.has(t));if(t.length>0)throw new C(`Task ${t.join(`, `)} not found`,S.TASK_NOT_FOUND)}return{repo:t,client:n,tag:r,tasks:i,remote:a,wanted:o}}async prepare(e){let{repo:t,client:n,tag:r,tasks:i,remote:a,wanted:o}=await this.scope(e),s=Ze({repo:t,tasks:i,remote:a,includeDetails:e.includeDetails,closeOrphans:e.closeOrphans,scopeTags:r?[r]:void 0});return{plan:o?{...s,steps:s.steps.filter(e=>e.action===`close`||o.includes(e.taskId))}:s,client:n,tasks:i}}async preparePull(e){let{repo:t,tasks:n,remote:r,wanted:i}=await this.scope(e),a=i?n.filter(e=>i.includes(String(e.task.id))):n;return{plan:it({repo:t,tasks:a,remote:r}),tasks:a}}async recordPull(e,t,{task:n}){let r=P(n,e);if(!r||!t.to)return;let i=this.requireTasks();t.action===`apply`&&await i.updateStatus(t.taskId,t.to,t.tag);let a={...r,lastPushedStatus:t.to,syncedAt:(this.options.now?.()??/* @__PURE__ */ new Date).toISOString()};await i.update(n.id,{metadata:{...n.metadata,github:a}},t.tag)}resolveTag(e){if(e.tag&&e.allTags)throw new C(`Use either tag or allTags, not both`,S.VALIDATION_ERROR);if(!e.allTags)return e.tag??this.options.getActiveTag?.()}resolveTaskIds(e){if(e!==void 0){if(Array.isArray(e))return e.map(String);try{return Be(e)}catch(e){throw new C(e instanceof Error?e.message:String(e),S.VALIDATION_ERROR)}}}async detectRepo(){let e=await Fe(this.projectRoot);return e?`${e.owner.login}/${e.name}`:null}requireTasks(){if(!this.tasksDomain)throw new C(`GitHubDomain is not wired to the tasks domain`,S.INTERNAL_ERROR);return this.tasksDomain}async loadTasks(e){let t=this.requireTasks(),n=e?[e]:(await t.getTagsWithStats()).tags.map(e=>e.name),r=[];for(let e of n){let{tasks:n}=await t.list({tag:e,includeSubtasks:!0});for(let t of n)r.push({tag:e,task:t})}return r}};const I={default:`SETUP: If taskgraph command not found, run: npm i -g taskgraph-ai
|
|
18
|
+
|
|
19
|
+
TASK: Implement ONE task/subtask from the TaskGraph backlog.
|
|
20
|
+
|
|
21
|
+
PROCESS:
|
|
22
|
+
1. Run taskgraph next (or use MCP) to get the next available task/subtask.
|
|
23
|
+
2. Read task details with taskgraph show <id>.
|
|
24
|
+
3. Implement following codebase patterns.
|
|
25
|
+
4. Write tests alongside implementation.
|
|
26
|
+
5. Run type check (e.g., \`npm run typecheck\`, \`tsc --noEmit\`).
|
|
27
|
+
6. Run tests (e.g., \`npm test\`, \`npm run test\`).
|
|
28
|
+
7. Mark complete: taskgraph set-status --id=<id> --status=done
|
|
29
|
+
8. Commit with message: feat(<scope>): <what was implemented>
|
|
30
|
+
9. Append super-concise notes to progress file: task ID, what was done, any learnings.
|
|
31
|
+
|
|
32
|
+
IMPORTANT:
|
|
33
|
+
- Complete ONLY ONE task per iteration.
|
|
34
|
+
- Keep changes small and focused.
|
|
35
|
+
- Do NOT start another task after completing one.
|
|
36
|
+
- If all tasks are done, output <loop-complete>ALL_DONE</loop-complete>.
|
|
37
|
+
- If blocked, output <loop-blocked>REASON</loop-blocked>.
|
|
38
|
+
`,"test-coverage":`# TaskGraph Loop - Test Coverage
|
|
39
|
+
|
|
40
|
+
Find uncovered code and write meaningful tests. ONE test per session.
|
|
41
|
+
|
|
42
|
+
## Files Available
|
|
43
|
+
|
|
44
|
+
- @.taskgraph/loop-progress.txt - Progress log (coverage %, what was tested)
|
|
45
|
+
|
|
46
|
+
## What Makes a Great Test
|
|
47
|
+
|
|
48
|
+
A great test covers behavior users depend on. It tests a feature that, if broken,
|
|
49
|
+
would frustrate or block users. It validates real workflows - not implementation details.
|
|
50
|
+
|
|
51
|
+
Do NOT write tests just to increase coverage. Use coverage as a guide to find
|
|
52
|
+
UNTESTED USER-FACING BEHAVIOR. If code is not worth testing (boilerplate, unreachable
|
|
53
|
+
branches, internal plumbing), add ignore comments instead of low-value tests.
|
|
54
|
+
|
|
55
|
+
## Process
|
|
56
|
+
|
|
57
|
+
1. Run coverage command (\`pnpm coverage\`, \`npm run coverage\`, etc.)
|
|
58
|
+
2. Identify the most important USER-FACING FEATURE that lacks tests
|
|
59
|
+
- Prioritize: error handling users hit, CLI commands, API endpoints, file parsing
|
|
60
|
+
- Deprioritize: internal utilities, edge cases users won't encounter, boilerplate
|
|
61
|
+
3. Write ONE meaningful test that validates the feature works correctly
|
|
62
|
+
4. Run coverage again - it should increase as a side effect of testing real behavior
|
|
63
|
+
5. Commit with message: \`test(<file>): <describe the user behavior being tested>\`
|
|
64
|
+
6. Append to progress file: what you tested, new coverage %, learnings
|
|
65
|
+
|
|
66
|
+
## Important
|
|
67
|
+
|
|
68
|
+
- Complete ONLY ONE test per session
|
|
69
|
+
- Keep tests focused on user-facing behavior
|
|
70
|
+
- Do NOT start another test after completing one
|
|
71
|
+
|
|
72
|
+
## Completion Criteria
|
|
73
|
+
|
|
74
|
+
- If coverage reaches target (or 100%), output: <loop-complete>COVERAGE_TARGET</loop-complete>
|
|
75
|
+
`,linting:`# TaskGraph Loop - Linting
|
|
76
|
+
|
|
77
|
+
Fix lint errors and type errors one by one. ONE fix per session.
|
|
78
|
+
|
|
79
|
+
## Files Available
|
|
80
|
+
|
|
81
|
+
- @.taskgraph/loop-progress.txt - Progress log (errors fixed, remaining count)
|
|
82
|
+
|
|
83
|
+
## Process
|
|
84
|
+
|
|
85
|
+
1. Run lint command (\`pnpm lint\`, \`npm run lint\`, \`eslint .\`, etc.)
|
|
86
|
+
2. Run type check (\`pnpm typecheck\`, \`tsc --noEmit\`, etc.)
|
|
87
|
+
3. Pick ONE error to fix - prioritize:
|
|
88
|
+
- Type errors (breaks builds)
|
|
89
|
+
- Security-related lint errors
|
|
90
|
+
- Errors in frequently-changed files
|
|
91
|
+
4. Fix the error with minimal changes - don't refactor surrounding code
|
|
92
|
+
5. Run lint/typecheck again to verify the fix doesn't introduce new errors
|
|
93
|
+
6. Commit with message: \`fix(<file>): <describe the lint/type error fixed>\`
|
|
94
|
+
7. Append to progress file: error fixed, remaining error count
|
|
95
|
+
|
|
96
|
+
## Important
|
|
97
|
+
|
|
98
|
+
- Complete ONLY ONE fix per session
|
|
99
|
+
- Keep changes minimal and focused
|
|
100
|
+
- Do NOT start another fix after completing one
|
|
101
|
+
|
|
102
|
+
## Completion Criteria
|
|
103
|
+
|
|
104
|
+
- If zero lint errors and zero type errors, output: <loop-complete>ZERO_ERRORS</loop-complete>
|
|
105
|
+
`,duplication:`# TaskGraph Loop - Duplication
|
|
106
|
+
|
|
107
|
+
Find duplicated code and refactor into shared utilities. ONE refactor per session.
|
|
108
|
+
|
|
109
|
+
## Files Available
|
|
110
|
+
|
|
111
|
+
- @.taskgraph/loop-progress.txt - Progress log (clones refactored, duplication %)
|
|
112
|
+
|
|
113
|
+
## Process
|
|
114
|
+
|
|
115
|
+
1. Run duplication detection (\`npx jscpd .\`, or similar tool)
|
|
116
|
+
2. Review the report and pick ONE clone to refactor - prioritize:
|
|
117
|
+
- Larger clones (more lines = more maintenance burden)
|
|
118
|
+
- Clones in frequently-changed files
|
|
119
|
+
- Clones with slight variations (consolidate logic)
|
|
120
|
+
3. Extract the duplicated code into a shared utility/function
|
|
121
|
+
4. Update all clone locations to use the shared utility
|
|
122
|
+
5. Run tests to ensure behavior is preserved
|
|
123
|
+
6. Commit with message: \`refactor(<file>): extract <utility> to reduce duplication\`
|
|
124
|
+
7. Append to progress file: what was refactored, new duplication %
|
|
125
|
+
|
|
126
|
+
## Important
|
|
127
|
+
|
|
128
|
+
- Complete ONLY ONE refactor per session
|
|
129
|
+
- Keep changes focused on the specific duplication
|
|
130
|
+
- Do NOT start another refactor after completing one
|
|
131
|
+
|
|
132
|
+
## Completion Criteria
|
|
133
|
+
|
|
134
|
+
- If duplication below threshold (e.g., <3%), output: <loop-complete>LOW_DUPLICATION</loop-complete>
|
|
135
|
+
`,entropy:`# TaskGraph Loop - Entropy (Code Smells)
|
|
136
|
+
|
|
137
|
+
Find code smells and clean them up. ONE cleanup per session.
|
|
138
|
+
|
|
139
|
+
## Files Available
|
|
140
|
+
|
|
141
|
+
- @.taskgraph/loop-progress.txt - Progress log (smells fixed, areas cleaned)
|
|
142
|
+
|
|
143
|
+
## Code Smells to Target
|
|
144
|
+
|
|
145
|
+
- Long functions (>60 lines) - extract into smaller functions
|
|
146
|
+
- Deep nesting (>3 levels) - use early returns, extract conditions
|
|
147
|
+
- Large files (>500 lines) - split into focused modules
|
|
148
|
+
- Magic numbers - extract into named constants
|
|
149
|
+
- Complex conditionals - extract into well-named functions
|
|
150
|
+
- God classes - split responsibilities
|
|
151
|
+
|
|
152
|
+
## Process
|
|
153
|
+
|
|
154
|
+
1. Scan the codebase for code smells (use your judgment or tools like \`complexity-report\`)
|
|
155
|
+
2. Pick ONE smell to fix - prioritize:
|
|
156
|
+
- Smells in frequently-changed files
|
|
157
|
+
- Smells that hurt readability the most
|
|
158
|
+
- Smells in critical paths (authentication, payments, etc.)
|
|
159
|
+
3. Refactor with minimal changes - don't over-engineer
|
|
160
|
+
4. Run tests to ensure behavior is preserved
|
|
161
|
+
5. Commit with message: \`refactor(<file>): <describe the cleanup>\`
|
|
162
|
+
6. Append to progress file: what was cleaned, smell type
|
|
163
|
+
|
|
164
|
+
## Important
|
|
165
|
+
|
|
166
|
+
- Complete ONLY ONE cleanup per session
|
|
167
|
+
- Keep refactoring focused and minimal
|
|
168
|
+
- Do NOT start another cleanup after completing one
|
|
169
|
+
|
|
170
|
+
## Completion Criteria
|
|
171
|
+
|
|
172
|
+
- If no significant smells remain, output: <loop-complete>LOW_ENTROPY</loop-complete>
|
|
173
|
+
`},ot=Object.keys(I);function st(e){return I[e]}function L(e){return e in I}const R=e=>t.join(e,`.taskgraph`,`loop-trusted.json`);function ct(e){try{let t=JSON.parse(c.readFileSync(R(e),`utf8`));return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch{return[]}}function lt(e){try{return c.realpathSync(e)}catch{return t.resolve(e)}}function ut(e,t=oe.homedir()){return ct(t).includes(lt(e))}function dt(e,n=oe.homedir()){let r=lt(e),i=ct(n);if(i.includes(r))return;c.mkdirSync(t.dirname(R(n)),{recursive:!0});let a=`${R(n)}.${process.pid}.tmp`;c.writeFileSync(a,JSON.stringify([...i,r],null,2)),c.renameSync(a,R(n))}var ft=class{projectRoot;logger=x(`LoopService`);_isRunning=!1;constructor(e){this.projectRoot=e.projectRoot}getProjectRoot(){return this.projectRoot}get isRunning(){return this._isRunning}checkSandboxAuth(){let e=ie(`docker`,[`sandbox`,`run`,`claude`,`-p`,`Say OK`],{cwd:this.projectRoot,timeout:3e4,encoding:`utf-8`,stdio:[`inherit`,`pipe`,`pipe`]});return e.error?e.error.code===`ENOENT`?{ready:!1,error:`Docker is not installed. Install Docker Desktop to use --sandbox mode.`}:{ready:!1,error:`Docker error: ${e.error.message}`}:{ready:((e.stdout||``)+(e.stderr||``)).toLowerCase().includes(`ok`)}}runInteractiveAuth(){let e=ie(`docker`,[`sandbox`,`run`,`claude`,`You're authenticated! Press Ctrl+C to continue.`],{cwd:this.projectRoot,stdio:`inherit`});return e.error?e.error.code===`ENOENT`?{success:!1,error:`Docker is not installed. Install Docker Desktop to use --sandbox mode.`}:{success:!1,error:`Docker error: ${e.error.message}`}:e.status===null?{success:!1,error:`Docker terminated abnormally (no exit code)`}:e.status===0?{success:!0}:{success:!1,error:`Docker exited with code ${e.status}`}}async run(e){if(e.verbose&&e.sandbox){let t=`Verbose mode is not supported with sandbox mode. Use --verbose without --sandbox, or remove --verbose.`;return this.reportError(e.callbacks,t),{iterations:[],totalIterations:0,tasksCompleted:0,finalStatus:`error`,errorMessage:t}}this._isRunning=!0;let t=[],n=0;await this.initProgressFile(e);for(let r=1;r<=e.iterations&&this._isRunning;r++){e.callbacks?.onIterationStart?.(r,e.iterations);let i=await this.buildPrompt(e,r),a=await this.executeIteration(i,r,e.sandbox??!1,e.includeOutput??!1,e.verbose??!1,e.callbacks);if(t.push(a),e.callbacks?.onIterationEnd?.(a),a.status===`complete`)return this.finalize(e,t,n+1,`all_complete`);if(a.status===`blocked`)return this.finalize(e,t,n,`blocked`);a.status===`success`&&n++,r<e.iterations&&e.sleepSeconds>0&&await new Promise(t=>setTimeout(t,e.sleepSeconds*1e3))}return this.finalize(e,t,n,`max_iterations`)}stop(){this._isRunning=!1}async finalize(e,t,n,r){this._isRunning=!1;let i={iterations:t,totalIterations:t.length,tasksCompleted:n,finalStatus:r};return await this.appendFinalSummary(e.progressFile,i),i}reportError(e,t,n=`error`){e?.onError?e.onError(t,n):n===`warning`?this.logger.warn(t):this.logger.error(t)}async initProgressFile(e){await o(t.dirname(e.progressFile),{recursive:!0});let n=[`# TaskGraph Loop Progress`,`# Started: ${(/* @__PURE__ */ new Date()).toISOString()}`,`# Preset: ${e.prompt}`,`# Max Iterations: ${e.iterations}`,...e.tag?[`# Tag: ${e.tag}`]:[],``,`---`,``];await a(e.progressFile,`
|
|
174
|
+
`+n.join(`
|
|
175
|
+
`)+`
|
|
176
|
+
`,`utf-8`)}async appendFinalSummary(e,t){await a(e,`
|
|
177
|
+
---
|
|
178
|
+
# Loop Complete: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
179
|
+
- Total iterations: ${t.totalIterations}
|
|
180
|
+
- Tasks completed: ${t.tasksCompleted}
|
|
181
|
+
- Final status: ${t.finalStatus}
|
|
182
|
+
`,`utf-8`)}isPreset(e){return L(e)}async resolvePrompt(e){if(this.isPreset(e))return I[e];let t=await s(e,`utf-8`);if(!t.trim())throw Error(`Custom prompt file '${e}' is empty`);return t}buildContextHeader(e,t){let n=e.tag?` (tag: ${e.tag})`:``;return`@${e.progressFile} @CLAUDE.md
|
|
183
|
+
|
|
184
|
+
Loop iteration ${t} of ${e.iterations}${n}`}async buildPrompt(e,t){let n=await this.resolvePrompt(e.prompt);return`${this.buildContextHeader(e,t)}\n\n${n}`}parseCompletion(e,t){let n=e.match(/<loop-complete>([^<]*)<\/loop-complete>/i);if(n)return{status:`complete`,message:n[1].trim()};let r=e.match(/<loop-blocked>([^<]*)<\/loop-blocked>/i);return r?{status:`blocked`,message:r[1].trim()}:t===0?{status:`success`}:{status:`error`,message:`Exit code ${t}`}}async executeIteration(e,t,n,r=!1,i=!1,a){let o=Date.now(),s=n?`docker`:`claude`;if(i)return this.executeVerboseIteration(e,t,s,n,r,o,a);let c=this.buildCommandArgs(e,n,!1),l=ie(s,c,{cwd:this.projectRoot,encoding:`utf-8`,maxBuffer:52428800,stdio:[`inherit`,`pipe`,`pipe`]});if(l.error){let e=this.formatCommandError(l.error,s,n);return this.reportError(a,e),this.createErrorIteration(t,o,e)}let u=(l.stdout||``)+(l.stderr||``);if(u&&a?.onOutput?.(u),l.status===null){let e=`Command terminated abnormally (no exit code)`;return this.reportError(a,e),this.createErrorIteration(t,o,e)}let{status:d,message:f}=this.parseCompletion(u,l.status);return{iteration:t,status:d,duration:Date.now()-o,message:f,...r&&{output:u}}}executeVerboseIteration(e,t,n,r,i,a,o){let s=this.buildCommandArgs(e,r,!0);return new Promise(e=>{let c=!1,l=t=>{c||(c=!0,e(t))},u=re(n,s,{cwd:this.projectRoot,stdio:[`inherit`,`pipe`,`pipe`]}),d=!1,f=``,p=``,m=e=>{if(e.startsWith(`{`))try{let t=JSON.parse(e);if(!this.isValidStreamEvent(t))return;this.handleStreamEvent(t,o),t.type===`result`&&(f=typeof t.result==`string`?t.result:``)}catch(t){if(e.trim().startsWith(`{`)){let n=`Failed to parse JSON event: ${t instanceof Error?t.message:`Unknown error`}. Line: ${e.substring(0,100)}...`;this.reportError(o,n,`warning`)}}};u.stdout?(u.stdout.on(`data`,e=>{try{let t=this.processBufferedLines(p,e.toString(`utf-8`));p=t.remaining;for(let e of t.complete)m(e)}catch(e){this.reportError(o,`Failed to process stdout data: ${e instanceof Error?e.message:`Unknown error`}`,`warning`)}}),u.stdout.on(`end`,()=>{d=!0,p&&=(m(p),``)}),u.stderr?.on(`data`,e=>{let n=e.toString(`utf-8`);o?.onStderr?.(t,n)}),u.on(`error`,e=>{let i=this.formatCommandError(e,n,r);if(this.reportError(o,i),u.stdout?.removeAllListeners(),u.stderr?.removeAllListeners(),!u.killed)try{u.kill(`SIGTERM`)}catch{}l(this.createErrorIteration(t,a,i))}),u.on(`close`,e=>{if(!d&&p&&m(p),e===null){let e=`Command terminated abnormally (no exit code)`;this.reportError(o,e),l(this.createErrorIteration(t,a,e));return}let{status:n,message:r}=this.parseCompletion(f,e);l({iteration:t,status:n,duration:Date.now()-a,message:r,...i&&{output:f}})})):l(this.createErrorIteration(t,a,`Failed to capture stdout from child process`))})}isValidStreamEvent(e){if(!e||typeof e!=`object`)return!1;let t=e;if(!(`type`in t)||typeof t.type!=`string`)return!1;if(`message`in t&&t.message!==void 0){if(typeof t.message!=`object`||t.message===null)return!1;let e=t.message;if(`content`in e&&!Array.isArray(e.content))return!1}return!0}buildCommandArgs(e,t,n){if(t)return[`sandbox`,`run`,`claude`,`-p`,e];let r=[`-p`,e,`--dangerously-skip-permissions`];return n&&r.push(`--output-format`,`stream-json`,`--verbose`),r}formatCommandError(e,t,n){return e.code===`ENOENT`?n?`Docker is not installed. Install Docker Desktop to use --sandbox mode.`:`Claude CLI is not installed. Install with: npm install -g @anthropic-ai/claude-code`:e.code===`EACCES`?`Permission denied executing '${t}'`:`Failed to execute '${t}': ${e.message}`}createErrorIteration(e,t,n){return{iteration:e,status:`error`,duration:Date.now()-t,message:n}}handleStreamEvent(e,t){if(e.type===`assistant`&&e.message?.content)for(let n of e.message.content)n.type===`text`&&n.text?t?.onText?.(n.text):n.type===`tool_use`&&n.name&&t?.onToolUse?.(n.name)}processBufferedLines(e,t){let n=(e+t).split(`
|
|
185
|
+
`);return{complete:n.slice(0,-1),remaining:n[n.length-1]}}},pt=class{logger=x(`LoopDomain`);loopService=null;projectRoot;constructor(e){this.projectRoot=e.getProjectRoot()}checkSandboxAuth(){return new ft({projectRoot:this.projectRoot}).checkSandboxAuth()}runInteractiveAuth(){return new ft({projectRoot:this.projectRoot}).runInteractiveAuth()}isTrusted(){return ut(this.projectRoot)}trust(){dt(this.projectRoot)}async run(e){if(!e.sandbox&&!e.trusted&&!this.isTrusted())throw Error(`loop runs Claude with permission prompts off in this project. Approve it first, or pass --trust (or use --sandbox).`);if(this.loopService?.isRunning)try{this.loopService.stop()}catch(e){this.logger.warn(`Failed to stop previous loop service:`,e)}let t=this.buildConfig(e);return this.loopService=new ft({projectRoot:this.projectRoot}),this.loopService.run(t)}stop(){this.loopService&&=(this.loopService.stop(),null)}getIsRunning(){return this.loopService?.isRunning??!1}isPreset(e){return L(e)}async resolvePrompt(e,t){if(L(e))return st(e);if(!t)throw Error(`Custom prompt file requires readFile callback: ${e}`);return t(e)}getAvailablePresets(){return[...ot]}resolveIterations(e){let{userIterations:t,preset:n,pendingTaskCount:r}=e;return t===void 0?n==="default"&&r!==void 0&&r>0?r:10:t}buildConfig(e){return{iterations:e.iterations??10,prompt:e.prompt??`default`,progressFile:e.progressFile??t.join(this.projectRoot,`.taskgraph`,`progress.txt`),sleepSeconds:e.sleepSeconds??5,tag:e.tag,sandbox:e.sandbox??!1,includeOutput:e.includeOutput??!1,verbose:e.verbose??!1,callbacks:e.callbacks}}},mt=class{detectFormat(e){return!e||typeof e!=`object`?`standard`:Object.keys(e).some(e=>e!==`tasks`&&e!==`metadata`)?`legacy`:`standard`}extractTasks(e,t){return e?this.detectFormat(e)===`legacy`?this.extractTasksFromLegacy(e,t):this.extractTasksFromStandard(e):[]}extractTasksFromLegacy(e,t){if(t in e)return e[t]?.tasks||[];let n=Object.keys(e).filter(e=>e!==`tasks`&&e!==`metadata`);return t===`master`&&n.length>0&&e[n[0]]?.tasks||[]}extractTasksFromStandard(e){return e?.tasks||[]}extractMetadata(e,t){return e?this.detectFormat(e)===`legacy`?this.extractMetadataFromLegacy(e,t):this.extractMetadataFromStandard(e):null}extractMetadataFromLegacy(e,t){if(t in e){let n=e[t];return!n?.metadata&&n?.tasks?this.generateMetadataFromTasks(n.tasks,t):n?.metadata||null}let n=Object.keys(e).filter(e=>e!==`tasks`&&e!==`metadata`);if(t===`master`&&n.length>0){let t=n[0],r=e[t];return!r?.metadata&&r?.tasks?this.generateMetadataFromTasks(r.tasks,t):r?.metadata||null}return null}extractMetadataFromStandard(e){return e?.metadata||null}extractTags(e){return e?this.detectFormat(e)===`legacy`?Object.keys(e).filter(e=>e!==`tasks`&&e!==`metadata`):[`master`]:[]}convertToSaveFormat(e,t,n,r){let i=r||`master`,a=this.normalizeTasks(e);return n&&this.detectFormat(n)===`legacy`?this.convertToLegacyFormat(a,t,i):this.convertToStandardFormat(a,t,r)}convertToLegacyFormat(e,t,n){return{[n]:{tasks:e,metadata:{...t,tags:[n]}}}}convertToStandardFormat(e,t,n){return{tasks:e,metadata:{...t,tags:n?[n]:[]}}}normalizeTasks(e){return e.map(e=>({...e,id:String(e.id),dependencies:e.dependencies?.map(e=>String(e))||[],subtasks:e.subtasks?.map(t=>({...t,id:Number(t.id),parentId:String(t.parentId??e.id)}))||[]}))}generateMetadataFromTasks(e,t){return{version:`1.0.0`,lastModified:(/* @__PURE__ */ new Date()).toISOString(),taskCount:e.length,completedCount:e.filter(e=>e.status===`done`).length,tags:[t]}}};function ht(e){return typeof e==`number`&&Number.isInteger(e)&&e>=1}function gt(e){let t=e?.schemaVersion;return ht(t)?t:1}function z(e,t){let n=ht(t?.schemaVersion)?t.schemaVersion:0;return{...e,schemaVersion:Math.max(1,n)}}const _t=/* @__PURE__ */ new Set;function vt(e,t,n){let r=e?.schemaVersion,i=r!==void 0&&!ht(r);if(!i&>(e)<=1)return;let a=`${n}::${t}`;if(_t.has(a))return;_t.add(a);let o=i?`an unrecognized schemaVersion (${JSON.stringify(r)})`:`schema version ${r}, newer than the 1 this version understands`;process.stderr.write(`Warning: tag "${t}" in ${n} has ${o}. Loading it anyway; consider upgrading taskgraph.\n`)}function yt(e,n){let r=t.relative(process.cwd(),e),i=r&&!r.startsWith(`..`)&&!t.isAbsolute(r)?r:e,a=/\(line (\d+) column (\d+)\)/.exec(n.message),o=a?` (line ${a[1]}, column ${a[2]})`:``;return new C(`Invalid JSON in file ${e}: ${n.message}`,S.JSON_PARSE_ERROR,{resource:e,userMessage:`${i} isn't valid JSON${o}`},n)}const bt={stale:1e4,retries:{retries:5,factor:2,minTimeout:100,maxTimeout:1e3},realpath:!1};var xt=class{writers=/* @__PURE__ */ new Map;getWriter(e){let t=this.writers.get(e);return t||(t=new ce(e),this.writers.set(e,t)),t}async readJson(e){try{let t=await i.readFile(e,`utf-8`);return JSON.parse(t)}catch(t){throw t.code===`ENOENT`?t:t instanceof SyntaxError?yt(e,t):Error(`Failed to read file ${e}: ${t.message}`)}}async acquireLock(e){let t=`${e}.lock`;try{let e=await i.lstat(t);if(!e.isDirectory()&&Date.now()-e.mtimeMs>bt.stale){let e=`${t}.stale.${process.pid}.${Date.now()}`;await i.rename(t,e).catch(()=>void 0),await i.rm(e,{force:!0})}}catch(e){if(e.code!==`ENOENT`)throw e}return se.lock(e,bt)}async writeJson(e,t){await this.ensureFileExists(e);let n=null;try{n=await this.acquireLock(e);let r=JSON.stringify(t,null,2);await this.getWriter(e).write(r)}finally{if(n)try{await n()}catch(t){(process.env.DEBUG||process.env.TASKGRAPH_DEBUG===`true`)&&console.warn(`[WARN] Lock release warning for ${e}: ${t.message}`)}}}async modifyJson(e,t){await this.ensureFileExists(e);let n=null;try{n=await this.acquireLock(e);let r;try{let t=await i.readFile(e,`utf-8`);r=JSON.parse(t)}catch(t){if(t.code===`ENOENT`)r={};else if(t instanceof SyntaxError){let n=await i.readFile(e,`utf-8`).catch(()=>``);if(n.trim()===``||n.trim()===`{}`)r={};else throw Error(`Corrupted JSON in ${e}: ${t.message}. File contains: ${n.substring(0,100)}...`)}else throw Error(`Failed to read ${e} for modification: ${t.message}`)}let a=await t(r),o=JSON.stringify(a,null,2);await this.getWriter(e).write(o)}finally{if(n)try{await n()}catch(t){(process.env.DEBUG||process.env.TASKGRAPH_DEBUG===`true`)&&console.warn(`[WARN] Lock release warning for ${e}: ${t.message}`)}}}async ensureFileExists(e){let n=t.dirname(e);await i.mkdir(n,{recursive:!0});try{await i.writeFile(e,`{}`,{flag:`wx`})}catch(e){if(e.code!==`EEXIST`)throw e}}async exists(e){try{return await i.access(e,l.F_OK),!0}catch{return!1}}async getStats(e){return i.stat(e)}async readDir(e){return i.readdir(e)}async ensureDir(e){try{await i.mkdir(e,{recursive:!0})}catch(t){throw Error(`Failed to create directory ${e}: ${t.message}`)}}async deleteFile(e){try{await i.unlink(e)}catch(t){if(t.code!==`ENOENT`)throw Error(`Failed to delete file ${e}: ${t.message}`)}}async moveFile(e,t){try{await i.rename(e,t)}catch(n){throw Error(`Failed to move file from ${e} to ${t}: ${n.message}`)}}async copyFile(e,t){try{await i.copyFile(e,t)}catch(n){throw Error(`Failed to copy file from ${e} to ${t}: ${n.message}`)}}async cleanup(){this.writers.clear()}},St=class{basePath;tasksDir;tasksFilePath;constructor(e){this.basePath=t.join(e,`.taskgraph`),this.tasksDir=t.join(this.basePath,`tasks`),this.tasksFilePath=t.join(this.tasksDir,`tasks.json`)}getBasePath(){return this.basePath}getTasksDir(){return this.tasksDir}getTasksPath(){return this.tasksFilePath}};function Ct(e){return typeof e==`number`&&e<=99}function B(e,t){return Ct(t)?`${e}.${t}`:String(t)}const V=x(`ComplexityReportManager`);var wt=class{projectRoot;reportCache=/* @__PURE__ */ new Map;constructor(e){this.projectRoot=e}getReportPath(e){let t=m.join(this.projectRoot,`.taskgraph`,`reports`),n=e&&e!==`master`?`_${e}`:``;return m.join(t,`task-complexity-report${n}.json`)}async loadReport(e){let t=e||`master`,n=t;if(this.reportCache.has(n))return this.reportCache.get(n);let r=this.getReportPath(e);try{await i.access(r);let e=await i.readFile(r,`utf-8`),a=JSON.parse(e);return!a.meta||!Array.isArray(a.complexityAnalysis)?(V.warn(`Invalid complexity report structure at ${r}, ignoring`),null):(this.reportCache.set(n,a),V.debug(`Loaded complexity report for tag '${t}' with ${a.complexityAnalysis.length} analyses`),a)}catch(e){return e.code===`ENOENT`?(V.debug(`No complexity report found for tag '${t}'`),null):(V.warn(`Failed to load complexity report for tag '${t}': ${e.message}`),null)}}async getComplexityForTask(e,t){let n=await this.loadReport(t);if(!n)return null;let r=n.complexityAnalysis.find(t=>String(t.taskId)===String(e));return r?{complexityScore:r.complexityScore,recommendedSubtasks:r.recommendedSubtasks,expansionPrompt:r.expansionPrompt,complexityReasoning:r.complexityReasoning}:null}async getComplexityForTasks(e,t){let n=/* @__PURE__ */ new Map,r=await this.loadReport(t);if(!r)return n;let i=/* @__PURE__ */ new Map;return r.complexityAnalysis.forEach(e=>{i.set(String(e.taskId),e)}),e.forEach(e=>{let t=i.get(String(e));t&&n.set(String(e),{complexityScore:t.complexityScore,recommendedSubtasks:t.recommendedSubtasks,expansionPrompt:t.expansionPrompt,complexityReasoning:t.complexityReasoning})}),n}clearCache(e){e?this.reportCache.delete(e):this.reportCache.clear()}async hasReport(e){let t=this.getReportPath(e);try{return await i.access(t),!0}catch{return!1}}};function Tt(e,t){return{version:`1.0.0`,lastModified:(/* @__PURE__ */ new Date()).toISOString(),taskCount:e.length,completedCount:e.filter(e=>e.status===`done`).length,tags:[t]}}function Et(e,t,n,r,i){let a=Dt(r);return e.detectFormat(t)===`legacy`||Object.keys(t).some(e=>e!==`tasks`&&e!==`metadata`)?(t[n]={tasks:a,metadata:z(i,t[n]?.metadata)},t):n===`master`?{tasks:a,metadata:z(i,t.metadata)}:{master:{tasks:t.tasks||[],metadata:z(t.metadata||i,t.metadata)},[n]:{tasks:a,metadata:z(i)}}}function Dt(e){return e.map(e=>({...e,id:String(e.id),dependencies:e.dependencies?.map(e=>String(e))||[],subtasks:e.subtasks?.map(t=>({...t,id:Number(t.id),parentId:String(t.parentId??e.id)}))||[]}))}var Ot=class{pathResolver;fileOps;formatHandler;reader;constructor(e,t,n,r){this.pathResolver=e,this.fileOps=t,this.formatHandler=n,this.reader=r}async createTag(e,t){let n=this.pathResolver.getTasksPath();try{await this.fileOps.modifyJson(n,n=>{if(this.formatHandler.detectFormat(n)===`legacy`){if(e in n)throw new C(`Tag ${e} already exists`,S.VALIDATION_ERROR);let r=[];return t?.copyFrom&&t.copyFrom in n&&n[t.copyFrom].tasks&&(r=JSON.parse(JSON.stringify(n[t.copyFrom].tasks))),n[e]={tasks:r,metadata:z({created:(/* @__PURE__ */ new Date()).toISOString(),updatedAt:(/* @__PURE__ */ new Date()).toISOString(),description:t?.description||`Tag created on ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`,tags:[e]})},n}{let r=n.tasks||[],i=n.metadata||{},a=[];return(t?.copyFrom===`master`||!t?.copyFrom)&&(a=JSON.parse(JSON.stringify(r))),{master:{tasks:r,metadata:z({...i,tags:[`master`]},i)},[e]:{tasks:a,metadata:z({created:(/* @__PURE__ */ new Date()).toISOString(),updatedAt:(/* @__PURE__ */ new Date()).toISOString(),description:t?.description||`Tag created on ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`,tags:[e]})}}}})}catch(e){throw e.code===`ENOENT`?Error(`Tasks file not found - initialize project first`):e}}async deleteTag(e){let t=this.pathResolver.getTasksPath();try{let n=!1;await this.fileOps.modifyJson(t,t=>{if(this.formatHandler.detectFormat(t)!==`legacy`&&e===`master`)return n=!0,t;if(this.formatHandler.detectFormat(t)===`legacy`){if(e in t)return delete t[e],t;throw Error(`Tag ${e} not found`)}throw Error(`Tag ${e} not found in standard format`)}),n&&await this.fileOps.deleteFile(t)}catch(t){throw t.code===`ENOENT`?Error(`Tag ${e} not found - file doesn't exist`):t}}async renameTag(e,t){let n=this.pathResolver.getTasksPath();try{await this.fileOps.modifyJson(n,n=>{if(this.formatHandler.detectFormat(n)===`legacy`){if(e in n)return n[t]=n[e],delete n[e],n[t].metadata&&(n[t].metadata.tags=[t]),n;throw Error(`Tag ${e} not found`)}if(e===`master`){let e=n.tasks||[],r=n.metadata||{};return{[t]:{tasks:e,metadata:z({...r,tags:[t]},r)}}}throw Error(`Tag ${e} not found in standard format`)})}catch(t){throw t.code===`ENOENT`?Error(`Tag ${e} not found - file doesn't exist`):t}}async getTagsWithStats(){let e=await this.reader.getAllTags(),t=await this.getActiveTagFromState(),n=await Promise.all(e.map(async e=>{try{let n=await this.reader.loadTasks(e),r={},i=0,a={totalSubtasks:0,subtasksByStatus:{}};n.forEach(e=>{let t=e.status||`pending`;r[t]=(r[t]||0)+1,t===`done`&&i++,e.subtasks&&e.subtasks.length>0&&(a.totalSubtasks+=e.subtasks.length,e.subtasks.forEach(e=>{let t=e.status||`pending`;a.subtasksByStatus[t]=(a.subtasksByStatus[t]||0)+1}))});let o=await this.reader.loadMetadata(e);return{name:e,isCurrent:e===t,taskCount:n.length,completedTasks:i,statusBreakdown:r,subtaskCounts:a.totalSubtasks>0?a:void 0,created:o?.created,description:o?.description}}catch{return{name:e,isCurrent:e===t,taskCount:0,completedTasks:0,statusBreakdown:{}}}}));return{tags:n,currentTag:t,totalTags:n.length}}async getActiveTagFromState(){try{let e=t.join(this.pathResolver.getBasePath(),`state.json`);return(await this.fileOps.readJson(e))?.currentTag||`master`}catch{return`master`}}};function H(e,t=e){return new C(e,S.TASK_NOT_FOUND,{userMessage:t})}var kt=class{formatHandler;fileOps;pathResolver;complexityManager;tagOps;constructor(e){this.formatHandler=new mt,this.fileOps=new xt,this.pathResolver=new St(e),this.complexityManager=new wt(e),this.tagOps=new Ot(this.pathResolver,this.fileOps,this.formatHandler,this)}async initialize(){await this.fileOps.ensureDir(this.pathResolver.getTasksDir())}async close(){await this.fileOps.cleanup()}async getStats(){let e=this.pathResolver.getTasksPath();try{let t=await this.fileOps.getStats(e),n=await this.fileOps.readJson(e),r=this.formatHandler.extractTags(n),i=0,a=r.map(e=>{let r=this.formatHandler.extractTasks(n,e).length;return i+=r,{tag:e,taskCount:r,lastModified:t.mtime.toISOString()}});return{totalTasks:i,totalTags:r.length,lastModified:t.mtime.toISOString(),storageSize:0,tagStats:a}}catch(e){if(e.code===`ENOENT`)return{totalTasks:0,totalTags:0,lastModified:(/* @__PURE__ */ new Date()).toISOString(),storageSize:0,tagStats:[]};throw Error(`Failed to get storage stats: ${e.message}`)}}async loadTasks(e,t){let n=this.pathResolver.getTasksPath(),r=e||`master`;try{let e=await this.fileOps.readJson(n),i=this.formatHandler.extractTasks(e,r);return vt(this.formatHandler.extractMetadata(e,r),r,n),t&&(t.status&&(i=i.filter(e=>e.status===t.status)),t.excludeSubtasks&&(i=i.map(e=>({...e,subtasks:[]})))),await this.enrichTasksWithComplexity(i,r)}catch(e){if(e.code===`ENOENT`)return[];throw e instanceof C?e:Error(`Failed to load tasks: ${e.message}`)}}async loadTask(e,t){let n=await this.loadTasks(t);if(e.includes(`.`)){let[t,r]=e.split(`.`),i=n.find(e=>String(e.id)===t);if(!i||!i.subtasks)return null;let a=i.subtasks.find(e=>String(e.id)===r);if(!a)return null;let o=a.dependencies?.map(e=>B(i.id,e))??[];return{...a,id:e,title:a.title||`Subtask ${r}`,description:a.description||``,status:a.status||`pending`,priority:a.priority||i.priority||`medium`,dependencies:o,details:a.details||``,testStrategy:a.testStrategy||``,subtasks:[],tags:i.tags||[],assignee:a.assignee||i.assignee,complexity:a.complexity||i.complexity,createdAt:a.createdAt||i.createdAt,updatedAt:a.updatedAt||i.updatedAt,parentTask:{id:i.id,title:i.title,status:i.status},isSubtask:!0}}return n.find(t=>String(t.id)===String(e))||null}async saveTasks(e,t){let n=this.pathResolver.getTasksPath(),r=t||`master`;await this.fileOps.ensureDir(this.pathResolver.getTasksDir());let i=Tt(e,r);await this.fileOps.modifyJson(n,t=>Et(this.formatHandler,t,r,e,i))}async mutateTasks(e,t){let n=this.pathResolver.getTasksPath(),r=e||`master`,i;return await this.fileOps.ensureDir(this.pathResolver.getTasksDir()),await this.fileOps.modifyJson(n,e=>{let n=t(this.formatHandler.extractTasks(e,r));return i=n.result,n.tasks===null?e:Et(this.formatHandler,e,r,n.tasks,Tt(n.tasks,r))}),i}async exists(e){let t=this.pathResolver.getTasksPath();return this.fileOps.exists(t)}async getAllTags(){try{let e=this.pathResolver.getTasksPath(),t=await this.fileOps.readJson(e);return this.formatHandler.extractTags(t)}catch(e){if(e.code===`ENOENT`)return[];throw Error(`Failed to get tags: ${e.message}`)}}async loadMetadata(e){let t=this.pathResolver.getTasksPath(),n=e||`master`;try{let e=await this.fileOps.readJson(t);return this.formatHandler.extractMetadata(e,n)}catch(e){if(e.code===`ENOENT`)return null;throw Error(`Failed to load metadata: ${e.message}`)}}async saveMetadata(e,t){let n=await this.loadTasks(t);await this.saveTasks(n,t)}async appendTasks(e,t){await this.mutateTasks(t,t=>({tasks:[...t,...e],result:void 0}))}async updateTask(e,t,n){await this.mutateTasks(n,n=>{let r=n.findIndex(t=>String(t.id)===String(e));if(r===-1)throw H(`Task ${e} not found`);let i=n[r],a=t.subtasks;t.subtasks&&i.subtasks&&(a=t.subtasks.map(e=>{let t=i.subtasks?.find(t=>String(t.id)===String(e.id)||e.title&&t.title===e.title);return t?.metadata||e.metadata?{...e,metadata:{...t?.metadata||{},...e.metadata||{}}}:e}));let o=[...n];return o[r]={...i,...t,...a&&{subtasks:a},id:String(e)},{tasks:o,result:void 0}})}async updateTaskStatus(e,t,n){return this.mutateTasks(n,n=>{if(e.includes(`.`))return this.updateSubtaskStatusInFile(n,e,t);let r=n.findIndex(t=>String(t.id)===String(e));if(r===-1)throw H(`Task ${e} not found`);let i=n[r].status,a={success:!0,oldStatus:i,newStatus:t,taskId:String(e)};if(i===t)return{tasks:null,result:a};let o=[...n];return o[r]={...n[r],status:t,updatedAt:(/* @__PURE__ */ new Date()).toISOString()},{tasks:o,result:a}})}updateSubtaskStatusInFile(e,t,n){let r=t.split(`.`);if(r.length!==2)throw Error(`Invalid subtask ID format: ${t}. Expected format: parentId.subtaskId`);let[i,a]=r,o=a.trim();if(!/^\d+$/.test(o))throw Error(`Invalid subtask ID: ${o}. Subtask ID must be a positive integer.`);let s=Number(o),c=e.findIndex(e=>String(e.id)===String(i));if(c===-1)throw H(`Parent task ${i} not found`,`Task ${i} not found`);let l={...e[c],subtasks:[...e[c].subtasks??[]]},u=l.subtasks.findIndex(e=>e.id===s||String(e.id)===o);if(u===-1)throw H(`Subtask ${t} not found in parent task ${i}`,`Subtask ${t} not found`);let d=l.subtasks[u].status||`pending`,f={success:!0,oldStatus:d,newStatus:n,taskId:t};if(d===n)return{tasks:null,result:f};let p=(/* @__PURE__ */ new Date()).toISOString();l.subtasks[u]={...l.subtasks[u],status:n,updatedAt:p};let m=l.subtasks,h=l.status;if(m.length>0){let e=e=>e.status||`pending`,t=t=>{let n=e(t);return n===`done`||n===`completed`},n=m.every(t),r=m.some(t=>e(t)===`in-progress`),i=m.some(t),a=m.every(t=>e(t)===`pending`);n?h=`done`:r||i?h=`in-progress`:a&&(h=`pending`)}let g=[...e];return g[c]={...l,...h===l.status?{}:{status:h},updatedAt:p},{tasks:g,result:f}}async deleteTask(e,t){await this.mutateTasks(t,t=>{let n=t.filter(t=>String(t.id)!==String(e));if(n.length===t.length)throw H(`Task ${e} not found`);return{tasks:n,result:void 0}})}async createTag(e,t){return this.tagOps.createTag(e,t)}async deleteTag(e){return this.tagOps.deleteTag(e)}async renameTag(e,t){return this.tagOps.renameTag(e,t)}async copyTag(e,t){let n=await this.loadTasks(e);if(n.length===0)throw Error(`Source tag ${e} not found or has no tasks`);await this.saveTasks(n,t)}async getTagsWithStats(){return this.tagOps.getTagsWithStats()}async watch(e,t){let n=this.pathResolver.getTasksPath(),r=t?.debounceMs??100;if(!await this.fileOps.exists(n))throw new C(`Tasks file not found. Initialize the project first.`,S.NOT_FOUND,{path:n});let i,a=!1,o=c.watch(n,(t,n)=>{a||n&&t===`change`&&(i&&clearTimeout(i),i=setTimeout(()=>{a||e({type:`change`,timestamp:/* @__PURE__ */ new Date})},r))});return o.on(`error`,t=>{a||e({type:`error`,timestamp:/* @__PURE__ */ new Date,error:t})}),{unsubscribe:()=>{a=!0,i&&clearTimeout(i),o.close()}}}async enrichTasksWithComplexity(e,t){let n=e.map(e=>e.id),r=await this.complexityManager.getComplexityForTasks(n,t);return r.size===0?e:e.map(e=>{let t=r.get(String(e.id));return t?{...e,complexity:t.complexityScore,recommendedSubtasks:t.recommendedSubtasks,expansionPrompt:t.expansionPrompt,complexityReasoning:t.complexityReasoning}:e})}},At=class{static async createFromStorageConfig(e,t){return new kt(e.basePath||t)}},jt=`taskgraph-ai`,Mt=`0.54.12`;const Nt={CLAUDE_CODE:`claude-code`},Pt=Object.values(Nt);[...Pt];const Ft=`.taskgraph/tasks/tasks.json`,It=[`.taskgraph`,`.taskgraph/config.json`,Ft],Lt=/* @__PURE__ */ `.git,.svn,.hg,.fossil,.github,.gitlab,.circleci,.travis.yml,.jenkins,.buildkite,.vscode,.idea,.project,.devcontainer,package-lock.json,yarn.lock,pnpm-lock.yaml,bun.lockb,bun.lock,deno.lock,deno.json,deno.jsonc,package.json,lerna.json,nx.json,turbo.json,rush.json,pnpm-workspace.yaml,Cargo.toml,Cargo.lock,go.mod,go.sum,go.work,pyproject.toml,setup.py,setup.cfg,poetry.lock,Pipfile,Pipfile.lock,uv.lock,Gemfile,Gemfile.lock,composer.json,composer.lock,build.gradle,build.gradle.kts,settings.gradle,settings.gradle.kts,pom.xml,build.sbt,project.clj,deps.edn,mix.exs,rebar.config,pubspec.yaml,Package.swift,CMakeLists.txt,Makefile,meson.build,BUILD.bazel,WORKSPACE,flake.nix,shell.nix,default.nix,Dockerfile,docker-compose.yml,docker-compose.yaml,Containerfile,kubernetes.yml,kubernetes.yaml,helm/Chart.yaml`.split(`,`),Rt=[`tasks.json`,...Lt,`requirements.txt`];[...It,...Rt];const zt=[`pending`,`in-progress`,`done`,`deferred`,`cancelled`,`blocked`,`review`],Bt=[`done`,`completed`,`cancelled`];function U(e){return Bt.includes(e)}const Vt=[`text`,`json`,`compact`],Ht={done:`✓`,completed:`✓`,"in-progress":`►`,blocked:`⭕`,pending:`○`,deferred:`⏸`,cancelled:`✗`,review:`👁`},W=(e,t)=>e.localeCompare(t,void 0,{numeric:!0});function G(e){let t=[];for(let n of e){let e=String(n.id);t.push({id:e,title:n.title,status:n.status,priority:n.priority,deps:(n.dependencies??[]).map(String)});for(let r of n.subtasks??[])t.push({id:`${e}.${r.id}`,title:r.title,status:r.status??`pending`,priority:r.priority??n.priority,deps:(r.dependencies??[]).map(t=>B(e,t))})}let n=/* @__PURE__ */ new Map;for(let e of t)n.set(e.id,{id:e.id,title:e.title,status:e.status,priority:e.priority,dependencies:[],missing:[],selfDependency:!1,dependents:[]});for(let e of t){let t=n.get(e.id);for(let r of new Set(e.deps)){if(r===e.id){t.selfDependency=!0;continue}let i=n.get(r);i?(t.dependencies.push(r),i.dependents.push(e.id)):t.missing.push(r)}}return{nodes:n}}const K=e=>!U(e.status);function Ut(e,t=G(e)){let{nodes:n}=t,r=/* @__PURE__ */ new Map,i=[];for(let e of n.values())K(e)?i.push(e.id):r.set(e.id,0);let a=qt(i,e=>n.get(e).dependents,e=>K(n.get(e))),o=/* @__PURE__ */ new Map;a.forEach((e,t)=>{for(let n of e)o.set(n,t)});let s=Math.ceil(a.length/32),c=[];return a.forEach((e,t)=>{let i=new Uint32Array(s);for(let r of e)for(let e of n.get(r).dependents){let n=o.get(e);if(n===void 0||n===t)continue;i[n>>5]|=1<<(n&31);let r=c[n];for(let e=0;e<s;e++)i[e]|=r[e]}let l=e.length-1;for(let e=0;e<t;e++)i[e>>5]&1<<(e&31)&&(l+=a[e].length);c.push(i);for(let t of e)r.set(t,l)}),r}function Wt(e,t,n=G(e)){let{nodes:r}=n,i=[];for(let[t,a]of Ut(e,n)){let e=r.get(t);a>0&&K(e)&&i.push({id:t,title:e.title,status:e.status,blocks:a})}return i.sort((e,t)=>t.blocks-e.blocks||W(e.id,t.id)),t===void 0?i:i.slice(0,Math.max(0,t))}function Gt(e){let{nodes:t}=G(e);return qt([...t.keys()],e=>t.get(e).dependencies).filter(e=>e.length>1).map(e=>e.sort(W)).sort((e,t)=>W(e[0],t[0]))}function Kt(e,t=G(e)){let{nodes:n}=t,r=[...n.values()].filter(K).map(e=>e.id),i=e=>K(n.get(e)),a=new Set(qt(r,e=>n.get(e).dependencies,i).filter(e=>e.length>1).flat()),o=[...a];for(let e=0;e<o.length;e++)for(let t of n.get(o[e]).dependents)i(t)&&!a.has(t)&&(a.add(t),o.push(t));let s=r.filter(e=>!a.has(e)),c=e=>n.get(e).dependencies.filter(e=>i(e)&&!a.has(e)),l=/* @__PURE__ */ new Map;for(let e of s)l.set(e,c(e).length);let u=s.filter(e=>l.get(e)===0);for(let e=0;e<u.length;e++)for(let t of n.get(u[e]).dependents){if(!l.has(t))continue;let e=l.get(t)-1;l.set(t,e),e===0&&u.push(t)}let d=/* @__PURE__ */ new Map,f=null;for(let e of u){let t=1,n=null;for(let r of c(e)){let e=d.get(r).length+1;(e>t||e===t&&n!==null&&W(r,n)<0)&&(t=e,n=r)}d.set(e,{length:t,via:n});let r=f===null?null:d.get(f);(r===null||t>r.length||t===r.length&&W(e,f)<0)&&(f=e)}let p=[];for(let e=f;e!==null;e=d.get(e)?.via??null)p.unshift(e);return p}function qt(e,t,n=()=>!0){let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map,a=/* @__PURE__ */ new Set,o=[],s=[],c=0;for(let l of e){if(r.has(l))continue;let e=[],u=s=>{r.set(s,c),i.set(s,c),c++,o.push(s),a.add(s),e.push({id:s,edges:t(s).filter(n),at:0})};for(u(l);e.length;){let t=e[e.length-1];if(t.at<t.edges.length){let e=t.edges[t.at++];r.has(e)?a.has(e)&&i.set(t.id,Math.min(i.get(t.id),r.get(e))):u(e);continue}if(e.pop(),i.get(t.id)===r.get(t.id)){let e=[],n;do n=o.pop(),a.delete(n),e.push(n);while(n!==t.id);s.push(e)}let n=e[e.length-1];n&&i.set(n.id,Math.min(i.get(n.id),i.get(t.id)))}}return s}const Jt=[`deferred`,`blocked`];function Yt(e,t){let{nodes:n}=G(e),r=n.get(String(t));if(!r)throw Error(`Task ${t} not found`);let i=Gt(e),a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Set,s=/* @__PURE__ */ new Map,c=e=>{let t=e.kind===`cycle`?`cycle:${e.cycle}`:e.id,n=a.get(t);if(!n)a.set(t,e);else{for(let t of e.listedBy??[])n.listedBy?.includes(t)||n.listedBy?.push(t);for(let t of e.edges??[])n.edges?.some(e=>e.from===t.from&&e.to===t.to)||n.edges?.push(t)}},l=(e,t)=>{let n=i.find(e=>e.includes(t.id))??[t.id];return c({id:t.id,title:t.title,status:t.status,kind:`cycle`,cycle:n,edges:[{from:e.id,to:t.id}]}),{id:t.id,title:t.title,status:t.status,kind:`cycle`,blockers:[]}},u=e=>{let t=[];return e.selfDependency&&t.push(l(e,e)),{node:e,deps:e.dependencies.map(e=>n.get(e)).filter(e=>!U(e.status)).sort((e,t)=>W(e.id,t.id)),at:0,out:t}};if(U(r.status))return{id:r.id,title:r.title,status:r.status,blocked:!1,blockers:[],rootCauses:[]};let d=/* @__PURE__ */ new Set([r.id]),f=[u(r)],p=[];for(;f.length;){let e=f[f.length-1];if(e.at<e.deps.length){let t=e.deps[e.at++];d.has(t.id)?e.out.push(l(e.node,t)):o.has(t.id)?e.out.push({id:t.id,title:t.title,status:t.status,kind:s.get(t.id),blockers:[],repeated:!0}):(o.add(t.id),d.add(t.id),f.push(u(t)));continue}for(let t of[...e.node.missing].sort(W))c({id:t,kind:`missing`,listedBy:[e.node.id]}),e.out.push({id:t,kind:`missing`,blockers:[]});f.pop();let t=f[f.length-1];if(!t){p=e.out;break}let n=e.node;d.delete(n.id);let r=e.out.length?void 0:Jt.includes(n.status)?`stalled`:`ready`;s.set(n.id,r),r&&c({id:n.id,title:n.title,status:n.status,kind:r}),t.out.push({id:n.id,title:n.title,status:n.status,kind:r,blockers:e.out})}return{id:r.id,title:r.title,status:r.status,blocked:p.length>0,blockers:p,rootCauses:[...a.values()]}}function Xt(e,t={}){let n={bottlenecks:Wt(e,t.limit??5),criticalPath:Kt(e),cycles:Gt(e)};return t.id!==void 0&&(n.explanation=Yt(e,String(t.id))),n}const q=`dependencySources`;function J(e,t){return t===void 0?String(e):B(t,e)}function Y(e){let t=e?.[q];return!t||typeof t!=`object`||Array.isArray(t)?{}:{...t}}function Zt(e,t,n){let r=J(t,n);return(e.dependencies??[]).some(e=>J(e,n)===r)}const Qt=Zt;function X(e,t){let{[q]:n,...r}=e??{};return Object.keys(t).length>0&&(r[q]=t),Object.keys(r).length>0?r:void 0}function $t(e){if(!e)return;let{[q]:t,...n}=e;return Object.keys(n).length>0?n:void 0}function en(e,t){t?e.metadata=t:delete e.metadata}function tn(e,t,n){return Qt(e,t,n)&&Y(e.metadata)[J(t,n)]===`inferred`?`inferred`:`explicit`}function nn(e,t,n){let r=Y(t?.metadata),i={};for(let a of e.dependencies??[]){let e=J(a,n);(!(t&&Qt(t,a,n))||r[e]===`inferred`)&&(i[e]=`inferred`)}return X(t?.metadata??e.metadata,i)}function rn(e,t,n,r){let i=Y(e),a=J(t,r);return n===`inferred`?i[a]=`inferred`:delete i[a],X(e,i)}function an(e,t){let n={};for(let[r,i]of Object.entries(Y(e.metadata)))Qt(e,r,t)&&(n[r]=i);return X(e.metadata,n)}function on(e,t,n,r){let i=Y(e),a=i[J(t,r)];return delete i[J(t,r)],delete i[J(n,r)],a===`inferred`&&(i[J(n,r)]=`inferred`),X(e,i)}function sn(e,t,n,r=e=>e){let i=Y(e.metadata),a={};for(let o of e.dependencies??[])i[J(o,t)]===`inferred`&&(a[J(r(o),n)]=`inferred`);return X(e.metadata,a)}function cn(e,t={}){let n=new Map(e.map(e=>[String(e.id),e])),r=e=>(e.dependencies??[]).map(String).filter(t=>t!==String(e.id)),i=e=>r(e).filter(e=>n.has(e)),a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Set,s=e=>{let t=a.get(e);if(t!==void 0)return t;if(o.has(e))return 1/0;o.add(e);let r=i(n.get(e)),c=r.length?Math.max(...r.map(s))+1:0;return o.delete(e),a.set(e,c),c},c=/* @__PURE__ */ new Map;for(let t of e){let e=String(t.id),n=i(t).filter(e=>Number.isFinite(s(e))).sort((e,t)=>s(e)-s(t)||W(e,t));c.set(e,n.length?n[n.length-1]:null)}let l=/* @__PURE__ */ new Map;for(let[e,t]of c)t&&l.set(t,[...l.get(t)??[],e]);let u=/* @__PURE__ */ new Set,d=e=>{u.add(e);let t=n.get(e),a=c.get(e);return{id:e,title:t.title,status:t.status,priority:t.priority,alsoNeeds:i(t).filter(e=>e!==a).sort(W),missing:r(t).filter(e=>!n.has(e)),inferred:i(t).filter(e=>tn(t,e)===`inferred`).sort(W),children:(l.get(e)??[]).filter(e=>!u.has(e)).sort(W).map(d)}},f=e=>Number.isFinite(s(String(e.id)))&&!c.get(String(e.id)),p=G(e),m={bottlenecks:Wt(e,5,p),criticalPath:Kt(e,p)},h;if(t.rootId!==void 0){let e=String(t.rootId);if(!n.has(e))throw Error(`Task ${e} not found`);let r=d(e);return r.inferred=r.inferred.filter(t=>t!==c.get(e)),h=[r],{roots:h,cyclic:[],total:ln(h),...m}}h=e.filter(f).map(e=>String(e.id)).sort(W).map(d);let g=e.map(e=>String(e.id)).filter(e=>!u.has(e)).sort(W).map(e=>({...d(e),children:[]}));return{roots:h,cyclic:g,total:e.length,...m}}function ln(e){return e.reduce((e,t)=>e+1+ln(t.children),0)}const un=e=>e===`done`||e===`completed`||e===`cancelled`;function dn(e){let t=e.filter(e=>[`pending`,`in-progress`,`done`,`completed`,`cancelled`].includes(e.status||`pending`)),n=Ut(e),r=e=>n.get(String(e.id))??0,i={critical:4,high:3,medium:2,low:1},a=/* @__PURE__ */ new Set;t.forEach(e=>{un(e.status)&&a.add(String(e.id)),Array.isArray(e.subtasks)&&e.subtasks.forEach(t=>{un(t.status)&&a.add(`${e.id}.${t.id}`)})});let o=[];if(t.filter(e=>e.status===`in-progress`&&Array.isArray(e.subtasks)).forEach(e=>{e.subtasks.forEach(t=>{let n=(t.status||`pending`).toLowerCase();if(n!==`pending`&&n!==`in-progress`)return;let r=t.dependencies?.map(t=>B(e.id,t))??[];(r.length===0||r.every(e=>a.has(String(e))))&&o.push({id:`${e.id}.${t.id}`,title:t.title||`Subtask ${t.id}`,status:t.status||`pending`,priority:t.priority||e.priority||`medium`,dependencies:r,parentId:String(e.id),description:t.description,details:t.details,testStrategy:t.testStrategy,subtasks:[]})})}),o.length>0)return o.sort((e,t)=>{let n=i[e.priority]??2,a=i[t.priority]??2;if(a!==n)return a-n;if(r(e)!==r(t))return r(t)-r(e);if(e.dependencies.length!==t.dependencies.length)return e.dependencies.length-t.dependencies.length;let[o,s]=String(e.id).split(`.`).map(Number),[c,l]=String(t.id).split(`.`).map(Number);return o===c?s-l:o-c}),o[0];let s=t.filter(e=>{let t=(e.status||`pending`).toLowerCase();return t!==`pending`&&t!==`in-progress`?!1:(e.dependencies??[]).every(e=>a.has(String(e)))});return s.length===0?null:s.sort((e,t)=>{let n=i[e.priority]??2,a=i[t.priority]??2;if(a!==n)return a-n;if(r(e)!==r(t))return r(t)-r(e);let o=(e.dependencies??[]).length,s=(t.dependencies??[]).length;return o===s?Number(e.id)-Number(t.id):o-s})[0]}var fn=class e{id;title;description;status;priority;dependencies;details;testStrategy;subtasks;createdAt;updatedAt;effort;actualEffort;tags;assignee;complexity;recommendedSubtasks;expansionPrompt;complexityReasoning;metadata;constructor(e){this.validate(e),this.id=String(e.id),this.title=e.title,this.description=e.description,this.status=e.status,this.priority=e.priority,this.dependencies=(e.dependencies||[]).map(e=>String(e)),this.details=e.details,this.testStrategy=e.testStrategy,this.subtasks=(e.subtasks||[]).map(e=>({...e,id:String(e.id),parentId:String(e.parentId)})),this.createdAt=e.createdAt,this.updatedAt=e.updatedAt,this.effort=e.effort,this.actualEffort=e.actualEffort,this.tags=e.tags,this.assignee=e.assignee,this.complexity=e.complexity,this.recommendedSubtasks=e.recommendedSubtasks,this.expansionPrompt=e.expansionPrompt,this.complexityReasoning=e.complexityReasoning,this.metadata=e.metadata}validate(e){if(e.id===void 0||e.id===null||typeof e.id!=`string`&&typeof e.id!=`number`)throw new C(`Task ID is required and must be a string or number`,S.VALIDATION_ERROR);if(!e.title||e.title.trim().length===0)throw new C(`Task title is required`,S.VALIDATION_ERROR);if(!e.description||e.description.trim().length===0)throw new C(`Task description is required`,S.VALIDATION_ERROR);if(!this.isValidStatus(e.status))throw new C(`Invalid task status: ${e.status}`,S.VALIDATION_ERROR);if(!this.isValidPriority(e.priority))throw new C(`Invalid task priority: ${e.priority}`,S.VALIDATION_ERROR)}isValidStatus(e){return[`pending`,`in-progress`,`done`,`deferred`,`cancelled`,`blocked`,`review`].includes(e)}isValidPriority(e){return[`low`,`medium`,`high`,`critical`].includes(e)}canComplete(){return this.status===`done`||this.status===`cancelled`||this.status===`blocked`?!1:this.subtasks.every(e=>e.status===`done`||e.status===`cancelled`)}markAsComplete(){if(!this.canComplete())throw new C(`Task cannot be marked as complete`,S.TASK_STATUS_ERROR,{taskId:this.id,currentStatus:this.status,hasIncompleteSubtasks:this.subtasks.some(e=>e.status!==`done`&&e.status!==`cancelled`)});this.status=`done`,this.updatedAt=(/* @__PURE__ */ new Date()).toISOString()}hasDependencies(){return this.dependencies.length>0}hasSubtasks(){return this.subtasks.length>0}addSubtask(e){let t=this.subtasks.length+1;this.subtasks.push({...e,id:t,parentId:this.id}),this.updatedAt=(/* @__PURE__ */ new Date()).toISOString()}updateStatus(e){if(!this.isValidStatus(e))throw new C(`Invalid status: ${e}`,S.VALIDATION_ERROR);if(this.status===`done`&&e===`pending`)throw new C(`Cannot move completed task back to pending`,S.TASK_STATUS_ERROR);this.status=e,this.updatedAt=(/* @__PURE__ */ new Date()).toISOString()}toJSON(){return{id:this.id,title:this.title,description:this.description,status:this.status,priority:this.priority,dependencies:this.dependencies,details:this.details,testStrategy:this.testStrategy,subtasks:this.subtasks,createdAt:this.createdAt,updatedAt:this.updatedAt,effort:this.effort,actualEffort:this.actualEffort,tags:this.tags,assignee:this.assignee,complexity:this.complexity,recommendedSubtasks:this.recommendedSubtasks,expansionPrompt:this.expansionPrompt,complexityReasoning:this.complexityReasoning,metadata:this.metadata}}static fromObject(t){return new e(t)}static fromArray(t){return t.map(t=>new e(t))}},pn=class{configManager;storage;initialized=!1;logger=x(`TaskService`);constructor(e){this.configManager=e,this.storage=null}async initialize(){if(this.initialized)return;let e=this.configManager.getStorageConfig(),t=this.configManager.getProjectRoot();this.storage=await At.createFromStorageConfig(e,t),await this.storage.initialize(),this.initialized=!0}async getTaskList(e={}){let t=this.configManager.getActiveTag(),n=e.tag||t;try{let t=e.filter?.status&&!e.filter.priority&&!e.filter.tags&&!e.filter.assignee&&!e.filter.search&&e.filter.hasSubtasks===void 0,r={};if(t){let t=Array.isArray(e.filter.status)?e.filter.status:[e.filter.status];t.length===1&&(r.status=t[0])}e.includeSubtasks===!1&&(r.excludeSubtasks=!0);let i=await this.storage.loadTasks(n,r),a={};e.includeSubtasks===!1&&(a.excludeSubtasks=!0);let o=r.status===void 0?i:await this.storage.loadTasks(n,a),s=fn.fromArray(i),c=s;return(e.filter&&!t||e.filter?.status&&Array.isArray(e.filter.status)&&e.filter.status.length>1)&&(c=this.applyFilters(s,e.filter)),{tasks:c.map(e=>e.toJSON()),total:o.length,filtered:c.length,tag:n}}catch(t){throw t instanceof C?t:(this.logger.error(`Failed to get task list`,t),new C(`Failed to get task list`,S.INTERNAL_ERROR,{operation:`getTaskList`,tag:n,hasFilter:!!e.filter},t))}}async getTask(e,t){let n=t||this.getActiveTag();try{return await this.storage.loadTask(String(e),n)}catch(t){throw t instanceof C?t:new C(`Failed to get task ${e}`,S.STORAGE_ERROR,{operation:`getTask`,resource:`task`,taskId:String(e),tag:n},t)}}async getTasksByStatus(e,t){let n=Array.isArray(e)?e:[e];return(await this.getTaskList({tag:t,filter:{status:n}})).tasks}async getTaskStats(e){let t=await this.getTaskList({tag:e,includeSubtasks:!0}),n={total:t.total,byStatus:{},withSubtasks:0,blocked:0};return[`pending`,`in-progress`,`done`,`deferred`,`cancelled`,`blocked`,`review`].forEach(e=>{n.byStatus[e]=0}),t.tasks.forEach(e=>{n.byStatus[e.status]++,e.subtasks&&e.subtasks.length>0&&n.withSubtasks++,e.status===`blocked`&&n.blocked++}),n}async getNextTask(e){return dn((await this.getTaskList({tag:e})).tasks)}applyFilters(e,t){return e.filter(e=>{if(t.status&&!(Array.isArray(t.status)?t.status:[t.status]).includes(e.status)||t.priority&&!(Array.isArray(t.priority)?t.priority:[t.priority]).includes(e.priority)||t.tags&&t.tags.length>0&&(!e.tags||!t.tags.some(t=>e.tags?.includes(t)))||t.assignee&&e.assignee!==t.assignee)return!1;if(t.search){let n=t.search.toLowerCase(),r=e.title.toLowerCase().includes(n),i=e.description.toLowerCase().includes(n),a=e.details.toLowerCase().includes(n);if(!r&&!i&&!a)return!1}return t.hasSubtasks===void 0||e.subtasks.length>0===t.hasSubtasks})}getStorage(){return this.storage}getActiveTag(){return this.configManager.getActiveTag()}async setActiveTag(e){await this.configManager.setActiveTag(e)}async updateTask(e,t,n){if(!this.storage)throw new C(`Storage not initialized`,S.STORAGE_ERROR);this.initialized||await this.initialize();let r=n||this.getActiveTag(),i=String(e);try{await this.storage.updateTask(i,t,r)}catch(t){throw t instanceof C?t:new C(`Failed to update task ${e}`,S.STORAGE_ERROR,{operation:`updateTask`,resource:`task`,taskId:i,tag:r},t)}}async updateTaskStatus(e,t,n){if(!this.storage)throw new C(`Storage not initialized`,S.STORAGE_ERROR);let r=n||this.getActiveTag(),i=String(e);try{return await this.storage.updateTaskStatus(i,t,r)}catch(e){throw e instanceof C?e:new C(`Failed to update task status for ${i}`,S.STORAGE_ERROR,{operation:`updateTaskStatus`,resource:`task`,taskId:i,newStatus:t,tag:r},e)}}async getTagsWithStats(){if(!this.storage)throw new C(`Storage not initialized`,S.STORAGE_ERROR);this.initialized||await this.initialize();try{return await this.storage.getTagsWithStats()}catch(e){throw e instanceof C?e:new C(`Failed to get tags with stats`,S.STORAGE_ERROR,{operation:`getTagsWithStats`,resource:`tags`},e)}}async close(){this.storage&&await this.storage.close(),this.initialized=!1}},mn=class{taskService;constructor(e){this.taskService=e}async startTask(e,t={}){try{let{parentId:n,subtaskId:r}=this.parseTaskId(e);if(!t.force){let t=await this.checkInProgressConflicts(e);if(!t.canProceed)return{task:null,found:!1,started:!1,error:`Conflicting tasks in progress: ${t.conflictingTasks.map(e=>`#${e.id}: ${e.title}`).join(`, `)}`}}let i=await this.taskService.getTask(n);if(!i)return{task:null,found:!1,started:!1,error:`Task ${n} not found`};let a;if(r&&i.subtasks&&(a=i.subtasks.find(e=>String(e.id)===r)),t.updateStatus&&!t.dryRun)try{await this.taskService.updateTaskStatus(n,`in-progress`)}catch(e){console.warn(`Could not update task status: ${e instanceof Error?e.message:String(e)}`)}let o=!1,s=`Task ready to execute`,c;return t.dryRun?(o=!0,s=`Dry run - task would be executed`,c=await this.prepareExecutionCommand(i,a)):(c=await this.prepareExecutionCommand(i,a),o=!!c,s=c?`Command prepared: ${c.executable} ${c.args.join(` `)}`:`Failed to prepare execution command`),{task:i,found:!0,started:o,subtaskId:r,subtask:a,executionOutput:s,command:c||void 0}}catch(e){return{task:null,found:!1,started:!1,error:e instanceof Error?e.message:String(e)}}}async checkInProgressConflicts(e){let t=(await this.taskService.getTaskList()).tasks.filter(e=>e.status===`in-progress`);if(t.find(t=>t.id===e))return{canProceed:!0,conflictingTasks:[]};let n=e.includes(`.`);if(n){let n=e.split(`.`)[0];if(t.find(e=>e.id===n))return{canProceed:!0,conflictingTasks:[]}}let r=t.filter(t=>{if(t.id===e)return!1;if(n){let n=e.split(`.`)[0];if(t.id===n)return!1}if(t.id.toString().includes(`.`)){let r=t.id.toString().split(`.`)[0];if(n&&r===e.split(`.`)[0])return!1}return!0});return r.length>0?{canProceed:!1,conflictingTasks:r,reason:`Other tasks are already in progress`}:{canProceed:!0,conflictingTasks:[]}}async getNextAvailableTask(){return(await this.taskService.getNextTask())?.id||null}parseTaskId(e){if(e.includes(`.`)){let[t,n]=e.split(`.`);return{parentId:t,subtaskId:n}}return{parentId:e}}async canStartTask(e,t=!1){return t?!0:(await this.checkInProgressConflicts(e)).canProceed}async prepareExecutionCommand(e,t){try{return{executable:`claude`,args:[this.formatTaskPrompt(e,t)],cwd:process.cwd()}}catch(e){return console.warn(`Failed to prepare execution command: ${e instanceof Error?e.message:String(e)}`),null}}formatTaskPrompt(e,t){let n=t||e,r=t?`Subtask`:`Task`,i=`${r} #${t?`${e.id}.${t.id}`:e.id}: ${n.title}\n\n`;return n.description&&(i+=`Description:\n${n.description}\n\n`),n.details&&(i+=`Implementation Details:\n${n.details}\n\n`),n.testStrategy&&(i+=`Test Strategy:\n${n.testStrategy}\n\n`),e.dependencies&&e.dependencies.length>0&&(i+=`Dependencies: ${e.dependencies.join(`, `)}\n\n`),i+=`Please help me implement this ${r.toLowerCase()}.`,i}async getTaskWithSubtask(e){let{parentId:t,subtaskId:n}=this.parseTaskId(e),r=await this.taskService.getTask(t);return r?n&&r.subtasks?{task:r,subtask:r.subtasks.find(e=>String(e.id)===n),subtaskId:n}:{task:r}:{task:null}}};const Z=x(`TaskLoader`);function hn(e,t){let n=B(e,t),r=`${e}.`;return n.startsWith(r)?n.slice(r.length):null}var gn=class{taskService;constructor(e){if(!e)throw Error(`taskService is required for TaskLoaderService`);this.taskService=e}async loadAndValidateTask(e){Z.info(`Loading task ${e}...`);let t=await this.loadTask(e);if(!t)return{success:!1,errorType:`task_not_found`,errorMessage:`Task with ID "${e}" not found`,suggestion:`Use "taskgraph list" to see available tasks or verify the task ID is correct.`};let n=this.validateTaskStatus(t);if(!n.success)return n;let r=this.validateSubtasksExist(t);if(!r.success)return r;let i=this.validateSubtaskStructure(t);if(!i.success)return i;let a=this.validateDependencies(t);return a.success?(Z.info(`Task ${e} validated successfully`),{success:!0,task:t}):a}async loadTask(e){try{return await this.taskService.getTask(e)}catch(t){return Z.error(`Failed to load task ${e}:`,t),null}}validateTaskStatus(e){return U(e.status)?{success:!1,errorType:`task_completed`,errorMessage:`Task "${e.title}" is already ${e.status}`,suggestion:`Autopilot can only execute tasks that are pending or in-progress. Use a different task.`}:{success:!0}}validateSubtasksExist(e){return!e.subtasks||e.subtasks.length===0?{success:!1,errorType:`no_subtasks`,errorMessage:`Task "${e.title}" has no subtasks`,suggestion:this.buildExpansionSuggestion(e)}:{success:!0}}buildExpansionSuggestion(e){let t=[`Autopilot requires tasks to be broken down into subtasks for execution.`];return t.push(`
|
|
186
|
+
Expand this task using:`),t.push(` taskgraph expand --id=${e.id}`),e.complexity||e.recommendedSubtasks?(t.push(`
|
|
187
|
+
This task has complexity analysis available. Consider reviewing it first:`),t.push(` taskgraph show ${e.id}`)):(t.push(`
|
|
188
|
+
Or analyze task complexity first to determine optimal subtask count:`),t.push(` taskgraph analyze-complexity --from=${e.id}`)),t.join(`
|
|
189
|
+
`)}validateSubtaskStructure(e){for(let t of e.subtasks){if(!t.title||!t.description)return{success:!1,errorType:`invalid_structure`,errorMessage:`Subtask ${e.id}.${t.id} is missing required fields`,suggestion:`Subtasks must have title and description. Re-expand the task or manually fix the subtask structure.`};if(t.dependencies&&!Array.isArray(t.dependencies))return{success:!1,errorType:`invalid_structure`,errorMessage:`Subtask ${e.id}.${t.id} has invalid dependencies format`,suggestion:`Dependencies must be an array. Fix the task structure manually.`}}return{success:!0}}validateDependencies(e){let t=[],n=new Set(e.subtasks.map(e=>String(e.id)));for(let r of e.subtasks){let i=`${e.id}.${r.id}`;if(r.dependencies&&r.dependencies.length>0)for(let a of r.dependencies){let r=hn(e.id,a);r!==null&&!n.has(r)&&t.push({subtaskId:i,issueType:`missing`,message:`References non-existent subtask ${r}`,dependencyRef:r})}let a=this.detectCircularDependency(r,e,/* @__PURE__ */ new Set);a&&t.push({subtaskId:i,issueType:`circular`,message:`Circular dependency detected: ${a.join(` -> `)}`})}return t.length>0?{success:!1,errorType:t[0].issueType===`circular`?`circular_dependencies`:`missing_dependencies`,errorMessage:`Task "${e.title}" has dependency issues`,suggestion:`Fix dependency issues manually or re-expand the task:
|
|
190
|
+
`+t.map(e=>` - ${e.subtaskId}: ${e.message}`).join(`
|
|
191
|
+
`),dependencyIssues:t}:{success:!0}}detectCircularDependency(e,t,n){let r=String(e.id);if(n.has(r))return[r];if(n.add(r),e.dependencies&&e.dependencies.length>0)for(let i of e.dependencies){let e=hn(t.id,i),a=t.subtasks.find(t=>String(t.id)===e);if(a){let e=this.detectCircularDependency(a,t,new Set(n));if(e)return[r,...e]}}return null}getExecutionOrder(e){let t=[],n=/* @__PURE__ */ new Set;for(;t.length<e.subtasks.length;){let r=!1;for(let i of e.subtasks){let a=String(i.id);if(!n.has(a)&&(!i.dependencies||i.dependencies.length===0||i.dependencies.every(t=>{let r=hn(e.id,t);return r===null||n.has(r)}))){t.push(i),n.add(a),r=!0;break}}if(!r&&t.length<e.subtasks.length){Z.warn(`Could not determine complete execution order for task ${e.id}`);for(let r of e.subtasks)n.has(String(r.id))||t.push(r);break}}return t}async cleanup(){}};const Q=x(`PreflightChecker`);var _n=class{projectRoot;constructor(e){if(!e)throw Error(`projectRoot is required for PreflightChecker`);this.projectRoot=e}async detectTestCommand(){try{let e=h(this.projectRoot,`package.json`),t=d(e,`utf-8`),n=JSON.parse(t);if(!n.scripts||!n.scripts.test)return{success:!1,message:`No test script found in package.json. Please add a "test" script.`};let r=n.scripts.test;return{success:!0,value:r,message:`Test command: ${r}`}}catch(e){return e.code===`ENOENT`?{success:!1,message:`package.json not found in project root`}:{success:!1,message:`Failed to read package.json: ${e.message}`}}}async checkGitWorkingTree(){try{return await je(this.projectRoot)?v(`git status --porcelain`,{cwd:this.projectRoot,encoding:`utf-8`,timeout:5e3}).trim().length>0?{success:!1,value:`dirty`,message:`Working tree has uncommitted or untracked changes. Please commit or stash them.`}:{success:!0,value:`clean`,message:`Working tree is clean`}:{success:!1,message:`Not a git repository. Initialize git first.`}}catch(e){return{success:!1,message:`Git check failed: ${e.message}`}}}detectProjectTypes(){let e=[];return u(h(this.projectRoot,`package.json`))&&e.push(`node`),(u(h(this.projectRoot,`requirements.txt`))||u(h(this.projectRoot,`setup.py`))||u(h(this.projectRoot,`pyproject.toml`)))&&e.push(`python`),(u(h(this.projectRoot,`pom.xml`))||u(h(this.projectRoot,`build.gradle`)))&&e.push(`java`),u(h(this.projectRoot,`go.mod`))&&e.push(`go`),u(h(this.projectRoot,`Cargo.toml`))&&e.push(`rust`),u(h(this.projectRoot,`composer.json`))&&e.push(`php`),u(h(this.projectRoot,`Gemfile`))&&e.push(`ruby`),f(this.projectRoot).some(e=>e.endsWith(`.csproj`)||e.endsWith(`.sln`))&&e.push(`dotnet`),e}getToolsForProjectType(e){return{node:[{command:`node`,args:[`--version`]},{command:`npm`,args:[`--version`]}],python:[{command:`python3`,args:[`--version`]},{command:`pip3`,args:[`--version`]}],java:[{command:`java`,args:[`--version`]}],go:[{command:`go`,args:[`version`]}],rust:[{command:`cargo`,args:[`--version`]}],php:[{command:`php`,args:[`--version`]},{command:`composer`,args:[`--version`]}],ruby:[{command:`ruby`,args:[`--version`]},{command:`bundle`,args:[`--version`]}],dotnet:[{command:`dotnet`,args:[`--version`]}]}[e]||[]}async validateRequiredTools(){let e=[];e.push(this.checkTool(`git`,[`--version`])),e.push(await this.checkGhCli());let t=this.detectProjectTypes();t.length===0?Q.warn(`No recognized project type detected`):Q.info(`Detected project types: ${t.join(`, `)}`);for(let n of t){let t=this.getToolsForProjectType(n);for(let n of t)e.push(this.checkTool(n.command,n.args))}let n=e.every(e=>e.available),r=e.filter(e=>!e.available).map(e=>e.name);return n?{success:!0,value:e,message:`All required tools are available`}:{success:!1,value:e,message:`Missing required tools: ${r.join(`, `)}`}}checkTool(e,t){try{let n=v(`${e} ${t.join(` `)}`,{cwd:this.projectRoot,encoding:`utf-8`,stdio:`pipe`,timeout:5e3}).trim().split(`
|
|
192
|
+
`)[0];return{name:e,available:!0,version:n,message:`${e} ${n}`}}catch{return{name:e,available:!1,message:`${e} not found`}}}async checkGhCli(){try{return{name:`gh`,available:!0,version:v(`gh --version`,{cwd:this.projectRoot,encoding:`utf-8`,stdio:`pipe`,timeout:5e3}).trim().split(`
|
|
193
|
+
`)[0],message:await D(this.projectRoot)?`GitHub CLI installed (authenticated)`:`GitHub CLI installed (not authenticated)`}}catch{return{name:`gh`,available:!1,message:`GitHub CLI not found`}}}async detectDefaultBranch(){try{let e=await Ie(this.projectRoot);return e?{success:!0,value:e,message:`Default branch: ${e}`}:{success:!1,message:`Could not determine default branch. Make sure remote is configured.`}}catch(e){return{success:!1,message:`Failed to detect default branch: ${e.message}`}}}async runAllChecks(){Q.info(`Running preflight checks...`);let e=await this.detectTestCommand(),t=await this.checkGitWorkingTree(),n=await this.validateRequiredTools(),r=await this.detectDefaultBranch(),i=e.success&&t.success&&n.success&&r.success,a=[],o=[];e.success?a.push(`Test command`):o.push(`Test command`),t.success?a.push(`Git working tree`):o.push(`Git working tree`),n.success?a.push(`Required tools`):o.push(`Required tools`),r.success?a.push(`Default branch`):o.push(`Default branch`);let s=a.length+o.length,c=i?`All preflight checks passed (${a.length}/${s})`:`Preflight checks failed: ${o.join(`, `)} (${a.length}/${s} passed)`;return Q.info(c),{success:i,testCommand:e,gitWorkingTree:t,requiredTools:n,defaultBranch:r,summary:c}}};const vn=[`master`];var yn=class{storage;constructor(e){this.storage=e}validateTagName(e,t=`Tag name`){if(!e||typeof e!=`string`)throw new C(`${t} is required and must be a string`,S.VALIDATION_ERROR);if(e.length>50)throw new C(`${t} must be 50 characters or less`,S.VALIDATION_ERROR,{tagName:e,maxLength:50});if(!/^[a-zA-Z0-9_-]+$/.test(e))throw new C(`${t} can only contain letters, numbers, hyphens, and underscores`,S.VALIDATION_ERROR,{tagName:e});if(vn.includes(e.toLowerCase()))throw new C(`"${e}" is a reserved tag name`,S.VALIDATION_ERROR,{tagName:e,reserved:!0})}async createTag(e,t={}){this.validateTagName(e);let n=await this.storage.getAllTags();if(n.includes(e))throw new C(`Tag "${e}" already exists`,S.VALIDATION_ERROR,{tagName:e});if(t.copyFromTag&&!n.includes(t.copyFromTag))throw new C(`Cannot copy from missing tag "${t.copyFromTag}"`,S.NOT_FOUND,{tagName:t.copyFromTag});let r;return t.copyFromTag?r=t.copyFromTag:t.copyFromCurrent&&(r=(await this.storage.getTagsWithStats()).currentTag||void 0),await this.storage.createTag(e,{copyFrom:r,description:t.description}),{name:e,taskCount:0,completedTasks:0,isCurrent:!1,statusBreakdown:{},description:t.description||`Tag created on ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`}}async deleteTag(e,t={}){if(this.validateTagName(e),e===`master`)throw new C(`Cannot delete the "master" tag`,S.VALIDATION_ERROR,{tagName:e,protected:!0});if(!(await this.storage.getAllTags()).includes(e))throw new C(`Tag "${e}" does not exist`,S.NOT_FOUND,{tagName:e});await this.storage.deleteTag(e)}async renameTag(e,t){if(this.validateTagName(e,`Old tag name`),this.validateTagName(t,`New tag name`),e===`master`)throw new C(`Cannot rename the "master" tag`,S.VALIDATION_ERROR,{tagName:e,protected:!0});let n=await this.storage.getAllTags();if(!n.includes(e))throw new C(`Tag "${e}" does not exist`,S.NOT_FOUND,{tagName:e});if(n.includes(t))throw new C(`Tag "${t}" already exists`,S.VALIDATION_ERROR,{tagName:t});await this.storage.renameTag(e,t)}async copyTag(e,t,n={}){this.validateTagName(e,`Source tag name`),this.validateTagName(t,`Target tag name`);let r=await this.storage.getAllTags();if(!r.includes(e))throw new C(`Source tag "${e}" does not exist`,S.NOT_FOUND,{tagName:e});if(r.includes(t))throw new C(`Target tag "${t}" already exists`,S.VALIDATION_ERROR,{tagName:t});await this.storage.copyTag(e,t)}async getTagsWithStats(){return await this.storage.getTagsWithStats()}},bn=class{storage;projectPath;configManager;constructor(e,t,n){this.storage=e,this.projectPath=t,this.configManager=n}async generateTaskFiles(e={}){let n=e.tag||this.configManager.getActiveTag(),r=e.outputDir||t.join(this.projectPath,`.taskgraph`,`tasks`);try{await i.mkdir(r,{recursive:!0});let e=await this.storage.loadTasks(n);if(e.length===0)return{success:!0,count:0,directory:r,orphanedFilesRemoved:0};let a=await this.cleanupOrphanedFiles(r,e,n),o={},s=await Promise.allSettled(e.map(async a=>{let o=this.formatTaskContent(a,e),s=this.getTaskFileName(a.id,n),c=t.join(r,s);return await i.writeFile(c,o,`utf-8`),a.id})),c=0;for(let t=0;t<s.length;t++){let n=s[t];if(n.status===`fulfilled`)c++;else{let r=String(e[t].id);o[r]=n.reason?.message||`Unknown error`}}return{success:Object.keys(o).length===0,count:c,directory:r,orphanedFilesRemoved:a,...Object.keys(o).length>0&&{fileErrors:o}}}catch(e){return{success:!1,count:0,directory:r,orphanedFilesRemoved:0,error:e.message}}}getTaskFileName(e,t){let n=String(e).padStart(3,`0`);return t===`master`?`task_${n}.md`:`task_${n}_${t}.md`}escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}async cleanupOrphanedFiles(e,n,r){let a=0;try{let o=await i.readdir(e),s=n.map(e=>String(e.id)),c=/^task_(\d+)\.md$/,l=RegExp(`^task_(\\d+)_${this.escapeRegExp(r)}\\.md$`),u=[];for(let e of o){let t=null,n=null;if(r===`master`?(t=e.match(c),t&&(n=t[1])):(t=e.match(l),t&&(n=t[1])),n!==null){let t=String(parseInt(n,10));s.includes(t)||u.push(e)}}await Promise.all(u.map(async n=>{let r=t.join(e,n);await i.unlink(r)})),a=u.length}catch{}return a}formatTaskContent(e,t){let n=`# Task ID: ${e.id}\n\n`;if(n+=`**Title:** ${e.title}\n\n`,n+=`**Status:** ${e.status||`pending`}\n\n`,e.dependencies&&e.dependencies.length>0){let r=this.formatDependenciesWithStatus(e.dependencies,t);n+=`**Dependencies:** ${r}\n\n`}else n+=`**Dependencies:** None
|
|
194
|
+
|
|
195
|
+
`;if(n+=`**Priority:** ${e.priority||`medium`}\n\n`,n+=`**Description:** ${e.description||``}\n\n`,n+=`**Details:**
|
|
196
|
+
|
|
197
|
+
`,n+=`${e.details||`No details provided.`}\n\n`,n+=`**Test Strategy:**
|
|
198
|
+
|
|
199
|
+
`,n+=`${e.testStrategy||`No test strategy provided.`}\n`,e.subtasks&&e.subtasks.length>0){n+=`
|
|
200
|
+
## Subtasks
|
|
201
|
+
`;for(let t of e.subtasks)n+=this.formatSubtaskContent(t,e)}return n.replace(/\n{3,}/g,`
|
|
202
|
+
|
|
203
|
+
`)}formatSubtaskContent(e,t){let n=`\n### ${t.id}.${e.id}. ${e.title}\n\n`;if(n+=`**Status:** ${e.status||`pending`} \n`,e.dependencies&&e.dependencies.length>0){let r=e.dependencies.map(e=>B(t.id,e)).join(`, `);n+=`**Dependencies:** ${r} \n`}else n+=`**Dependencies:** None
|
|
204
|
+
`;return n+=`
|
|
205
|
+
`,e.description&&(n+=`${e.description}\n`),e.details&&(n+=`\n**Details:**\n\n${e.details}\n`),n}formatDependenciesWithStatus(e,t){return e.map(e=>{let n=t.find(t=>String(t.id)===String(e));return n?`${e}${this.getStatusSymbol(n.status)}`:String(e)}).join(`, `)}getStatusSymbol(e){switch(e){case`done`:return` ✓`;case`in-progress`:return` ⧖`;case`blocked`:return` ⛔`;case`cancelled`:return` ✗`;case`deferred`:return` ⏸`;default:return``}}},xn=class{taskService;executionService;loaderService;preflightChecker;tagService;taskFileGenerator;projectRoot;configManager;constructor(e){this.projectRoot=e.getProjectRoot(),this.configManager=e,this.taskService=new pn(e),this.executionService=new mn(this.taskService),this.loaderService=new gn(this.taskService),this.preflightChecker=new _n(this.projectRoot)}async initialize(){await this.taskService.initialize(),this.tagService=new yn(this.taskService.getStorage()),this.taskFileGenerator=new bn(this.taskService.getStorage(),this.projectRoot,this.configManager)}async list(e){return this.taskService.getTaskList(e)}async get(e,t){let n=e.split(`.`),r=n[0],i=n[1],a=await this.taskService.getTask(r,t);if(!a)return{task:null,isSubtask:!1};if(i&&a.subtasks){let e=a.subtasks.find(e=>String(e.id)===i);return e?{task:e,isSubtask:!0}:{task:null,isSubtask:!0}}return{task:a,isSubtask:!1}}async getByStatus(e,t){return this.taskService.getTasksByStatus(e,t)}async getStats(e){return this.taskService.getTaskStats(e)}async getNext(e){return this.taskService.getNextTask(e)}async getDependencyTree(e={}){let{tasks:t}=await this.list({tag:e.tag});return cn(t,{rootId:e.rootId})}async analyzeDependencies(e={}){let{tasks:t}=await this.list({tag:e.tag});return Xt(t,{limit:e.limit,id:e.taskId})}async explainBlockers(e,t={}){let{tasks:n}=await this.list({tag:t.tag});return Yt(n,String(e))}async getCount(e,t){let n=await this.list({tag:t}),r=0;for(let t of n.tasks)if(t.status===e&&r++,t.subtasks&&t.subtasks.length>0)for(let n of t.subtasks)(n.status===e||e===`pending`&&!n.status)&&r++;return r}async update(e,t,n){return this.taskService.updateTask(e,t,n)}async updateStatus(e,t,n){return this.taskService.updateTaskStatus(e,t,n)}async setActiveTag(e){return this.taskService.setActiveTag(e)}async start(e,t){return this.executionService.startTask(e,t)}async checkInProgressConflicts(e){return this.executionService.checkInProgressConflicts(e)}async getNextAvailable(){return this.executionService.getNextAvailableTask()}async canStart(e,t){return this.executionService.canStartTask(e,t)}async loadAndValidate(e){return this.loaderService.loadAndValidateTask(e)}getExecutionOrder(e){return this.loaderService.getExecutionOrder(e)}async runPreflightChecks(){return this.preflightChecker.runAllChecks()}async detectTestCommand(){return this.preflightChecker.detectTestCommand()}async checkGitWorkingTree(){return this.preflightChecker.checkGitWorkingTree()}async validateRequiredTools(){return this.preflightChecker.validateRequiredTools()}async detectDefaultBranch(){return this.preflightChecker.detectDefaultBranch()}async createTag(e,t){return this.tagService.createTag(e,t)}async deleteTag(e,t){return this.tagService.deleteTag(e,t)}async renameTag(e,t){return this.tagService.renameTag(e,t)}async copyTag(e,t,n){return this.tagService.copyTag(e,t,n)}async getTagsWithStats(){return this.tagService.getTagsWithStats()}async watch(e,t){return this.taskService.getStorage().watch(e,t)}async generateTaskFiles(e){return this.taskFileGenerator.generateTaskFiles(e)}async close(){await this.taskService.close()}},Sn=class{projectRoot;statePath;backupDir;sessionDir;maxBackups;logger=x(`WorkflowStateManager`);writer=null;writerInitPromise=null;constructor(e,n=5){this.projectRoot=t.resolve(e),this.maxBackups=n;let r=this.getProjectIdentifier(this.projectRoot),i=oe.homedir(),a=t.join(i,`.taskgraph`,r);this.sessionDir=t.join(a,`sessions`),this.statePath=t.join(this.sessionDir,`workflow-state.json`),this.backupDir=t.join(this.sessionDir,`backups`)}getProjectIdentifier(e){return`-`+t.resolve(e).replace(/^\//,``).replace(/[^a-zA-Z0-9]+/g,`-`).replace(/-+/g,`-`).replace(/-+$/,``)}async ensureWriter(){this.writer||(this.writerInitPromise?await this.writerInitPromise:(this.writerInitPromise=(async()=>{await i.mkdir(this.sessionDir,{recursive:!0}),this.writer=new ce(this.statePath)})(),await this.writerInitPromise,this.writerInitPromise=null))}async exists(){try{return await i.access(this.statePath),!0}catch{return!1}}async load(){try{let e=await i.readFile(this.statePath,`utf-8`);return JSON.parse(e)}catch(e){throw e.code===`ENOENT`?Error(`Workflow state file not found at ${this.statePath}`):Error(`Failed to load workflow state: ${e.message}`)}}async save(e){try{await this.ensureWriter();let t=JSON.stringify(e,null,2);try{JSON.parse(t)}catch{throw this.logger.error(`Generated invalid JSON:`,t),Error(`Failed to generate valid JSON from workflow state`)}await this.writer.write(t+`
|
|
206
|
+
`),this.logger.debug(`Saved workflow state (${t.length} bytes)`)}catch(e){throw Error(`Failed to save workflow state: ${e.message}`)}}async createBackup(){try{if(!await this.exists())return;let e=await this.load();await i.mkdir(this.backupDir,{recursive:!0});let n=(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g,`-`),r=t.join(this.backupDir,`workflow-state-${n}.json`),a={timestamp:(/* @__PURE__ */ new Date()).toISOString(),state:e};await i.writeFile(r,JSON.stringify(a,null,2),`utf-8`),await this.pruneBackups()}catch(e){throw Error(`Failed to create backup: ${e.message}`)}}async delete(){try{await i.unlink(this.statePath)}catch(e){if(e.code!==`ENOENT`)throw Error(`Failed to delete workflow state: ${e.message}`)}}async listBackups(){try{return(await i.readdir(this.backupDir)).filter(e=>e.startsWith(`workflow-state-`)&&e.endsWith(`.json`)).sort().reverse()}catch(e){if(e.code===`ENOENT`)return[];throw Error(`Failed to list backups: ${e.message}`)}}async restoreBackup(e){try{let n=t.join(this.backupDir,e),r=await i.readFile(n,`utf-8`),a=JSON.parse(r);await this.save(a.state)}catch(e){throw Error(`Failed to restore backup: ${e.message}`)}}async pruneBackups(){try{let e=await this.listBackups();if(e.length>this.maxBackups){let n=e.slice(this.maxBackups);for(let e of n)await i.unlink(t.join(this.backupDir,e))}}catch(e){this.logger.warn(`Failed to prune backups: ${e.message}`)}}getStatePath(){return this.statePath}getBackupDir(){return this.backupDir}getSessionDir(){return this.sessionDir}getProjectRoot(){return this.projectRoot}getActivityLogPath(){return t.join(this.sessionDir,`activity.jsonl`)}},Cn=class{currentPhase;context;transitions;eventListeners;persistCallback;autoPersistEnabled=!1;phaseGuards;aborted=!1;testResultValidator;gitOperationHook;executeHook;constructor(e){this.currentPhase=`PREFLIGHT`,this.context={...e},this.transitions=this.defineTransitions(),this.eventListeners=/* @__PURE__ */ new Map,this.phaseGuards=/* @__PURE__ */ new Map}defineTransitions(){return[{from:`PREFLIGHT`,to:`BRANCH_SETUP`,event:`PREFLIGHT_COMPLETE`},{from:`BRANCH_SETUP`,to:`SUBTASK_LOOP`,event:`BRANCH_CREATED`},{from:`SUBTASK_LOOP`,to:`FINALIZE`,event:`ALL_SUBTASKS_COMPLETE`},{from:`FINALIZE`,to:`COMPLETE`,event:`FINALIZE_COMPLETE`}]}getCurrentPhase(){return this.currentPhase}getCurrentTDDPhase(){if(this.currentPhase===`SUBTASK_LOOP`)return this.context.currentTDDPhase||`RED`}getContext(){return{...this.context}}async transition(e){if(this.aborted&&e.type!==`ABORT`)throw Error(`Workflow has been aborted`);if(e.type===`ERROR`){this.handleError(e.error),await this.triggerAutoPersist();return}if(e.type===`ABORT`){this.aborted=!0,await this.triggerAutoPersist();return}if(e.type===`RETRY`){this.handleRetry(),await this.triggerAutoPersist();return}if(this.currentPhase===`SUBTASK_LOOP`){await this.handleTDDPhaseTransition(e),await this.triggerAutoPersist();return}let t=this.transitions.find(t=>t.from===this.currentPhase&&t.event===e.type);if(!t)throw Error(`Invalid transition: ${e.type} from ${this.currentPhase}`);this.executeTransition(t,e),await this.triggerAutoPersist()}async handleTDDPhaseTransition(e){let t=this.context.currentTDDPhase||`RED`;switch(e.type){case`RED_PHASE_COMPLETE`:if(t!==`RED`)throw Error(`Invalid transition: RED_PHASE_COMPLETE from non-RED phase`);if(!e.testResults)throw Error(`Test results required for RED phase transition`);if(this.context.lastTestResults=e.testResults,e.testResults.failed===0){this.emit(`tdd:red:completed`),this.emit(`tdd:feature-already-implemented`,{subtaskId:this.getCurrentSubtaskId(),testResults:e.testResults});let t=this.context.subtasks[this.context.currentSubtaskIndex];t&&(t.status=`completed`),this.emit(`subtask:completed`),this.context.currentSubtaskIndex++;let n=this.getProgress();this.emit(`progress:updated`,{completed:n.completed,total:n.total,percentage:n.percentage}),this.context.currentSubtaskIndex<this.context.subtasks.length?(this.context.currentTDDPhase=`RED`,this.emit(`tdd:red:started`),this.emit(`subtask:started`)):await this.transition({type:`ALL_SUBTASKS_COMPLETE`});break}this.emit(`tdd:red:completed`),this.context.currentTDDPhase=`GREEN`,this.emit(`tdd:green:started`);break;case`GREEN_PHASE_COMPLETE`:if(t!==`GREEN`)throw Error(`Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase`);if(!e.testResults)throw Error(`Test results required for GREEN phase transition`);if(e.testResults.failed!==0)throw Error(`GREEN phase must have zero failures`);this.context.lastTestResults=e.testResults,this.emit(`tdd:green:completed`),this.context.currentTDDPhase=`COMMIT`,this.emit(`tdd:commit:started`);break;case`COMMIT_COMPLETE`:if(t!==`COMMIT`)throw Error(`Invalid transition: COMMIT_COMPLETE from non-COMMIT phase`);this.emit(`tdd:commit:completed`);let n=this.context.subtasks[this.context.currentSubtaskIndex];n&&(n.status=`completed`);break;case`SUBTASK_COMPLETE`:this.emit(`subtask:completed`),this.context.currentSubtaskIndex++;let r=this.getProgress();this.emit(`progress:updated`,{completed:r.completed,total:r.total,percentage:r.percentage}),this.context.currentSubtaskIndex<this.context.subtasks.length?(this.context.currentTDDPhase=`RED`,this.emit(`tdd:red:started`),this.emit(`subtask:started`)):await this.transition({type:`ALL_SUBTASKS_COMPLETE`});break;case`ALL_SUBTASKS_COMPLETE`:this.emit(`phase:exited`),this.currentPhase=`FINALIZE`,this.context.currentTDDPhase=void 0,this.emit(`phase:entered`);break;default:throw Error(`Invalid transition: ${e.type} in SUBTASK_LOOP`)}}executeTransition(e,t){if(e.guard&&!e.guard(this.context))throw Error(`Guard condition failed for transition to ${e.to}`);let n=this.phaseGuards.get(e.to);if(n&&!n(this.context))throw Error(`Guard condition failed`);this.emit(`phase:exited`),this.updateContext(t),this.currentPhase=e.to,this.emit(`phase:entered`),this.currentPhase===`SUBTASK_LOOP`&&(this.context.currentTDDPhase=`RED`,this.emit(`tdd:red:started`),this.emit(`subtask:started`))}updateContext(e){switch(e.type){case`BRANCH_CREATED`:this.context.branchName=e.branchName,this.emit(`git:branch:created`,{branchName:e.branchName}),this.gitOperationHook&&this.gitOperationHook(`branch:created`,{branchName:e.branchName});break;case`ERROR`:this.context.errors.push(e.error),this.emit(`error:occurred`,{error:e.error})}}getState(){return{phase:this.currentPhase,context:{...this.context}}}restoreState(e){this.currentPhase=e.phase,this.context={...e.context},this.emit(`workflow:resumed`,{phase:this.currentPhase,progress:this.getProgress()})}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,/* @__PURE__ */ new Set),this.eventListeners.get(e).add(t)}off(e,t){let n=this.eventListeners.get(e);n&&n.delete(t)}emit(e,t){let n={type:e,timestamp:/* @__PURE__ */ new Date,phase:this.currentPhase,tddPhase:this.context.currentTDDPhase,subtaskId:this.getCurrentSubtaskId(),data:{...t,adapters:{testValidator:!!this.testResultValidator,gitHook:!!this.gitOperationHook,executeHook:!!this.executeHook}}},r=this.eventListeners.get(e);r&&r.forEach(e=>e(n))}getCurrentSubtaskId(){return this.context.subtasks[this.context.currentSubtaskIndex]?.id}onStatePersist(e){this.persistCallback=e}enableAutoPersist(e){this.persistCallback=e,this.autoPersistEnabled=!0}disableAutoPersist(){this.autoPersistEnabled=!1}async persistState(){this.persistCallback&&await this.persistCallback(this.getState()),this.emit(`state:persisted`)}async triggerAutoPersist(){this.autoPersistEnabled&&this.persistCallback&&await this.persistCallback(this.getState())}addGuard(e,t){this.phaseGuards.set(e,t)}removeGuard(e){this.phaseGuards.delete(e)}getCurrentSubtask(){return this.context.subtasks[this.context.currentSubtaskIndex]}getProgress(){let e=this.context.subtasks.filter(e=>e.status===`completed`).length,t=this.context.subtasks.length;return{completed:e,total:t,current:this.context.currentSubtaskIndex+1,percentage:t>0?Math.round(e/t*100):0}}canProceed(){return this.currentPhase===`SUBTASK_LOOP`&&this.getCurrentSubtask()?.status===`completed`}incrementAttempts(){let e=this.getCurrentSubtask();e&&e.attempts++}hasExceededMaxAttempts(){let e=this.getCurrentSubtask();return!e||!e.maxAttempts?!1:e.attempts>e.maxAttempts}handleError(e){this.context.errors.push(e),this.emit(`error:occurred`,{error:e})}handleRetry(){this.currentPhase===`SUBTASK_LOOP`&&(this.context.currentTDDPhase=`RED`,this.emit(`tdd:red:started`))}retryCurrentSubtask(){this.currentPhase===`SUBTASK_LOOP`&&(this.context.currentTDDPhase=`RED`,this.emit(`tdd:red:started`))}handleMaxAttemptsExceeded(){let e=this.getCurrentSubtask();e&&(e.status=`failed`,this.emit(`subtask:failed`,{subtaskId:e.id,attempts:e.attempts,maxAttempts:e.maxAttempts}))}isAborted(){return this.aborted}canResumeFromState(e){return!(![`PREFLIGHT`,`BRANCH_SETUP`,`SUBTASK_LOOP`,`FINALIZE`,`COMPLETE`].includes(e.phase)||!e.context||typeof e.context!=`object`||!e.context.taskId||!Array.isArray(e.context.subtasks)||typeof e.context.currentSubtaskIndex!=`number`||!Array.isArray(e.context.errors))}setTestResultValidator(e){this.testResultValidator=e,this.emit(`adapter:configured`,{adapterType:`test-validator`})}hasTestResultValidator(){return!!this.testResultValidator}removeTestResultValidator(){this.testResultValidator=void 0}onGitOperation(e){this.gitOperationHook=e}onExecute(e){this.executeHook=e}executeCommand(e){this.executeHook&&this.executeHook(e,this.context)}};const wn=y.object({line:y.number().min(0).max(100),branch:y.number().min(0).max(100),function:y.number().min(0).max(100),statement:y.number().min(0).max(100)}),Tn=y.object({total:y.number().int().nonnegative(),passed:y.number().int().nonnegative(),failed:y.number().int().nonnegative(),skipped:y.number().int().nonnegative(),phase:y.enum([`RED`,`GREEN`,`REFACTOR`]),coverage:wn.optional()});var En=class{validate(e){let t=[],n=[],r=[],i=Tn.safeParse(e);if(!i.success){let e=i.error.issues||[];return t.push(...e.map(e=>`${e.path.length>0?`${e.path.join(`.`)}: `:``}${e.message}`)),{valid:!1,errors:t,warnings:n,suggestions:r}}return e.passed+e.failed+e.skipped!==e.total&&t.push(`Total tests must equal passed + failed + skipped`),t.length>0?{valid:!1,errors:t,warnings:n,suggestions:r}:{valid:!0,errors:t,warnings:n,suggestions:r}}validateRedPhase(e){let t=this.validate(e);if(!t.valid)return t;let n=[],r=[];return e.failed===0&&(n.push(`RED phase must have at least one failing test`),r.push(`Write failing tests first to follow TDD workflow`)),e.total===0&&(n.push(`Cannot validate empty test suite`),r.push(`Add at least one test to begin TDD cycle`)),{valid:n.length===0,errors:n,suggestions:r}}validateGreenPhase(e,t){let n=this.validate(e);if(!n.valid)return n;let r=[],i=[],a=[];return e.failed>0&&(r.push(`GREEN phase must have zero failures`),a.push(`Fix implementation to make all tests pass`)),e.passed===0&&(r.push(`GREEN phase must have at least one passing test`),a.push(`Ensure tests exist and implementation makes them pass`)),t!==void 0&&e.total<t&&(i.push(`Test count decreased from ${t} to ${e.total}`),a.push(`Verify that no tests were accidentally removed`)),{valid:r.length===0,errors:r,warnings:i,suggestions:a}}validateCoverage(e,t){let n=this.validate(e);if(!n.valid)return n;let r=[],i=[];if(!e.coverage)return{valid:!0,errors:[],suggestions:[]};let a=e.coverage,o=[];return t.line!==void 0&&a.line<t.line&&o.push(`line coverage (${a.line}% < ${t.line}%)`),t.branch!==void 0&&a.branch<t.branch&&o.push(`branch coverage (${a.branch}% < ${t.branch}%)`),t.function!==void 0&&a.function<t.function&&o.push(`function coverage (${a.function}% < ${t.function}%)`),t.statement!==void 0&&a.statement<t.statement&&o.push(`statement coverage (${a.statement}% < ${t.statement}%)`),o.length>0&&(r.push(`Coverage thresholds not met: ${o.join(`, `)}`),i.push(`Add more tests to improve code coverage`)),{valid:r.length===0,errors:r,suggestions:i}}validatePhase(e,t){let n=t?.phase??e.phase,r;if(r=n===`RED`?this.validateRedPhase(e):this.validateGreenPhase(e,t?.previousTestCount),!r.valid)return r;if(t?.coverageThresholds){let n=this.validateCoverage(e,t.coverageThresholds);return{valid:n.valid,errors:[...r.errors||[],...n.errors],warnings:r.warnings,suggestions:[...r.suggestions||[],...n.suggestions||[]]}}return r}};async function Dn(e,t){let n={...t,timestamp:(/* @__PURE__ */ new Date()).toISOString()};await g.ensureDir(m.dirname(e));let r=JSON.stringify(n)+`
|
|
207
|
+
`;await g.appendFile(e,r,`utf-8`)}const On=[`workflow:started`,`workflow:completed`,`workflow:error`,`workflow:resumed`,`phase:entered`,`phase:exited`,`tdd:feature-already-implemented`,`tdd:red:started`,`tdd:red:completed`,`tdd:green:started`,`tdd:green:completed`,`tdd:commit:started`,`tdd:commit:completed`,`subtask:started`,`subtask:completed`,`subtask:failed`,`test:run`,`test:passed`,`test:failed`,`git:branch:created`,`git:commit:created`,`error:occurred`,`state:persisted`,`progress:updated`,`adapter:configured`];var kn=class{activityLogPath;orchestrator;logger=x(`WorkflowActivityLogger`);listenerMap=/* @__PURE__ */ new Map;isActive=!1;constructor(e,t){this.orchestrator=e,this.activityLogPath=t}start(){this.isActive?this.logger.warn(`Activity logger is already active`):(On.forEach(e=>{let t=e=>this.logEvent(e);this.listenerMap.set(e,t),this.orchestrator.on(e,t)}),this.isActive=!0,this.logger.debug(`Activity logger started, logging to: ${this.activityLogPath}`))}stop(){this.isActive&&(this.listenerMap.forEach((e,t)=>{this.orchestrator.off(t,e)}),this.listenerMap.clear(),this.isActive=!1,this.logger.debug(`Activity logger stopped and listeners removed`))}async logEvent(e){if(this.isActive)try{let t=e.timestamp instanceof Date?e.timestamp.toISOString():new Date(e.timestamp).toISOString(),n={type:e.type,phase:e.phase,tddPhase:e.tddPhase,subtaskId:e.subtaskId,eventTimestamp:t,...e.data||{}};await Dn(this.activityLogPath,n)}catch(t){this.logger.error(`Failed to log activity event ${e.type}: ${t.message}`)}}getActivityLogPath(){return this.activityLogPath}isLogging(){return this.isActive}};function An(e,t){let n=String(t);return n.includes(`.`)?n:`${e}.${n}`}var jn=class{projectRoot;stateManager;taskStatusUpdater;tag;logger=x(`WorkflowService`);orchestrator;activityLogger;constructor(e){typeof e==`string`?this.projectRoot=e:(this.projectRoot=e.projectRoot,this.taskStatusUpdater=e.taskStatusUpdater,this.tag=e.tag),this.stateManager=new Sn(this.projectRoot)}async updateTaskStatus(e,t,n){if(this.taskStatusUpdater)try{await this.taskStatusUpdater.updateStatus(e,t,n??this.tag)}catch(t){let n=t instanceof Error?t.message:String(t);this.logger.warn(`Failed to update task ${e} status: ${n}`)}}async hasWorkflow(){return await this.stateManager.exists()}async startWorkflow(e){let{taskId:t,taskTitle:n,subtasks:r,maxAttempts:i=3,force:a,tag:o}=e;if(await this.hasWorkflow()&&!a)throw Error(`Workflow already exists. Use force=true to override or resume existing workflow.`);let s=new T(this.projectRoot);await s.ensureGitRepository(),await s.ensureCleanWorkingTree();let c=r.map(e=>({id:An(t,e.id),title:e.title,status:e.status===`done`?`completed`:`pending`,attempts:0,maxAttempts:e.maxAttempts||i})),l=c.findIndex(e=>e.status!==`completed`);if(l===-1)throw Error(`All subtasks for task ${t} are already completed. Nothing to do.`);let u={taskId:t,subtasks:c,currentSubtaskIndex:l,tag:o,errors:[],metadata:{startedAt:(/* @__PURE__ */ new Date()).toISOString(),taskTitle:n,resumedFromSubtask:l>0?c[l].id:void 0}};this.orchestrator=new Cn(u),this.orchestrator.enableAutoPersist(async e=>{await this.stateManager.save(e)}),this.activityLogger=new kn(this.orchestrator,this.stateManager.getActivityLogPath()),this.activityLogger.start(),await this.orchestrator.transition({type:`PREFLIGHT_COMPLETE`});let d=this.generateBranchName(t,n,o);try{if(await s.getCurrentBranch()!==d){if(await s.branchExists(d))throw Error(`Branch ${d} already exists from an earlier run. Check it out to continue there, or delete it with: git branch -D ${d}`);await s.createAndCheckoutBranch(d)}}catch(e){try{this.activityLogger?.stop(),await this.abortWorkflow()}catch(e){this.logger.warn(`Could not clear workflow state: ${e}`)}throw e}return await this.orchestrator.transition({type:`BRANCH_CREATED`,branchName:d}),await this.updateTaskStatus(t,`in-progress`,o),this.getStatus()}async resumeWorkflow(){let e=await this.stateManager.load();if(e.context.subtasks=e.context.subtasks.map(t=>({...t,id:An(e.context.taskId,t.id)})),this.orchestrator=new Cn(e.context),!this.orchestrator.canResumeFromState(e))throw Error(`Invalid workflow state. State may be corrupted. Consider starting a new workflow.`);return this.orchestrator.restoreState(e),this.orchestrator.enableAutoPersist(async e=>{await this.stateManager.save(e)}),this.activityLogger=new kn(this.orchestrator,this.stateManager.getActivityLogPath()),this.activityLogger.start(),this.getStatus()}getStatus(){if(!this.orchestrator)throw Error(`No active workflow. Start or resume a workflow first.`);let e=this.orchestrator.getContext(),t=this.orchestrator.getProgress(),n=this.orchestrator.getCurrentSubtask();return{taskId:e.taskId,phase:this.orchestrator.getCurrentPhase(),tddPhase:this.orchestrator.getCurrentTDDPhase(),branchName:e.branchName,currentSubtask:n?{id:n.id,title:n.title,attempts:n.attempts,maxAttempts:n.maxAttempts||3}:void 0,progress:t}}getContext(){if(!this.orchestrator)throw Error(`No active workflow. Start or resume a workflow first.`);return this.orchestrator.getContext()}getNextAction(){if(!this.orchestrator)throw Error(`No active workflow. Start or resume a workflow first.`);let e=this.orchestrator.getCurrentPhase(),t=this.orchestrator.getCurrentTDDPhase(),n=this.orchestrator.getCurrentSubtask();if(e===`COMPLETE`)return{action:`workflow_complete`,description:`All subtasks completed`,nextSteps:`All subtasks completed! Review the entire implementation and merge your branch when ready.`,phase:e};if(e===`FINALIZE`)return{action:`finalize_workflow`,description:`Finalize and complete the workflow`,nextSteps:`All subtasks are complete! Use autopilot_finalize to verify no uncommitted changes remain and mark the workflow as complete.`,phase:e};if(e!==`SUBTASK_LOOP`||!t||!n)return{action:`unknown`,description:`Workflow is not in active state`,nextSteps:`Use autopilot_status to check workflow state.`,phase:e};let r={phase:e,tddPhase:t,subtask:{id:n.id,title:n.title}};switch(t){case`RED`:return{...r,action:`generate_test`,description:`Generate failing test for current subtask`,nextSteps:`Write failing tests for subtask ${n.id}: "${n.title}". Create test file(s) that validate the expected behavior. Run tests and use autopilot_complete_phase with results. Note: If all tests pass (0 failures), the feature is already implemented and the subtask will be auto-completed.`};case`GREEN`:return{...r,action:`implement_code`,description:`Implement feature to make tests pass`,nextSteps:`Implement code to make tests pass for subtask ${n.id}: "${n.title}". Write the minimal code needed to pass all tests (GREEN phase), then use autopilot_complete_phase with test results.`};case`COMMIT`:return{...r,action:`commit_changes`,description:`Commit RED-GREEN cycle changes`,nextSteps:`Review and commit your changes for subtask ${n.id}: "${n.title}". Use autopilot_commit to create the commit and advance to the next subtask.`};default:return{...r,action:`unknown`,description:`Unknown TDD phase`,nextSteps:`Use autopilot_status to check workflow state.`}}}async completePhase(e){if(!this.orchestrator)throw Error(`No active workflow. Start or resume a workflow first.`);let t=this.orchestrator.getCurrentTDDPhase();if(!t)throw Error(`Not in active TDD phase`);switch(this.assertTrustworthy(t,e),t){case`RED`:await this.orchestrator.transition({type:`RED_PHASE_COMPLETE`,testResults:e});break;case`GREEN`:await this.orchestrator.transition({type:`GREEN_PHASE_COMPLETE`,testResults:e});break;case`COMMIT`:throw Error(`Cannot complete COMMIT phase with test results. Use commit() instead.`);default:throw Error(`Unknown TDD phase: ${t}`)}return this.getStatus()}assertTrustworthy(e,t){let n=new En,r=[...(e===`GREEN`?n.validateGreenPhase(t):n.validate(t)).errors];if(e===`RED`&&t.total===0&&r.push(`Cannot validate empty test suite`),r.length>0)throw Error(`Invalid test results: ${r.join(`; `)}`)}async commit(){if(!this.orchestrator)throw Error(`No active workflow. Start or resume a workflow first.`);let e=this.orchestrator.getCurrentTDDPhase();if(e!==`COMMIT`)throw Error(`Cannot commit in ${e} phase. Complete RED and GREEN phases first.`);let t=this.orchestrator.getCurrentSubtask()?.id;await this.orchestrator.transition({type:`COMMIT_COMPLETE`});let n=this.orchestrator.getProgress();if(n.current<n.total?await this.orchestrator.transition({type:`SUBTASK_COMPLETE`}):await this.orchestrator.transition({type:`ALL_SUBTASKS_COMPLETE`}),t){let e=this.orchestrator.getContext();await this.updateTaskStatus(An(e.taskId,t),`done`,e.tag)}return this.getStatus()}async finalizeWorkflow(){if(!this.orchestrator)throw Error(`No active workflow. Start or resume a workflow first.`);let e=this.orchestrator.getCurrentPhase();if(e!==`FINALIZE`)throw Error(`Cannot finalize workflow in ${e} phase. Complete all subtasks first.`);let t=await new T(this.projectRoot).getStatusSummary();if(!t.isClean)throw Error(`Cannot finalize workflow: working tree has uncommitted changes.\nStaged: ${t.staged}, Modified: ${t.modified}, Deleted: ${t.deleted}, Untracked: ${t.untracked}\nPlease commit all changes before finalizing the workflow.`);let n=this.orchestrator.getContext(),r=n.taskId;await this.orchestrator.transition({type:`FINALIZE_COMPLETE`});let i=this.getStatus();return await this.updateTaskStatus(r,`done`,n.tag),await this.stateManager.delete(),this.orchestrator=void 0,i}async abortWorkflow(){this.orchestrator&&await this.orchestrator.transition({type:`ABORT`}),await this.stateManager.delete(),this.orchestrator=void 0}generateBranchName(e,t,n){let r=t.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).substring(0,50),i=e.replace(/\./g,`-`);return`${n?`tm/${n}`:`tm`}/task-${i}-${r}`}},Mn=class{workflowService=null;projectRoot;configManager;tasksDomain=null;constructor(e){this.configManager=e,this.projectRoot=e.getProjectRoot()}setTasksDomain(e){this.tasksDomain=e}getWorkflowService(){if(!this.workflowService){let e=this.configManager.getActiveTag(),t=this.tasksDomain?{updateStatus:async(e,t,n)=>{await this.tasksDomain.updateStatus(e,t,n)}}:void 0;this.workflowService=new jn({projectRoot:this.projectRoot,taskStatusUpdater:t,tag:e})}return this.workflowService}resetWorkflowService(){this.workflowService=null}async start(e){return this.resetWorkflowService(),this.getWorkflowService().startWorkflow(e)}async resume(){return this.resetWorkflowService(),this.getWorkflowService().resumeWorkflow()}getStatus(){return this.getWorkflowService().getStatus()}getContext(){return this.getWorkflowService().getContext()}getNextAction(){return this.getWorkflowService().getNextAction()}async completePhase(e){return this.getWorkflowService().completePhase(e)}async commit(){return this.getWorkflowService().commit()}async finalize(){let e=await this.getWorkflowService().finalizeWorkflow();return this.resetWorkflowService(),e}async abort(){await this.getWorkflowService().abortWorkflow(),this.resetWorkflowService()}async hasWorkflow(){return this.getWorkflowService().hasWorkflow()}},Nn=class e{_projectPath;_configManager;_logger;_tasks;_workflow;_git;_config;_loop;_github;get tasks(){return this._tasks}get workflow(){return this._workflow}get git(){return this._git}get config(){return this._config}get loop(){return this._loop}get github(){return this._github}get logger(){return this._logger}static async create(t){let n=new e(t);return await n.initialize(),n}_options;constructor(e){if(!e.projectPath)throw new C(`Project path is required`,S.MISSING_CONFIGURATION);if(!t.isAbsolute(e.projectPath))throw new C(`Project path must be an absolute path, received: "${e.projectPath}"`,S.INVALID_INPUT);this._projectPath=t.resolve(e.projectPath),this._options=e}async initialize(){try{this._logger=he(this._options.loggerConfig),this._configManager=await Se.create(this._projectPath),this._options.configuration&&await this._configManager.updateConfig(this._options.configuration),this._tasks=new xn(this._configManager),this._workflow=new Mn(this._configManager),this._git=new Ae(this._projectPath),this._config=new ue(this._configManager),this._loop=new pt(this._configManager),await this._tasks.initialize(),this._workflow.setTasksDomain(this._tasks),this._github=new at(this._projectPath,{getActiveTag:()=>this._configManager.getActiveTag()}),this._github.setTasksDomain(this._tasks),this._logger.info(`TmCore initialized successfully`)}catch(e){throw this._logger&&this._logger.error(`Failed to initialize TmCore:`,e),new C(`Failed to initialize TmCore`,S.INTERNAL_ERROR,{operation:`initialize`},e)}}get projectPath(){return this._projectPath}async close(){this._tasks&&await this._tasks.close()}};async function Pn(e){return Nn.create(e)}function Fn(e,n){try{return c.existsSync(t.join(e,n))}catch{return!1}}function $(e,t){return t.some(t=>Fn(e,t))}function In(e=process.cwd()){let n=t.resolve(e),r=t.parse(n).root,i=0,a=null;if($(n,It)||$(n,Lt))return n;let o=t.dirname(n);for(i=1;i<50;){let e=$(o,It),n=$(o,Lt);if(e&&(n||i===1))return o;if(n&&!e){a=o;break}if(o===r)break;let s=t.dirname(o);if(s===o)break;o=s,i++}for(n=a||t.resolve(e),i=0;i<50;){if($(n,Rt))return n;if(n===r)break;let e=t.dirname(n);if(e===n)break;n=e,i++}return t.resolve(e)}function Ln(e){return Fn(e,`.taskgraph/config.json`)||Fn(e,`.taskgraph/tasks/tasks.json`)}function Rn(e){if(!e)return``;let n=String(e).split(t.sep),r=n.findIndex(e=>e===`.taskgraph`);return r===-1?String(e):n.slice(0,r).join(t.sep)||t.sep}function zn(e){let t=e?r(process.cwd(),e):In();return{projectRoot:t,tasksPath:n(t,Ft)}}function Bn(e){return le(e,`hh:mm:ss a`)}const Vn=/^\d+$/,Hn=/^\d+\.\d+$/;function Un(e){if(!e)return!1;let t=e.trim();return Vn.test(t)||Hn.test(t)}function Wn(e){return e?Vn.test(e.trim()):!1}const Gn=y.string().trim().refine(Un,{message:`Invalid task ID format. Expected numeric ("1") or subtask ("1.2")`}),Kn=y.string().trim().refine(Un,{message:`Invalid task ID format. Expected numeric ("1")`}).refine(Wn,{message:`Subtask IDs are not allowed. Please provide a main task ID (e.g., "1")`}),qn=x(`TaskFilters`),Jn=[`pending`,`in-progress`,`review`];function Yn(e){let t=new Map(e.map(e=>[String(e.id),[]])),n=[];for(let r of e)for(let e of r.dependencies??[]){let i=String(e),a=t.get(i);a?a.push(String(r.id)):n.push({taskId:String(r.id),depId:i})}return{blocksMap:t,invalidDependencies:n}}function Xn(e){let t=new Set(e.filter(e=>U(e.status)).map(e=>String(e.id)));return e.filter(e=>(zt.includes(e.status)||qn.warn(`Task ${e.id} has unexpected status "${e.status}". Valid statuses are: ${zt.join(`, `)}`),Jn.includes(e.status)?(e.dependencies??[]).every(e=>t.has(String(e))):!1))}function Zn(e){return e.filter(e=>e.blocks.length>0)}export{B as A,be as B,zt as C,jt as D,Pt as E,ze as F,Me as I,je as L,ot as M,Be as N,Mt as O,Re as P,ke as R,Ht as S,Nt as T,sn as _,Gn as a,$t as b,In as c,Pn as d,dn as f,nn as g,an as h,Kn as i,xt as j,Ct as k,Ln as l,Zt as m,Zn as n,Bn as o,en as p,Xn as r,zn as s,Yn as t,Rn as u,on as v,U as w,Vt as x,rn as y,T as z};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{c as createTagFromBranch,m as useTag}from"./src-CK3olqVP.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as e,a as t,b as n,c as r,d as i,g as a,h as o,i as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g,v as _,y as v}from"./task-manager-BipYRXFW.js";import{K as y,rt as b}from"./config-manager-CKwBfpAp.js";import{h as x}from"./src-CK3olqVP.js";import{n as S}from"./research-C8rAJdp0.js";import{t as C}from"./response-language-C8euRV2w.js";export{n as addSubtask,v as addTask,_ as analyzeTaskComplexity,e as clearSubtasks,o as expandAllTasks,a as expandTask,x as findNextTask,y as findTaskById,l as isTaskDependentOn,f as listTasks,i as moveTask,g as parsePRD,S as performResearch,b as readComplexityReport,c as removeSubtask,m as removeTask,s as scopeDownTask,t as scopeUpTask,C as setResponseLanguage,r as taskExists,p as updateSubtaskById,u as updateTaskById,h as updateTasks,d as validateStrength};
|