taskgraph-ai 0.52.23 → 0.54.7
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-443pp39A.js +1 -0
- package/dist/ai-services-unified-C29ea2f8.js +3 -0
- package/dist/assets/AGENTS.md +12 -13
- package/dist/commands-BQrpRKx2.js +126 -0
- package/dist/config-manager-1Zhw_Ob5.js +1 -0
- package/dist/config-manager-CecOyMub.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-Dm0XBXMD.js +24 -0
- package/dist/mcp-server.js +2 -2
- package/dist/research-BY8APYWo.js +585 -0
- package/dist/research-CJ_GBjgH.js +1 -0
- package/dist/{response-language-ip84ug-k.js → response-language-29GrZl4K.js} +1 -1
- package/dist/response-language-mSXkRdue.js +1 -0
- package/dist/rolldown-runtime-BxhE39jg.js +1 -0
- package/dist/src-ZjjcZr3a.js +468 -0
- package/dist/src-xo9dUCIC.js +207 -0
- package/dist/tag-management-D4lpCywn.js +1 -0
- package/dist/task-manager-GG4EpQXs.js +168 -0
- package/dist/task-manager-gnhcGsE8.js +1 -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/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
package/README.md
CHANGED
|
@@ -25,11 +25,11 @@ TaskGraph turns a product requirements document (PRD) into a dependency-aware ta
|
|
|
25
25
|
|
|
26
26
|
- **Plan from a PRD:** generate tasks and subtasks, with dependencies, priorities and test strategies.
|
|
27
27
|
- **Always know what's next:** `next` picks the first unblocked task.
|
|
28
|
-
- **Stay in Claude Code:** use slash commands (`/
|
|
28
|
+
- **Stay in Claude Code:** use slash commands (`/taskgraph:next-task`) or the `taskgraph` CLI. An MCP server is available if you want plain-language control, but it is optional.
|
|
29
29
|
- **No API keys:** every AI call runs through your signed-in Claude Code.
|
|
30
30
|
|
|
31
31
|
<p align="center">
|
|
32
|
-
<a href="images/demo/03-pick-up-where-you-left-off.mp4"><img src="images/demo/03-pick-up-where-you-left-off.svg" alt="A new Claude Code session running /
|
|
32
|
+
<a href="images/demo/03-pick-up-where-you-left-off.mp4"><img src="images/demo/03-pick-up-where-you-left-off.svg" alt="A new Claude Code session running /taskgraph:next-task and resuming from the notes logged last time" width="820"></a>
|
|
33
33
|
<br>
|
|
34
34
|
<sub>3. Pick up where you left off · <a href="images/demo/03-pick-up-where-you-left-off.mp4">MP4</a></sub>
|
|
35
35
|
</p>
|
|
@@ -44,13 +44,13 @@ TaskGraph turns a product requirements document (PRD) into a dependency-aware ta
|
|
|
44
44
|
</p>
|
|
45
45
|
|
|
46
46
|
<p align="center">
|
|
47
|
-
<a href="images/demo/02-prd-to-tasks.mp4"><img src="images/demo/02-prd-to-tasks.svg" alt="Claude Code running /
|
|
47
|
+
<a href="images/demo/02-prd-to-tasks.mp4"><img src="images/demo/02-prd-to-tasks.svg" alt="Claude Code running /taskgraph:parse-prd to turn a PRD into 8 dependent tasks" width="820"></a>
|
|
48
48
|
<br>
|
|
49
49
|
<sub>2. Turn your PRD into a plan · <a href="images/demo/02-prd-to-tasks.mp4">MP4</a></sub>
|
|
50
50
|
</p>
|
|
51
51
|
|
|
52
52
|
<p align="center">
|
|
53
|
-
<a href="images/demo/03-pick-up-where-you-left-off.mp4"><img src="images/demo/03-pick-up-where-you-left-off.svg" alt="A new Claude Code session running /
|
|
53
|
+
<a href="images/demo/03-pick-up-where-you-left-off.mp4"><img src="images/demo/03-pick-up-where-you-left-off.svg" alt="A new Claude Code session running /taskgraph:next-task and resuming from the notes logged last time" width="820"></a>
|
|
54
54
|
<br>
|
|
55
55
|
<sub>3. Pick up where you left off · <a href="images/demo/03-pick-up-where-you-left-off.mp4">MP4</a></sub>
|
|
56
56
|
</p>
|
|
@@ -63,7 +63,7 @@ TaskGraph turns a product requirements document (PRD) into a dependency-aware ta
|
|
|
63
63
|
|
|
64
64
|
</details>
|
|
65
65
|
|
|
66
|
-
> **Attribution:** TaskGraph is based on [Task Master](https://github.com/eyaltoledano/claude-task-master) by Eyal Toledano ([@eyaltoledano](https://x.com/eyaltoledano)) and Ralph Khreish ([@RalphEcom](https://x.com/RalphEcom)). It is named `taskgraph-ai` with `taskgraph` and `taskgraph-mcp` binaries, so it can sit next to the original, and it keeps the original license and copyright notices (see [Licensing](#licensing)). Coming from Task Master or
|
|
66
|
+
> **Attribution:** TaskGraph is based on [Task Master](https://github.com/eyaltoledano/claude-task-master) by Eyal Toledano ([@eyaltoledano](https://x.com/eyaltoledano)) and Ralph Khreish ([@RalphEcom](https://x.com/RalphEcom)). It is named `taskgraph-ai` with `taskgraph` and `taskgraph-mcp` binaries, so it can sit next to the original, and it keeps the original license and copyright notices (see [Licensing](#licensing)). Coming from Task Master, or already using TaskGraph's earlier slash commands? See [Upgrading](#upgrading).
|
|
67
67
|
|
|
68
68
|
## Contents
|
|
69
69
|
|
|
@@ -89,20 +89,20 @@ From your own project folder:
|
|
|
89
89
|
taskgraph init --rules claude
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
This creates `.taskgraph/` (tasks, docs, config), a `CLAUDE.md` import with TaskGraph's workflow guide, and
|
|
92
|
+
This creates `.taskgraph/` (tasks, docs, config), a `CLAUDE.md` import with TaskGraph's workflow guide, and the MCP entry below. It does not install slash commands: step 3 does. Don't run it twice; it only re-adds the same files.
|
|
93
93
|
|
|
94
94
|
`init` also writes a `.mcp.json` that registers the MCP server with the 7 `core` tools. Delete that file if you only want the slash commands and CLI; see step 4 to keep and configure the server.
|
|
95
95
|
|
|
96
|
-
### 3.
|
|
96
|
+
### 3. Install the plugin
|
|
97
97
|
|
|
98
|
-
The plugin
|
|
98
|
+
The plugin is the only way to get slash commands. It adds 39 of them (as `/taskgraph:<name>`) and three agents (`task-orchestrator`, `task-executor`, `task-checker`).
|
|
99
99
|
|
|
100
100
|
```txt
|
|
101
101
|
/plugin marketplace add stephennmiller/taskgraph-ai
|
|
102
102
|
/plugin install taskgraph@taskgraph
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
The plugin does not register an MCP server. The `task-checker` agent uses the `taskgraph show` CLI, and uses the MCP tool instead if you registered the server as `taskgraph-ai` (step 4).
|
|
105
|
+
The plugin does not register an MCP server. The `task-checker` agent uses the `taskgraph show` CLI, and uses the MCP tool instead if you registered the server as `taskgraph-ai` (step 4).
|
|
106
106
|
|
|
107
107
|
### 4. (Optional) Add the MCP server
|
|
108
108
|
|
|
@@ -114,7 +114,7 @@ claude mcp add taskgraph-ai --scope user \
|
|
|
114
114
|
-- npx -y taskgraph-ai
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
Restart Claude Code and run `/mcp`; `taskgraph-ai` should show as connected. `standard` loads
|
|
117
|
+
Restart Claude Code and run `/mcp`; `taskgraph-ai` should show as connected. `standard` loads 15 tools, enough for everything in [How to use it](#how-to-use-it). See [MCP tool tiers](#mcp-tool-tiers) for the others.
|
|
118
118
|
|
|
119
119
|
To share the setup with your team instead, put it in the project's `.mcp.json`:
|
|
120
120
|
|
|
@@ -139,7 +139,7 @@ To share the setup with your team instead, put it in the project's `.mcp.json`:
|
|
|
139
139
|
</picture>
|
|
140
140
|
</p>
|
|
141
141
|
|
|
142
|
-
The examples below use slash commands (`/
|
|
142
|
+
The examples below use slash commands (`/taskgraph:...`), with the CLI equivalent in each table. If you added the optional MCP server, you can say the same things in plain language instead.
|
|
143
143
|
|
|
144
144
|
### 1. Write a PRD
|
|
145
145
|
|
|
@@ -148,38 +148,39 @@ Create `.taskgraph/docs/prd.md` describing what you want to build. The more deta
|
|
|
148
148
|
### 2. Generate tasks
|
|
149
149
|
|
|
150
150
|
```txt
|
|
151
|
-
/
|
|
151
|
+
/taskgraph:parse-prd .taskgraph/docs/prd.md
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
Then, optionally, check how big each task is and break the big ones down:
|
|
155
155
|
|
|
156
156
|
```txt
|
|
157
|
-
/
|
|
158
|
-
/
|
|
157
|
+
/taskgraph:analyze-complexity
|
|
158
|
+
/taskgraph:expand-all-tasks
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
### 3. Work through them
|
|
162
162
|
|
|
163
163
|
| Slash command | CLI | What happens |
|
|
164
164
|
|---|---|---|
|
|
165
|
-
| `/
|
|
166
|
-
| `/
|
|
167
|
-
| `/
|
|
168
|
-
| `/
|
|
169
|
-
| `/
|
|
170
|
-
| `/
|
|
171
|
-
| `/
|
|
172
|
-
| `/
|
|
165
|
+
| `/taskgraph:next-task` | `taskgraph next` | Shows the first unblocked, pending task |
|
|
166
|
+
| `/taskgraph:graph` | `taskgraph graph` | Draws tasks as a dependency tree, with the next one marked |
|
|
167
|
+
| `/taskgraph:why 7` | `taskgraph why 7` | Explains why a task is blocked and what to do first |
|
|
168
|
+
| `/taskgraph:show-task 1` | `taskgraph show 1,3,5` | Full details for each task |
|
|
169
|
+
| `/taskgraph:to-in-progress 3`, then ask Claude to implement it | `taskgraph set-status --id=3 --status=in-progress` | Claude Code does the work, guided by the task |
|
|
170
|
+
| `/taskgraph:to-done 3` | `taskgraph set-status --id=3 --status=done` | Updates status and unlocks dependent tasks |
|
|
171
|
+
| `/taskgraph:expand-task 4` | `taskgraph expand --id=4` | Splits it into subtasks |
|
|
172
|
+
| `/taskgraph:add-task <what you want>` | `taskgraph add-task --prompt="..."` | Creates a task with AI help |
|
|
173
|
+
| `/taskgraph:update-task 5 we now use Postgres` | `taskgraph update-task --id=5 --prompt="..."` | Rewrites it for the new direction |
|
|
173
174
|
| (CLI only) | `taskgraph research "JWT best practices"` | Web research with project context |
|
|
174
175
|
|
|
175
176
|
The loop is simple: **next, implement, done, next.** Task IDs look like `3` for a task and `3.2` for a subtask.
|
|
176
177
|
|
|
177
|
-
If you use the MCP server instead, research, tags and the dependency tools are only loaded with `TASKGRAPH_TOOLS=all` (see [MCP tool tiers](#mcp-tool-tiers)); everything else here works with `standard`.
|
|
178
|
+
If you use the MCP server instead, research, tags and the dependency editing tools (`add_dependency`, `remove_dependency`, `validate_dependencies`, `fix_dependencies`) are only loaded with `TASKGRAPH_TOOLS=all`; `dependency_analysis` is in `standard` (see [MCP tool tiers](#mcp-tool-tiers)); everything else here works with `standard`.
|
|
178
179
|
|
|
179
180
|
### 4. Keep it organized
|
|
180
181
|
|
|
181
182
|
- **Tags** keep separate task lists per branch, feature or experiment: `taskgraph tags`, `taskgraph tags add`, `taskgraph tags use` (MCP: `list_tags`, `add_tag`, `use_tag`).
|
|
182
|
-
- **Dependencies** decide what counts as "next". Run `/
|
|
183
|
+
- **Dependencies** decide what counts as "next". Run `/taskgraph:validate-dependencies` or `/taskgraph:fix-dependencies` if the graph looks wrong.
|
|
183
184
|
- **Statuses:** `pending`, `in-progress`, `done`, `review`, `deferred`, `cancelled`.
|
|
184
185
|
|
|
185
186
|
## Commands
|
|
@@ -188,17 +189,17 @@ Slash commands and the `taskgraph` CLI cover everything. The MCP server (optiona
|
|
|
188
189
|
|
|
189
190
|
### Slash commands
|
|
190
191
|
|
|
191
|
-
|
|
192
|
+
Install the plugin (step 3) and they appear as `/taskgraph:<name>` (type `/taskgraph` to see them). `taskgraph init` does not write slash commands.
|
|
192
193
|
|
|
193
194
|
| Purpose | Commands |
|
|
194
195
|
|---|---|
|
|
195
|
-
| Set up | `init-project`, `
|
|
196
|
+
| Set up | `init-project`, `install-taskgraph`, `setup-models`, `view-models` |
|
|
196
197
|
| Plan | `parse-prd`, `parse-prd-with-research`, `analyze-complexity`, `complexity-report`, `analyze-project` |
|
|
197
|
-
| See | `list-tasks`, `
|
|
198
|
-
| Change status | `
|
|
199
|
-
| Edit | `add-task`, `update-task`, `update-
|
|
200
|
-
| Subtasks | `expand-task`, `expand-all-tasks`, `add-subtask`, `remove-subtask`, `
|
|
201
|
-
| Dependencies | `add-dependency`, `remove-dependency`, `validate-dependencies`, `fix-dependencies` |
|
|
198
|
+
| See | `list-tasks`, `next-task`, `show-task`, `project-status` |
|
|
199
|
+
| Change status | `set-status`, `to-in-progress`, `to-done` |
|
|
200
|
+
| Edit | `add-task`, `update-task`, `update-tasks-from-id`, `remove-task`, `convert-task-to-subtask` |
|
|
201
|
+
| Subtasks | `expand-task`, `expand-all-tasks`, `add-subtask`, `remove-subtask`, `clear-subtasks` |
|
|
202
|
+
| Dependencies | `graph`, `why`, `add-dependency`, `remove-dependency`, `validate-dependencies`, `fix-dependencies` |
|
|
202
203
|
| Workflows | `smart-workflow`, `command-pipeline`, `auto-implement-tasks`, `sync-readme`, `learn`, `help` |
|
|
203
204
|
|
|
204
205
|
### CLI cheat sheet
|
|
@@ -208,6 +209,7 @@ taskgraph parse-prd .taskgraph/docs/prd.md # generate tasks from a PRD
|
|
|
208
209
|
taskgraph list # all tasks and their status
|
|
209
210
|
taskgraph next # next task to work on
|
|
210
211
|
taskgraph graph # tasks as a dependency tree
|
|
212
|
+
taskgraph why 7 # why task 7 is blocked
|
|
211
213
|
taskgraph show 1,3,5 # one or more tasks
|
|
212
214
|
taskgraph set-status --id=3 --status=done # change status
|
|
213
215
|
taskgraph add-task --prompt="..." --research # add a task
|
|
@@ -237,8 +239,8 @@ Only relevant if you added the optional MCP server. Each tool you load costs con
|
|
|
237
239
|
| Value | Tools | Includes |
|
|
238
240
|
|---|---|---|
|
|
239
241
|
| `core` (default, alias `lean`) | 7 | `get_tasks`, `next_task`, `get_task`, `set_task_status`, `update_subtask`, `parse_prd`, `expand_task` |
|
|
240
|
-
| `standard` |
|
|
241
|
-
| `all` |
|
|
242
|
+
| `standard` | 15 | core, plus `initialize_project`, `analyze_project_complexity`, `expand_all`, `add_subtask`, `remove_task`, `add_task`, `complexity_report`, `dependency_analysis` |
|
|
243
|
+
| `all` | 46 | everything: updates, dependencies, tags, `research`, `move_task`, `scope_up_task`, `scope_down_task`, `generate`, and the `autopilot_*` TDD workflow |
|
|
242
244
|
| a list | custom | e.g. `get_tasks,next_task,set_task_status` |
|
|
243
245
|
|
|
244
246
|
Set it when you add the server:
|
|
@@ -259,7 +261,9 @@ In Claude Code, say `Change the main model to opus`, or run `taskgraph models --
|
|
|
259
261
|
|
|
260
262
|
## Upgrading
|
|
261
263
|
|
|
262
|
-
|
|
264
|
+
**Slash commands now come from the plugin (every existing project).** `taskgraph init --rules claude` no longer writes slash commands into `.claude/commands/tm/`. Install the plugin (`/plugin marketplace add stephennmiller/taskgraph-ai`, `/plugin install taskgraph@taskgraph`, then `/plugin marketplace update taskgraph` to refresh), and re-run `taskgraph init --rules claude` once: it removes the old TaskGraph command files there and keeps any you added. Details are in the [upgrade guide](apps/docs/upgrading.mdx#5-move-slash-commands-to-the-plugin).
|
|
265
|
+
|
|
266
|
+
Projects created with Task Master, or with a TaskGraph release before 0.47, also need a few manual changes:
|
|
263
267
|
|
|
264
268
|
- **Rename `.taskmaster/` to `.taskgraph/`** by hand. TaskGraph no longer detects the old folder.
|
|
265
269
|
- **Rename environment variables:** `TASK_MASTER_*` and `TASKMASTER_*` are now `TASKGRAPH_*`. The old names are ignored.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{r as logAiUsage}from"./ai-services-unified-C29ea2f8.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
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};
|
package/dist/assets/AGENTS.md
CHANGED
|
@@ -85,19 +85,19 @@ project/
|
|
|
85
85
|
|
|
86
86
|
Use slash commands or the `taskgraph` CLI first. They need no MCP server and load no extra tool definitions into context. Only fall back to the MCP tools below when a command you need has no slash command or CLI equivalent.
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
With the TaskGraph plugin installed (`/plugin marketplace add stephennmiller/taskgraph-ai`, then `/plugin install taskgraph@taskgraph`), the slash commands live under `/taskgraph:` (type `/taskgraph` to list them). `taskgraph init` does not write slash commands:
|
|
89
89
|
|
|
90
90
|
| Task | Slash command | CLI |
|
|
91
91
|
|------|---------------|-----|
|
|
92
|
-
| Find next task | `/
|
|
93
|
-
| View a task | `/
|
|
94
|
-
| List tasks | `/
|
|
95
|
-
| Parse a PRD | `/
|
|
96
|
-
| Add a task | `/
|
|
97
|
-
| Break into subtasks | `/
|
|
98
|
-
| Change status | `/
|
|
99
|
-
| Update a task | `/
|
|
100
|
-
| Check complexity | `/
|
|
92
|
+
| Find next task | `/taskgraph:next-task` | `taskgraph next` |
|
|
93
|
+
| View a task | `/taskgraph:show-task <id>` | `taskgraph show <id>` |
|
|
94
|
+
| List tasks | `/taskgraph:list-tasks` | `taskgraph list` |
|
|
95
|
+
| Parse a PRD | `/taskgraph:parse-prd` | `taskgraph parse-prd <file>` |
|
|
96
|
+
| Add a task | `/taskgraph:add-task` | `taskgraph add-task --prompt="..."` |
|
|
97
|
+
| Break into subtasks | `/taskgraph:expand-task <id>` | `taskgraph expand --id=<id>` |
|
|
98
|
+
| Change status | `/taskgraph:set-status <id> <status>`, or the shortcuts `/taskgraph:to-in-progress <id>` and `/taskgraph:to-done <id>` | `taskgraph set-status --id=<id> --status=<status>` |
|
|
99
|
+
| Update a task | `/taskgraph:update-task <id>` | `taskgraph update-task --id=<id> --prompt="..."` |
|
|
100
|
+
| Check complexity | `/taskgraph:analyze-complexity` | `taskgraph analyze-complexity` |
|
|
101
101
|
|
|
102
102
|
## MCP Integration (Optional)
|
|
103
103
|
|
|
@@ -124,15 +124,14 @@ Default: `core` (7 tools). Set via `TASKGRAPH_TOOLS` env var.
|
|
|
124
124
|
| Tier | Count | Tools |
|
|
125
125
|
|------|-------|-------|
|
|
126
126
|
| `core` | 7 | `get_tasks`, `next_task`, `get_task`, `set_task_status`, `update_subtask`, `parse_prd`, `expand_task` |
|
|
127
|
-
| `standard` |
|
|
128
|
-
| `all` |
|
|
127
|
+
| `standard` | 15 | core + `initialize_project`, `analyze_project_complexity`, `expand_all`, `add_subtask`, `remove_task`, `add_task`, `complexity_report`, `dependency_analysis` |
|
|
128
|
+
| `all` | 46 | standard + dependencies, tags, research, autopilot, scoping, models |
|
|
129
129
|
|
|
130
130
|
**Upgrade when tool unavailable:** Edit MCP config, change `TASKGRAPH_TOOLS` from `"core"` to `"standard"` or `"all"`, restart MCP.
|
|
131
131
|
|
|
132
132
|
### Essential MCP Tools
|
|
133
133
|
|
|
134
134
|
```javascript
|
|
135
|
-
help; // = shows available taskgraph commands
|
|
136
135
|
// Project setup
|
|
137
136
|
initialize_project; // = taskgraph init
|
|
138
137
|
parse_prd; // = taskgraph parse-prd
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import{c as e,l as t}from"./src-xo9dUCIC.js";import{C as n,D as r,E as i,S as a,T as o,_ as s,b as c,c as l,d as u,f as d,h as f,l as p,m,n as h,o as g,p as _,r as v,s as ee,t as te,u as ne,v as re,w as ie,x as ae,y as oe}from"./task-manager-GG4EpQXs.js";import{$ as y,Ct as b,G as se,I as ce,K as le,O as ue,Ot as x,St as S,T as C,Tt as w,_t as T,b as de,ft as E,g as fe,gt as D,it as O,s as pe,t as me,wt as k}from"./config-manager-CecOyMub.js";import{C as A,D as j,E as he,M as ge,N as M,O as _e,P as ve,S as ye,T as be,_ as xe,a as Se,b as Ce,g as we,i as Te,k as Ee,l as De,m as Oe,n as ke,o as Ae,r as je,s as Me,t as Ne,u as Pe,v as Fe,x as N,y as P}from"./src-ZjjcZr3a.js";import{C as Ie,D as Le,E as F,O as Re,T as ze,c as Be,h as I,l as Ve,m as He,o as Ue,p as We,s as Ge,x as Ke,y as L}from"./research-BY8APYWo.js";import{t as qe}from"./response-language-29GrZl4K.js";import{n as R,r as Je,t as Ye}from"./init-Dm0XBXMD.js";import z from"chalk";import Xe from"node:path";import B from"path";import V from"node:fs";import Ze from"node:os";import H from"fs";import{Command as Qe,Option as U}from"commander";import W from"boxen";import $e from"process";import G from"https";import K from"inquirer";function et(){let e=$e.env.TM_PUBLIC_VERSION;return e&&e!==`unknown`?e:(console.warn(`Could not read version from TM_PUBLIC_VERSION, using fallback`),`0.0.0`)}function q(e,t){let n=e=>{let[t,n=``]=e.split(`-`,2);return{nums:t.split(`.`).map(e=>Number.parseInt(e,10)||0),pre:n}},r=n(e),i=n(t),a=Math.max(r.nums.length,i.nums.length);for(let e=0;e<a;e++){let t=(r.nums[e]||0)-(i.nums[e]||0);if(t!==0)return t<0?-1:1}return r.pre&&!i.pre?-1:!r.pre&&i.pre?1:r.pre===i.pre?0:r.pre<i.pre?-1:1}async function tt(e){return new Promise(t=>{let n={hostname:`raw.githubusercontent.com`,path:`/${Ce}/main/CHANGELOG.md`,method:`GET`,headers:{"User-Agent":`${P}/${e}`}},r=G.request(n,n=>{let r=``;n.on(`data`,e=>{r+=e}),n.on(`end`,()=>{try{if(n.statusCode!==200){t([]);return}t(nt(r,e))}catch{t([])}})});r.on(`error`,()=>{t([])}),r.setTimeout(3e3,()=>{r.destroy(),t([])}),r.end()})}function nt(e,t){try{if(!/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/.test(t))return[];let n=RegExp(`## ${t.replace(/\./g,`\\.`)}\\s*\\n`,`i`),r=e.match(n);if(!r)return[];let i=r.index+r[0].length,a=e.indexOf(`
|
|
2
|
+
## `,i),o=(a>0?e.slice(i,a):e.slice(i)).match(/### Minor Changes\s*\n([\s\S]*?)(?=\n###|\n##|$)/i);if(!o)return[];let s=o[1],c=[],l=/^-\s+\[#\d+\][^\n]*?!\s+-\s+(.+?)$/gm,u;for(;(u=l.exec(s))!==null;){let e=u[1].trim();c.push(e)}return c}catch{return[]}}const J=()=>Xe.join(Ze.tmpdir(),`taskgraph-update-cache.json`);function rt(){try{let e=J();if(!V.existsSync(e))return null;let t=JSON.parse(V.readFileSync(e,`utf-8`));return Date.now()-t.timestamp>36e5?null:t}catch{return null}}function it(e,t){try{V.writeFileSync(J(),JSON.stringify({timestamp:Date.now(),latestVersion:e,highlights:t},null,2))}catch{}}function at(e){return new Promise(t=>{let n=G.request({hostname:`registry.npmjs.org`,path:`/${P}`,method:`GET`,headers:{Accept:`application/vnd.npm.install-v1+json`,"User-Agent":`${P}/${e}`}},e=>{let n=``;e.on(`data`,e=>n+=e),e.on(`end`,()=>{try{if(e.statusCode!==200){t(null);return}t(JSON.parse(n)[`dist-tags`]?.latest||null)}catch{t(null)}})});n.on(`error`,()=>t(null)),n.setTimeout(3e3,()=>{n.destroy(),t(null)}),n.end()})}function Y(e,t,n){return{currentVersion:e,latestVersion:t,needsUpdate:q(e,t)<0,highlights:n}}async function ot(e){let t=e||et(),n=rt();if(n)return Y(t,n.latestVersion,n.highlights);let r=await at(t);if(!r)return Y(t,t);let i=q(t,r)<0?await tt(r):void 0;return it(r,i),Y(t,r,i)}function st(e){return`# ${e}
|
|
3
|
+
|
|
4
|
+
This project is managed using TaskGraph.
|
|
5
|
+
|
|
6
|
+
`}function ct(e){let t=B.basename(e).toLowerCase().replace(/[^a-z0-9]/g,`-`).replace(/-+/g,`-`).replace(/^-|-$/g,``);return`https://github.com/stephennmiller/taskgraph-ai?${new URLSearchParams({utm_source:`github-readme`,utm_medium:`readme-export`,utm_campaign:t||`task-sync`,utm_content:`task-export-link`}).toString()}`}function lt(e){let{timestamp:t,withSubtasks:n,status:r,projectRoot:i}=e,a=r?`Status filter: ${r}`:`Status filter: none`;return`<!-- TASKGRAPH_EXPORT_START -->\n> ${`🎯 **TaskGraph Export** - ${t}\n📋 Export: ${n?`with subtasks`:`without subtasks`} • ${a}\n🔗 Powered by [TaskGraph](${ct(i)})`.split(`
|
|
7
|
+
`).join(`
|
|
8
|
+
> `)}\n\n`}function ut(){return`
|
|
9
|
+
> 📋 **End of TaskGraph Export** - Tasks are synced from your project using the \`sync-readme\` command.
|
|
10
|
+
<!-- TASKGRAPH_EXPORT_END -->
|
|
11
|
+
`}async function dt(e=null,t={}){try{let n=e||se()||`.`,{withSubtasks:r=!1,status:i,tasksPath:a,tag:o}=t,s=await f(a||B.join(n,`.taskgraph`,`tasks`,`tasks.json`),i,null,r,`markdown-readme`,{projectRoot:e,tag:o});if(!s)return console.log(z.red(`❌ Failed to generate task output`)),!1;let c=(/* @__PURE__ */ new Date()).toISOString().replace(`T`,` `).substring(0,19)+` UTC`,l=fe(n),u=lt({timestamp:c,withSubtasks:r,status:i,projectRoot:n}),d=ut(),p=u+s+d,m=B.join(n,`README.md`),h=``;try{h=H.readFileSync(m,`utf8`)}catch(e){if(e.code===`ENOENT`)h=st(l);else throw e}let g,_=h.indexOf(`<!-- TASKGRAPH_EXPORT_START -->`),v=h.indexOf(`<!-- TASKGRAPH_EXPORT_END -->`);if(_!==-1&&v!==-1){let e=h.substring(0,_),t=h.substring(v+29);g=e+p+t}else g=h+`
|
|
12
|
+
`+p;return H.writeFileSync(m,g,`utf8`),console.log(z.green(`✅ Successfully synced tasks to README.md`)),console.log(z.cyan(`📋 Export details: ${r?`with`:`without`} subtasks${i?`, status: ${i}`:``}`)),console.log(z.dim(`📍 Location: ${m}`)),!0}catch(e){return console.log(z.red(`❌ Failed to sync tasks to README:`),e.message),y(`error`,`README sync error: ${e.message}`),!1}}var ft=class{#e;#t;constructor(e,t){this.#e=Object.freeze({...e}),this.#t=t}getProjectRoot(){return this.#e.projectRoot}getTaskGraphDir(){return this.#e.taskGraphDir}getTasksPath(){return this.#e.tasksPath}getPrdPath(){return this.#e.prdPath}getComplexityReportPath(){if(this.#e.complexityReportPath)return this.#e.complexityReportPath;let e=this.getCurrentTag()===`master`?T:T.replace(`.json`,`_${this.getCurrentTag()}.json`);return B.join(this.#e.projectRoot,e)}getConfigPath(){return this.#e.configPath}getStatePath(){return this.#e.statePath}getAllPaths(){return this.#e}getCurrentTag(){if(this.#t)return this.#t;try{if(H.existsSync(this.#e.statePath)){let e=H.readFileSync(this.#e.statePath,`utf8`),t=JSON.parse(e);if(t&&t.currentTag)return t.currentTag}}catch{}try{if(H.existsSync(this.#e.configPath)){let e=H.readFileSync(this.#e.configPath,`utf8`),t=JSON.parse(e);if(t&&t.global&&t.global.defaultTag)return t.global.defaultTag}}catch{}return`master`}};function X(e={}){let t=(e,t,n=[],r=null,i=!1)=>{if(typeof t==`string`){let n=B.isAbsolute(t)?t:B.resolve(r||process.cwd(),t);if(i){let t=B.dirname(n);if(!H.existsSync(t))try{H.mkdirSync(t,{recursive:!0})}catch(n){throw Error(`Could not create directory for ${e}: ${t}. Error: ${n.message}`)}}else if(!H.existsSync(n))throw Error(`${e} override path does not exist: ${n}`);return n}if(t===!0){for(let e of n){let t=B.isAbsolute(e)?e:B.join(r||process.cwd(),e);if(H.existsSync(t))return t}throw Error(`Required ${e} not found. Searched: ${n.join(`, `)}`)}for(let e of n){let t=B.isAbsolute(e)?e:B.join(r||process.cwd(),e);if(H.existsSync(t))return t}return null},n={};if(e.projectRoot){let t=B.resolve(e.projectRoot);if(!H.existsSync(t))throw Error(`Project root override path does not exist: ${t}`);if(!H.existsSync(B.join(t,`.taskgraph`)))throw Error(`Project root override is not a valid taskgraph project: ${t}`);n.projectRoot=t}else n.projectRoot=ce();return n.taskGraphDir=`taskGraphDir`in e?t(`taskgraph directory`,e.taskGraphDir,[b],n.projectRoot):t(`taskgraph directory`,!1,[b],n.projectRoot),n.configPath=B.join(n.projectRoot,S),n.statePath=B.join(n.taskGraphDir||B.join(n.projectRoot,`.taskgraph`),`state.json`),n.tasksPath=B.join(n.projectRoot,x),`configPath`in e&&(n.configPath=t(`config file`,e.configPath,[S],n.projectRoot)),`statePath`in e&&(n.statePath=t(`state file`,e.statePath,[`state.json`],n.taskGraphDir)),`tasksPath`in e&&(n.tasksPath=t(`tasks file`,e.tasksPath,[x],n.projectRoot)),`prdPath`in e&&(n.prdPath=t(`PRD file`,e.prdPath,[B.join(k,`PRD.md`),B.join(k,`prd.md`),B.join(k,`PRD.txt`),B.join(k,`prd.txt`)],n.projectRoot)),`complexityReportPath`in e&&(n.complexityReportPath=t(`complexity report`,e.complexityReportPath,[B.join(w,`task-complexity-report.json`),B.join(w,`complexity-report.json`)],n.projectRoot,!0)),new ft(n,e.tag)}function Z(e){let t=e?.message??``;if(/overloaded|rate limit|\b(429|529)\b/i.test(t))return`Claude is busy. Wait a minute and try again`;if(/empty stream response|did not return a valid/i.test(t))return`Try again, or reword the prompt`}async function Q(e){try{return await e()}catch(e){if(!(e instanceof n))throw e;A(e.message,e.data?.hint),process.exit(1)}}function pt(e){e.on(`option:unknown`,function(e){let t=this._name||`unknown`;A(`Unknown option '${e}'`,`Run \`taskgraph ${t} --help\` to see its options`),process.exit(1)}),e.command(`help`).description(`Show help information`).action(()=>{e.help()}),e.configureHelp({helpWidth:120,sortSubcommands:!1}),e.command(`parse-prd`).description(`Parse a PRD file and generate tasks`).argument(`[file]`,`Path to the PRD file`).option(`-i, --input <file>`,`Path to the PRD file (alternative to positional argument)`).option(`-o, --output <file>`,`Output file path`).option(`-n, --num-tasks <number>`,`Number of tasks to generate`,pe()).option(`-f, --force`,`Skip confirmation when overwriting existing tasks`).option(`--append`,`Append new tasks to existing tasks.json instead of overwriting`).option(`-r, --research`,`Use Claude Code web research for research-backed task generation, providing more comprehensive and accurate task breakdown`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async(e,t)=>{let n=t.input||e;n&&!H.existsSync(B.resolve(n))&&(A(`${n} not found`,`Check the spelling; paths are relative to this folder`),process.exit(1));let r;try{let e={prdPath:n||!0,tag:t.tag};t.output&&(e.tasksPath=t.output),r=X(e)}catch(e){N(e,{hint:n?void 0:"Try `taskgraph parse-prd prd.md`"}),process.exit(1)}let i=parseInt(t.numTasks,10),a=t.force||!1,o=t.append||!1,s=t.research||!1,c=a,l=o,u=r.getCurrentTag();await F(u);async function f(){let e=!1,t=r.getTasksPath();if(H.existsSync(t))try{let n=H.readFileSync(t,`utf8`),r=JSON.parse(n);r[u]&&Array.isArray(r[u].tasks)&&r[u].tasks.length>0&&(e=!0)}catch{e=!1}if(e&&!c&&!l){if(!await We(t))return y(`info`,`Operation cancelled.`),!1;c=!0}return!0}try{if(!await f())return;o&&console.log(D.brand(`Appending to existing tasks...`)),s&&console.log(D.brand(`Using Claude Code web research for research-backed task generation`));let e=r.getTasksPath()||B.join(r.getProjectRoot(),`.taskgraph/tasks/tasks.json`);await d(r.getPrdPath(),e,i,{append:l,force:c,research:s,projectRoot:r.getProjectRoot(),tag:u})}catch(e){console.error(z.red(`Error parsing PRD: ${e.message}`)),process.exit(1)}}),e.command(`update`).description(`Update multiple tasks with ID >= "from" based on new information or implementation changes`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--from <id>`,`Task ID to start updating from (tasks with ID >= this value will be updated)`,`1`).option(`-p, --prompt <text>`,`Prompt explaining the changes or new context (required)`).option(`-r, --research`,`Use Claude Code web research for research-backed task updates`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=parseInt(e.from,10),r=e.prompt,i=e.research||!1;t.getTasksPath();let a=t.getCurrentTag();await F(a),(process.argv.includes(`--id`)||process.argv.some(e=>e.startsWith(`--id=`)))&&(console.error(z.red(`Error: The update command uses --from=<id>, not --id=<id>`)),console.log(z.yellow(`
|
|
13
|
+
To update multiple tasks:`)),console.log(` taskgraph update --from=${n} --prompt="Your prompt here"`),console.log(z.yellow(`
|
|
14
|
+
To update a single specific task, use the update-task command instead:`)),console.log(` taskgraph update-task --id=<id> --prompt="Your prompt here"`),process.exit(1)),r||(console.error(z.red(`Error: --prompt parameter is required. Please provide information about the changes.`)),process.exit(1)),i&&console.log(D.brand(`Using Claude Code web research for research-backed task updates`)),await te(t.getTasksPath(),n,r,i,{projectRoot:t.getProjectRoot(),tag:a})}),e.command(`update-task`).description(`Update a single specific task by ID with new information`).argument(`[id]`,`Task ID to update (e.g., 1, 1.1, TAS-123)`).argument(`[prompt...]`,`Update prompt - multiple words, no quotes needed`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-i, --id <id>`,`Task ID to update (fallback if not using positional)`).option(`-p, --prompt <text>`,`Prompt (fallback if not using positional)`).option(`-r, --research`,`Use Claude Code web research for research-backed task updates`).option(`--append`,`Append timestamped information to task details instead of full update`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async(e,t,n)=>{try{let r=X({tasksPath:n.file||!0,tag:n.tag}),i=r.getTasksPath(),a=r.getCurrentTag();await F(a);let o=e||n.id,s=t.length>0?t.join(` `):n.prompt;o||(A(`Missing task ID`,'Try `taskgraph update-task --id=3 --prompt="what changed"`'),process.exit(1)),/^\d+$/.test(o)||(A(`"${o}" isn't a task ID`,o.includes(`.`)?"Update subtasks with `taskgraph update-subtask`":"Use a number like `3`"),process.exit(1)),s||(A(`Missing prompt`,`Try \`taskgraph update-task --id=${o} --prompt="what changed"\``),process.exit(1));let c=n.research||!1;H.existsSync(i)||(console.error(z.red(`Error: Tasks file not found at path: ${i}`)),console.log(i===`.taskgraph/tasks/tasks.json`?z.yellow(`Hint: Run taskgraph init or taskgraph parse-prd to create tasks.json first`):z.yellow(`Hint: Check if the file path is correct: ${i}`)),process.exit(1)),c&&console.log(D.brand(`Using Claude Code web research for this update`));let l=n.append||!1;await h(r.getTasksPath(),o,s,c,{projectRoot:r.getProjectRoot(),tag:a},`text`,l)||console.log(z.yellow(`
|
|
15
|
+
Task update was not completed. Review the messages above for details.`))}catch(e){N(e,{hint:Z(e)})}}),e.command(`update-subtask`).description(`Update a subtask by appending additional timestamped information`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-i, --id <id>`,`Subtask ID to update (required)`).option(`-p, --prompt <text>`,`Prompt explaining what information to add (required)`).option(`-r, --research`,`Use Claude Code web research for research-backed updates`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{try{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=t.getTasksPath(),r=t.getCurrentTag();await F(r),e.id||(console.error(z.red(`Error: --id parameter is required`)),console.log(z.yellow(`Usage example: taskgraph update-subtask --id=5.2 --prompt="Add more details about the API endpoint"`)),process.exit(1));let i=e.id;e.prompt||(console.error(z.red(`Error: --prompt parameter is required. Please provide information to add to the subtask.`)),console.log(z.yellow(`Usage example: taskgraph update-subtask --id=5.2 --prompt="Add more details about the API endpoint"`)),process.exit(1));let a=e.prompt,o=e.research||!1;H.existsSync(n)||(console.error(z.red(`Error: Tasks file not found at path: ${n}`)),console.log(n===`.taskgraph/tasks/tasks.json`?z.yellow(`Hint: Run taskgraph init or taskgraph parse-prd to create tasks.json first`):z.yellow(`Hint: Check if the file path is correct: ${n}`)),process.exit(1)),o&&console.log(D.brand(`Using Claude Code web research for this update`)),await v(t.getTasksPath(),i,a,o,{projectRoot:t.getProjectRoot(),tag:r})}catch(e){N(e,{hint:Z(e)})}}),e.command(`scope-up`).description(`Increase task complexity with AI assistance`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-i, --id <ids>`,`Comma-separated task/subtask IDs to scope up (required)`).option(`-s, --strength <level>`,`Complexity increase strength: light, regular, heavy`,`regular`).option(`-p, --prompt <text>`,`Custom instructions for targeted scope adjustments`).option(`-r, --research`,`Use research AI for more informed adjustments`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{try{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=t.getTasksPath(),r=t.getCurrentTag();await F(r),e.id||(console.error(z.red(`Error: --id parameter is required`)),console.log(z.yellow(`Usage example: taskgraph scope-up --id=1,2,3 --strength=regular`)),process.exit(1));let i=e.id.split(`,`).map(e=>{let t=parseInt(e.trim(),10);return(Number.isNaN(t)||t<=0)&&(console.error(z.red(`Error: Invalid task ID: ${e.trim()}`)),process.exit(1)),t});l(e.strength)||(console.error(z.red(`Error: Invalid strength level: ${e.strength}. Must be one of: light, regular, heavy`)),process.exit(1)),H.existsSync(n)||(console.error(z.red(`Error: Tasks file not found at path: ${n}`)),process.exit(1)),console.log(D.brand(`Scoping up ${i.length===1?`task`:`tasks`} ${i.join(`, `)}`));let a={projectRoot:t.getProjectRoot(),tag:r,commandName:`scope-up`,outputType:`cli`,research:e.research||!1},o=await ee(n,i,e.strength,e.prompt||null,a,`text`);console.log(z.green(`✓ Scoped up ${o.updatedTasks.length} ${o.updatedTasks.length===1?`task`:`tasks`}`))}catch(e){N(e,{hint:Z(e)})}}),e.command(`scope-down`).description(`Decrease task complexity with AI assistance`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-i, --id <ids>`,`Comma-separated task/subtask IDs to scope down (required)`).option(`-s, --strength <level>`,`Complexity decrease strength: light, regular, heavy`,`regular`).option(`-p, --prompt <text>`,`Custom instructions for targeted scope adjustments`).option(`-r, --research`,`Use research AI for more informed adjustments`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{try{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=t.getTasksPath(),r=t.getCurrentTag();await F(r),e.id||(console.error(z.red(`Error: --id parameter is required`)),console.log(z.yellow(`Usage example: taskgraph scope-down --id=1,2,3 --strength=regular`)),process.exit(1));let i=e.id.split(`,`).map(e=>{let t=parseInt(e.trim(),10);return(Number.isNaN(t)||t<=0)&&(console.error(z.red(`Error: Invalid task ID: ${e.trim()}`)),process.exit(1)),t});l(e.strength)||(console.error(z.red(`Error: Invalid strength level: ${e.strength}. Must be one of: light, regular, heavy`)),process.exit(1)),H.existsSync(n)||(console.error(z.red(`Error: Tasks file not found at path: ${n}`)),process.exit(1)),console.log(D.brand(`Scoping down ${i.length===1?`task`:`tasks`} ${i.join(`, `)}`));let a={projectRoot:t.getProjectRoot(),tag:r,commandName:`scope-down`,outputType:`cli`,research:e.research||!1},o=await g(n,i,e.strength,e.prompt||null,a,`text`);console.log(z.green(`✓ Scoped down ${o.updatedTasks.length} ${o.updatedTasks.length===1?`task`:`tasks`}`))}catch(e){N(e,{hint:Z(e)})}}),Se(e),e.command(`expand`).description(`Expand a task into subtasks using AI`).option(`-i, --id <id>`,`ID of the task to expand`).option(`-a, --all`,`Expand all pending tasks based on complexity analysis`).option(`-n, --num <number>`,`Number of subtasks to generate (uses complexity analysis by default if available)`).option(`-r, --research`,`Enable research-backed generation (web research via Claude Code)`,!1).option(`-p, --prompt <text>`,`Additional context for subtask generation`).option(`-f, --force`,`Force expansion even if subtasks exist`,!1).option(`--file <file>`,`Path to the tasks file (relative to project root)`,x).option(`--complexity-report <file>`,`Path to the complexity report file (use this to specify the complexity report, not --file)`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t={tasksPath:e.file||!0,tag:e.tag};e.complexityReport&&(t.complexityReportPath=e.complexityReport);let n=X(t),r=n.getCurrentTag();if(await F(r),e.all){console.log(D.brand(`Expanding all pending tasks...`));try{await s(n.getTasksPath(),e.num,e.research,e.prompt,e.force,{projectRoot:n.getProjectRoot(),tag:r,complexityReportPath:n.getComplexityReportPath()})}catch(e){N(e,{hint:Z(e)})}}else if(e.id){e.id||(console.error(z.red(`Error: Task ID is required unless using --all.`)),process.exit(1));try{await re(n.getTasksPath(),e.id,e.num,e.research,e.prompt,{projectRoot:n.getProjectRoot(),tag:r,complexityReportPath:n.getComplexityReportPath()},e.force)}catch(e){N(e,{hint:Z(e)})}}else A(`Missing task ID`,"Try `taskgraph expand --id=3`, or `--all` for every pending task"),process.exit(1)}),e.command(`analyze-complexity`).description(`Analyze tasks and generate expansion recommendations${z.reset(``)}`).option(`-o, --output <file>`,`Output file path for the report`).option(`-m, --model <model>`,`LLM model to use for analysis (defaults to configured model)`).option(`-t, --threshold <number>`,`Minimum complexity score to recommend expansion (1-10)`,`5`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-r, --research`,`Use configured research model for research-backed complexity analysis`).option(`-i, --id <ids>`,`Comma-separated list of specific task IDs to analyze (e.g., "1,3,5")`).option(`--from <id>`,`Starting task ID in a range to analyze`).option(`--to <id>`,`Ending task ID in a range to analyze`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t={tasksPath:e.file||!0,tag:e.tag};e.output&&(t.complexityReportPath=e.output);let n=X(t),r=n.getCurrentTag();await F(r);let i=n.getComplexityReportPath(),a={...e,output:i,tag:r,projectRoot:n.getProjectRoot(),file:n.getTasksPath()};await c(a)}),e.command(`research`).description(`Perform AI-powered research queries with project context`).argument(`[prompt]`,`Research prompt to investigate`).option(`--file <file>`,`Path to the tasks file`).option(`-i, --id <ids>`,`Comma-separated task/subtask IDs to include as context (e.g., "15,16.2")`).option(`-f, --files <paths>`,`Comma-separated file paths to include as context`).option(`-c, --context <text>`,`Additional custom context to include in the research prompt`).option(`-t, --tree`,`Include project file tree structure in the research context`).option(`-s, --save <file>`,`Save the answer to a markdown file`).option(`-d, --detail <level>`,`Output detail level: low, medium, high`,`medium`).option(`--save-to <id>`,`Add the answer to a task or subtask's details (e.g., "15" or "15.2")`).option(`--save-file`,`Save the answer to .taskgraph/docs/research/`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async(e,t)=>{let n=X({tasksPath:t.file||!0,tag:t.tag}),r=(e,t)=>{A(e,t),process.exit(1)},i=e=>/^\d+(\.\d+)?$/.test(e);(!e||typeof e!=`string`||e.trim().length===0)&&r(`Research needs a question`,`Example: taskgraph research "How should we store sessions?" --id=3`);let a=(t.detail||`medium`).toLowerCase();[`low`,`medium`,`high`].includes(a)||r(`--detail must be low, medium or high, not "${t.detail}"`);let o=t.id?t.id.split(`,`).map(e=>e.trim()).filter(Boolean):[],s=o.find(e=>!i(e));s&&r(`"${s}" isn't a task ID`,`Use --id=15 or --id=15,16.2`);let c=t.files?t.files.split(`,`).map(e=>e.trim()).filter(Boolean):[],l=t.saveTo?t.saveTo.trim():null;t.saveTo!==void 0&&!i(l||``)&&r(`"${t.saveTo}" isn't a task ID`,`Use --save-to=15 for a task or --save-to=15.2 for a subtask`);let u=t.save?t.save.trim():null;t.save!==void 0&&(!u||u.includes(`..`)||B.isAbsolute(u))&&r(`--save needs a relative file path without ".."`);let d=n.getCurrentTag(),f=n.getProjectRoot(),p=n.getTasksPath();await F(d);let m=[...o,...l?[l]:[]];if(m.length>0){let e=O(p,f,d)?.tasks||[],t=t=>{let[n,r]=t.split(`.`).map(e=>Number.parseInt(e,10)),i=e.find(e=>Number(e.id)===n);return!i||r===void 0?!!i:(i.subtasks||[]).some(e=>Number(e.id)===r)},n=m.find(e=>!t(e));n&&r(`${n.includes(`.`)?`Subtask`:`Task`} ${n} doesn't exist in tag '${d}'`,"Run `taskgraph list` to see task IDs")}let h=c.find(e=>!H.existsSync(B.isAbsolute(e)?e:B.join(f,e)));h&&r(`File not found: ${h}`);let g=e.trim(),_;try{let{performResearch:e}=await import(`./research-CJ_GBjgH.js`);_=await e(g,{taskIds:o,filePaths:c,customContext:t.context?t.context.trim():``,includeProjectTree:!!t.tree,detailLevel:a,projectRoot:f,saveToFile:!!t.saveFile,tag:d},{commandName:`research`,outputType:`cli`,tag:d},`text`,!0)}catch(e){r(`Research failed: ${e.message}`)}if(l&&!_.interactiveSaveOccurred){let e={kind:`task`,id:l};try{let{appendResearchToTask:t}=await import(`./research-CJ_GBjgH.js`);t(p,l,`## Research: ${g}\n\n${_.result}`,{projectRoot:f,tag:d}),console.log(M(e))}catch(t){console.log(ge(e,t.message)),process.exitCode=1}}if(u){let e={kind:`file`,path:u};try{H.writeFileSync(u,`# Research: ${g}\n\n${_.result}\n`,`utf-8`),console.log(M(e))}catch(t){console.log(ge(e,t.message)),process.exitCode=1}}}),e.command(`clear-subtasks`).description(`Clear subtasks from specified tasks`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-i, --id <ids>`,`Task IDs (comma-separated) to clear subtasks from`).option(`--all`,`Clear subtasks from all tasks`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{!e.id&&!e.all&&(A(`Choose which tasks to clear`,"Use `--id=<ids>`, such as `--id=3,5`, or `--all`"),process.exit(1));let t=X({tasksPath:e.file||!0,tag:e.tag}),n=t.getCurrentTag(),r=t.getTasksPath(),i=t.getProjectRoot();await F(n);let a=e.id;if(e.all&&(a=(O(r,i,n)?.tasks??[]).map(e=>e.id).join(`,`),!a))console.log(`No subtasks to clear`);else try{let e=oe(r,a,{projectRoot:i,tag:n});console.log(ve(e))}catch(e){if(e?.name!==`ClearSubtasksError`)throw e;let t={SUBTASK_ID:`Use \`taskgraph remove-subtask --id=${e.taskId}\` for one subtask, or \`--id=${String(e.taskId).split(`.`)[0]}\` for all`,INVALID_TASK_ID:"Use task numbers, such as `--id=3,5`",TASK_NOT_FOUND:"Run `taskgraph list` to see task IDs",NO_TASKS:`Run \`taskgraph list\` to check the "${n}" tag`};A(`${e.message}; nothing was cleared`,t[e.code]),process.exit(1)}}),e.command(`add-task`).description(`Add a new task using AI, optionally providing manual details`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-p, --prompt <prompt>`,`Description of the task to add (required if not using manual fields)`).option(`-t, --title <title>`,`Task title (for manual task creation)`).option(`-d, --description <description>`,`Task description (for manual task creation)`).option(`--details <details>`,`Implementation details (for manual task creation)`).option(`--dependencies <dependencies>`,`Comma-separated list of task IDs this task depends on`).option(`--priority <priority>`,`Task priority (high, medium, low)`,`medium`).option(`-r, --research`,`Whether to use research capabilities for task creation`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=e.title&&e.description;!e.prompt&&!t&&(console.error(z.red(`Error: Either --prompt or both --title and --description must be provided`)),process.exit(1));let n=X({tasksPath:e.file||!0,tag:e.tag}),r=n.getProjectRoot(),i=n.getCurrentTag();await F(i);let a=null;t&&(a={title:e.title,description:e.description,details:e.details||``,testStrategy:e.testStrategy||``});let o=e.dependencies?e.dependencies.split(`,`).map(e=>e.trim()):[],s={projectRoot:r,tag:i,commandName:`add-task`,outputType:`cli`};try{let{newTaskId:t,telemetryData:r}=await ae(n.getTasksPath(),e.prompt,o,e.priority,s,`text`,a,e.research)}catch(e){N(e,{hint:Z(e)})}}),e.command(`add-dependency`).description(`Add a dependency to a task`).option(`-i, --id <id>`,`Task ID to add dependency to`).option(`-d, --depends-on <id>`,`Task ID that will become a dependency`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=e.id,r=e.dependsOn,i=t.getCurrentTag();await F(i),(!n||!r)&&(console.error(z.red(`Error: Both --id and --depends-on are required`)),process.exit(1));let a=n.includes(`.`)?n:parseInt(n,10),o=r.includes(`.`)?r:parseInt(r,10);await Q(()=>ie(t.getTasksPath(),a,o,{projectRoot:t.getProjectRoot(),tag:i}))}),e.command(`remove-dependency`).description(`Remove a dependency from a task`).option(`-i, --id <id>`,`Task ID to remove dependency from`).option(`-d, --depends-on <id>`,`Task ID to remove as a dependency`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=e.id,r=e.dependsOn,a=t.getCurrentTag();await F(a),(!n||!r)&&(console.error(z.red(`Error: Both --id and --depends-on are required`)),process.exit(1));let o=n.includes(`.`)?n:parseInt(n,10),s=r.includes(`.`)?r:parseInt(r,10);await Q(()=>i(t.getTasksPath(),o,s,{projectRoot:t.getProjectRoot(),tag:a}))}),e.command(`validate-dependencies`).description(`Identify invalid dependencies without fixing them${z.reset(``)}`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=t.getCurrentTag();await F(n);let i=await Q(()=>r(t.getTasksPath(),{context:{projectRoot:t.getProjectRoot(),tag:n}}));console.log(be(i)),i.valid||(process.exitCode=1)}),e.command(`fix-dependencies`).description(`Fix invalid dependencies automatically${z.reset(``)}`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=t.getCurrentTag();await F(n);let r=await Q(()=>o(t.getTasksPath(),{context:{projectRoot:t.getProjectRoot(),tag:n}}));console.log(he(r)),r.remaining.length>0&&(process.exitCode=1)}),e.command(`complexity-report`).description(`Display the complexity analysis report${z.reset(``)}`).option(`-f, --file <file>`,`Path to the report file`,T).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t={tag:e.tag};e.file&&e.file!==`.taskgraph/reports/task-complexity-report.json`&&(t.complexityReportPath=e.file);let n=X(t);await F(n.getCurrentTag()),await He(n.getComplexityReportPath())}),e.command(`add-subtask`).description(`Add a subtask to an existing task`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-p, --parent <id>`,`Parent task ID (required)`).option(`-i, --task-id <id>`,`Existing task ID to convert to subtask`).option(`-t, --title <title>`,`Title for the new subtask (when creating a new subtask)`).option(`-d, --description <text>`,`Description for the new subtask`).option(`--details <text>`,`Implementation details for the new subtask`).option(`--dependencies <ids>`,`Comma-separated list of dependency IDs for the new subtask`).option(`-s, --status <status>`,`Status for the new subtask`,`pending`).option(`--generate`,`Regenerate task files after adding subtask`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let n=X({tasksPath:e.file||!0,tag:e.tag}),r=e.parent,i=e.taskId,o=e.generate||!1,s=n.getCurrentTag();await F(s),r||(console.error(z.red(`Error: --parent parameter is required. Please provide a parent task ID.`)),t(),process.exit(1));let c=[];e.dependencies&&(c=e.dependencies.split(`,`).map(e=>e.includes(`.`)?e.trim():parseInt(e.trim(),10)));try{if(i)console.log(D.brand(`Converting task ${i} to a subtask of ${r}...`)),await a(n.getTasksPath(),r,i,null,o,{projectRoot:n.getProjectRoot(),tag:s}),console.log(z.green(`✓ Task ${i} successfully converted to a subtask of task ${r}`));else if(e.title){let t={title:e.title,description:e.description||``,details:e.details||``,status:e.status||`pending`,dependencies:c},i=await a(n.getTasksPath(),r,null,t,o,{projectRoot:n.getProjectRoot(),tag:s});console.log(W(D.text.bold(`Subtask ${r}.${i.id} Added Successfully`)+`
|
|
16
|
+
|
|
17
|
+
`+D.text(`Title: ${i.title}`)+`
|
|
18
|
+
`+D.text(`Status: ${L(i.status)}`)+`
|
|
19
|
+
`+(c.length>0?D.text(`Dependencies: ${c.join(`, `)}`)+`
|
|
20
|
+
`:``)+`
|
|
21
|
+
`+D.text.bold(`Next Steps:`)+`
|
|
22
|
+
`+z.cyan(`1. Run ${z.yellow(`taskgraph show ${r}`)} to see the parent task with all subtasks`)+`
|
|
23
|
+
`+z.cyan(`2. Run ${z.yellow(`taskgraph set-status --id=${r}.${i.id} --status=in-progress`)} to start working on it`),{padding:1,borderColor:`green`,borderStyle:`round`,margin:{top:1}}))}else console.error(z.red(`Error: Either --task-id or --title must be provided.`)),console.log(W(D.text.bold(`Usage Examples:`)+`
|
|
24
|
+
|
|
25
|
+
`+D.text(`Convert existing task to subtask:`)+`
|
|
26
|
+
`+z.yellow(` taskgraph add-subtask --parent=5 --task-id=8`)+`
|
|
27
|
+
|
|
28
|
+
`+D.text(`Create new subtask:`)+`
|
|
29
|
+
`+z.yellow(` taskgraph add-subtask --parent=5 --title="Implement login UI" --description="Create the login form"`)+`
|
|
30
|
+
|
|
31
|
+
`,{padding:1,borderColor:E,borderStyle:`round`})),process.exit(1)}catch(e){N(e)}}).on(`error`,function(e){console.error(z.red(`Error: ${e.message}`)),t(),process.exit(1)});function t(){console.log(W(`${D.text.bold(`Add Subtask Command Help`)}\n\n${z.cyan(`Usage:`)}\n taskgraph add-subtask --parent=<id> [options]\n\n${z.cyan(`Options:`)}\n -p, --parent <id> Parent task ID (required)\n -i, --task-id <id> Existing task ID to convert to subtask\n -t, --title <title> Title for the new subtask\n -d, --description <text> Description for the new subtask\n --details <text> Implementation details for the new subtask\n --dependencies <ids> Comma-separated list of dependency IDs\n -s, --status <status> Status for the new subtask (default: "pending")\n -f, --file <file> Path to the tasks file (default: "${x}")\n --generate Regenerate task files after adding subtask\n\n${z.cyan(`Examples:`)}\n taskgraph add-subtask --parent=5 --task-id=8\n taskgraph add-subtask -p 5 -t "Implement login UI" -d "Create the login form" --generate`,{padding:1,borderColor:E,borderStyle:`round`}))}e.command(`remove-subtask`).description(`Remove a subtask from its parent task`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-i, --id <id>`,`Subtask ID(s) to remove in format "parentId.subtaskId" (can be comma-separated for multiple subtasks)`).option(`-c, --convert`,`Convert the subtask to a standalone task instead of deleting it`).option(`--generate`,`Regenerate task files after removing subtask`).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),r=e.id,i=e.convert||!1,a=e.generate||!1,o=t.getCurrentTag();r||(console.error(z.red(`Error: --id parameter is required. Please provide subtask ID(s) in format "parentId.subtaskId".`)),n(),process.exit(1));try{let e=r.split(`,`).map(e=>e.trim());for(let r of e){r.includes(`.`)||(console.error(z.red(`Error: Subtask ID "${r}" must be in format "parentId.subtaskId"`)),n(),process.exit(1)),i&&console.log(D.brand(`The subtask will be converted to a standalone task`));let e=await u(t.getTasksPath(),r,i,a,{projectRoot:t.getProjectRoot(),tag:o});console.log(W(i&&e?D.text.bold(`Subtask ${r} Converted to Task #${e.id}`)+`
|
|
32
|
+
|
|
33
|
+
`+D.text(`Title: ${e.title}`)+`
|
|
34
|
+
`+D.text(`Status: ${L(e.status)}`)+`
|
|
35
|
+
`+D.text(`Dependencies: ${e.dependencies.join(`, `)}`)+`
|
|
36
|
+
|
|
37
|
+
`+D.text.bold(`Next Steps:`)+`
|
|
38
|
+
`+z.cyan(`1. Run ${z.yellow(`taskgraph show ${e.id}`)} to see details of the new task`)+`
|
|
39
|
+
`+z.cyan(`2. Run ${z.yellow(`taskgraph set-status --id=${e.id} --status=in-progress`)} to start working on it`):D.text.bold(`Subtask ${r} Removed`)+`
|
|
40
|
+
|
|
41
|
+
`+D.text(`The subtask has been successfully deleted.`),{padding:1,borderColor:`green`,borderStyle:`round`,margin:{top:1}}))}}catch(e){N(e)}}).on(`error`,function(e){console.error(z.red(`Error: ${e.message}`)),n(),process.exit(1)});function n(){console.log(W(D.text.bold(`Remove Subtask Command Help`)+`
|
|
42
|
+
|
|
43
|
+
`+z.cyan(`Usage:`)+`
|
|
44
|
+
taskgraph remove-subtask --id=<parentId.subtaskId> [options]
|
|
45
|
+
|
|
46
|
+
`+z.cyan(`Options:`)+`
|
|
47
|
+
-i, --id <id> Subtask ID(s) to remove in format "parentId.subtaskId" (can be comma-separated, required)
|
|
48
|
+
-c, --convert Convert the subtask to a standalone task instead of deleting it
|
|
49
|
+
-f, --file <file> Path to the tasks file (default: "`+x+`")
|
|
50
|
+
--skip-generate Skip regenerating task files
|
|
51
|
+
|
|
52
|
+
`+z.cyan(`Examples:`)+`
|
|
53
|
+
taskgraph remove-subtask --id=5.2
|
|
54
|
+
taskgraph remove-subtask --id=5.2,6.3,7.1
|
|
55
|
+
taskgraph remove-subtask --id=5.2 --convert`,{padding:1,borderColor:E,borderStyle:`round`}))}e.command(`remove-task`).description(`Remove one or more tasks or subtasks permanently`).option(`-i, --id <ids>`,`ID(s) of the task(s) or subtask(s) to remove (e.g., "5", "5.2", or "5,6.1,7")`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-y, --yes`,`Skip confirmation prompt`,!1).option(`--tag <tag>`,`Specify tag context for task operations`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=e.id,r=t.getCurrentTag();await F(r),n||(console.error(z.red(`Error: Task ID(s) are required`)),console.error(z.yellow(`Usage: taskgraph remove-task --id=<taskId1,taskId2...>`)),process.exit(1));let i=n.split(`,`).map(e=>e.trim()).filter(Boolean);i.length===0&&(console.error(z.red(`Error: No valid task IDs provided.`)),process.exit(1));try{let n=t.getTasksPath(),a=O(n,t.getProjectRoot(),r);(!a||!a.tasks)&&(console.error(z.red(`Error: No valid tasks found in ${n}`)),process.exit(1));let o=[],s=[],c=0,l=[];for(let e of i)if(!ne(a.tasks,e))s.push(e);else{let t=le(a.tasks,e).task;if(t){if(o.push({id:e,task:t}),!t.isSubtask){t.subtasks&&t.subtasks.length>0&&(c+=t.subtasks.length);let n=a.tasks.filter(t=>t.dependencies&&t.dependencies.includes(parseInt(e,10)));n.length>0&&l.push(` - Task ${e}: ${n.length} dependent tasks (${n.map(e=>e.id).join(`, `)})`)}}else s.push(`${e} (error finding details)`)}let u=`${s.length===1?`Task`:`Tasks`} ${s.join(`, `)} not found`;if(o.length===0&&(A(u,"Run `taskgraph list` to see task IDs"),process.exit(1)),s.length>0&&console.warn(z.yellow(`${u}, skipping`)),!e.yes){console.log(),console.log(z.red.bold(`⚠️ WARNING: This will permanently delete the following ${o.length} item(s):`)),console.log(),o.forEach(({id:e,task:t})=>{t&&(t.isSubtask?(console.log(D.text(` Subtask ${e}: ${t.title||`(no title)`}`)),t.parentTask&&console.log(D.muted(` (Parent: ${t.parentTask.id} - ${t.parentTask.title||`(no title)`})`))):console.log(D.text.bold(` Task ${e}: ${t.title||`(no title)`}`)))}),c>0&&console.log(z.yellow(`⚠️ This will also delete ${c} subtasks associated with the selected main tasks!`)),l.length>0&&(console.log(z.yellow(`⚠️ Warning: Dependencies on the following tasks will be removed:`)),l.forEach(e=>console.log(z.yellow(e)))),console.log();let{confirm:e}=await K.prompt([{type:`confirm`,name:`confirm`,message:z.red.bold(`Are you sure you want to permanently delete these ${o.length} item(s)?`),default:!1}]);e||(console.log(D.brand(`Task deletion cancelled.`)),process.exit(0))}let d=Ke(`Removing ${o.length} task(s)/subtask(s)...`),f=o.map(({id:e})=>e).join(`,`),m=await p(t.getTasksPath(),f,{projectRoot:t.getProjectRoot(),tag:r});Ie(d),m.success?console.log(W(z.green(`Successfully removed ${m.removedTasks.length} task(s)/subtask(s).`)+(m.message?`\n\nDetails:\n${m.message}`:``)+(m.error?`\n\nWarnings:\n${z.yellow(m.error)}`:``),{padding:1,borderColor:`green`,borderStyle:`round`})):(console.error(W(z.red(`Operation completed with errors. Removed ${m.removedTasks.length} task(s)/subtask(s).`)+(m.message?`\n\nDetails:\n${m.message}`:``)+(m.error?`\n\nErrors:\n${z.red(m.error)}`:``),{padding:1,borderColor:`red`,borderStyle:`round`})),process.exit(1)),s.length>0&&(console.warn(z.yellow(`Note: The following IDs were not found initially and were skipped: ${s.join(`, `)}`)),m.removedTasks.length===0&&process.exit(1))}catch(e){N(e)}}),e.command(`init`).description(`Initialize a new project with TaskGraph structure`).option(`-y, --yes`,`Skip prompts and use default values`).option(`-n, --name <name>`,`Project name`).option(`-d, --description <description>`,`Project description`).option(`-v, --version <version>`,`Project version`,`0.1.0`).option(`-a, --author <author>`,`Author name`).option(`-r, --rules <rules...>`,`Rule profiles to install (claude). Accepts comma or space separated values.`).option(`--skip-install`,`Skip installing dependencies`).option(`--dry-run`,`Show what would be done without making changes`).option(`--aliases`,`Add shell aliases (tm, taskgraph)`).option(`--no-aliases`,`Skip shell aliases (tm, taskgraph)`).option(`--git`,`Initialize Git repository`).option(`--no-git`,`Skip Git repository initialization`).option(`--git-tasks`,`Store tasks in Git`).option(`--no-git-tasks`,`No Git storage of tasks`).action(async e=>{let t=R,n=!1;if(e.rules&&Array.isArray(e.rules)){let r=e.rules.flatMap(e=>e.split(`,`)).map(e=>e.trim()).filter(Boolean),i=Je(r);i.length>0&&(A(`"${i.join(`, `)}" isn't a rule profile`,`TaskGraph supports Claude Code only; use \`--rules ${R.join(`,`)}\``),process.exit(1)),r.length>0&&(t=r,n=!0)}e.rules=t,e.rulesExplicitlyProvided=n;try{await Ye(e)}catch(e){N(e)}}),e.command(`models`).description(`Manage AI model configurations (Claude Code models)`).option(`--set-main <model_id>`,`Set the primary model for task generation/updates`).option(`--set-research <model_id>`,`Set the model for research-backed operations`).option(`--set-fallback <model_id>`,`Set the model to use if the primary fails`).option(`--setup`,`Run interactive setup to configure models`).addOption(new U(`--claude-code`).hideHelp()).addOption(new U(`--openrouter`).hideHelp()).addOption(new U(`--ollama`).hideHelp()).addOption(new U(`--bedrock`).hideHelp()).addOption(new U(`--azure`).hideHelp()).addOption(new U(`--vertex`).hideHelp()).addOption(new U(`--gemini-cli`).hideHelp()).addOption(new U(`--codex-cli`).hideHelp()).addOption(new U(`--lmstudio`).hideHelp()).addOption(new U(`--openai-compatible`).hideHelp()).addOption(new U(`--baseURL <url>`).hideHelp()).addHelpText(`after`,`
|
|
56
|
+
TaskGraph uses Claude Code. Available models: opus, sonnet, haiku.
|
|
57
|
+
|
|
58
|
+
Examples:
|
|
59
|
+
$ taskgraph models # View current configuration
|
|
60
|
+
$ taskgraph models --set-main sonnet # Set main model
|
|
61
|
+
$ taskgraph models --set-research opus # Set research model
|
|
62
|
+
$ taskgraph models --set-fallback haiku # Set fallback
|
|
63
|
+
$ taskgraph models --setup # Run interactive setup`).action(async e=>{let t=X({tasksPath:e.file||!1}).getProjectRoot(),n=[`openrouter`,`ollama`,`bedrock`,`azure`,`vertex`,`geminiCli`,`codexCli`,`lmstudio`,`openaiCompatible`,`baseURL`].filter(t=>e[t]);if(n.length>0){let e=`--${n[0]===`baseURL`?`baseURL`:n[0].replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}`;A(`${e} isn't supported any more; TaskGraph runs on Claude Code`,"Choose a model with `taskgraph models --set-main <opus|sonnet|haiku>`"),process.exit(1)}if(C(t)||(A(`No .taskgraph/config.json in this project`,"Run `taskgraph init` to recreate it"),process.exit(1)),e.setup){try{!await Ne(t)&&!process.exitCode&&(process.exitCode=1)}catch(e){N(e)}return}let r=await je({projectRoot:t});r.success||(A(`Couldn't read the model settings: ${r.error.message}`,"Run `taskgraph init` to recreate .taskgraph/config.json"),process.exit(1));let i=r.data.activeModels,a=await ke({projectRoot:t}),o=a.success?a.data.models.map(e=>e.modelId):[],s=[[`main`,e.setMain],[`research`,e.setResearch],[`fallback`,e.setFallback]].filter(([,e])=>e);if(s.length>0){let e=s.find(([,e])=>!o.includes(e));e&&(A(`"${e[1]}" isn't a Claude Code model`,`Choose ${_e(o)}`),process.exit(1));let n=[];for(let[e,r]of s){let a=await Te(e,r,{projectRoot:t});a.success||(n.length>0&&console.log(j(n)),A(`Couldn't set the ${e} model: ${a.error.message}`),process.exit(1)),n.push({role:e,from:i[e]?.modelId??null,to:r})}console.log(j(n))}else console.log(Ee({main:i.main?.modelId??null,research:i.research?.modelId??null,fallback:i.fallback?.modelId??null},o))}),e.command(`lang`).description(`Show or set the language AI responses are written in`).option(`--response <response_language>`,`Set the response language`).option(`--setup`,`Choose the response language interactively`).addHelpText(`after`,`
|
|
64
|
+
Examples:
|
|
65
|
+
$ taskgraph lang # Show the response language
|
|
66
|
+
$ taskgraph lang --response French # Set it
|
|
67
|
+
$ taskgraph lang --setup # Choose it interactively`).action(async e=>{let t=X({}).getProjectRoot();C(t)||(A(`No .taskgraph/config.json in this project`,"Run `taskgraph init` to recreate it"),process.exit(1));let n=de(t)||`English`,r=e.response;if(e.setup)try{r=(await K.prompt([{type:`input`,name:`language`,message:`Language for AI responses?`,default:n}])).language}catch(e){if(e?.name!==`ExitPromptError`)throw e;console.log(D.muted(`
|
|
68
|
+
Cancelled; response language unchanged`)),process.exitCode=130;return}if(r===void 0){console.log(`Response language: ${D.brand(n)}`),console.log(D.muted(`Change it with `)+D.brand(`taskgraph lang --response <language>`));return}r=r.trim();let i=qe(r,{projectRoot:t});i.success||(A(`Couldn't set the response language: ${i.error.message}`),process.exit(1));let a=r===n?`${D.brand(r)} ${D.muted(`(unchanged)`)}`:`${n} → ${D.brand(r)}`;console.log(`${z.green(`✓`)} Response language ${a}`)}),e.command(`move`).description(`Move tasks between tags or reorder within tags. Supports cross-tag moves with dependency resolution options.`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--from <id>`,`ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")`).option(`--to <id>`,`ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated`).option(`--tag <tag>`,`Specify tag context for task operations`).option(`--from-tag <tag>`,`Source tag for cross-tag moves`).option(`--to-tag <tag>`,`Target tag for cross-tag moves`).option(`--with-dependencies`,`Move dependent tasks along with main task`).option(`--ignore-dependencies`,`Break cross-tag dependencies during move`).action(async e=>{function t(){console.log(D.text.bold(`Move Command Help`)+`
|
|
69
|
+
|
|
70
|
+
`+z.cyan(`Move tasks between tags or reorder within tags.`)+`
|
|
71
|
+
|
|
72
|
+
`+z.yellow.bold(`Within-Tag Moves:`)+`
|
|
73
|
+
`+D.text(` taskgraph move --from=5 --to=7`)+`
|
|
74
|
+
`+D.text(` taskgraph move --from=5.2 --to=7.3`)+`
|
|
75
|
+
`+D.text(` taskgraph move --from=5,6,7 --to=10,11,12`)+`
|
|
76
|
+
|
|
77
|
+
`+z.yellow.bold(`Cross-Tag Moves:`)+`
|
|
78
|
+
`+D.text(` taskgraph move --from=5 --from-tag=backlog --to-tag=in-progress`)+`
|
|
79
|
+
`+D.text(` taskgraph move --from=5,6 --from-tag=backlog --to-tag=done`)+`
|
|
80
|
+
|
|
81
|
+
`+z.yellow.bold(`Dependency Resolution:`)+`
|
|
82
|
+
`+D.text(` # Move with dependencies`)+`
|
|
83
|
+
`+D.text(` taskgraph move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies`)+`
|
|
84
|
+
|
|
85
|
+
`+D.text(` # Break dependencies`)+`
|
|
86
|
+
`+D.text(` taskgraph move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies`)+`
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
`+z.yellow.bold(`Best Practices:`)+`
|
|
90
|
+
`+D.text(` • Use --with-dependencies to move dependent tasks together`)+`
|
|
91
|
+
`+D.text(` • Use --ignore-dependencies to break cross-tag dependencies`)+`
|
|
92
|
+
`+D.text(` • Check dependencies first: taskgraph validate-dependencies`)+`
|
|
93
|
+
`+D.text(` • Fix dependency issues: taskgraph fix-dependencies`)+`
|
|
94
|
+
|
|
95
|
+
`+z.yellow.bold(`Error Resolution:`)+`
|
|
96
|
+
`+D.text(` • Cross-tag dependency conflicts: Use --with-dependencies or --ignore-dependencies`)+`
|
|
97
|
+
`+D.text(` • Subtask movement: Promote subtask first with remove-subtask --convert`)+`
|
|
98
|
+
`+D.text(` • Invalid tags: Check available tags with taskgraph tags`)+`
|
|
99
|
+
|
|
100
|
+
`+D.muted(`For more help, run: taskgraph move --help`))}async function n(e,n){let{sourceId:r,sourceTag:i,toTag:a,taskGraph:o}=e;r||(console.error(z.red(`Error: --from parameter is required for cross-tag moves`)),t(),process.exit(1));let s=r.split(`,`).map(e=>e.trim()),c={withDependencies:n.withDependencies||!1,ignoreDependencies:n.ignoreDependencies||!1};console.log(D.brand(`Moving tasks ${s.join(`, `)} from "${i}" to "${a}"...`));let l=await m(o.getTasksPath(),s,i,a,c,{projectRoot:o.getProjectRoot()});console.log(z.green(`✓ ${l.message}`)),Array.isArray(l.tips)&&l.tips.length>0&&(console.log(`
|
|
101
|
+
`+z.yellow.bold(`Next Steps:`)),l.tips.forEach(e=>console.log(D.text(` • ${e}`))))}async function r(e){let{sourceId:t,destinationId:n,tag:r,taskGraph:i}=e;(!t||!n)&&(console.error(z.red(`Error: Both --from and --to parameters are required for within-tag moves`)),console.log(z.yellow(`Usage: taskgraph move --from=<sourceId> --to=<destinationId>`)),process.exit(1));let a=t.split(`,`).map(e=>e.trim()),o=n.split(`,`).map(e=>e.trim());if(a.length!==o.length&&(console.error(z.red(`Error: The number of source and destination IDs must match`)),console.log(z.yellow(`Example: taskgraph move --from=5,6,7 --to=10,11,12`)),process.exit(1)),a.length>1){console.log(D.brand(`Moving multiple tasks: ${a.join(`, `)} to ${o.join(`, `)}...`));let e=O(i.getTasksPath(),i.getProjectRoot(),r);(!e||!e.tasks)&&(console.error(z.red(`Error: Invalid or missing tasks file at ${i.getTasksPath()}`)),process.exit(1));let t=[],n=[];for(let e=0;e<a.length;e++){let s=a[e],c=o[e];if(s===c)console.log(z.yellow(`Skipping ${s} -> ${c} (same ID)`));else try{await _(i.getTasksPath(),s,c,e===a.length-1,{projectRoot:i.getProjectRoot(),tag:r}),console.log(z.green(`✓ Successfully moved task/subtask ${s} to ${c}`)),n.push({fromId:s,toId:c})}catch(e){let n={fromId:s,toId:c,error:e.message};t.push(n),console.error(z.red(`Error moving ${s} to ${c}: ${e.message}`))}}t.length>0?(console.log(z.yellow(`
|
|
102
|
+
--- Move Operation Summary ---`)),console.log(z.green(`✓ Successfully moved: ${n.length} tasks`)),console.log(z.red(`✗ Failed to move: ${t.length} tasks`)),n.length>0&&(console.log(z.cyan(`
|
|
103
|
+
Successful moves:`)),n.forEach(({fromId:e,toId:t})=>{console.log(z.cyan(` ${e} → ${t}`))})),console.log(z.red(`
|
|
104
|
+
Failed moves:`)),t.forEach(({fromId:e,toId:t,error:n})=>{console.log(z.red(` ${e} → ${t}: ${n}`))}),console.log(z.yellow(`
|
|
105
|
+
Note: Some tasks were moved successfully. Check the errors above for failed moves.`))):console.log(z.green(`
|
|
106
|
+
✓ All tasks moved successfully!`))}else await _(i.getTasksPath(),t,n,!0,{projectRoot:i.getProjectRoot(),tag:r}),console.log(z.green(`✓ Successfully moved task/subtask ${t} to ${n}`))}function i(e,t){if([`CROSS_TAG_DEPENDENCY_CONFLICTS`,`CANNOT_MOVE_SUBTASK`,`SOURCE_TARGET_TAGS_SAME`,`SAME_SOURCE_TARGET_TAG`].includes(e.code)||N(e,{hint:/already exists/i.test(e.message)?"Run `taskgraph list` to see which IDs are taken":void 0}),console.error(z.red(`Error: ${e.message}`)),e.code===`CROSS_TAG_DEPENDENCY_CONFLICTS`){let n=e.data.conflicts||[],r=e.data.taskIds||[];Ue(n,t.sourceTag,t.toTag,r.join(`, `))}else if(e.code===`CANNOT_MOVE_SUBTASK`){let n=e.data.taskId||t.sourceId?.split(`,`)[0];Ve(n,t.sourceTag,t.toTag)}else e.code===`SOURCE_TARGET_TAGS_SAME`||e.code===`SAME_SOURCE_TARGET_TAG`?Be(t.sourceTag,t.toTag,`Source and target tags are identical`):Ge(`after-error`);process.exit(1)}let a=X({tasksPath:e.file||!0,tag:e.tag}),o=e.from,s=e.to,c=e.fromTag,l=e.toTag,u=a.getCurrentTag(),d=c||a.getCurrentTag(),f=d&&l&&d!==l,p={sourceId:o,destinationId:s,sourceTag:d,toTag:l,tag:u,taskGraph:a};try{f?await n(p,e):await r(p)}catch(e){let t=String(e&&(e.message||e));t.includes(`already exists in target tag`)&&(console.error(z.red(`Error: ${t}`)),console.log(`
|
|
107
|
+
`+z.yellow.bold(`Conflict: ID already exists in target tag`)+`
|
|
108
|
+
`+D.text(` • Choose a different target tag without conflicting IDs`)+`
|
|
109
|
+
`+D.text(` • Move a different set of IDs (avoid existing ones)`)+`
|
|
110
|
+
`+D.text(` • If needed, move within-tag to a new ID first, then cross-tag move`)),process.exit(1)),i(e,p)}}),e.command(`sync-readme`).description(`Sync the current task list to README.md in the project root`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--with-subtasks`,`Include subtasks in the README output`).option(`-s, --status <status>`,`Show only tasks matching this status (e.g., pending, done)`).option(`-t, --tag <tag>`,`Tag to use for the task list (default: master)`).action(async e=>{let t=X({tasksPath:e.file||!0,tag:e.tag}),n=e.withSubtasks||!1,r=e.status||null,i=t.getCurrentTag();console.log(D.brand(`📝 Syncing tasks to README.md${n?` (with subtasks)`:``}${r?` (status: ${r})`:``}...`)),await dt(t.getProjectRoot(),{withSubtasks:n,status:r,tasksPath:t.getTasksPath(),tag:i})||(console.error(z.red(`❌ Failed to sync tasks to README.md`)),process.exit(1))});function f(e,t){console.error(`${z.yellow(`⚠`)} ${e} is deprecated; use ${D.brand(`taskgraph ${t}`)}`)}return e.command(`add-tag`).description(`[DEPRECATED] Create a new tag context for organizing tasks (use "taskgraph tags add" instead)`).argument(`[tagName]`,`Name of the new tag to create (optional when using --from-branch)`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`--copy-from-current`,`Copy tasks from the current tag to the new tag`).option(`--copy-from <tag>`,`Copy tasks from the specified tag to the new tag`).option(`--from-branch`,`Create tag name from current git branch (ignores tagName argument)`).option(`-d, --description <text>`,`Optional description for the tag`).action(async(e,t)=>{f(`add-tag`,`tags add`);try{let n=X({tasksPath:t.file||!0}),r=n.getTasksPath();H.existsSync(r)||(A(`No tasks yet`,$),process.exit(1)),!e&&!t.fromBranch&&(A(`add-tag needs a tag name`,"Example: `taskgraph tags add feature-auth`, or `--from-branch`"),process.exit(1));let i={projectRoot:n.getProjectRoot(),commandName:`add-tag`,outputType:`cli`};if(t.fromBranch){let{createTagFromBranch:e}=await import(`./tag-management-D4lpCywn.js`),r=await import(`./git-utils-CRR3iPaC.js`);await r.isGitRepository(i.projectRoot)||(A(`--from-branch needs a git repository, and this isn't one`,"Name the tag instead: `taskgraph tags add <name>`"),process.exit(1));let a=await r.getCurrentBranch(i.projectRoot);a||(A(`Couldn't tell which git branch is checked out`,"Name the tag instead: `taskgraph tags add <name>`"),process.exit(1));let o={copyFromCurrent:t.copyFromCurrent||!1,copyFromTag:t.copyFrom,description:t.description};await e(n.getTasksPath(),a,o,i,`text`)}else{let r={copyFromCurrent:t.copyFromCurrent||!1,copyFromTag:t.copyFrom,description:t.description};await Me(n.getTasksPath(),e,r,i,`text`)}if(t.autoSwitch){let{useTag:r}=await import(`./tag-management-D4lpCywn.js`),a=t.fromBranch?(await import(`./git-utils-CRR3iPaC.js`)).sanitizeBranchNameForTag(await(await import(`./git-utils-CRR3iPaC.js`)).getCurrentBranch(projectRoot)):e;await r(n.getTasksPath(),a,{},i,`text`)}}catch(e){N(e)}}).on(`error`,function(e){N(e)}),e.command(`delete-tag`).description(`[DEPRECATED] Delete an existing tag and all its tasks (use "taskgraph tags remove" instead)`).argument(`<tagName>`,`Name of the tag to delete`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-y, --yes`,`Skip confirmation prompts`).action(async(e,t)=>{f(`delete-tag`,`tags remove`);try{let n=X({tasksPath:t.file||!0}),r=n.getTasksPath();H.existsSync(r)||(A(`No tasks yet`,$),process.exit(1));let i={yes:t.yes||!1},a={projectRoot:n.getProjectRoot(),commandName:`delete-tag`,outputType:`cli`};await De(n.getTasksPath(),e,i,a,`text`)}catch(e){N(e)}}).on(`error`,function(e){N(e)}),e.command(`use-tag`).description(`[DEPRECATED] Switch to a different tag context (use "taskgraph tags use" instead)`).argument(`<tagName>`,`Name of the tag to switch to`).option(`-f, --file <file>`,`Path to the tasks file`,x).action(async(e,t)=>{f(`use-tag`,`tags use`);try{let n=X({tasksPath:t.file||!0}),r=n.getTasksPath();H.existsSync(r)||(A(`No tasks yet`,$),process.exit(1));let i={projectRoot:n.getProjectRoot(),commandName:`use-tag`,outputType:`cli`};await Oe(n.getTasksPath(),e,{},i,`text`)}catch(e){N(e)}}).on(`error`,function(e){N(e)}),e.command(`rename-tag`).description(`[DEPRECATED] Rename an existing tag (use "taskgraph tags rename" instead)`).argument(`<oldName>`,`Current name of the tag`).argument(`<newName>`,`New name for the tag`).option(`-f, --file <file>`,`Path to the tasks file`,x).action(async(e,t,n)=>{f(`rename-tag`,`tags rename`);try{let r=X({tasksPath:n.file||!0}),i=r.getTasksPath();H.existsSync(i)||(A(`No tasks yet`,$),process.exit(1));let a={projectRoot:r.getProjectRoot(),commandName:`rename-tag`,outputType:`cli`};await Pe(r.getTasksPath(),e,t,{},a,`text`)}catch(e){N(e)}}).on(`error`,function(e){N(e)}),e.command(`copy-tag`).description(`[DEPRECATED] Copy an existing tag to create a new tag with the same tasks (use "taskgraph tags copy" instead)`).argument(`<sourceName>`,`Name of the source tag to copy from`).argument(`<targetName>`,`Name of the new tag to create`).option(`-f, --file <file>`,`Path to the tasks file`,x).option(`-d, --description <text>`,`Optional description for the new tag`).action(async(e,t,n)=>{f(`copy-tag`,`tags copy`);try{let r=X({tasksPath:n.file||!0}),i=r.getTasksPath();H.existsSync(i)||(A(`No tasks yet`,$),process.exit(1));let a={description:n.description},o={projectRoot:r.getProjectRoot(),commandName:`copy-tag`,outputType:`cli`};await Ae(i,e,t,a,o,`text`)}catch(e){N(e)}}).on(`error`,function(e){N(e)}),e.command(`tui`).alias(`repl`).description(`Launch the interactive TUI/REPL mode`).action(async()=>{await ht()}),e}async function mt(){return null}async function ht(){let e=await mt();if(!e){console.log(z.yellow(`TUI mode coming soon!`)),console.log(z.dim(`Showing help instead...
|
|
111
|
+
`)),I();return}let t=await import(`react`),{render:n,Shell:r}=e,i=`master`,a=process.cwd();try{let e=X({});i=e.getCurrentTag(),a=e.getProjectRoot()||process.cwd()}catch{}let o=process.stdin.isTTY&&typeof process.stdin.setRawMode==`function`;console.clear();let s={showBanner:!0,showSplash:o,initialTag:i,isInteractive:o,projectRoot:a,onExit:()=>{console.log(z.dim(`
|
|
112
|
+
Goodbye! 👋`)),process.exit(0)}},c=n(t.createElement(r,s));o||setTimeout(()=>{c.unmount(),console.log(z.dim(`
|
|
113
|
+
💡 Run in an interactive terminal for full REPL mode.`)),process.exit(0)},200)}const $="Create some with `taskgraph parse-prd <file>`",gt=/* @__PURE__ */ new Set([`init`,`help`,`completion`,`tui`,`repl`]);function _t(n,r){let i=r;for(;i.parent?.parent;)i=i.parent;if([i.name(),...i.aliases()].some(e=>gt.has(e))||r.getOptionValueSource(`file`)===`cli`)return;let a=r.options.some(e=>e.attributeName()===`project`)?r.opts().project:null,o=typeof a==`string`?B.resolve(a):null;t(o||e())||(o?A(`No TaskGraph project at ${o}`,"Run `taskgraph init` in that folder to set one up"):A(`No TaskGraph project in this folder`,"Run `taskgraph init` to set one up, or cd into your project"),process.exit(1))}function vt(){let e=new Qe().name(`taskgraph`).description(`AI-driven development task management`).version(`0.54.7`).helpOption(`-h, --help`,`Display help`).addHelpCommand(!1),t=e.helpInformation.bind(e);e.helpInformation=function(){return this.parent&&this.parent!==e?t():(I(),``)},pt(e);let n=(e,t)=>{e.configureOutput({outputError:(e,n)=>n(`\n${ye(e,t||void 0)}\n`)});for(let r of e.commands)n(r,t?`${t} ${r.name()}`:r.name())};return n(e,``),e.hook(`preAction`,_t),e}async function yt(e=process.argv){try{if(e.length<=2){await ht();return}let t=e.includes(`init`);[`init`,`models`,`lang`].includes(e[2])&&ue(!0);let n=e.includes(`tui`)||e.includes(`repl`),r=e.includes(`--no-banner`);process.stdout.isTTY&&!t&&!n&&!r&&ze();let i=e[2]===`completion`,a=process.env.TASKGRAPH_SKIP_AUTO_UPDATE===`1`||process.env.CI||process.env.NODE_ENV===`test`||i,o=Re(),s=a?{currentVersion:o,latestVersion:o,needsUpdate:!1}:await ot(o);if(s.needsUpdate&&(Fe(s.currentVersion,s.latestVersion,s.highlights),await xe(s.latestVersion))){we(e);return}let c=vt(),l=e.filter(e=>e!==`--no-banner`);await c.parseAsync(l);try{if(i)return;let e=X({}).getStatePath();if(e&&H.existsSync(e)){let t=JSON.parse(H.readFileSync(e,`utf8`));t?.migrationNoticePending===!0&&(Le({_migrationHappened:!0}),t.migrationNoticePending=!1,t.migrationNoticeShown=!0,H.writeFileSync(e,JSON.stringify(t,null,2)))}}catch{}}catch(e){e instanceof me?console.error(W(z.red.bold(`Configuration Update Required!`)+`
|
|
114
|
+
|
|
115
|
+
`+D.text(`TaskGraph now uses a `)+z.yellow.bold(`configuration file`)+D.text(` in your project for AI model choices and settings.
|
|
116
|
+
|
|
117
|
+
This file appears to be `)+z.red.bold(`missing`)+D.text(`. No worries though.
|
|
118
|
+
|
|
119
|
+
`)+z.cyan.bold(`To create this file, run the interactive setup:`)+`
|
|
120
|
+
`+z.green(` taskgraph models --setup`)+`
|
|
121
|
+
|
|
122
|
+
`+D.text.bold(`Key Points:`)+`
|
|
123
|
+
`+D.text(`* `)+z.yellow.bold(`Configuration file`)+D.text(`: Stores your AI model settings (do not manually edit)
|
|
124
|
+
`)+D.text(`* `)+z.yellow.bold(`.env & .mcp.json`)+D.text(`: Still used `)+z.red.bold(`only`)+D.text(` for your AI provider API keys.
|
|
125
|
+
|
|
126
|
+
`)+z.cyan("`taskgraph models` to check your config & available models\n")+z.cyan("`taskgraph models --setup` to adjust the AI models used by TaskGraph"),{padding:1,margin:{top:1},borderColor:`red`,borderStyle:`round`})):N(e),process.exit(1)}}export{yt as runCLI};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./src-xo9dUCIC.js";export{S as getUserId}from"./config-manager-CecOyMub.js";
|