Skip to content

Commit 772f4e9

Browse files
committed
Merge branch 'dev'
2 parents c204df1 + 28ff2ce commit 772f4e9

10 files changed

Lines changed: 892 additions & 434 deletions

File tree

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ANTHROPIC_API_KEY="your_anthropic_api_key_here" # Required: Format: sk-ant
33
PERPLEXITY_API_KEY="your_perplexity_api_key_here" # Optional: Format: pplx-...
44
OPENAI_API_KEY="your_openai_api_key_here" # Optional, for OpenAI/OpenRouter models. Format: sk-proj-...
55
GOOGLE_API_KEY="your_google_api_key_here" # Optional, for Google Gemini models.
6+
GEMINI_API_KEY="your_gemini_api_key_here"
67
MISTRAL_API_KEY="your_mistral_key_here" # Optional, for Mistral AI models.
78
XAI_API_KEY="YOUR_XAI_KEY_HERE" # Optional, for xAI AI models.
89
AZURE_OPENAI_API_KEY="your_azure_key_here" # Optional, for Azure OpenAI models (requires endpoint in .taskmaster/config.json).

.taskmaster/config.json

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
11
{
22
"models": {
33
"main": {
4-
"provider": "openrouter",
5-
"modelId": "deepseek/deepseek-chat-v3-0324:free",
6-
"maxTokens": 120000,
4+
"provider": "gemini-cli",
5+
"modelId": "gemini-2.5-pro",
6+
"maxTokens": 65536,
77
"temperature": 0.2
88
},
99
"research": {
10-
"provider": "openrouter",
11-
"modelId": "deepseek/deepseek-chat-v3-0324:free",
12-
"maxTokens": 8700,
10+
"provider": "gemini-cli",
11+
"modelId": "gemini-2.5-pro",
12+
"maxTokens": 65536,
1313
"temperature": 0.1
1414
},
1515
"fallback": {
16-
"provider": "openrouter",
17-
"modelId": "deepseek/deepseek-chat-v3-0324:free",
18-
"maxTokens": 8192,
19-
"temperature": 0.1
16+
"provider": "gemini-cli",
17+
"modelId": "gemini-2.5-pro",
18+
"maxTokens": 65536,
19+
"temperature": 0.2
2020
}
2121
},
2222
"global": {
2323
"logLevel": "info",
2424
"debug": false,
25+
"defaultNumTasks": 10,
2526
"defaultSubtasks": 5,
2627
"defaultPriority": "medium",
2728
"projectName": "Taskmaster",
2829
"ollamaBaseURL": "http://localhost:11434/api",
2930
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
31+
"responseLanguage": "English",
3032
"defaultTag": "master",
3133
"azureOpenaiBaseURL": "https://your-endpoint.openai.azure.com/",
3234
"userId": "1234567890"
33-
}
34-
}
35+
},
36+
"claudeCode": {}
37+
}

.taskmaster/state.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"currentTag": "master",
3-
"lastSwitched": "2025-06-23T12:15:36.989Z",
3+
"lastSwitched": "2025-07-22T11:49:13.447Z",
44
"branchTagMapping": {},
55
"migrationNoticeShown": true
66
}

src/constants/index.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ export const DEFAULT_SUBTASKS_TO_GENERATE = 5;
3535
export const MIN_TASKS_TO_GENERATE = 3;
3636
export const MAX_TASKS_TO_GENERATE = 30;
3737
export const MIN_SUBTASKS_TO_GENERATE = 1;
38-
export const MAX_SUBTASKS_TO_GENERATE = 10;
38+
export const MAX_SUBTASKS_TO_GENERATE = 15;
3939
export const MAX_TITLE_LENGTH = 100;
4040
export const MAX_TITLE_TRUNC_LENGTH = 40;
4141
export const MAX_DESCRIPTION_LENGTH = 250;
4242
export const MAX_PROMPT_LENGTH = 1024;
43-
export const MIN_PARENT_ID = 1;
4443

