taskgraph-ai 0.54.7 → 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/dist/taskgraph.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{c as e}from"./src-xo9dUCIC.js";import t from"chalk";import{join as n}from"node:path";import r from"dotenv";function i(e,n=t){e.NO_COLOR&&e.FORCE_COLOR===void 0&&(e.FORCE_COLOR=`0`,n.level=0)}i(process.env);const a=process.cwd(),o=e();r.config({path:n(o,`.env`),quiet:!0}),process.env.TASKGRAPH_ORIGINAL_CWD=a,process.env.DEBUG===`1`&&console.error(`DEBUG - dev.js received args:`,process.argv.slice(2));const{runCLI:s}=await import(`./commands-BQrpRKx2.js`);s(process.argv);export{};
2
+ import{c as e}from"./src-DRpuDe-k.js";import t from"chalk";import{join as n}from"node:path";import r from"dotenv";function i(e,n=t){e.NO_COLOR&&e.FORCE_COLOR===void 0&&(e.FORCE_COLOR=`0`,n.level=0)}i(process.env);const a=process.cwd(),o=e();r.config({path:n(o,`.env`),quiet:!0}),process.env.TASKGRAPH_ORIGINAL_CWD=a,process.env.DEBUG===`1`&&console.error(`DEBUG - dev.js received args:`,process.argv.slice(2));const{runCLI:s}=await import(`./commands-DJc5m0Gi.js`);s(process.argv);export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskgraph-ai",
3
- "version": "0.54.7",
3
+ "version": "0.54.12",
4
4
  "description": "Task management for AI-driven development, built for Claude Code: plan from a PRD, then work tasks one at a time with slash commands, a CLI or an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- export{r as logAiUsage}from"./ai-services-unified-C29ea2f8.js";
