taskgraph-ai 0.0.0-stage → 0.50.1

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 CHANGED
@@ -1,3 +1,236 @@
1
- # Temporary Holding Version
1
+ <a name="readme-top"></a>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <p align="center">
4
+ <picture>
5
+ <source media="(prefers-color-scheme: dark)" srcset="./images/taskgraph-logo-dark.png">
6
+ <img src="./images/taskgraph-logo.png" alt="TaskGraph logo" width="640">
7
+ </picture>
8
+ </p>
9
+
10
+ <p align="center">
11
+ <b>TaskGraph</b>: task management for AI-driven development, built for Claude Code.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://github.com/stephennmiller/taskgraph-ai/actions/workflows/ci.yml"><img src="https://github.com/stephennmiller/taskgraph-ai/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
16
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT%20with%20Commons%20Clause-blue.svg" alt="License"></a>
17
+ </p>
18
+
19
+ TaskGraph turns a product requirements document (PRD) into a dependency-aware task list, then walks Claude Code through it one task at a time. You describe what to build, TaskGraph plans the work, and Claude Code implements it, with the plan and progress stored in your repo.
20
+
21
+ - **Plan from a PRD:** generate tasks and subtasks, with dependencies, priorities and test strategies.
22
+ - **Always know what's next:** `next` picks the first unblocked task.
23
+ - **Stay in Claude Code:** use slash commands (`/tm:next-task`) or the `taskgraph` CLI. An MCP server is available if you want plain-language control, but it is optional.
24
+ - **No API keys:** every AI call runs through your signed-in Claude Code.
25
+
26
+ > **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)). Existing `.taskmaster/` project folders work unchanged.
27
+
28
+ ## Contents
29
+
30
+ [Set up in Claude Code](#set-up-in-claude-code) · [How to use it](#how-to-use-it) · [Commands](#commands) · [MCP tool tiers](#mcp-tool-tiers) · [Models](#models) · [Troubleshooting](#troubleshooting) · [Docs](#documentation) · [Licensing](#licensing)
31
+
32
+ ## Set up in Claude Code
33
+
34
+ You need [Claude Code](https://docs.claude.com/en/docs/claude-code) installed and signed in (run `claude` once) and Node.js 22 or later.
35
+
36
+ ### 1. Install
37
+
38
+ ```bash
39
+ npm install -g taskgraph-ai
40
+ ```
41
+
42
+ This gives you the `taskgraph` command (the CLI) and `taskgraph-mcp`, the optional MCP server. To skip the install, run any command through npx, for example `npx -y -p taskgraph-ai taskgraph list`. To build from source instead, see [Working on TaskGraph itself](#troubleshooting).
43
+
44
+ ### 2. Initialize your project
45
+
46
+ From your own project folder:
47
+
48
+ ```bash
49
+ taskgraph init --rules claude
50
+ ```
51
+
52
+ This creates `.taskgraph/` (tasks, docs, config), a `CLAUDE.md` import with TaskGraph's workflow guide, and 46 slash commands in `.claude/commands/tm/`. Don't run it twice; it only re-adds the same files.
53
+
54
+ `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.
55
+
56
+ ### 3. (Optional) Install the plugin
57
+
58
+ The plugin adds a nearly identical set of 46 slash commands (as `/taskgraph:<name>`) and three agents (`task-orchestrator`, `task-executor`, `task-checker`).
59
+
60
+ ```txt
61
+ /plugin marketplace add stephennmiller/taskgraph-ai
62
+ /plugin install taskgraph@taskgraph
63
+ ```
64
+
65
+ 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). Its slash commands overlap with the ones from step 2, so install the plugin only if you want the agents.
66
+
67
+ ### 4. (Optional) Add the MCP server
68
+
69
+ Skip this unless you want to drive TaskGraph in plain language. Slash commands and the CLI do everything without it.
70
+
71
+ ```bash
72
+ claude mcp add taskgraph-ai --scope user \
73
+ --env TASKGRAPH_TOOLS=standard \
74
+ -- npx -y taskgraph-ai
75
+ ```
76
+
77
+ Restart Claude Code and run `/mcp`; `taskgraph-ai` should show as connected. `standard` loads 14 tools, enough for everything in [How to use it](#how-to-use-it). See [MCP tool tiers](#mcp-tool-tiers) for the others.
78
+
79
+ To share the setup with your team instead, put it in the project's `.mcp.json`:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "taskgraph-ai": {
85
+ "command": "npx",
86
+ "args": ["-y", "taskgraph-ai"],
87
+ "env": { "TASKGRAPH_TOOLS": "standard" }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## How to use it
94
+
95
+ The examples below use slash commands (`/tm:...`), with the CLI equivalent in each table. If you added the optional MCP server, you can say the same things in plain language instead.
96
+
97
+ ### 1. Write a PRD
98
+
99
+ Create `.taskgraph/docs/prd.md` describing what you want to build. The more detail you give, the better the tasks. Skipping the PRD is fine for small jobs; just ask Claude to add a task.
100
+
101
+ ### 2. Generate tasks
102
+
103
+ ```txt
104
+ /tm:parse-prd .taskgraph/docs/prd.md
105
+ ```
106
+
107
+ Then, optionally, check how big each task is and break the big ones down:
108
+
109
+ ```txt
110
+ /tm:analyze-complexity
111
+ /tm:expand-all-tasks
112
+ ```
113
+
114
+ ### 3. Work through them
115
+
116
+ | Slash command | CLI | What happens |
117
+ |---|---|---|
118
+ | `/tm:next-task` | `taskgraph next` | Shows the first unblocked, pending task |
119
+ | `/tm:show-task 1` | `taskgraph show 1,3,5` | Full details for each task |
120
+ | `/tm: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 |
121
+ | `/tm:to-done 3` | `taskgraph set-status --id=3 --status=done` | Updates status and unlocks dependent tasks |
122
+ | `/tm:expand-task 4` | `taskgraph expand --id=4` | Splits it into subtasks |
123
+ | `/tm:add-task <what you want>` | `taskgraph add-task --prompt="..."` | Creates a task with AI help |
124
+ | `/tm:update-task 5 we now use Postgres` | `taskgraph update-task --id=5 --prompt="..."` | Rewrites it for the new direction |
125
+ | (CLI only) | `taskgraph research "JWT best practices"` | Web research with project context |
126
+
127
+ The loop is simple: **next, implement, done, next.** Task IDs look like `3` for a task and `3.2` for a subtask.
128
+
129
+ 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`.
130
+
131
+ ### 4. Keep it organized
132
+
133
+ - **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`).
134
+ - **Dependencies** decide what counts as "next". Run `/tm:validate-dependencies` or `/tm:fix-dependencies` if the graph looks wrong.
135
+ - **Statuses:** `pending`, `in-progress`, `done`, `review`, `deferred`, `cancelled`.
136
+
137
+ ## Commands
138
+
139
+ Slash commands and the `taskgraph` CLI cover everything. The MCP server (optional) lets you do the same in plain language.
140
+
141
+ ### Slash commands
142
+
143
+ After `taskgraph init --rules claude` they appear as `/tm:<name>` (type `/tm` to see them). The plugin provides the same set as `/taskgraph:<name>`.
144
+
145
+ | Purpose | Commands |
146
+ |---|---|
147
+ | Set up | `init-project`, `init-project-quick`, `install-taskgraph`, `setup-models`, `view-models` |
148
+ | Plan | `parse-prd`, `parse-prd-with-research`, `analyze-complexity`, `complexity-report`, `analyze-project` |
149
+ | See | `list-tasks`, `list-tasks-by-status`, `list-tasks-with-subtasks`, `next-task`, `show-task`, `project-status` |
150
+ | Change status | `to-pending`, `to-in-progress`, `to-done`, `to-review`, `to-deferred`, `to-cancelled` |
151
+ | Edit | `add-task`, `update-task`, `update-single-task`, `update-tasks-from-id`, `remove-task`, `convert-task-to-subtask` |
152
+ | Subtasks | `expand-task`, `expand-all-tasks`, `add-subtask`, `remove-subtask`, `remove-subtasks`, `remove-all-subtasks` |
153
+ | Dependencies | `add-dependency`, `remove-dependency`, `validate-dependencies`, `fix-dependencies` |
154
+ | Workflows | `smart-workflow`, `command-pipeline`, `auto-implement-tasks`, `sync-readme`, `learn`, `help` |
155
+
156
+ ### CLI cheat sheet
157
+
158
+ ```bash
159
+ taskgraph parse-prd .taskgraph/docs/prd.md # generate tasks from a PRD
160
+ taskgraph list # all tasks and their status
161
+ taskgraph next # next task to work on
162
+ taskgraph show 1,3,5 # one or more tasks
163
+ taskgraph set-status --id=3 --status=done # change status
164
+ taskgraph add-task --prompt="..." --research # add a task
165
+ taskgraph expand --id=4 --research # split into subtasks
166
+ taskgraph expand --all # expand every eligible task
167
+ taskgraph update-task --id=5 --prompt="..." # change one task
168
+ taskgraph update --from=5 --prompt="..." # change task 5 onward
169
+ taskgraph update-subtask --id=3.2 --prompt="notes"
170
+ taskgraph analyze-complexity --research
171
+ taskgraph complexity-report
172
+ taskgraph add-dependency --id=5 --depends-on=3
173
+ taskgraph validate-dependencies
174
+ taskgraph move --from=5 --from-tag=backlog --to-tag=in-progress
175
+ taskgraph research "latest JWT best practices"
176
+ taskgraph models --setup # choose models
177
+ ```
178
+
179
+ Every command, flag and example is in the [command reference](docs/command-reference.md).
180
+
181
+ ## MCP tool tiers
182
+
183
+ Only relevant if you added the optional MCP server. Each tool you load costs context. `TASKGRAPH_TOOLS` controls how many are registered. If it's unset, you get `core`.
184
+
185
+ | Value | Tools | Includes |
186
+ |---|---|---|
187
+ | `core` (default, alias `lean`) | 7 | `get_tasks`, `next_task`, `get_task`, `set_task_status`, `update_subtask`, `parse_prd`, `expand_task` |
188
+ | `standard` | 14 | core, plus `initialize_project`, `analyze_project_complexity`, `expand_all`, `add_subtask`, `remove_task`, `add_task`, `complexity_report` |
189
+ | `all` | 43 | everything: updates, dependencies, tags, `research`, `move_task`, `scope_up_task`, `scope_down_task`, `generate`, and the `autopilot_*` TDD workflow |
190
+ | a list | custom | e.g. `get_tasks,next_task,set_task_status` |
191
+
192
+ Set it when you add the server:
193
+
194
+ ```bash
195
+ claude mcp add taskgraph-ai --scope user \
196
+ --env TASKGRAPH_TOOLS=all \
197
+ -- npx -y taskgraph-ai
198
+ ```
199
+
200
+ Or edit the `env` block in `.mcp.json`, then restart Claude Code.
201
+
202
+ ## Models
203
+
204
+ TaskGraph uses three roles: **main**, **research** and **fallback** (used if either of the others fails). The choices are `opus`, `sonnet` and `haiku`. The research role gets web search and fetch only. It cannot read or change your files and has no shell.
205
+
206
+ In Claude Code, say `Change the main model to opus`, or run `taskgraph models --setup`. Details are in [Models](docs/models.md) and the [Claude Code guide](docs/examples/claude-code-usage.md).
207
+
208
+ ## Troubleshooting
209
+
210
+ - **`0 tools enabled` or the server won't connect:** restart Claude Code, run `/mcp`, and confirm `npx -y taskgraph-ai` starts on its own in a terminal (it waits for input; press Ctrl+C to stop).
211
+ - **Working on TaskGraph itself:** build from source with `git clone https://github.com/stephennmiller/taskgraph-ai.git && cd taskgraph-ai && npm install && npm run build`, then run `node dist/taskgraph.js` for the CLI. This repository's own `.mcp.json` starts `dist/mcp-server.js` relative to the repo, so run `npm run build` first or the server won't connect.
212
+ - **AI commands fail:** run `claude` once and make sure you're signed in.
213
+ - **Fewer tools than expected:** `TASKGRAPH_TOOLS` defaults to `core`. Set `standard` or `all`.
214
+ - **Commands behave oddly after `git pull`:** run `npm run build` again; the CLI and server run from `dist/`.
215
+ - **Task files look out of sync:** run `taskgraph fix-dependencies`, or ask Claude to fix dependencies.
216
+ - Don't hand-edit `.taskgraph/tasks/tasks.json` or `.taskgraph/config.json`; use the commands.
217
+
218
+ ## Documentation
219
+
220
+ [Tutorial](docs/tutorial.md) · [Command reference](docs/command-reference.md) · [Configuration](docs/configuration.md) · [Task structure](docs/task-structure.md) · [Examples](docs/examples.md) · [Models](docs/models.md) · [Plugin](CLAUDE_CODE_PLUGIN.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
221
+
222
+ ## Contributors
223
+
224
+ <a href="https://github.com/stephennmiller/taskgraph-ai/graphs/contributors">
225
+ <img src="https://contrib.rocks/image?repo=stephennmiller/taskgraph-ai" alt="TaskGraph project contributors" />
226
+ </a>
227
+
228
+ ## Licensing
229
+
230
+ TaskGraph is licensed under the MIT License with Commons Clause, the same license as Task Master.
231
+
232
+ **Allowed:** use it for any purpose (personal, commercial, academic), modify it, distribute copies, and sell products built with it.
233
+
234
+ **Not allowed:** selling TaskGraph itself, offering it as a hosted service, or creating competing products based on it.
235
+
236
+ See [LICENSE](LICENSE) and [licensing details](docs/licensing.md).
@@ -0,0 +1,3 @@
1
+ import{D as e,Q as t,S as n,b as r,d as i,h as a,i as o,j as s,lt as c,m as l,nt as u,o as d,p as f,q as p,u as m,v as h,x as g,y as _}from"./config-manager-Da0wEujO.js";import{createRequire as v}from"node:module";import{execSync as y}from"child_process";import{createClaudeCode as b}from"ai-sdk-provider-claude-code";import*as x from"ai";import{jsonrepair as S}from"jsonrepair";import{EnvHttpProxyAgent as C}from"undici";var w=Object.defineProperty,T=e=>{let t={};for(var n in e)w(t,n,{get:e[n],enumerable:!0});return t},E=v(import.meta.url);const{JSONParseError:D,NoObjectGeneratedError:O,generateObject:k,generateText:A,streamObject:j,streamText:ee,zodSchema:te}=x,M=x.jsonSchema,N=new Set([`minimum`,`maximum`,`exclusiveMinimum`,`exclusiveMaximum`]),P=[`additionalProperties`,`contains`,`if`,`then`,`else`,`not`,`propertyNames`],F=[`allOf`,`anyOf`,`oneOf`,`prefixItems`],I=[`definitions`,`$defs`,`dependentSchemas`,`patternProperties`,`properties`],ne=e=>e?Array.isArray(e)?e.includes(`integer`):e===`integer`:!1,L=e=>{if(!e||typeof e!=`object`)return e;if(Array.isArray(e))return e.map(L);let t={...e};if(ne(t.type))for(let e of N)e in t&&delete t[e];for(let e of P)t[e]&&(t[e]=L(t[e]));for(let e of F)Array.isArray(t[e])&&(t[e]=t[e].map(L));for(let e of I)if(t[e]&&typeof t[e]==`object`){let n={};for(let[r,i]of Object.entries(t[e]))n[r]=L(i);t[e]=n}return t.items&&=L(t.items),t},R=e=>{let t=te(e);if(!t||typeof t!=`object`||!t.jsonSchema)return t;let n=L(t.jsonSchema);return typeof M==`function`?M(n,{validate:t.validate}):{...t,jsonSchema:n}};var z=class t{constructor(){if(this.constructor===t)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||=p();let t=this._projectRoot;if(e(null,t))return this._proxyAgent||=new C,(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,t){let n=t.message||`Unknown error occurred`;throw u(`error`,`${this.name} ${e} failed: ${n}`,{error:t}),Error(`${this.name} API error during ${e}: ${n}`)}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),u(`debug`,`Generating ${this.name} text with model: ${e.modelId}`);let t=await A({model:(await this.getClient(e))(e.modelId),...this.prepareMessages(e.messages),...this.prepareTokenParam(e.modelId,e.maxTokens),...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{}});u(`debug`,`${this.name} generateText completed successfully for model: ${e.modelId}`);let n=t.usage?.inputTokens??t.usage?.promptTokens??0,r=t.usage?.outputTokens??t.usage?.completionTokens??0,i=t.usage?.totalTokens??n+r;return{text:t.text,usage:{inputTokens:n,outputTokens:r,totalTokens:i}}}catch(e){this.handleError(`text generation`,e)}}async streamText(e){try{this.validateParams(e),this.validateMessages(e.messages),u(`debug`,`Streaming ${this.name} text with model: ${e.modelId}`);let t=await ee({model:(await this.getClient(e))(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 u(`debug`,`${this.name} streamText initiated successfully for model: ${e.modelId}`),t}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`);u(`debug`,`Streaming ${this.name} object with model: ${e.modelId}`);let t=await this.getClient(e),n=R(e.schema),r=await j({model:t(e.modelId),...this.prepareMessages(e.messages),schema:n,mode:e.mode||`auto`,maxOutputTokens:e.maxTokens,...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{}});return u(`debug`,`${this.name} streamObject initiated successfully for model: ${e.modelId}`),r}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`);u(`debug`,`Generating ${this.name} object ('${e.objectName}') with model: ${e.modelId}`);let t=await this.getClient(e),n=R(e.schema),r=await k({model:t(e.modelId),...this.prepareMessages(e.messages),schema:n,mode:this.needsExplicitJsonSchema?`json`:`auto`,schemaName:e.objectName,schemaDescription:`Generate a valid JSON object for ${e.objectName}`,maxTokens:e.maxTokens,...this.supportsTemperature&&e.temperature!==void 0?{temperature:e.temperature}:{}});u(`debug`,`${this.name} generateObject 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;return{object:r.object,usage:{inputTokens:i,outputTokens:a,totalTokens:o}}}catch(e){if(O.isInstance(e)&&e.cause instanceof D&&e.cause.text){u(`warn`,`${this.name} generated malformed JSON, attempting to repair...`);try{let t=S(e.cause.text),n=JSON.parse(t);return u(`info`,`Successfully repaired ${this.name} JSON output`),{object:n,usage:{inputTokens:e.usage?.promptTokens||e.usage?.inputTokens||0,outputTokens:e.usage?.completionTokens||e.usage?.outputTokens||0,totalTokens:e.usage?.totalTokens||0}}}catch(e){u(`error`,`Failed to repair ${this.name} JSON: ${e.message}`)}}this.handleError(`object generation`,e)}}};let B=!1;const V=[`WebSearch`,`WebFetch`],H=[`Read`,`Grep`,`Glob`];function U(e,t){let n=e===`research`,r=n?V:H;return{tools:[...r],allowedTools:[...r],permissionMode:`dontAsk`,permissionPrompts:`none`,strictMcpConfig:!0,mcpServers:{},...n&&{settingSources:[],maxTurns:t.maxTurns??8}}}var W=class extends z{constructor(){super(),this.name=`Claude Code`,this.supportedModels=g(`claude-code`),this.supportedModels.length===0&&u(`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`&&!B&&!process.env.CLAUDE_CODE_OAUTH_TOKEN)try{y(`claude --version`,{stdio:`pipe`,timeout:1e3})}catch{u(`warn`,`Claude Code CLI not detected. Install it with: npm install -g @anthropic-ai/claude-code`)}finally{B=!0}}getClient(e={}){try{let t=o(e.commandName)||{},n={...process.env};return process.env.CLAUDE_CODE_API_KEY?n.ANTHROPIC_API_KEY=process.env.CLAUDE_CODE_API_KEY:delete n.ANTHROPIC_API_KEY,b({defaultSettings:{systemPrompt:{type:`preset`,preset:`claude_code`},settingSources:[`user`,`project`,`local`],env:n,...t,...U(e.role,t)}})}catch(e){let t=String(e?.message||``);if(e?.code===`ENOENT`||/claude/i.test(t)){let t=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 G=null;var K=class e{constructor(){this._providers=new Map,this._initialized=!1}static getInstance(){return G||=new e,G}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: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}};K.getInstance().initialize();var q=K;const J={"claude-code":new W};function Y(e){if(J[e])return J[e];let t=q.getInstance();return t.hasProvider(e)?(u(`debug`,`Provider "${e}" found in dynamic registry`),t.getProvider(e)):null}function X(e,t){let n={inputCost:0,outputCost:0,currency:`USD`,isUnknown:!1};if(!s||!s[e])return u(`warn`,`Provider "${e}" not found in MODEL_MAP. Cannot determine cost for model ${t}.`),{...n,isUnknown:!0};let r=s[e].find(e=>e.id===t);if(!r)return u(`debug`,`Model "${t}" not found under provider "${e}". Assuming unknown cost.`),{...n,isUnknown:!0};if(r.cost_per_1m_tokens===null)return u(`debug`,`Cost data is null for model "${t}" under provider "${e}". Pricing unknown.`),{...n,isUnknown:!0};if(r.cost_per_1m_tokens===void 0)return u(`debug`,`Cost data not found for model "${t}" under provider "${e}". Pricing unknown.`),{...n,isUnknown:!0};let i=r.cost_per_1m_tokens;return{inputCost:i.input||0,outputCost:i.output||0,currency:i.currency||`USD`,isUnknown:!1}}function re(e,t,n,r){let i=(e||0)/1e6*n+(t||0)/1e6*r;return parseFloat(i.toFixed(6))}function ie(e){let n={currentTag:`master`,availableTags:[`master`]};try{return e?{currentTag:t(e)||`master`,availableTags:ae(e)}:n}catch(e){return d()&&u(`debug`,`Error getting tag information: ${e.message}`),n}}function ae(e){let t=[`master`];try{let n=E(`path`),r=E(`fs`),i=n.join(e,`.taskgraph`,`tasks`,`tasks.json`);if(!r.existsSync(i))return t;let a=JSON.parse(r.readFileSync(i,`utf8`));if(!a||typeof a!=`object`)return t;let o=Object.keys(a).filter(e=>oe(a[e]));return o.length>0?o:t}catch(e){return d()&&u(`debug`,`Could not read tasks file for available tags: ${e.message}`),t}}function oe(e){return e&&typeof e==`object`&&Array.isArray(e.tasks)}function se(e){let t=e.message?.toLowerCase()||``;return 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 ce(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:l(t),modelId:f(t)},research:{provider:_(t),modelId:h(t)},fallback:{provider:i(t),modelId:m(t)}}[e]||null}function le(e,t,n=null){let r=Y(e);if(!r)throw Error(`Unknown provider '${e}' for API key resolution.`);let i=r.getRequiredApiKeyName();if(i===null)return null;let a=c(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 ue(e,t,n,r,i,a){let o=0,s=t;for(;o<=2;)try{d()&&u(`info`,`Attempt ${o+1}/3 calling ${s} (Provider: ${r}, Model: ${i}, Role: ${a})`);let c=await e[t](n);return d()&&u(`info`,`${s} succeeded for role ${a} (Provider: ${r}) on attempt ${o+1}`),c}catch(e){if(u(`warn`,`Attempt ${o+1} failed for role ${a} (${s} / ${r}): ${e.message}`),se(e)&&o<2){o++;let e=1e3*2**(o-1);u(`info`,`Something went wrong on the provider side. Retrying in ${e/1e3}s...`),await new Promise(t=>setTimeout(t,e))}else throw u(`error`,`Something went wrong on the provider side. Max retries reached for role ${a} (${s} / ${r}).`),e}throw Error(`Exhausted all retries for role ${a} (${s} / ${r})`)}async function Q(e,t){let{role:i,session:o,projectRoot:s,systemPrompt:c,prompt:l,schema:f,objectName:m,commandName:h,outputType:g,experimental_transform:_,...v}=t;d()&&u(`info`,`${e}Service called`,{role:i,commandName:h,outputType:g,projectRoot:s});let y=s||p(),b=n(y),x;i===`main`?x=[`main`,`fallback`,`research`]:i===`research`?x=[`research`,`fallback`,`main`]:i===`fallback`?x=[`fallback`,`main`,`research`]:(u(`warn`,`Unknown initial role: ${i}. Defaulting to main -> fallback -> research sequence.`),x=[`main`,`fallback`,`research`]);let S=new Set,C=null,w=`AI service call failed for all configured roles.`;for(let t of x){let n,i,s,d,p,x,T=null;try{u(`debug`,`New AI service call with role: ${t}`);let w=Z(t,y);if(!w){u(`error`,`Unknown role encountered in _unifiedServiceRunner: ${t}`),C||=Error(`Unknown AI role specified: ${t}`);continue}if(n=w.provider,i=w.modelId,!n||!i){u(`warn`,`Skipping role '${t}': Provider or Model ID not configured.`),C||=Error(`Configuration missing for role '${t}'. Provider: ${n}, Model: ${i}`);continue}if(S.has(`${n}/${i}`)){u(`debug`,`Skipping role '${t}': ${n}/${i} already failed for this request.`);continue}if(p=Y(n?.toLowerCase()),!p){u(`warn`,`Skipping role '${t}': Provider '${n}' not supported.`),C||=Error(`Unsupported provider configured: ${n}`);continue}d=a(t,y),s=le(n?.toLowerCase(),o,y);let E=[],D=`${c} \n\n Always respond in ${r(y)}.`;if(E.push({role:`system`,content:D.trim()}),l)E.push({role:`user`,content:l});else throw Error(`User prompt content is missing.`);let O={apiKey:s,modelId:i,maxTokens:d.maxTokens,temperature:d.temperature,messages:E,...(e===`generateObject`||e===`streamObject`)&&{schema:f,objectName:m},role:t,...h&&{commandName:h},..._&&{experimental_transform:_},...v};if(x=await ue(p,e,O,n,i,t),b&&x&&x.usage)try{T=await $({userId:b,commandName:h,providerName:n,modelId:i,inputTokens:x.usage.inputTokens,outputTokens:x.usage.outputTokens,outputType:g})}catch{}else b&&x&&!x.usage&&u(`warn`,`Cannot log telemetry for ${h} (${n}/${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`||u(`error`,`Unknown serviceType in _unifiedServiceRunner: ${e}`),k=x);let A=ie(y);return{mainResult:k,telemetryData:T,tagInfo:A,providerName:n,modelId:i}}catch(r){let a=ce(r);if(u(`error`,`Service call failed for role ${t} (Provider: ${n||`unknown`}, Model: ${i||`unknown`}): ${a}`),C=r,w=a,n&&i&&S.add(`${n}/${i}`),e===`generateObject`){let e=a.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 '${n||`unknown`}' does not support the 'tool use' required by generateObjectService. Please configure a model that supports tool/function calling for the '${t}' role, or use generateTextService if structured output is not strictly required.`;throw u(`error`,`[Tool Support Error] ${e}`),Error(e)}}}}throw u(`error`,`All roles in the sequence [${x.join(`, `)}] failed.`),Error(w)}async function de(e){return Q(`generateText`,{outputType:`cli`,...e})}async function fe(e){return Q(`streamText`,{outputType:`cli`,...e})}async function pe(e){let t={outputType:`cli`,...e};if(!t.schema)throw Error(`streamObjectService requires a schema parameter`);return Q(`streamObject`,t)}async function me(e){return Q(`generateObject`,{objectName:`generated_object`,maxRetries:3,outputType:`cli`,...e})}async function $({userId:e,commandName:t,providerName:n,modelId:r,inputTokens:i,outputTokens:a,outputType:o}){try{let o=new Date().toISOString(),s=(i||0)+(a||0),{inputCost:c,outputCost:l,currency:f,isUnknown:p}=X(n,r),m=re(i,a,c,l),h={timestamp:o,userId:e,commandName:t,modelUsed:r,providerName:n,inputTokens:i||0,outputTokens:a||0,totalTokens:s,totalCost:m,currency:f,isUnknownCost:p};return d()&&u(`info`,`AI Usage Telemetry:`,h),h}catch(e){return u(`error`,`Failed to log AI usage telemetry: ${e.message}`,{error:e}),null}}export{fe as a,pe as i,de as n,T as o,$ as r,me as t};
@@ -0,0 +1 @@
1
+ import{a as e,i as t,n,r,t as i}from"./ai-services-unified-DbjBfAvG.js";import"./src-lvp18X-h.js";import"./config-manager-Da0wEujO.js";import"./git-utils-CrYiSKu0.js";export{i as generateObjectService,n as generateTextService,r as logAiUsage,t as streamObjectService,e as streamTextService};