4544
// status configuration
4645
export const STATUS_CONFIG = {
@@ -61,3 +60,15 @@ export const PACKAGE_MANAGERS = ["npm", "pnpm", "bun"] as const;
6160
export const TASKMASTER_INIT_MSG = "TaskMaster AI Core initialized";
6261
export const TASKS_FILE_WARN: (path: string) => string = (path: string) =>
6362
`tasks.json not found at "${path}". Please generate tasks from PRD first.`;
63+
64+
// Supported languages for TMAI responses
65+
export const LANG = [
66+
"English",
67+
"French",
68+
"Spanish",
69+
"German",
70+
"Italian",
71+
"Portuguese",
72+
"Chinese",
73+
"Japanese",
74+
] as const;

src/core/taskmaster/TaskMaster.ts

Lines changed: 177 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import {
2727
readJsonFileAsync,
2828
} from "@/utils/extras";
2929

30+
/* asks */
31+
import { askLangAsync } from "@/core/taskmaster/asks";
32+
3033
/* types */
3134
import type { T_PackageManager } from "@/@types/index";
3235
import type { I_Tasks, Status, Priority } from "@/@types/tasks";
@@ -74,6 +77,9 @@ export class TaskMaster {
7477
// Getters and Setters
7578
// ==============================================
7679

80+
/**
81+
* @description Retrieves the contents of the tasks.json file
82+
*/
7783
public async getTasksContentAsync(): Promise<I_Tasks> {
7884
const oraOptions = {
7985
text: `Fetching tasks from ${chalk.bold(this._tasksFilePath)}...`,
@@ -87,10 +93,54 @@ export class TaskMaster {
8793
);
8894
}
8995

96+
/**
97+
* @description Sets the tasks file path to use for task-master operations
98+
*/
9099
public setTasksFilePath(tasksFilePath: string): void {
91100
this._tasksFilePath = tasksFilePath;
92101
}
93102

103+
/**
104+
* @description Retrieves all dependencies for a given task or subtask.
105+
* @param tasks The tasks data structure
106+
* @param taskId The task ID (either a simple number as string or hierarchical like "1.2")
107+
*/
108+
private async getAllDependenciesAsync(
109+
tasks: I_Tasks,
110+
taskId: string,
111+
): Promise<number[]> {
112+
if (!taskId.includes(".")) {
113+
const idNum = Number.parseInt(taskId, 10);
114+
const task = tasks.master.tasks.find((t) => t.id === idNum);
115+
if (!task) {
116+
throw new Error(`Task not found: ${taskId}`);
117+
}
118+
return task.dependencies;
119+
}
120+
121+
const parts = taskId.split(".");
122+
if (parts.length < 2) {
123+
throw new Error(`Invalid hierarchical task ID: ${taskId}`);
124+
}
125+
const parentId = Number.parseInt(parts[0], 10);
126+
const subtaskIndex = Number.parseInt(parts[1], 10) - 1;
127+
const parentTask = tasks.master.tasks.find((t) => t.id === parentId);
128+
if (!parentTask) {
129+
throw new Error(`Parent task not found for subtask: ${taskId}`);
130+
}
131+
132+
if (
133+
!parentTask.subtasks ||
134+
subtaskIndex < 0 ||
135+
subtaskIndex >= parentTask.subtasks.length
136+
) {
137+
throw new Error(`Subtask not found: ${taskId}`);
138+
}
139+
140+
const subtask = parentTask.subtasks[subtaskIndex];
141+
return subtask.dependencies;
142+
}
143+
94144
// ==============================================
95145
// Helpers
96146
// ==============================================
@@ -135,7 +185,7 @@ export class TaskMaster {
135185
* @description Fixes the format of the tasks.json file if necessary
136186
* by encapsulating the 'tasks' and 'metadata' keys under a 'master' key
137187
*/
138-
private async fixTasksFileFormatAsync(): Promise<void> {
188+
public async fixTasksFileFormatAsync(): Promise<void> {
139189
const oraOptions = {
140190
text: "Verifying tasks.json file format...",
141191
successText: chalk.bgGreen("tasks.json format validated successfully!"),
@@ -276,6 +326,21 @@ export class TaskMaster {
276326
}, oraOptions);
277327
}
278328

329+
// TODO: done
330+
/**
331+
* @description Sets the response language for TMAI
332+
* @param lang Language to set for TMAI responses
333+
*/
334+
public async setLangAsync(lang: string): Promise<void> {
335+
await this.executeCommandAsync(
336+
`Setting TMAI response language to ${chalk.bold(lang)}...`,
337+
`Language set to ${lang} successfully!`,
338+
`Failed to set language to ${lang}`,
339+
this._mainCommand,
340+
["lang", `--response=${lang}`],
341+
);
342+
}
343+
279344
// TODO: done
280345
/**
281346
* @description Initializes the task-master AI by creating a PRD file
@@ -495,6 +560,31 @@ export class TaskMaster {
495560
return output;
496561
}
497562

563+
// TODO: done
564+
/**
565+
* @description Extracts all main task IDs and subtask IDs from the tasks data
566+
* @param tasks Tasks data to process
567+
* @returns Object containing two arrays: mainIDs (numbers) and subtasksIDs (strings in the format "parentId.subtaskIndex")
568+
*/
569+
public async getAllTaskIdsAsync(tasks: I_Tasks): Promise<{
570+
mainIDs: number[];
571+
subtasksIDs: string[];
572+
}> {
573+
const mainIDs: number[] = [];
574+
const subtasksIDs: string[] = [];
575+
576+
for (const task of tasks.master.tasks) {
577+
mainIDs.push(task.id);
578+
if (task.subtasks && task.subtasks.length > 0) {
579+
for (let i = 0; i < task.subtasks.length; i++) {
580+
subtasksIDs.push(`${task.id}.${i + 1}`);
581+
}
582+
}
583+
}
584+
585+
return { mainIDs, subtasksIDs };
586+
}
587+
498588
// TODO: done
499589
/**
500590
* @description Lists tasks with optional status filtering and subtask display
@@ -640,7 +730,7 @@ export class TaskMaster {
640730
// Updating Methods
641731
// ==============================================
642732

643-
// TODO: validate
733+
// TODO: in-progress
644734
/**
645735
* @description Modifies a task using AI
646736
* @param id ID of the task to modify
@@ -669,7 +759,7 @@ export class TaskMaster {
669759
);
670760
}
671761

672-
// TODO: validate
762+
// TODO: in-progress
673763
/**
674764
* @description Updates multiple tasks using AI from a starting ID
675765
* @param startingId Starting ID for the update
@@ -698,7 +788,7 @@ export class TaskMaster {
698788
);
699789
}
700790

701-
// TODO: validate
791+
// TODO: in-progress
702792
/**
703793
* @description Modifies a subtask using AI
704794
* @param hierarchicalId Hierarchical ID of the subtask
@@ -727,7 +817,7 @@ export class TaskMaster {
727817
);
728818
}
729819

730-
// TODO: validate
820+
// TODO: in-progress
731821
/**
732822
* @description Converts an existing task to a subtask
733823
* @param subtaskId ID of the task to convert into a subtask
@@ -737,6 +827,7 @@ export class TaskMaster {
737827
subtaskId: number,
738828
parentId: number,
739829
): Promise<void> {
830+
await this.clearAllDependenciesAsync(subtaskId.toString());
740831
await this.executeCommandAsync(
741832
`Converting task ${subtaskId} to subtask of ${parentId}...`,
742833
`Task ${subtaskId} converted to subtask successfully!`,
@@ -746,6 +837,87 @@ export class TaskMaster {
746837
);
747838
}
748839

840+
// ==============================================
841+
// Dependencies
842+
// ==============================================
843+
844+
// TODO: in-progress
845+
/**
846+
* @description Adds a dependency to a task
847+
* @param taskId ID of the task to modify
848+
* @param dependencyIds IDs of the dependencies to add
849+
*/
850+
public async addDependencyAsync(
851+
taskId: string,
852+
dependencyIds: string[],
853+
): Promise<void> {
854+
const formatedDepsIds =
855+
dependencyIds.length > 1 ? dependencyIds.join(",") : dependencyIds[0];
856+
await this.executeCommandAsync(
857+
`Adding dependency ${formatedDepsIds} to task ${taskId}...`,
858+
`Dependency ${formatedDepsIds} added successfully to task ${taskId}!`,
859+
`Failed to add dependency ${formatedDepsIds} to task ${taskId}`,
860+
this._mainCommand,
861+
["add-dependency", `--id=${taskId}`, `--depends-on=${formatedDepsIds}`],
862+
);
863+
}
864+
865+
// TODO: done
866+
/**
867+
* @description Validates task dependencies
868+
*/
869+
public async validateDependenciesAsync(): Promise<void> {
870+
await this.executeCommandAsync(
871+
"Validating dependencies...",
872+
"Dependencies validated successfully!",
873+
"Failed to validate dependencies",
874+
this._mainCommand,
875+
["validate-dependencies"],
876+
);
877+
}
878+
879+
// TODO: done
880+
/**
881+
* @description Automatically fixes dependency issues
882+
*/
883+
public async fixDependenciesAsync(): Promise<void> {
884+
await this.executeCommandAsync(
885+
"Fixing dependencies...",
886+
"Dependencies fixed successfully!",
887+
"Failed to fix dependencies",
888+
this._mainCommand,
889+
["fix-dependencies"],
890+
);
891+
}
892+
893+
// TODO: done
894+
/**
895+
* @description Clears all dependencies for the specified task or subtask.
896+
* @param taskId The task ID (either a simple number as string or hierarchical like "1.2")
897+
*/
898+
public async clearAllDependenciesAsync(taskId: string): Promise<void> {
899+
const tasks = await this.getTasksContentAsync();
900+
const dependencyIds = await this.getAllDependenciesAsync(tasks, taskId);
901+
902+
if (dependencyIds.length === 0) {
903+
console.log(chalk.yellow(`No dependencies found for task ${taskId}.`));
904+
return;
905+
}
906+
907+
for (const dependencyId of dependencyIds) {
908+
const dependsOnId = taskId.includes(".")
909+
? `${taskId.split(".")[0]}.${dependencyId}`
910+
: dependencyId;
911+
await this.executeCommandAsync(
912+
`Removing dependency ${dependsOnId} from task ${taskId}...`,
913+
`Dependency ${dependsOnId} removed successfully from task ${taskId}!`,
914+
`Failed to remove dependency ${dependsOnId} from task ${taskId}`,
915+
this._mainCommand,
916+
["remove-dependency", `--id=${taskId}`, `--depends-on=${dependsOnId}`],
917+
);
918+
}
919+
}
920+
749921
// ==============================================
750922
// Backup, Restore and Clear Methods
751923
// ==============================================

0 commit comments

Comments
 (0)