@@ -1,3 +0,0 @@
1
- import{n as e}from"./rolldown-runtime-BxhE39jg.js";import{$ as t,D as n,G as r,M as i,S as a,X as o,at as s,b as c,d as l,h as u,i as d,m as f,o as p,p as m,u as h,v as g,x as _,y as v}from"./config-manager-CecOyMub.js";import y from"path";import{execSync as b}from"child_process";import x from"fs";import{createClaudeCode as S}from"ai-sdk-provider-claude-code";import*as C from"ai";import{jsonrepair as w}from"jsonrepair";import{EnvHttpProxyAgent as T}from"undici";const E=[`Read`,`Grep`,`Glob`];function D(e){let t=e,n=[];for(;;)try{return y.join(x.realpathSync(t),...n.reverse())}catch{let r=y.dirname(t);if(r===t)return e;n.push(y.basename(t)),t=r}}function O(e,t){if(typeof t!=`string`||t.startsWith(`~`))return!1;let n=D(y.resolve(e,t)),r=y.relative(e,n),i=r===`..`||r.startsWith(`..${y.sep}`);return r===``||!i&&!y.isAbsolute(r)}const k=/^(\.env(\..*)?|\.git|\.npmrc|id_(rsa|dsa|ecdsa|ed25519).*)$|\.pem$/i,A=/^\.env\.(example|sample|template)$/i;function j(e){return e.split(/[\\/,]/).map(e=>e.replace(/[*?[\]{}]/g,``)).some(e=>k.test(e)&&!A.test(e))}function ee(e,t){let n=y.resolve(e,t);return[n,D(n)].some(t=>j(y.relative(e,t)))}function M(e){return e.split(/[\\/]/).includes(`..`)}function N(e){if(!y.posix.isAbsolute(e)&&!y.win32.isAbsolute(e))return null;if(!/[*?[{]/.test(e)){let t=e.replace(/[\\/]+$/,``)||e;return y.win32.isAbsolute(t)&&!y.posix.isAbsolute(t)?y.win32.dirname(t):y.posix.dirname(t)}let t=e.split(/[*?[{]/)[0],n=Math.max(t.lastIndexOf(`/`),t.lastIndexOf(`\\`));return t.slice(0,n+1)||t}function P(e,t,n={}){if(!E.includes(t))return null;let r=[n.file_path,n.path].filter(e=>e!=null);for(let n of r){if(!O(e,n))return`${t} is limited to the project directory (${e}); ${n} is outside it.`;if(ee(e,n))return`${t} may not open secret files such as .env, .git or keys (${n}).`}if(typeof n.glob==`string`&&j(n.glob))return`${t} globs may not match secret files such as .env, .git or keys.`;let i=t===`Glob`?n.pattern:void 0;if(typeof i==`string`){let n=`${t} patterns may not leave the project directory (${e}).`;if(M(i)||i.startsWith(`~`))return n;if(j(i))return`${t} patterns may not match secret files such as .env, .git or keys.`;let r=N(i);if(r!==null&&!O(e,r))return n}return null}function F(e){let t=D(y.resolve(e));return{matcher:E.join(`|`),hooks:[async e=>{let n=P(t,e?.tool_name,e?.tool_input);return n?{hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:`deny`,permissionDecisionReason:n}}:{}}]}}const{JSONParseError:te,NoObjectGeneratedError:ne,generateObject:re,generateText:ie,streamObject:ae,streamText:oe,zodSchema:se}=C,I=C.jsonSchema,ce=/* @__PURE__ */ new Set([`minimum`,`maximum`,`exclusiveMinimum`,`exclusiveMaximum`]),L=[`additionalProperties`,`contains`,`if`,`then`,`else`,`not`,`propertyNames`],R=[`allOf`,`anyOf`,`oneOf`,`prefixItems`],z=[`definitions`,`$defs`,`dependentSchemas`,`patternProperties`,`properties`],B=e=>e?Array.isArray(e)?e.includes(`integer`):e===`integer`:!1,V=e=>{if(!e||typeof e!=`object`)return e;if(Array.isArray(e))return e.map(V);let t={...e};if(B(t.type))for(let e of ce)e in t&&delete t[e];for(let e of L)t[e]&&(t[e]=V(t[e]));for(let e of R)Array.isArray(t[e])&&(t[e]=t[e].map(V));for(let e of z)if(t[e]&&typeof t[e]==`object`){let n={};for(let[r,i]of Object.entries(t[e]))n[r]=V(i);t[e]=n}return t.items&&=V(t.items),t},H=e=>{let t=se(e);if(!t||typeof t!=`object`||!t.jsonSchema)return t;let n=V(t.jsonSchema);return typeof I==`function`?I(n,{validate:t.validate}):{...t,jsonSchema:n}};function U(){let e=Number(process.env.TASKGRAPH_AI_TIMEOUT_MS);return AbortSignal.timeout(Number.isFinite(e)&&e>0?e:6e5)}var W=class e{constructor(){if(this.constructor===e)throw Error(`BaseAIProvider cannot be instantiated directly`);this.name=this.constructor.name,this._proxyAgent=null,this.needsExplicitJsonSchema=!1,this.supportsTemperature=!0}validateAuth(e){if(!e.apiKey)throw Error(`${this.name} API key is required`)}createProxyFetch(){this._projectRoot||=r();let e=this._projectRoot;if(n(null,e))return this._proxyAgent||=new T,(e,t={})=>fetch(e,{...t,dispatcher:this._proxyAgent})}validateParams(e){if(this.validateAuth(e),!e.modelId)throw Error(`${this.name} Model ID is required`);this.validateOptionalParams(e)}validateOptionalParams(e){if(e.temperature!==void 0&&(e.temperature<0||e.temperature>1))throw Error(`Temperature must be between 0 and 1`);if(e.maxTokens!==void 0){let t=Number(e.maxTokens);if(!Number.isFinite(t)||t<=0)throw Error(`maxTokens must be a finite number greater than 0`)}}validateMessages(e){if(!e||!Array.isArray(e)||e.length===0)throw Error(`Invalid or empty messages array provided`);for(let t of e)if(!t.role||!t.content)throw Error(`Invalid message format. Each message must have role and content`)}handleError(e,n){let r=n.message||`Unknown error occurred`;throw t(`error`,`${this.name} ${e} failed: ${r}`,{error:n}),Error(`${this.name} API error during ${e}: ${r}`)}getClient(e){throw Error(`getClient must be implemented by provider`)}isRequiredApiKey(){return!0}getRequiredApiKeyName(){throw Error(`getRequiredApiKeyName must be implemented by provider`)}prepareTokenParam(e,t){return t===void 0?{}:{maxOutputTokens:Math.floor(Number(t))}}prepareMessages(e){let t=e.filter(e=>e.role===`system`);return t.length===0?{messages:e}:{instructions:t.map(e=>typeof e.content==`string`?e.content:e.content.map(e=>e.text??``).join(``)).join(`
2
-
3
- `),messages:e.filter(e=>e.role!==`system`)}}async generateText(e){try{this.validateParams(e),this.validateMessages(e.messages),t(`debug`,`Generating ${this.name} text with model: ${e.modelId}`);let n=await this.getClient(e),r=await ie({abortSignal:U(),model:n(e.modelId),...this.prepareMessages(e.messages),...this.prepareTokenParam(e.modelId,e.maxTokens),...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{}});t(`debug`,`${this.name} generateText completed successfully for model: ${e.modelId}`);let i=r.usage?.inputTokens??r.usage?.promptTokens??0,a=r.usage?.outputTokens??r.usage?.completionTokens??0,o=r.usage?.totalTokens??i+a,s=r.usage?.inputTokenDetails?.cacheReadTokens??r.usage?.cachedInputTokens??0;return{text:r.text,usage:{inputTokens:i,outputTokens:a,totalTokens:o,cachedInputTokens:s}}}catch(e){this.handleError(`text generation`,e)}}async streamText(e){try{this.validateParams(e),this.validateMessages(e.messages),t(`debug`,`Streaming ${this.name} text with model: ${e.modelId}`);let n=await this.getClient(e),r=await oe({model:n(e.modelId),...this.prepareMessages(e.messages),...this.prepareTokenParam(e.modelId,e.maxTokens),...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{},...e.experimental_transform&&{experimental_transform:e.experimental_transform}});return t(`debug`,`${this.name} streamText initiated successfully for model: ${e.modelId}`),r}catch(e){this.handleError(`text streaming`,e)}}async streamObject(e){try{if(this.validateParams(e),this.validateMessages(e.messages),!e.schema)throw Error(`Schema is required for object streaming`);t(`debug`,`Streaming ${this.name} object with model: ${e.modelId}`);let n=await this.getClient(e),r=H(e.schema),i=await ae({model:n(e.modelId),...this.prepareMessages(e.messages),schema:r,mode:this.needsExplicitJsonSchema?`json`:`auto`,...e.objectName&&{schemaName:e.objectName,schemaDescription:`Generate a valid JSON object for ${e.objectName}`},...this.prepareTokenParam(e.modelId,e.maxTokens),...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{},onError:({error:e})=>t(`debug`,`${this.name} stream error: ${e?.message??e}`)});return t(`debug`,`${this.name} streamObject initiated successfully for model: ${e.modelId}`),i}catch(e){this.handleError(`object streaming`,e)}}async generateObject(e){try{if(this.validateParams(e),this.validateMessages(e.messages),!e.schema)throw Error(`Schema is required for object generation`);if(!e.objectName)throw Error(`Object name is required for object generation`);t(`debug`,`Generating ${this.name} object ('${e.objectName}') with model: ${e.modelId}`);let n=await this.getClient(e),r=H(e.schema),i=await re({abortSignal:U(),model:n(e.modelId),...this.prepareMessages(e.messages),schema:r,mode:this.needsExplicitJsonSchema?`json`:`auto`,schemaName:e.objectName,schemaDescription:`Generate a valid JSON object for ${e.objectName}`,...this.prepareTokenParam(e.modelId,e.maxTokens),...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{}});t(`debug`,`${this.name} generateObject completed successfully for model: ${e.modelId}`);let a=i.usage?.inputTokens??i.usage?.promptTokens??0,o=i.usage?.outputTokens??i.usage?.completionTokens??0,s=i.usage?.totalTokens??a+o,c=i.usage?.inputTokenDetails?.cacheReadTokens??i.usage?.cachedInputTokens??0;return{object:i.object,usage:{inputTokens:a,outputTokens:o,totalTokens:s,cachedInputTokens:c}}}catch(e){if(ne.isInstance(e)&&e.cause instanceof te&&e.cause.text){t(`warn`,`${this.name} generated malformed JSON, attempting to repair...`);try{let n=w(e.cause.text),r=JSON.parse(n);return t(`debug`,`Successfully repaired ${this.name} JSON output`),{object:r,usage:{inputTokens:e.usage?.promptTokens||e.usage?.inputTokens||0,outputTokens:e.usage?.completionTokens||e.usage?.outputTokens||0,totalTokens:e.usage?.totalTokens||0}}}catch(e){t(`error`,`Failed to repair ${this.name} JSON: ${e.message}`)}}this.handleError(`object generation`,e)}}};let G=!1;const le=[`WebSearch`,`WebFetch`],ue=[`Read`,`Grep`,`Glob`],de=/^claude(\.exe|\.cmd)?$/i;function K(e,t){let n=y.relative(e,t);return n===``||n!==`..`&&!n.startsWith(`..${y.sep}`)&&!y.isAbsolute(n)}function fe(e,t){if(typeof e!=`string`||!y.isAbsolute(e)||!de.test(y.basename(e)))return!1;let n;try{n=x.realpathSync(e)}catch{return!1}if(!t)return!0;let r=y.resolve(t),i=r;try{i=x.realpathSync(r)}catch{}return!K(r,y.resolve(e))&&!K(i,n)}function pe(e,n){let r=e.pathToClaudeCodeExecutable;if(r===void 0||fe(r,n))return e;t(`warn`,`Ignoring pathToClaudeCodeExecutable "${r}": it must be an absolute path to a file named claude outside the project`);let{pathToClaudeCodeExecutable:i,...a}=e;return a}function me(e,t,n){let r=e===`research`,i=r?le:n?ue:[];return{tools:[...i],allowedTools:[...i],permissionMode:`dontAsk`,permissionPrompts:`none`,strictMcpConfig:!0,mcpServers:{},settingSources:[],...r?{maxTurns:t.maxTurns??8}:{cwd:n??void 0,additionalDirectories:void 0,hooks:n?{PreToolUse:[F(n)]}:{}}}}var he=class extends W{constructor(){super(),this.name=`Claude Code`,this.supportedModels=_(`claude-code`),this.supportedModels.length===0&&t(`warn`,`No supported models found for claude-code provider. Check supported-models.json configuration.`),this.needsExplicitJsonSchema=!0,this.supportsTemperature=!1}getRequiredApiKeyName(){return`CLAUDE_CODE_API_KEY`}isRequiredApiKey(){return!1}validateAuth(e){if(process.env.NODE_ENV!==`test`&&!G&&!process.env.CLAUDE_CODE_OAUTH_TOKEN)try{b(`claude --version`,{stdio:`pipe`,timeout:1e3})}catch{t(`debug`,"No `claude` on PATH; using the Claude Code bundled with the Agent SDK")}finally{G=!0}}getClient(e={}){try{let n=e.projectRoot?y.resolve(e.projectRoot):null,r=pe(d(e.commandName)||{},n),i={...process.env};return process.env.CLAUDE_CODE_API_KEY?i.ANTHROPIC_API_KEY=process.env.CLAUDE_CODE_API_KEY:delete i.ANTHROPIC_API_KEY,S({defaultSettings:{systemPrompt:{type:`preset`,preset:`claude_code`},env:i,logger:{debug:e=>t(`debug`,e),info:e=>t(`debug`,e),warn:e=>t(`debug`,e),error:e=>t(`error`,e)},...r,...me(e.role,r,n)}})}catch(e){let t=String(e?.message||``);if(e?.code===`ENOENT`||/claude/i.test(t)){let t=/* @__PURE__ */ Error(`Claude Code CLI not available. Please install Claude Code CLI first. Original error: ${e.message}`);t.cause=e,this.handleError(`Claude Code CLI initialization`,t)}else this.handleError(`client initialization`,e)}}getSupportedModels(){return this.supportedModels}isModelSupported(e){return e?this.supportedModels.includes(String(e).toLowerCase()):!1}};let q=null;var J=class e{constructor(){this._providers=/* @__PURE__ */ new Map,this._initialized=!1}static getInstance(){return q||=new e,q}initialize(){return this._initialized||=!0,this}registerProvider(e,t,n={}){if(!e||typeof e!=`string`)throw Error(`Provider name must be a non-empty string`);if(!t)throw Error(`Provider instance is required`);if(typeof t.generateText!=`function`||typeof t.streamText!=`function`||typeof t.generateObject!=`function`)throw Error(`Provider must implement BaseAIProvider interface`);return this._providers.set(e,{instance:t,options:n,registeredAt:/* @__PURE__ */ new Date}),this}hasProvider(e){return this._providers.has(e)}getProvider(e){let t=this._providers.get(e);return t?t.instance:null}getAllProviders(){return new Map(this._providers)}unregisterProvider(e){return this._providers.has(e)?(this._providers.delete(e),!0):!1}reset(){this._providers.clear(),this._initialized=!1}};J.getInstance().initialize(),globalThis.AI_SDK_LOG_WARNINGS===void 0&&(globalThis.AI_SDK_LOG_WARNINGS=({warnings:e,provider:n,model:r})=>{for(let i of e){let e=i.message||i.feature||i.setting||i.type;t(`debug`,`AI SDK warning (${n??`?`}/${r??`?`}): ${e}`)}});const Y={"claude-code":new he};function X(e){if(Y[e])return Y[e];let n=J.getInstance();return n.hasProvider(e)?(t(`debug`,`Provider "${e}" found in dynamic registry`),n.getProvider(e)):null}function ge(e,n){let r={inputCost:0,outputCost:0,currency:`USD`,isUnknown:!1};if(!i||!i[e])return t(`warn`,`Provider "${e}" not found in MODEL_MAP. Cannot determine cost for model ${n}.`),{...r,isUnknown:!0};let a=i[e].find(e=>e.id===n);if(!a)return t(`debug`,`Model "${n}" not found under provider "${e}". Assuming unknown cost.`),{...r,isUnknown:!0};if(a.cost_per_1m_tokens===null)return t(`debug`,`Cost data is null for model "${n}" under provider "${e}". Pricing unknown.`),{...r,isUnknown:!0};if(a.cost_per_1m_tokens===void 0)return t(`debug`,`Cost data not found for model "${n}" under provider "${e}". Pricing unknown.`),{...r,isUnknown:!0};let o=a.cost_per_1m_tokens;return{inputCost:o.input||0,outputCost:o.output||0,currency:o.currency||`USD`,isUnknown:!1}}function _e(e,t,n,r){let i=(e||0)/1e6*n+(t||0)/1e6*r;return parseFloat(i.toFixed(6))}function ve(e){let n={currentTag:`master`,availableTags:[`master`]};try{return e?{currentTag:o(e)||`master`,availableTags:ye(e)}:n}catch(e){return p()&&t(`debug`,`Error getting tag information: ${e.message}`),n}}function ye(n){let r=[`master`];try{let t=e(`path`),i=e(`fs`),a=t.join(n,`.taskgraph`,`tasks`,`tasks.json`);if(!i.existsSync(a))return r;let o=JSON.parse(i.readFileSync(a,`utf8`));if(!o||typeof o!=`object`)return r;let s=Object.keys(o).filter(e=>be(o[e]));return s.length>0?s:r}catch(e){return p()&&t(`debug`,`Could not read tasks file for available tags: ${e.message}`),r}}function be(e){return e&&typeof e==`object`&&Array.isArray(e.tasks)}function xe(e){let t=e.message?.toLowerCase()||``;return e.name===`TimeoutError`||t.includes(`aborted`)?!1:t.includes(`rate limit`)||t.includes(`overloaded`)||t.includes(`service temporarily unavailable`)||t.includes(`timeout`)||t.includes(`network error`)||e.status===429||e.status>=500}function Se(e){try{if(e?.data?.error?.message)return e.data.error.message;if(e?.error?.message)return e.error.message;if(typeof e?.responseBody==`string`)try{let t=JSON.parse(e.responseBody);if(t?.error?.message)return t.error.message}catch{}return typeof e?.message==`string`&&e.message?e.message:typeof e==`string`?e:`An unknown AI service error occurred.`}catch{return`Failed to extract error message.`}}function Z(e,t){return{main:{provider:f(t),modelId:m(t)},research:{provider:v(t),modelId:g(t)},fallback:{provider:l(t),modelId:h(t)}}[e]||null}function Ce(e,t,n=null){let r=X(e);if(!r)throw Error(`Unknown provider '${e}' for API key resolution.`);let i=r.getRequiredApiKeyName();if(i===null)return null;let a=s(i,t,n);if(!r.isRequiredApiKey())return a||null;if(!a)throw Error(`Required API key ${i} for provider '${e}' is not set in environment, session, or .env file.`);return a}async function we(e,n,r,i,a,o){let s=0,c=n;for(;s<=2;)try{p()&&t(`info`,`Attempt ${s+1}/3 calling ${c} (Provider: ${i}, Model: ${a}, Role: ${o})`);let l=await e[n](r);return p()&&t(`info`,`${c} succeeded for role ${o} (Provider: ${i}) on attempt ${s+1}`),l}catch(e){if(t(`warn`,`Attempt ${s+1} failed for role ${o} (${c} / ${i}): ${e.message}`),xe(e)&&s<2){s++;let e=1e3*2**(s-1);t(`info`,`Something went wrong on the provider side. Retrying in ${e/1e3}s...`),await new Promise(t=>setTimeout(t,e))}else throw t(`error`,`Something went wrong on the provider side. Max retries reached for role ${o} (${c} / ${i}).`),e}throw Error(`Exhausted all retries for role ${o} (${c} / ${i})`)}async function Q(e,n){let{role:i,session:o,projectRoot:s,systemPrompt:l,prompt:d,schema:f,objectName:m,commandName:h,outputType:g,experimental_transform:_,...v}=n;p()&&t(`debug`,`${e}Service called`,{role:i,commandName:h,outputType:g,projectRoot:s});let y=s||r(),b=a(y),x;i===`main`?x=[`main`,`fallback`,`research`]:i===`research`?x=[`research`,`fallback`,`main`]:i===`fallback`?x=[`fallback`,`main`,`research`]:(t(`warn`,`Unknown initial role: ${i}. Defaulting to main -> fallback -> research sequence.`),x=[`main`,`fallback`,`research`]);let S=/* @__PURE__ */ new Set,C=null,w=`AI service call failed for all configured roles.`;for(let n of x){let r,i,a,s,p,x,T=null;try{t(`debug`,`New AI service call with role: ${n}`);let w=Z(n,y);if(!w){t(`error`,`Unknown role encountered in _unifiedServiceRunner: ${n}`),C||=/* @__PURE__ */ Error(`Unknown AI role specified: ${n}`);continue}if(r=w.provider,i=w.modelId,!r||!i){t(`warn`,`Skipping role '${n}': Provider or Model ID not configured.`),C||=/* @__PURE__ */ Error(`Configuration missing for role '${n}'. Provider: ${r}, Model: ${i}`);continue}if(S.has(`${r}/${i}`)){t(`debug`,`Skipping role '${n}': ${r}/${i} already failed for this request.`);continue}if(p=X(r?.toLowerCase()),!p){t(`warn`,`Skipping role '${n}': Provider '${r}' not supported.`),C||=/* @__PURE__ */ Error(`Unsupported provider configured: ${r}`);continue}s=u(n,y),a=Ce(r?.toLowerCase(),o,y);let E=[],D=`${l} \n\n Always respond in ${c(y)}.`;if(E.push({role:`system`,content:D.trim()}),d)E.push({role:`user`,content:d});else throw Error(`User prompt content is missing.`);let O={apiKey:a,modelId:i,maxTokens:s.maxTokens,temperature:s.temperature,messages:E,...(e===`generateObject`||e===`streamObject`)&&{schema:f,objectName:m},role:n,projectRoot:y,...h&&{commandName:h},..._&&{experimental_transform:_},...v};if(x=await we(p,e,O,r,i,n),b&&x&&x.usage)try{T=await $({userId:b,commandName:h,providerName:r,modelId:i,inputTokens:x.usage.inputTokens,outputTokens:x.usage.outputTokens,cachedInputTokens:x.usage.cachedInputTokens,outputType:g})}catch{}else b&&x&&!x.usage&&t(`warn`,`Cannot log telemetry for ${h} (${r}/${i}): AI result missing 'usage' data. (May be expected for streams)`);let k;e===`generateText`?k=x.text:e===`generateObject`?k=x.object:(e===`streamText`||e===`streamObject`||t(`error`,`Unknown serviceType in _unifiedServiceRunner: ${e}`),k=x);let A=ve(y);return{mainResult:k,telemetryData:T,tagInfo:A,providerName:r,modelId:i}}catch(a){let o=Se(a);if(t(`error`,`Service call failed for role ${n} (Provider: ${r||`unknown`}, Model: ${i||`unknown`}): ${o}`),C=a,w=o,r&&i&&S.add(`${r}/${i}`),e===`generateObject`){let e=o.toLowerCase();if(e.includes(`no endpoints found that support tool use`)||e.includes(`does not support tool_use`)||e.includes(`tool use is not supported`)||e.includes(`tools are not supported`)||e.includes(`function calling is not supported`)||e.includes(`tool use is not supported`)){let e=`Model '${i||`unknown`}' via provider '${r||`unknown`}' does not support the 'tool use' required by generateObjectService. Please configure a model that supports tool/function calling for the '${n}' role, or use generateTextService if structured output is not strictly required.`;throw t(`error`,`[Tool Support Error] ${e}`),Error(e)}}}}throw t(`error`,`All roles in the sequence [${x.join(`, `)}] failed.`),Error(w)}async function Te(e){return Q(`generateText`,{outputType:`cli`,...e})}async function Ee(e){let t={outputType:`cli`,...e};if(!t.schema)throw Error(`streamObjectService requires a schema parameter`);return Q(`streamObject`,t)}async function De(e){return Q(`generateObject`,{objectName:`generated_object`,maxRetries:3,outputType:`cli`,...e})}async function $({userId:e,commandName:n,providerName:r,modelId:i,inputTokens:a,outputTokens:o,cachedInputTokens:s,outputType:c}){try{let c=(/* @__PURE__ */ new Date()).toISOString(),l=(a||0)+(o||0),{inputCost:u,outputCost:d,currency:f,isUnknown:m}=ge(r,i),h=_e(a,o,u,d),g={timestamp:c,userId:e,commandName:n,modelUsed:i,providerName:r,inputTokens:a||0,outputTokens:o||0,totalTokens:l,...s>0?{cachedInputTokens:s}:{},totalCost:h,currency:f,isUnknownCost:m};return p()&&t(`debug`,`AI Usage Telemetry:`,g),g}catch(e){return t(`error`,`Failed to log AI usage telemetry: ${e.message}`,{error:e}),null}}export{O as a,Ee as i,Te as n,$ as r,De as t};
@@ -1,443 +0,0 @@
1
- # Meta-Development Script
2
-
3
- This folder contains a **meta-development script** (`dev.js`) and related utilities that manage tasks for an AI-driven or traditional software development workflow. The script revolves around a `tasks.json` file, which holds an up-to-date list of development tasks.
4
-
5
- ## Overview
6
-
7
- In an AI-driven development process—particularly with tools like [Cursor](https://www.cursor.so/)—it's beneficial to have a **single source of truth** for tasks. This script allows you to:
8
-
9
- 1. **Parse** a PRD or requirements document (`.txt`) to initialize a set of tasks (`tasks.json`).
10
- 2. **List** all existing tasks (IDs, statuses, titles).
11
- 3. **Update** tasks to accommodate new prompts or architecture changes (useful if you discover "implementation drift").
12
- 4. **Generate** individual task files (e.g., `task_001.txt`) for easy reference or to feed into an AI coding workflow.
13
- 5. **Set task status**—mark tasks as `done`, `pending`, or `deferred` based on progress.
14
- 6. **Expand** tasks with subtasks—break down complex tasks into smaller, more manageable subtasks.
15
- 7. **Research-backed subtask generation**—use the research model to generate more informed and contextually relevant subtasks.
16
- 8. **Clear subtasks**—remove subtasks from specified tasks to allow regeneration or restructuring.
17
- 9. **Show task details**—display detailed information about a specific task and its subtasks.
18
-
19
- ## Configuration (Updated)
20
-
21
- TaskGraph configuration is now managed through two primary methods:
22
-
23
- 1. **`.taskgraph/config.json` File (Project Root - Primary)**
24
-
25
- - Stores AI model selections (`main`, `research`, `fallback`), model parameters (`maxTokens`, `temperature`), `logLevel`, `defaultSubtasks`, `defaultPriority`, `projectName`, etc.
26
- - Managed using the `taskgraph models --setup` command or the `models` MCP tool.
27
- - This is the main configuration file for most settings.
28
-
29
- 2. **Environment Variables (optional)**
30
- - TaskGraph needs no provider API keys; AI calls run through your signed-in Claude Code.
31
- - `CLAUDE_CODE_API_KEY` optionally bills calls to a specific Anthropic API key.
32
- - See the Configuration page in the TaskGraph docs for the `TASKGRAPH_*` variables.
33
-
34
- **Important:** Settings like `MODEL`, `MAX_TOKENS`, `TEMPERATURE`, `TASKGRAPH_LOG_LEVEL`, etc., are **no longer set via `.env`**. Use `taskgraph models --setup` instead.
35
-
36
- ## How It Works
37
-
38
- 1. **`tasks.json`**:
39
-
40
- - A JSON file at the project root containing an array of tasks (each with `id`, `title`, `description`, `status`, etc.).
41
- - The `meta` field can store additional info like the project's name, version, or reference to the PRD.
42
- - Tasks can have `subtasks` for more detailed implementation steps.
43
- - Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending) to easily track progress.
44
-
45
- 2. **CLI Commands**
46
- You can run the commands via:
47
-
48
- ```bash
49
- # If installed globally
50
- taskgraph [command] [options]
51
-
52
- # If using locally within the project
53
- node scripts/dev.js [command] [options]
54
- ```
55
-
56
- Available commands:
57
-
58
- - `init`: Initialize a new project
59
- - `parse-prd`: Generate tasks from a PRD document
60
- - `list`: Display all tasks with their status
61
- - `update`: Update tasks based on new information
62
- - `generate`: Create individual task files
63
- - `set-status`: Change a task's status
64
- - `expand`: Add subtasks to a task or all tasks
65
- - `clear-subtasks`: Remove subtasks from specified tasks
66
- - `next`: Determine the next task to work on based on dependencies
67
- - `show`: Display detailed information about a specific task
68
- - `analyze-complexity`: Analyze task complexity and generate recommendations
69
- - `complexity-report`: Display the complexity analysis in a readable format
70
- - `add-dependency`: Add a dependency between tasks
71
- - `remove-dependency`: Remove a dependency from a task
72
- - `validate-dependencies`: Check for invalid dependencies
73
- - `fix-dependencies`: Fix invalid dependencies automatically
74
- - `add-task`: Add a new task using AI
75
-
76
- Run `taskgraph --help` or `node scripts/dev.js --help` to see detailed usage information.
77
-
78
- ## Listing Tasks
79
-
80
- The `list` command allows you to view all tasks and their status:
81
-
82
- ```bash
83
- # List all tasks
84
- taskgraph list
85
-
86
- # List tasks with a specific status
87
- taskgraph list --status=pending
88
-
89
- # List tasks and include their subtasks
90
- taskgraph list --with-subtasks
91
-
92
- # List tasks with a specific status and include their subtasks
93
- taskgraph list --status=pending --with-subtasks
94
- ```
95
-
96
- ## Updating Tasks
97
-
98
- The `update` command allows you to update tasks based on new information or implementation changes:
99
-
100
- ```bash
101
- # Update tasks starting from ID 4 with a new prompt
102
- taskgraph update --from=4 --prompt="Refactor tasks from ID 4 onward to use Express instead of Fastify"
103
-
104
- # Update all tasks (default from=1)
105
- taskgraph update --prompt="Add authentication to all relevant tasks"
106
-
107
- # Specify a different tasks file
108
- taskgraph update --file=custom-tasks.json --from=5 --prompt="Change database from MongoDB to PostgreSQL"
109
- ```
110
-
111
- Notes:
112
-
113
- - The `--prompt` parameter is required and should explain the changes or new context
114
- - Only tasks that aren't marked as 'done' will be updated
115
- - Tasks with ID >= the specified --from value will be updated
116
-
117
- ## Setting Task Status
118
-
119
- The `set-status` command allows you to change a task's status:
120
-
121
- ```bash
122
- # Mark a task as done
123
- taskgraph set-status --id=3 --status=done
124
-
125
- # Mark a task as pending
126
- taskgraph set-status --id=4 --status=pending
127
-
128
- # Mark a specific subtask as done
129
- taskgraph set-status --id=3.1 --status=done
130
-
131
- # Mark multiple tasks at once
132
- taskgraph set-status --id=1,2,3 --status=done
133
- ```
134
-
135
- Notes:
136
-
137
- - When marking a parent task as "done", all of its subtasks will automatically be marked as "done" as well
138
- - Common status values are 'done', 'pending', and 'deferred', but any string is accepted
139
- - You can specify multiple task IDs by separating them with commas
140
- - Subtask IDs are specified using the format `parentId.subtaskId` (e.g., `3.1`)
141
- - Dependencies are updated to show completion status (✅ for completed, ⏱️ for pending) throughout the system
142
-
143
- ## Expanding Tasks
144
-
145
- The `expand` command allows you to break down tasks into subtasks for more detailed implementation:
146
-
147
- ```bash
148
- # Expand a specific task with 3 subtasks (default)
149
- taskgraph expand --id=3
150
-
151
- # Expand a specific task with 5 subtasks
152
- taskgraph expand --id=3 --num=5
153
-
154
- # Expand a task with additional context
155
- taskgraph expand --id=3 --prompt="Focus on security aspects"
156
-
157
- # Expand all pending tasks that don't have subtasks
158
- taskgraph expand --all
159
-
160
- # Force regeneration of subtasks for all pending tasks
161
- taskgraph expand --all --force
162
-
163
- # Use the research model for research-backed subtask generation
164
- taskgraph expand --id=3 --research
165
-
166
- # Use the research model for research-backed generation on all pending tasks
167
- taskgraph expand --all --research
168
- ```
169
-
170
- ## Clearing Subtasks
171
-
172
- The `clear-subtasks` command allows you to remove subtasks from specified tasks:
173
-
174
- ```bash
175
- # Clear subtasks from a specific task
176
- taskgraph clear-subtasks --id=3
177
-
178
- # Clear subtasks from multiple tasks
179
- taskgraph clear-subtasks --id=1,2,3
180
-
181
- # Clear subtasks from all tasks
182
- taskgraph clear-subtasks --all
183
- ```
184
-
185
- Notes:
186
-
187
- - After clearing subtasks, task files are automatically regenerated
188
- - This is useful when you want to regenerate subtasks with a different approach
189
- - Can be combined with the `expand` command to immediately generate new subtasks
190
- - Works with both parent tasks and individual subtasks
191
-
192
- ## AI Integration (Updated)
193
-
194
- - The script now uses a unified AI service layer (`ai-services-unified.js`).
195
- - Model selection (for example `opus`, `sonnet` or `haiku`) is determined by the configuration in `.taskgraph/config.json` based on the requested `role` (`main` or `research`).
196
- - No provider API keys are needed; calls run through your signed-in Claude Code.
197
- - To use the research capabilities (for example `expand --research`), configure a model for the `research` role with `taskgraph models --setup`.
198
-
199
- ## Logging
200
-
201
- The script supports different logging levels controlled by the `TASKGRAPH_LOG_LEVEL` environment variable:
202
-
203
- - `debug`: Detailed information, typically useful for troubleshooting
204
- - `info`: Confirmation that things are working as expected (default)
205
- - `warn`: Warning messages that don't prevent execution
206
- - `error`: Error messages that might prevent execution
207
-
208
- When `DEBUG=true` is set, debug logs are also written to a `dev-debug.log` file in the project root.
209
-
210
- ## Managing Task Dependencies
211
-
212
- The `add-dependency` and `remove-dependency` commands allow you to manage task dependencies:
213
-
214
- ```bash
215
- # Add a dependency to a task
216
- taskgraph add-dependency --id=<id> --depends-on=<id>
217
-
218
- # Remove a dependency from a task
219
- taskgraph remove-dependency --id=<id> --depends-on=<id>
220
- ```
221
-
222
- These commands:
223
-
224
- 1. **Allow precise dependency management**:
225
-
226
- - Add dependencies between tasks with automatic validation
227
- - Remove dependencies when they're no longer needed
228
- - Update task files automatically after changes
229
-
230
- 2. **Include validation checks**:
231
-
232
- - Prevent circular dependencies (a task depending on itself)
233
- - Prevent duplicate dependencies
234
- - Verify that both tasks exist before adding/removing dependencies
235
- - Check if dependencies exist before attempting to remove them
236
-
237
- 3. **Provide clear feedback**:
238
-
239
- - Success messages confirm when dependencies are added/removed
240
- - Error messages explain why operations failed (if applicable)
241
-
242
- 4. **Automatically update task files**:
243
- - Regenerates task files to reflect dependency changes
244
- - Ensures tasks and their files stay synchronized
245
-
246
- ## Dependency Validation and Fixing
247
-
248
- The script provides two specialized commands to ensure task dependencies remain valid and properly maintained:
249
-
250
- ### Validating Dependencies
251
-
252
- The `validate-dependencies` command allows you to check for invalid dependencies without making changes:
253
-
254
- ```bash
255
- # Check for invalid dependencies in tasks.json
256
- taskgraph validate-dependencies
257
-
258
- # Specify a different tasks file
259
- taskgraph validate-dependencies --file=custom-tasks.json
260
- ```
261
-
262
- This command:
263
-
264
- - Scans all tasks and subtasks for non-existent dependencies
265
- - Identifies potential self-dependencies (tasks referencing themselves)
266
- - Reports all found issues without modifying files
267
- - Provides a comprehensive summary of dependency state
268
- - Gives detailed statistics on task dependencies
269
-
270
- Use this command to audit your task structure before applying fixes.
271
-
272
- ### Fixing Dependencies
273
-
274
- The `fix-dependencies` command proactively finds and fixes all invalid dependencies:
275
-
276
- ```bash
277
- # Find and fix all invalid dependencies
278
- taskgraph fix-dependencies
279
-
280
- # Specify a different tasks file
281
- taskgraph fix-dependencies --file=custom-tasks.json
282
- ```
283
-
284
- This command:
285
-
286
- 1. **Validates all dependencies** across tasks and subtasks
287
- 2. **Automatically removes**:
288
- - References to non-existent tasks and subtasks
289
- - Self-dependencies (tasks depending on themselves)
290
- 3. **Fixes issues in both**:
291
- - The tasks.json data structure
292
- - Individual task files during regeneration
293
- 4. **Provides a detailed report**:
294
- - Types of issues fixed (non-existent vs. self-dependencies)
295
- - Number of tasks affected (tasks vs. subtasks)
296
- - Where fixes were applied (tasks.json vs. task files)
297
- - List of all individual fixes made
298
-
299
- This is especially useful when tasks have been deleted or IDs have changed, potentially breaking dependency chains.
300
-
301
- ## Analyzing Task Complexity
302
-
303
- The `analyze-complexity` command allows you to automatically assess task complexity and generate expansion recommendations:
304
-
305
- ```bash
306
- # Analyze all tasks and generate expansion recommendations
307
- taskgraph analyze-complexity
308
-
309
- # Specify a custom output file
310
- taskgraph analyze-complexity --output=custom-report.json
311
-
312
- # Override the model used for analysis
313
- taskgraph analyze-complexity --model=claude-3-opus-20240229
314
-
315
- # Set a custom complexity threshold (1-10)
316
- taskgraph analyze-complexity --threshold=6
317
-
318
- # Use the research model for research-backed complexity analysis
319
- taskgraph analyze-complexity --research
320
- ```
321
-
322
- Notes:
323
-
324
- - The command uses Claude to analyze each task's complexity (or the research model with the --research flag)
325
- - Tasks are scored on a scale of 1-10
326
- - Each task receives a recommended number of subtasks based on DEFAULT_SUBTASKS configuration
327
- - The default output path is `scripts/task-complexity-report.json`
328
- - Each task in the analysis includes a ready-to-use `expansionCommand` that can be copied directly to the terminal or executed programmatically
329
- - Tasks with complexity scores below the threshold (default: 5) may not need expansion
330
- - The research flag provides more contextual and informed complexity assessments
331
-
332
- ### Integration with Expand Command
333
-
334
- The `expand` command automatically checks for and uses complexity analysis if available:
335
-
336
- ```bash
337
- # Expand a task, using complexity report recommendations if available
338
- taskgraph expand --id=8
339
-
340
- # Expand all tasks, prioritizing by complexity score if a report exists
341
- taskgraph expand --all
342
-
343
- # Override recommendations with explicit values
344
- taskgraph expand --id=8 --num=5 --prompt="Custom prompt"
345
- ```
346
-
347
- When a complexity report exists:
348
-
349
- - The `expand` command will use the recommended subtask count from the report (unless overridden)
350
- - It will use the tailored expansion prompt from the report (unless a custom prompt is provided)
351
- - When using `--all`, tasks are sorted by complexity score (highest first)
352
- - The `--research` flag is preserved from the complexity analysis to expansion
353
-
354
- The output report structure is:
355
-
356
- ```json
357
- {
358
- "meta": {
359
- "generatedAt": "2023-06-15T12:34:56.789Z",
360
- "tasksAnalyzed": 20,
361
- "thresholdScore": 5,
362
- "projectName": "Your Project Name",
363
- "usedResearch": true
364
- },
365
- "complexityAnalysis": [
366
- {
367
- "taskId": 8,
368
- "taskTitle": "Develop Implementation Drift Handling",
369
- "complexityScore": 9.5,
370
- "recommendedSubtasks": 6,
371
- "expansionPrompt": "Create subtasks that handle detecting...",
372
- "reasoning": "This task requires sophisticated logic...",
373
- "expansionCommand": "taskgraph expand --id=8 --num=6 --prompt=\"Create subtasks...\" --research"
374
- }
375
- // More tasks sorted by complexity score (highest first)
376
- ]
377
- }
378
- ```
379
-
380
- ## Finding the Next Task
381
-
382
- The `next` command helps you determine which task to work on next based on dependencies and status:
383
-
384
- ```bash
385
- # Show the next task to work on
386
- taskgraph next
387
-
388
- # Specify a different tasks file
389
- taskgraph next --file=custom-tasks.json
390
- ```
391
-
392
- This command:
393
-
394
- 1. Identifies all **eligible tasks** - pending or in-progress tasks whose dependencies are all satisfied (marked as done)
395
- 2. **Prioritizes** these eligible tasks by:
396
- - Priority level (high > medium > low)
397
- - Number of dependencies (fewer dependencies first)
398
- - Task ID (lower ID first)
399
- 3. **Displays** comprehensive information about the selected task:
400
- - Basic task details (ID, title, priority, dependencies)
401
- - Detailed description and implementation details
402
- - Subtasks if they exist
403
- 4. Provides **contextual suggested actions**:
404
- - Command to mark the task as in-progress
405
- - Command to mark the task as done when completed
406
- - Commands for working with subtasks (update status or expand)
407
-
408
- This feature ensures you're always working on the most appropriate task based on your project's current state and dependency structure.
409
-
410
- ## Showing Task Details
411
-
412
- The `show` command allows you to view detailed information about a specific task:
413
-
414
- ```bash
415
- # Show details for a specific task
416
- taskgraph show 1
417
-
418
- # Alternative syntax with --id option
419
- taskgraph show --id=1
420
-
421
- # Show details for a subtask
422
- taskgraph show --id=1.2
423
-
424
- # Specify a different tasks file
425
- taskgraph show 3 --file=custom-tasks.json
426
- ```
427
-
428
- This command:
429
-
430
- 1. **Displays comprehensive information** about the specified task:
431
- - Basic task details (ID, title, priority, dependencies, status)
432
- - Full description and implementation details
433
- - Test strategy information
434
- - Subtasks if they exist
435
- 2. **Handles both regular tasks and subtasks**:
436
- - For regular tasks, shows all subtasks and their status
437
- - For subtasks, shows the parent task relationship
438
- 3. **Provides contextual suggested actions**:
439
- - Commands to update the task status
440
- - Commands for working with subtasks
441
- - For subtasks, provides a link to view the parent task
442
-
443
- This command is particularly useful when you need to examine a specific task in detail before implementing it or when you want to check the status and details of a particular task.