chore: passes tests and linting

This commit is contained in:
Eyal Toledano
2025-06-07 20:30:51 -04:00
parent 27edbd8f3f
commit faae0b419d
13 changed files with 6781 additions and 6777 deletions

View File

@@ -1,33 +1,33 @@
{ {
"models": { "models": {
"main": { "main": {
"provider": "anthropic", "provider": "anthropic",
"modelId": "claude-sonnet-4-20250514", "modelId": "claude-sonnet-4-20250514",
"maxTokens": 50000, "maxTokens": 50000,
"temperature": 0.2 "temperature": 0.2
}, },
"research": { "research": {
"provider": "perplexity", "provider": "perplexity",
"modelId": "sonar-pro", "modelId": "sonar-pro",
"maxTokens": 8700, "maxTokens": 8700,
"temperature": 0.1 "temperature": 0.1
}, },
"fallback": { "fallback": {
"provider": "anthropic", "provider": "anthropic",
"modelId": "claude-3-7-sonnet-20250219", "modelId": "claude-3-7-sonnet-20250219",
"maxTokens": 128000, "maxTokens": 128000,
"temperature": 0.2 "temperature": 0.2
} }
}, },
"global": { "global": {
"logLevel": "info", "logLevel": "info",
"debug": false, "debug": false,
"defaultSubtasks": 5, "defaultSubtasks": 5,
"defaultPriority": "medium", "defaultPriority": "medium",
"projectName": "Taskmaster", "projectName": "Taskmaster",
"ollamaBaseURL": "http://localhost:11434/api", "ollamaBaseURL": "http://localhost:11434/api",
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com", "bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
"userId": "1234567890", "userId": "1234567890",
"azureBaseURL": "https://your-endpoint.azure.com/" "azureBaseURL": "https://your-endpoint.azure.com/"
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,427 +1,427 @@
{ {
"anthropic": [ "anthropic": [
{ {
"id": "claude-sonnet-4-20250514", "id": "claude-sonnet-4-20250514",
"swe_score": 0.727, "swe_score": 0.727,
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 }, "cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 64000 "max_tokens": 64000
}, },
{ {
"id": "claude-opus-4-20250514", "id": "claude-opus-4-20250514",
"swe_score": 0.725, "swe_score": 0.725,
"cost_per_1m_tokens": { "input": 15.0, "output": 75.0 }, "cost_per_1m_tokens": { "input": 15.0, "output": 75.0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 32000 "max_tokens": 32000
}, },
{ {
"id": "claude-3-7-sonnet-20250219", "id": "claude-3-7-sonnet-20250219",
"swe_score": 0.623, "swe_score": 0.623,
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 }, "cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 120000 "max_tokens": 120000
}, },
{ {
"id": "claude-3-5-sonnet-20241022", "id": "claude-3-5-sonnet-20241022",
"swe_score": 0.49, "swe_score": 0.49,
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 }, "cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 64000 "max_tokens": 64000
} }
], ],
"openai": [ "openai": [
{ {
"id": "gpt-4o", "id": "gpt-4o",
"swe_score": 0.332, "swe_score": 0.332,
"cost_per_1m_tokens": { "input": 2.5, "output": 10.0 }, "cost_per_1m_tokens": { "input": 2.5, "output": 10.0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 16384 "max_tokens": 16384
}, },
{ {
"id": "o1", "id": "o1",
"swe_score": 0.489, "swe_score": 0.489,
"cost_per_1m_tokens": { "input": 15.0, "output": 60.0 }, "cost_per_1m_tokens": { "input": 15.0, "output": 60.0 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "o3", "id": "o3",
"swe_score": 0.5, "swe_score": 0.5,
"cost_per_1m_tokens": { "input": 10.0, "output": 40.0 }, "cost_per_1m_tokens": { "input": 10.0, "output": 40.0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "o3-mini", "id": "o3-mini",
"swe_score": 0.493, "swe_score": 0.493,
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 }, "cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
"allowed_roles": ["main"], "allowed_roles": ["main"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "o4-mini", "id": "o4-mini",
"swe_score": 0.45, "swe_score": 0.45,
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 }, "cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "o1-mini", "id": "o1-mini",
"swe_score": 0.4, "swe_score": 0.4,
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 }, "cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "o1-pro", "id": "o1-pro",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 150.0, "output": 600.0 }, "cost_per_1m_tokens": { "input": 150.0, "output": 600.0 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "gpt-4-5-preview", "id": "gpt-4-5-preview",
"swe_score": 0.38, "swe_score": 0.38,
"cost_per_1m_tokens": { "input": 75.0, "output": 150.0 }, "cost_per_1m_tokens": { "input": 75.0, "output": 150.0 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "gpt-4-1-mini", "id": "gpt-4-1-mini",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 }, "cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "gpt-4-1-nano", "id": "gpt-4-1-nano",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 }, "cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "gpt-4o-mini", "id": "gpt-4o-mini",
"swe_score": 0.3, "swe_score": 0.3,
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 }, "cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
"allowed_roles": ["main"] "allowed_roles": ["main"]
}, },
{ {
"id": "gpt-4o-search-preview", "id": "gpt-4o-search-preview",
"swe_score": 0.33, "swe_score": 0.33,
"cost_per_1m_tokens": { "input": 2.5, "output": 10.0 }, "cost_per_1m_tokens": { "input": 2.5, "output": 10.0 },
"allowed_roles": ["research"] "allowed_roles": ["research"]
}, },
{ {
"id": "gpt-4o-mini-search-preview", "id": "gpt-4o-mini-search-preview",
"swe_score": 0.3, "swe_score": 0.3,
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 }, "cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
"allowed_roles": ["research"] "allowed_roles": ["research"]
} }
], ],
"google": [ "google": [
{ {
"id": "gemini-2.5-pro-preview-05-06", "id": "gemini-2.5-pro-preview-05-06",
"swe_score": 0.638, "swe_score": 0.638,
"cost_per_1m_tokens": null, "cost_per_1m_tokens": null,
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048000 "max_tokens": 1048000
}, },
{ {
"id": "gemini-2.5-pro-preview-03-25", "id": "gemini-2.5-pro-preview-03-25",
"swe_score": 0.638, "swe_score": 0.638,
"cost_per_1m_tokens": null, "cost_per_1m_tokens": null,
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048000 "max_tokens": 1048000
}, },
{ {
"id": "gemini-2.5-flash-preview-04-17", "id": "gemini-2.5-flash-preview-04-17",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": null, "cost_per_1m_tokens": null,
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048000 "max_tokens": 1048000
}, },
{ {
"id": "gemini-2.0-flash", "id": "gemini-2.0-flash",
"swe_score": 0.754, "swe_score": 0.754,
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 }, "cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048000 "max_tokens": 1048000
}, },
{ {
"id": "gemini-2.0-flash-lite", "id": "gemini-2.0-flash-lite",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": null, "cost_per_1m_tokens": null,
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048000 "max_tokens": 1048000
} }
], ],
"perplexity": [ "perplexity": [
{ {
"id": "sonar-pro", "id": "sonar-pro",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 3, "output": 15 }, "cost_per_1m_tokens": { "input": 3, "output": 15 },
"allowed_roles": ["main", "research"], "allowed_roles": ["main", "research"],
"max_tokens": 8700 "max_tokens": 8700
}, },
{ {
"id": "sonar", "id": "sonar",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 1, "output": 1 }, "cost_per_1m_tokens": { "input": 1, "output": 1 },
"allowed_roles": ["research"], "allowed_roles": ["research"],
"max_tokens": 8700 "max_tokens": 8700
}, },
{ {
"id": "deep-research", "id": "deep-research",
"swe_score": 0.211, "swe_score": 0.211,
"cost_per_1m_tokens": { "input": 2, "output": 8 }, "cost_per_1m_tokens": { "input": 2, "output": 8 },
"allowed_roles": ["research"], "allowed_roles": ["research"],
"max_tokens": 8700 "max_tokens": 8700
}, },
{ {
"id": "sonar-reasoning-pro", "id": "sonar-reasoning-pro",
"swe_score": 0.211, "swe_score": 0.211,
"cost_per_1m_tokens": { "input": 2, "output": 8 }, "cost_per_1m_tokens": { "input": 2, "output": 8 },
"allowed_roles": ["main", "research", "fallback"], "allowed_roles": ["main", "research", "fallback"],
"max_tokens": 8700 "max_tokens": 8700
}, },
{ {
"id": "sonar-reasoning", "id": "sonar-reasoning",
"swe_score": 0.211, "swe_score": 0.211,
"cost_per_1m_tokens": { "input": 1, "output": 5 }, "cost_per_1m_tokens": { "input": 1, "output": 5 },
"allowed_roles": ["main", "research", "fallback"], "allowed_roles": ["main", "research", "fallback"],
"max_tokens": 8700 "max_tokens": 8700
} }
], ],
"xai": [ "xai": [
{ {
"id": "grok-3", "id": "grok-3",
"name": "Grok 3", "name": "Grok 3",
"swe_score": null, "swe_score": null,
"cost_per_1m_tokens": { "input": 3, "output": 15 }, "cost_per_1m_tokens": { "input": 3, "output": 15 },
"allowed_roles": ["main", "fallback", "research"], "allowed_roles": ["main", "fallback", "research"],
"max_tokens": 131072 "max_tokens": 131072
}, },
{ {
"id": "grok-3-fast", "id": "grok-3-fast",
"name": "Grok 3 Fast", "name": "Grok 3 Fast",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 5, "output": 25 }, "cost_per_1m_tokens": { "input": 5, "output": 25 },
"allowed_roles": ["main", "fallback", "research"], "allowed_roles": ["main", "fallback", "research"],
"max_tokens": 131072 "max_tokens": 131072
} }
], ],
"ollama": [ "ollama": [
{ {
"id": "devstral:latest", "id": "devstral:latest",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "qwen3:latest", "id": "qwen3:latest",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "qwen3:14b", "id": "qwen3:14b",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "qwen3:32b", "id": "qwen3:32b",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "mistral-small3.1:latest", "id": "mistral-small3.1:latest",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "llama3.3:latest", "id": "llama3.3:latest",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
}, },
{ {
"id": "phi4:latest", "id": "phi4:latest",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"] "allowed_roles": ["main", "fallback"]
} }
], ],
"openrouter": [ "openrouter": [
{ {
"id": "google/gemini-2.5-flash-preview-05-20", "id": "google/gemini-2.5-flash-preview-05-20",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 }, "cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048576 "max_tokens": 1048576
}, },
{ {
"id": "google/gemini-2.5-flash-preview-05-20:thinking", "id": "google/gemini-2.5-flash-preview-05-20:thinking",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.15, "output": 3.5 }, "cost_per_1m_tokens": { "input": 0.15, "output": 3.5 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048576 "max_tokens": 1048576
}, },
{ {
"id": "google/gemini-2.5-pro-exp-03-25", "id": "google/gemini-2.5-pro-exp-03-25",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "deepseek/deepseek-chat-v3-0324:free", "id": "deepseek/deepseek-chat-v3-0324:free",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 163840 "max_tokens": 163840
}, },
{ {
"id": "deepseek/deepseek-chat-v3-0324", "id": "deepseek/deepseek-chat-v3-0324",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.27, "output": 1.1 }, "cost_per_1m_tokens": { "input": 0.27, "output": 1.1 },
"allowed_roles": ["main"], "allowed_roles": ["main"],
"max_tokens": 64000 "max_tokens": 64000
}, },
{ {
"id": "openai/gpt-4.1", "id": "openai/gpt-4.1",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 2, "output": 8 }, "cost_per_1m_tokens": { "input": 2, "output": 8 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "openai/gpt-4.1-mini", "id": "openai/gpt-4.1-mini",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 }, "cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "openai/gpt-4.1-nano", "id": "openai/gpt-4.1-nano",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 }, "cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "openai/o3", "id": "openai/o3",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 10, "output": 40 }, "cost_per_1m_tokens": { "input": 10, "output": 40 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 200000 "max_tokens": 200000
}, },
{ {
"id": "openai/codex-mini", "id": "openai/codex-mini",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 1.5, "output": 6 }, "cost_per_1m_tokens": { "input": 1.5, "output": 6 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "openai/gpt-4o-mini", "id": "openai/gpt-4o-mini",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 }, "cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "openai/o4-mini", "id": "openai/o4-mini",
"swe_score": 0.45, "swe_score": 0.45,
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 }, "cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "openai/o4-mini-high", "id": "openai/o4-mini-high",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 }, "cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "openai/o1-pro", "id": "openai/o1-pro",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 150, "output": 600 }, "cost_per_1m_tokens": { "input": 150, "output": 600 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "meta-llama/llama-3.3-70b-instruct", "id": "meta-llama/llama-3.3-70b-instruct",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 120, "output": 600 }, "cost_per_1m_tokens": { "input": 120, "output": 600 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1048576 "max_tokens": 1048576
}, },
{ {
"id": "meta-llama/llama-4-maverick", "id": "meta-llama/llama-4-maverick",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.18, "output": 0.6 }, "cost_per_1m_tokens": { "input": 0.18, "output": 0.6 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "meta-llama/llama-4-scout", "id": "meta-llama/llama-4-scout",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.08, "output": 0.3 }, "cost_per_1m_tokens": { "input": 0.08, "output": 0.3 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "qwen/qwen-max", "id": "qwen/qwen-max",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 1.6, "output": 6.4 }, "cost_per_1m_tokens": { "input": 1.6, "output": 6.4 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 32768 "max_tokens": 32768
}, },
{ {
"id": "qwen/qwen-turbo", "id": "qwen/qwen-turbo",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.05, "output": 0.2 }, "cost_per_1m_tokens": { "input": 0.05, "output": 0.2 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 1000000 "max_tokens": 1000000
}, },
{ {
"id": "qwen/qwen3-235b-a22b", "id": "qwen/qwen3-235b-a22b",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.14, "output": 2 }, "cost_per_1m_tokens": { "input": 0.14, "output": 2 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 24000 "max_tokens": 24000
}, },
{ {
"id": "mistralai/mistral-small-3.1-24b-instruct:free", "id": "mistralai/mistral-small-3.1-24b-instruct:free",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 96000 "max_tokens": 96000
}, },
{ {
"id": "mistralai/mistral-small-3.1-24b-instruct", "id": "mistralai/mistral-small-3.1-24b-instruct",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 }, "cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 128000 "max_tokens": 128000
}, },
{ {
"id": "mistralai/devstral-small", "id": "mistralai/devstral-small",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 }, "cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
"allowed_roles": ["main"], "allowed_roles": ["main"],
"max_tokens": 110000 "max_tokens": 110000
}, },
{ {
"id": "mistralai/mistral-nemo", "id": "mistralai/mistral-nemo",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0.03, "output": 0.07 }, "cost_per_1m_tokens": { "input": 0.03, "output": 0.07 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 100000 "max_tokens": 100000
}, },
{ {
"id": "thudm/glm-4-32b:free", "id": "thudm/glm-4-32b:free",
"swe_score": 0, "swe_score": 0,
"cost_per_1m_tokens": { "input": 0, "output": 0 }, "cost_per_1m_tokens": { "input": 0, "output": 0 },
"allowed_roles": ["main", "fallback"], "allowed_roles": ["main", "fallback"],
"max_tokens": 32768 "max_tokens": 32768
} }
] ]
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
import path from "path"; import path from 'path';
import chalk from "chalk"; import chalk from 'chalk';
import boxen from "boxen"; import boxen from 'boxen';
import Table from "cli-table3"; import Table from 'cli-table3';
import { log, readJSON, writeJSON, truncate, isSilentMode } from "../utils.js"; import { log, readJSON, writeJSON, truncate, isSilentMode } from '../utils.js';
import { displayBanner } from "../ui.js"; import { displayBanner } from '../ui.js';
import generateTaskFiles from "./generate-task-files.js"; import generateTaskFiles from './generate-task-files.js';
/** /**
* Clear subtasks from specified tasks * Clear subtasks from specified tasks
@@ -13,138 +13,138 @@ import generateTaskFiles from "./generate-task-files.js";
* @param {string} taskIds - Task IDs to clear subtasks from * @param {string} taskIds - Task IDs to clear subtasks from
*/ */
function clearSubtasks(tasksPath, taskIds) { function clearSubtasks(tasksPath, taskIds) {
log("info", `Reading tasks from ${tasksPath}...`); log('info', `Reading tasks from ${tasksPath}...`);
const data = readJSON(tasksPath); const data = readJSON(tasksPath);
if (!data || !data.tasks) { if (!data || !data.tasks) {
log("error", "No valid tasks found."); log('error', 'No valid tasks found.');
process.exit(1); process.exit(1);
} }
if (!isSilentMode()) { if (!isSilentMode()) {
console.log( console.log(
boxen(chalk.white.bold("Clearing Subtasks"), { boxen(chalk.white.bold('Clearing Subtasks'), {
padding: 1, padding: 1,
borderColor: "blue", borderColor: 'blue',
borderStyle: "round", borderStyle: 'round',
margin: { top: 1, bottom: 1 }, margin: { top: 1, bottom: 1 }
}) })
); );
} }
// Handle multiple task IDs (comma-separated) // Handle multiple task IDs (comma-separated)
const taskIdArray = taskIds.split(",").map((id) => id.trim()); const taskIdArray = taskIds.split(',').map((id) => id.trim());
let clearedCount = 0; let clearedCount = 0;
// Create a summary table for the cleared subtasks // Create a summary table for the cleared subtasks
const summaryTable = new Table({ const summaryTable = new Table({
head: [ head: [
chalk.cyan.bold("Task ID"), chalk.cyan.bold('Task ID'),
chalk.cyan.bold("Task Title"), chalk.cyan.bold('Task Title'),
chalk.cyan.bold("Subtasks Cleared"), chalk.cyan.bold('Subtasks Cleared')
], ],
colWidths: [10, 50, 20], colWidths: [10, 50, 20],
style: { head: [], border: [] }, style: { head: [], border: [] }
}); });
taskIdArray.forEach((taskId) => { taskIdArray.forEach((taskId) => {
const id = parseInt(taskId, 10); const id = parseInt(taskId, 10);
if (isNaN(id)) { if (isNaN(id)) {
log("error", `Invalid task ID: ${taskId}`); log('error', `Invalid task ID: ${taskId}`);
return; return;
} }
const task = data.tasks.find((t) => t.id === id); const task = data.tasks.find((t) => t.id === id);
if (!task) { if (!task) {
log("error", `Task ${id} not found`); log('error', `Task ${id} not found`);
return; return;
} }
if (!task.subtasks || task.subtasks.length === 0) { if (!task.subtasks || task.subtasks.length === 0) {
log("info", `Task ${id} has no subtasks to clear`); log('info', `Task ${id} has no subtasks to clear`);
summaryTable.push([ summaryTable.push([
id.toString(), id.toString(),
truncate(task.title, 47), truncate(task.title, 47),
chalk.yellow("No subtasks"), chalk.yellow('No subtasks')
]); ]);
return; return;
} }
const subtaskCount = task.subtasks.length; const subtaskCount = task.subtasks.length;
task.subtasks = []; task.subtasks = [];
clearedCount++; clearedCount++;
log("info", `Cleared ${subtaskCount} subtasks from task ${id}`); log('info', `Cleared ${subtaskCount} subtasks from task ${id}`);
summaryTable.push([ summaryTable.push([
id.toString(), id.toString(),
truncate(task.title, 47), truncate(task.title, 47),
chalk.green(`${subtaskCount} subtasks cleared`), chalk.green(`${subtaskCount} subtasks cleared`)
]); ]);
}); });
if (clearedCount > 0) { if (clearedCount > 0) {
writeJSON(tasksPath, data); writeJSON(tasksPath, data);
// Show summary table // Show summary table
if (!isSilentMode()) { if (!isSilentMode()) {
console.log( console.log(
boxen(chalk.white.bold("Subtask Clearing Summary:"), { boxen(chalk.white.bold('Subtask Clearing Summary:'), {
padding: { left: 2, right: 2, top: 0, bottom: 0 }, padding: { left: 2, right: 2, top: 0, bottom: 0 },
margin: { top: 1, bottom: 0 }, margin: { top: 1, bottom: 0 },
borderColor: "blue", borderColor: 'blue',
borderStyle: "round", borderStyle: 'round'
}) })
); );
console.log(summaryTable.toString()); console.log(summaryTable.toString());
} }
// Regenerate task files to reflect changes // Regenerate task files to reflect changes
log("info", "Regenerating task files..."); log('info', 'Regenerating task files...');
generateTaskFiles(tasksPath, path.dirname(tasksPath)); generateTaskFiles(tasksPath, path.dirname(tasksPath));
// Success message // Success message
if (!isSilentMode()) { if (!isSilentMode()) {
console.log( console.log(
boxen( boxen(
chalk.green( chalk.green(
`Successfully cleared subtasks from ${chalk.bold(clearedCount)} task(s)` `Successfully cleared subtasks from ${chalk.bold(clearedCount)} task(s)`
), ),
{ {
padding: 1, padding: 1,
borderColor: "green", borderColor: 'green',
borderStyle: "round", borderStyle: 'round',
margin: { top: 1 }, margin: { top: 1 }
} }
) )
); );
// Next steps suggestion // Next steps suggestion
console.log( console.log(
boxen( boxen(
chalk.white.bold("Next Steps:") + chalk.white.bold('Next Steps:') +
"\n\n" + '\n\n' +
`${chalk.cyan("1.")} Run ${chalk.yellow("task-master expand --id=<id>")} to generate new subtasks\n` + `${chalk.cyan('1.')} Run ${chalk.yellow('task-master expand --id=<id>')} to generate new subtasks\n` +
`${chalk.cyan("2.")} Run ${chalk.yellow("task-master list --with-subtasks")} to verify changes`, `${chalk.cyan('2.')} Run ${chalk.yellow('task-master list --with-subtasks')} to verify changes`,
{ {
padding: 1, padding: 1,
borderColor: "cyan", borderColor: 'cyan',
borderStyle: "round", borderStyle: 'round',
margin: { top: 1 }, margin: { top: 1 }
} }
) )
); );
} }
} else { } else {
if (!isSilentMode()) { if (!isSilentMode()) {
console.log( console.log(
boxen(chalk.yellow("No subtasks were cleared"), { boxen(chalk.yellow('No subtasks were cleared'), {
padding: 1, padding: 1,
borderColor: "yellow", borderColor: 'yellow',
borderStyle: "round", borderStyle: 'round',
margin: { top: 1 }, margin: { top: 1 }
}) })
); );
} }
} }
} }
export default clearSubtasks; export default clearSubtasks;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
import path from "path"; import path from 'path';
import chalk from "chalk"; import chalk from 'chalk';
import boxen from "boxen"; import boxen from 'boxen';
import { log, readJSON, writeJSON, findTaskById } from "../utils.js"; import { log, readJSON, writeJSON, findTaskById } from '../utils.js';
import { displayBanner } from "../ui.js"; import { displayBanner } from '../ui.js';
import { validateTaskDependencies } from "../dependency-manager.js"; import { validateTaskDependencies } from '../dependency-manager.js';
import { getDebugFlag } from "../config-manager.js"; import { getDebugFlag } from '../config-manager.js';
import updateSingleTaskStatus from "./update-single-task-status.js"; import updateSingleTaskStatus from './update-single-task-status.js';
import generateTaskFiles from "./generate-task-files.js"; import generateTaskFiles from './generate-task-files.js';
import { import {
isValidTaskStatus, isValidTaskStatus,
TASK_STATUS_OPTIONS, TASK_STATUS_OPTIONS
} from "../../../src/constants/task-status.js"; } from '../../../src/constants/task-status.js';
/** /**
* Set the status of a task * Set the status of a task
@@ -22,100 +22,100 @@ import {
* @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode * @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode
*/ */
async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) { async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) {
try { try {
if (!isValidTaskStatus(newStatus)) { if (!isValidTaskStatus(newStatus)) {
throw new Error( throw new Error(
`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(", ")}` `Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
); );
} }
// Determine if we're in MCP mode by checking for mcpLog // Determine if we're in MCP mode by checking for mcpLog
const isMcpMode = !!options?.mcpLog; const isMcpMode = !!options?.mcpLog;
// Only display UI elements if not in MCP mode // Only display UI elements if not in MCP mode
if (!isMcpMode) { if (!isMcpMode) {
console.log( console.log(
boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), { boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), {
padding: 1, padding: 1,
borderColor: "blue", borderColor: 'blue',
borderStyle: "round", borderStyle: 'round'
}) })
); );
} }
log("info", `Reading tasks from ${tasksPath}...`); log('info', `Reading tasks from ${tasksPath}...`);
const data = readJSON(tasksPath); const data = readJSON(tasksPath);
if (!data || !data.tasks) { if (!data || !data.tasks) {
throw new Error(`No valid tasks found in ${tasksPath}`); throw new Error(`No valid tasks found in ${tasksPath}`);
} }
// Handle multiple task IDs (comma-separated) // Handle multiple task IDs (comma-separated)
const taskIds = taskIdInput.split(",").map((id) => id.trim()); const taskIds = taskIdInput.split(',').map((id) => id.trim());
const updatedTasks = []; const updatedTasks = [];
// Update each task // Update each task
for (const id of taskIds) { for (const id of taskIds) {
await updateSingleTaskStatus(tasksPath, id, newStatus, data, !isMcpMode); await updateSingleTaskStatus(tasksPath, id, newStatus, data, !isMcpMode);
updatedTasks.push(id); updatedTasks.push(id);
} }
// Write the updated tasks to the file // Write the updated tasks to the file
writeJSON(tasksPath, data); writeJSON(tasksPath, data);
// Validate dependencies after status update // Validate dependencies after status update
log("info", "Validating dependencies after status update..."); log('info', 'Validating dependencies after status update...');
validateTaskDependencies(data.tasks); validateTaskDependencies(data.tasks);
// Generate individual task files // Generate individual task files
log("info", "Regenerating task files..."); log('info', 'Regenerating task files...');
await generateTaskFiles(tasksPath, path.dirname(tasksPath), { await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
mcpLog: options.mcpLog, mcpLog: options.mcpLog
}); });
// Display success message - only in CLI mode // Display success message - only in CLI mode
if (!isMcpMode) { if (!isMcpMode) {
for (const id of updatedTasks) { for (const id of updatedTasks) {
const task = findTaskById(data.tasks, id); const task = findTaskById(data.tasks, id);
const taskName = task ? task.title : id; const taskName = task ? task.title : id;
console.log( console.log(
boxen( boxen(
chalk.white.bold(`Successfully updated task ${id} status:`) + chalk.white.bold(`Successfully updated task ${id} status:`) +
"\n" + '\n' +
`From: ${chalk.yellow(task ? task.status : "unknown")}\n` + `From: ${chalk.yellow(task ? task.status : 'unknown')}\n` +
`To: ${chalk.green(newStatus)}`, `To: ${chalk.green(newStatus)}`,
{ padding: 1, borderColor: "green", borderStyle: "round" } { padding: 1, borderColor: 'green', borderStyle: 'round' }
) )
); );
} }
} }
// Return success value for programmatic use // Return success value for programmatic use
return { return {
success: true, success: true,
updatedTasks: updatedTasks.map((id) => ({ updatedTasks: updatedTasks.map((id) => ({
id, id,
status: newStatus, status: newStatus
})), }))
}; };
} catch (error) { } catch (error) {
log("error", `Error setting task status: ${error.message}`); log('error', `Error setting task status: ${error.message}`);
// Only show error UI in CLI mode // Only show error UI in CLI mode
if (!options?.mcpLog) { if (!options?.mcpLog) {
console.error(chalk.red(`Error: ${error.message}`)); console.error(chalk.red(`Error: ${error.message}`));
// Pass session to getDebugFlag // Pass session to getDebugFlag
if (getDebugFlag(options?.session)) { if (getDebugFlag(options?.session)) {
// Use getter // Use getter
console.error(error); console.error(error);
} }
process.exit(1); process.exit(1);
} else { } else {
// In MCP mode, throw the error for the caller to handle // In MCP mode, throw the error for the caller to handle
throw error; throw error;
} }
} }
} }
export default setTaskStatus; export default setTaskStatus;

File diff suppressed because it is too large Load Diff

View File

@@ -1,214 +1,214 @@
import { generateText, streamText, generateObject } from "ai"; import { generateText, streamText, generateObject } from 'ai';
import { log } from "../../scripts/modules/index.js"; import { log } from '../../scripts/modules/index.js';
/** /**
* Base class for all AI providers * Base class for all AI providers
*/ */
export class BaseAIProvider { export class BaseAIProvider {
constructor() { constructor() {
if (this.constructor === BaseAIProvider) { if (this.constructor === BaseAIProvider) {
throw new Error("BaseAIProvider cannot be instantiated directly"); throw new Error('BaseAIProvider cannot be instantiated directly');
} }
// Each provider must set their name // Each provider must set their name
this.name = this.constructor.name; this.name = this.constructor.name;
} }
/** /**
* Validates authentication parameters - can be overridden by providers * Validates authentication parameters - can be overridden by providers
* @param {object} params - Parameters to validate * @param {object} params - Parameters to validate
*/ */
validateAuth(params) { validateAuth(params) {
// Default: require API key (most providers need this) // Default: require API key (most providers need this)
if (!params.apiKey) { if (!params.apiKey) {
throw new Error(`${this.name} API key is required`); throw new Error(`${this.name} API key is required`);
} }
} }
/** /**
* Validates common parameters across all methods * Validates common parameters across all methods
* @param {object} params - Parameters to validate * @param {object} params - Parameters to validate
*/ */
validateParams(params) { validateParams(params) {
// Validate authentication (can be overridden by providers) // Validate authentication (can be overridden by providers)
this.validateAuth(params); this.validateAuth(params);
// Validate required model ID // Validate required model ID
if (!params.modelId) { if (!params.modelId) {
throw new Error(`${this.name} Model ID is required`); throw new Error(`${this.name} Model ID is required`);
} }
// Validate optional parameters // Validate optional parameters
this.validateOptionalParams(params); this.validateOptionalParams(params);
} }
/** /**
* Validates optional parameters like temperature and maxTokens * Validates optional parameters like temperature and maxTokens
* @param {object} params - Parameters to validate * @param {object} params - Parameters to validate
*/ */
validateOptionalParams(params) { validateOptionalParams(params) {
if ( if (
params.temperature !== undefined && params.temperature !== undefined &&
(params.temperature < 0 || params.temperature > 1) (params.temperature < 0 || params.temperature > 1)
) { ) {
throw new Error("Temperature must be between 0 and 1"); throw new Error('Temperature must be between 0 and 1');
} }
if (params.maxTokens !== undefined && params.maxTokens <= 0) { if (params.maxTokens !== undefined && params.maxTokens <= 0) {
throw new Error("maxTokens must be greater than 0"); throw new Error('maxTokens must be greater than 0');
} }
} }
/** /**
* Validates message array structure * Validates message array structure
*/ */
validateMessages(messages) { validateMessages(messages) {
if (!messages || !Array.isArray(messages) || messages.length === 0) { if (!messages || !Array.isArray(messages) || messages.length === 0) {
throw new Error("Invalid or empty messages array provided"); throw new Error('Invalid or empty messages array provided');
} }
for (const msg of messages) { for (const msg of messages) {
if (!msg.role || !msg.content) { if (!msg.role || !msg.content) {
throw new Error( throw new Error(
"Invalid message format. Each message must have role and content" 'Invalid message format. Each message must have role and content'
); );
} }
} }
} }
/** /**
* Common error handler * Common error handler
*/ */
handleError(operation, error) { handleError(operation, error) {
const errorMessage = error.message || "Unknown error occurred"; const errorMessage = error.message || 'Unknown error occurred';
log("error", `${this.name} ${operation} failed: ${errorMessage}`, { log('error', `${this.name} ${operation} failed: ${errorMessage}`, {
error, error
}); });
throw new Error( throw new Error(
`${this.name} API error during ${operation}: ${errorMessage}` `${this.name} API error during ${operation}: ${errorMessage}`
); );
} }
/** /**
* Creates and returns a client instance for the provider * Creates and returns a client instance for the provider
* @abstract * @abstract
*/ */
getClient(params) { getClient(params) {
throw new Error("getClient must be implemented by provider"); throw new Error('getClient must be implemented by provider');
} }
/** /**
* Generates text using the provider's model * Generates text using the provider's model
*/ */
async generateText(params) { async generateText(params) {
try { try {
this.validateParams(params); this.validateParams(params);
this.validateMessages(params.messages); this.validateMessages(params.messages);
log( log(
"debug", 'debug',
`Generating ${this.name} text with model: ${params.modelId}` `Generating ${this.name} text with model: ${params.modelId}`
); );
const client = this.getClient(params); const client = this.getClient(params);
const result = await generateText({ const result = await generateText({
model: client(params.modelId), model: client(params.modelId),
messages: params.messages, messages: params.messages,
maxTokens: params.maxTokens, maxTokens: params.maxTokens,
temperature: params.temperature, temperature: params.temperature
}); });
log( log(
"debug", 'debug',
`${this.name} generateText completed successfully for model: ${params.modelId}` `${this.name} generateText completed successfully for model: ${params.modelId}`
); );
return { return {
text: result.text, text: result.text,
usage: { usage: {
inputTokens: result.usage?.promptTokens, inputTokens: result.usage?.promptTokens,
outputTokens: result.usage?.completionTokens, outputTokens: result.usage?.completionTokens,
totalTokens: result.usage?.totalTokens, totalTokens: result.usage?.totalTokens
}, }
}; };
} catch (error) { } catch (error) {
this.handleError("text generation", error); this.handleError('text generation', error);
} }
} }
/** /**
* Streams text using the provider's model * Streams text using the provider's model
*/ */
async streamText(params) { async streamText(params) {
try { try {
this.validateParams(params); this.validateParams(params);
this.validateMessages(params.messages); this.validateMessages(params.messages);
log("debug", `Streaming ${this.name} text with model: ${params.modelId}`); log('debug', `Streaming ${this.name} text with model: ${params.modelId}`);
const client = this.getClient(params); const client = this.getClient(params);
const stream = await streamText({ const stream = await streamText({
model: client(params.modelId), model: client(params.modelId),
messages: params.messages, messages: params.messages,
maxTokens: params.maxTokens, maxTokens: params.maxTokens,
temperature: params.temperature, temperature: params.temperature
}); });
log( log(
"debug", 'debug',
`${this.name} streamText initiated successfully for model: ${params.modelId}` `${this.name} streamText initiated successfully for model: ${params.modelId}`
); );
return stream; return stream;
} catch (error) { } catch (error) {
this.handleError("text streaming", error); this.handleError('text streaming', error);
} }
} }
/** /**
* Generates a structured object using the provider's model * Generates a structured object using the provider's model
*/ */
async generateObject(params) { async generateObject(params) {
try { try {
this.validateParams(params); this.validateParams(params);
this.validateMessages(params.messages); this.validateMessages(params.messages);
if (!params.schema) { if (!params.schema) {
throw new Error("Schema is required for object generation"); throw new Error('Schema is required for object generation');
} }
if (!params.objectName) { if (!params.objectName) {
throw new Error("Object name is required for object generation"); throw new Error('Object name is required for object generation');
} }
log( log(
"debug", 'debug',
`Generating ${this.name} object ('${params.objectName}') with model: ${params.modelId}` `Generating ${this.name} object ('${params.objectName}') with model: ${params.modelId}`
); );
const client = this.getClient(params); const client = this.getClient(params);
const result = await generateObject({ const result = await generateObject({
model: client(params.modelId), model: client(params.modelId),
messages: params.messages, messages: params.messages,
schema: params.schema, schema: params.schema,
mode: "auto", mode: 'auto',
maxTokens: params.maxTokens, maxTokens: params.maxTokens,
temperature: params.temperature, temperature: params.temperature
}); });
log( log(
"debug", 'debug',
`${this.name} generateObject completed successfully for model: ${params.modelId}` `${this.name} generateObject completed successfully for model: ${params.modelId}`
); );
return { return {
object: result.object, object: result.object,
usage: { usage: {
inputTokens: result.usage?.promptTokens, inputTokens: result.usage?.promptTokens,
outputTokens: result.usage?.completionTokens, outputTokens: result.usage?.completionTokens,
totalTokens: result.usage?.totalTokens, totalTokens: result.usage?.totalTokens
}, }
}; };
} catch (error) { } catch (error) {
this.handleError("object generation", error); this.handleError('object generation', error);
} }
} }
} }

View File

@@ -1,400 +1,404 @@
/** /**
* Tests for the add-task.js module * Tests for the add-task.js module
*/ */
import { jest } from '@jest/globals'; import { jest } from "@jest/globals";
// Mock the dependencies before importing the module under test // Mock the dependencies before importing the module under test
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({ jest.unstable_mockModule("../../../../../scripts/modules/utils.js", () => ({
readJSON: jest.fn(), readJSON: jest.fn(),
writeJSON: jest.fn(), writeJSON: jest.fn(),
log: jest.fn(), log: jest.fn(),
CONFIG: { CONFIG: {
model: 'mock-claude-model', model: "mock-claude-model",
maxTokens: 4000, maxTokens: 4000,
temperature: 0.7, temperature: 0.7,
debug: false debug: false,
}, },
truncate: jest.fn((text) => text) truncate: jest.fn((text) => text),
})); }));
jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ jest.unstable_mockModule("../../../../../scripts/modules/ui.js", () => ({
displayBanner: jest.fn(), displayBanner: jest.fn(),
getStatusWithColor: jest.fn((status) => status), getStatusWithColor: jest.fn((status) => status),
startLoadingIndicator: jest.fn(), startLoadingIndicator: jest.fn(),
stopLoadingIndicator: jest.fn(), stopLoadingIndicator: jest.fn(),
displayAiUsageSummary: jest.fn() succeedLoadingIndicator: jest.fn(),
failLoadingIndicator: jest.fn(),
warnLoadingIndicator: jest.fn(),
infoLoadingIndicator: jest.fn(),
displayAiUsageSummary: jest.fn(),
})); }));
jest.unstable_mockModule( jest.unstable_mockModule(
'../../../../../scripts/modules/ai-services-unified.js', "../../../../../scripts/modules/ai-services-unified.js",
() => ({ () => ({
generateObjectService: jest.fn().mockResolvedValue({ generateObjectService: jest.fn().mockResolvedValue({
mainResult: { mainResult: {
object: { object: {
title: 'Task from prompt: Create a new authentication system', title: "Task from prompt: Create a new authentication system",
description: description:
'Task generated from: Create a new authentication system', "Task generated from: Create a new authentication system",
details: details:
'Implementation details for task generated from prompt: Create a new authentication system', "Implementation details for task generated from prompt: Create a new authentication system",
testStrategy: 'Write unit tests to verify functionality', testStrategy: "Write unit tests to verify functionality",
dependencies: [] dependencies: [],
} },
}, },
telemetryData: { telemetryData: {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
userId: '1234567890', userId: "1234567890",
commandName: 'add-task', commandName: "add-task",
modelUsed: 'claude-3-5-sonnet', modelUsed: "claude-3-5-sonnet",
providerName: 'anthropic', providerName: "anthropic",
inputTokens: 1000, inputTokens: 1000,
outputTokens: 500, outputTokens: 500,
totalTokens: 1500, totalTokens: 1500,
totalCost: 0.012414, totalCost: 0.012414,
currency: 'USD' currency: "USD",
} },
}) }),
}) })
); );
jest.unstable_mockModule( jest.unstable_mockModule(
'../../../../../scripts/modules/config-manager.js', "../../../../../scripts/modules/config-manager.js",
() => ({ () => ({
getDefaultPriority: jest.fn(() => 'medium') getDefaultPriority: jest.fn(() => "medium"),
}) })
); );
jest.unstable_mockModule( jest.unstable_mockModule(
'../../../../../scripts/modules/task-manager/generate-task-files.js', "../../../../../scripts/modules/task-manager/generate-task-files.js",
() => ({ () => ({
default: jest.fn().mockResolvedValue() default: jest.fn().mockResolvedValue(),
}) })
); );
// Mock external UI libraries // Mock external UI libraries
jest.unstable_mockModule('chalk', () => ({ jest.unstable_mockModule("chalk", () => ({
default: { default: {
white: { bold: jest.fn((text) => text) }, white: { bold: jest.fn((text) => text) },
cyan: Object.assign( cyan: Object.assign(
jest.fn((text) => text), jest.fn((text) => text),
{ {
bold: jest.fn((text) => text) bold: jest.fn((text) => text),
} }
), ),
green: jest.fn((text) => text), green: jest.fn((text) => text),
yellow: jest.fn((text) => text), yellow: jest.fn((text) => text),
bold: jest.fn((text) => text) bold: jest.fn((text) => text),
} },
})); }));
jest.unstable_mockModule('boxen', () => ({ jest.unstable_mockModule("boxen", () => ({
default: jest.fn((text) => text) default: jest.fn((text) => text),
})); }));
jest.unstable_mockModule('cli-table3', () => ({ jest.unstable_mockModule("cli-table3", () => ({
default: jest.fn().mockImplementation(() => ({ default: jest.fn().mockImplementation(() => ({
push: jest.fn(), push: jest.fn(),
toString: jest.fn(() => 'mocked table') toString: jest.fn(() => "mocked table"),
})) })),
})); }));
// Import the mocked modules // Import the mocked modules
const { readJSON, writeJSON, log } = await import( const { readJSON, writeJSON, log } = await import(
'../../../../../scripts/modules/utils.js' "../../../../../scripts/modules/utils.js"
); );
const { generateObjectService } = await import( const { generateObjectService } = await import(
'../../../../../scripts/modules/ai-services-unified.js' "../../../../../scripts/modules/ai-services-unified.js"
); );
const generateTaskFiles = await import( const generateTaskFiles = await import(
'../../../../../scripts/modules/task-manager/generate-task-files.js' "../../../../../scripts/modules/task-manager/generate-task-files.js"
); );
// Import the module under test // Import the module under test
const { default: addTask } = await import( const { default: addTask } = await import(
'../../../../../scripts/modules/task-manager/add-task.js' "../../../../../scripts/modules/task-manager/add-task.js"
); );
describe('addTask', () => { describe("addTask", () => {
const sampleTasks = { const sampleTasks = {
tasks: [ tasks: [
{ {
id: 1, id: 1,
title: 'Task 1', title: "Task 1",
description: 'First task', description: "First task",
status: 'pending', status: "pending",
dependencies: [] dependencies: [],
}, },
{ {
id: 2, id: 2,
title: 'Task 2', title: "Task 2",
description: 'Second task', description: "Second task",
status: 'pending', status: "pending",
dependencies: [] dependencies: [],
}, },
{ {
id: 3, id: 3,
title: 'Task 3', title: "Task 3",
description: 'Third task', description: "Third task",
status: 'pending', status: "pending",
dependencies: [1] dependencies: [1],
} },
] ],
}; };
// Create a helper function for consistent mcpLog mock // Create a helper function for consistent mcpLog mock
const createMcpLogMock = () => ({ const createMcpLogMock = () => ({
info: jest.fn(), info: jest.fn(),
warn: jest.fn(), warn: jest.fn(),
error: jest.fn(), error: jest.fn(),
debug: jest.fn(), debug: jest.fn(),
success: jest.fn() success: jest.fn(),
}); });
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
readJSON.mockReturnValue(JSON.parse(JSON.stringify(sampleTasks))); readJSON.mockReturnValue(JSON.parse(JSON.stringify(sampleTasks)));
// Mock console.log to avoid output during tests // Mock console.log to avoid output during tests
jest.spyOn(console, 'log').mockImplementation(() => {}); jest.spyOn(console, "log").mockImplementation(() => {});
}); });
afterEach(() => { afterEach(() => {
console.log.mockRestore(); console.log.mockRestore();
}); });
test('should add a new task using AI', async () => { test("should add a new task using AI", async () => {
// Arrange // Arrange
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act // Act
const result = await addTask( const result = await addTask(
'tasks/tasks.json', "tasks/tasks.json",
prompt, prompt,
[], [],
'medium', "medium",
context, context,
'json' "json"
); );
// Assert // Assert
expect(readJSON).toHaveBeenCalledWith('tasks/tasks.json'); expect(readJSON).toHaveBeenCalledWith("tasks/tasks.json");
expect(generateObjectService).toHaveBeenCalledWith(expect.any(Object)); expect(generateObjectService).toHaveBeenCalledWith(expect.any(Object));
expect(writeJSON).toHaveBeenCalledWith( expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json', "tasks/tasks.json",
expect.objectContaining({ expect.objectContaining({
tasks: expect.arrayContaining([ tasks: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: 4, // Next ID after existing tasks id: 4, // Next ID after existing tasks
title: expect.stringContaining( title: expect.stringContaining(
'Create a new authentication system' "Create a new authentication system"
), ),
status: 'pending' status: "pending",
}) }),
]) ]),
}) })
); );
expect(generateTaskFiles.default).toHaveBeenCalled(); expect(generateTaskFiles.default).toHaveBeenCalled();
expect(result).toEqual( expect(result).toEqual(
expect.objectContaining({ expect.objectContaining({
newTaskId: 4, newTaskId: 4,
telemetryData: expect.any(Object) telemetryData: expect.any(Object),
}) })
); );
}); });
test('should validate dependencies when adding a task', async () => { test("should validate dependencies when adding a task", async () => {
// Arrange // Arrange
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const validDependencies = [1, 2]; // These exist in sampleTasks const validDependencies = [1, 2]; // These exist in sampleTasks
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act // Act
const result = await addTask( const result = await addTask(
'tasks/tasks.json', "tasks/tasks.json",
prompt, prompt,
validDependencies, validDependencies,
'medium', "medium",
context, context,
'json' "json"
); );
// Assert // Assert
expect(writeJSON).toHaveBeenCalledWith( expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json', "tasks/tasks.json",
expect.objectContaining({ expect.objectContaining({
tasks: expect.arrayContaining([ tasks: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: 4, id: 4,
dependencies: validDependencies dependencies: validDependencies,
}) }),
]) ]),
}) })
); );
}); });
test('should filter out invalid dependencies', async () => { test("should filter out invalid dependencies", async () => {
// Arrange // Arrange
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const invalidDependencies = [999]; // Non-existent task ID const invalidDependencies = [999]; // Non-existent task ID
const context = { mcpLog: createMcpLogMock() }; const context = { mcpLog: createMcpLogMock() };
// Act // Act
const result = await addTask( const result = await addTask(
'tasks/tasks.json', "tasks/tasks.json",
prompt, prompt,
invalidDependencies, invalidDependencies,
'medium', "medium",
context, context,
'json' "json"
); );
// Assert // Assert
expect(writeJSON).toHaveBeenCalledWith( expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json', "tasks/tasks.json",
expect.objectContaining({ expect.objectContaining({
tasks: expect.arrayContaining([ tasks: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: 4, id: 4,
dependencies: [] // Invalid dependencies should be filtered out dependencies: [], // Invalid dependencies should be filtered out
}) }),
]) ]),
}) })
); );
expect(context.mcpLog.warn).toHaveBeenCalledWith( expect(context.mcpLog.warn).toHaveBeenCalledWith(
expect.stringContaining( expect.stringContaining(
'The following dependencies do not exist or are invalid: 999' "The following dependencies do not exist or are invalid: 999"
) )
); );
}); });
test('should use specified priority', async () => { test("should use specified priority", async () => {
// Arrange // Arrange
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const priority = 'high'; const priority = "high";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act // Act
await addTask('tasks/tasks.json', prompt, [], priority, context, 'json'); await addTask("tasks/tasks.json", prompt, [], priority, context, "json");
// Assert // Assert
expect(writeJSON).toHaveBeenCalledWith( expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json', "tasks/tasks.json",
expect.objectContaining({ expect.objectContaining({
tasks: expect.arrayContaining([ tasks: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
priority: priority priority: priority,
}) }),
]) ]),
}) })
); );
}); });
test('should handle empty tasks file', async () => { test("should handle empty tasks file", async () => {
// Arrange // Arrange
readJSON.mockReturnValue({ tasks: [] }); readJSON.mockReturnValue({ tasks: [] });
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act // Act
const result = await addTask( const result = await addTask(
'tasks/tasks.json', "tasks/tasks.json",
prompt, prompt,
[], [],
'medium', "medium",
context, context,
'json' "json"
); );
// Assert // Assert
expect(result.newTaskId).toBe(1); // First task should have ID 1 expect(result.newTaskId).toBe(1); // First task should have ID 1
expect(writeJSON).toHaveBeenCalledWith( expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json', "tasks/tasks.json",
expect.objectContaining({ expect.objectContaining({
tasks: expect.arrayContaining([ tasks: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: 1 id: 1,
}) }),
]) ]),
}) })
); );
}); });
test('should handle missing tasks file', async () => { test("should handle missing tasks file", async () => {
// Arrange // Arrange
readJSON.mockReturnValue(null); readJSON.mockReturnValue(null);
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act // Act
const result = await addTask( const result = await addTask(
'tasks/tasks.json', "tasks/tasks.json",
prompt, prompt,
[], [],
'medium', "medium",
context, context,
'json' "json"
); );
// Assert // Assert
expect(result.newTaskId).toBe(1); // First task should have ID 1 expect(result.newTaskId).toBe(1); // First task should have ID 1
expect(writeJSON).toHaveBeenCalledTimes(2); // Once to create file, once to add task expect(writeJSON).toHaveBeenCalledTimes(2); // Once to create file, once to add task
}); });
test('should handle AI service errors', async () => { test("should handle AI service errors", async () => {
// Arrange // Arrange
generateObjectService.mockRejectedValueOnce(new Error('AI service failed')); generateObjectService.mockRejectedValueOnce(new Error("AI service failed"));
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act & Assert // Act & Assert
await expect( await expect(
addTask('tasks/tasks.json', prompt, [], 'medium', context, 'json') addTask("tasks/tasks.json", prompt, [], "medium", context, "json")
).rejects.toThrow('AI service failed'); ).rejects.toThrow("AI service failed");
}); });
test('should handle file read errors', async () => { test("should handle file read errors", async () => {
// Arrange // Arrange
readJSON.mockImplementation(() => { readJSON.mockImplementation(() => {
throw new Error('File read failed'); throw new Error("File read failed");
}); });
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act & Assert // Act & Assert
await expect( await expect(
addTask('tasks/tasks.json', prompt, [], 'medium', context, 'json') addTask("tasks/tasks.json", prompt, [], "medium", context, "json")
).rejects.toThrow('File read failed'); ).rejects.toThrow("File read failed");
}); });
test('should handle file write errors', async () => { test("should handle file write errors", async () => {
// Arrange // Arrange
writeJSON.mockImplementation(() => { writeJSON.mockImplementation(() => {
throw new Error('File write failed'); throw new Error("File write failed");
}); });
const prompt = 'Create a new authentication system'; const prompt = "Create a new authentication system";
const context = { const context = {
mcpLog: createMcpLogMock() mcpLog: createMcpLogMock(),
}; };
// Act & Assert // Act & Assert
await expect( await expect(
addTask('tasks/tasks.json', prompt, [], 'medium', context, 'json') addTask("tasks/tasks.json", prompt, [], "medium", context, "json")
).rejects.toThrow('File write failed'); ).rejects.toThrow("File write failed");
}); });
}); });

View File

@@ -82,19 +82,19 @@ describe("UI Module", () => {
test("should return done status with emoji for console output", () => { test("should return done status with emoji for console output", () => {
const result = getStatusWithColor("done"); const result = getStatusWithColor("done");
expect(result).toMatch(/done/); expect(result).toMatch(/done/);
expect(result).toContain(""); expect(result).toContain("");
}); });
test("should return pending status with emoji for console output", () => { test("should return pending status with emoji for console output", () => {
const result = getStatusWithColor("pending"); const result = getStatusWithColor("pending");
expect(result).toMatch(/pending/); expect(result).toMatch(/pending/);
expect(result).toContain("⏱️"); expect(result).toContain("");
}); });
test("should return deferred status with emoji for console output", () => { test("should return deferred status with emoji for console output", () => {
const result = getStatusWithColor("deferred"); const result = getStatusWithColor("deferred");
expect(result).toMatch(/deferred/); expect(result).toMatch(/deferred/);
expect(result).toContain("⏱️"); expect(result).toContain("x");
}); });
test("should return in-progress status with emoji for console output", () => { test("should return in-progress status with emoji for console output", () => {