chore: cleanup and apply requested changes
This commit is contained in:
@@ -1,37 +1,37 @@
|
|||||||
{
|
{
|
||||||
"models": {
|
"models": {
|
||||||
"main": {
|
"main": {
|
||||||
"provider": "anthropic",
|
"provider": "anthropic",
|
||||||
"modelId": "claude-3-7-sonnet-20250219",
|
"modelId": "claude-3-7-sonnet-20250219",
|
||||||
"maxTokens": 120000,
|
"maxTokens": 120000,
|
||||||
"temperature": 0.2
|
"temperature": 0.2
|
||||||
},
|
},
|
||||||
"research": {
|
"research": {
|
||||||
"provider": "perplexity",
|
"provider": "perplexity",
|
||||||
"modelId": "sonar",
|
"modelId": "sonar",
|
||||||
"maxTokens": 8700,
|
"maxTokens": 8700,
|
||||||
"temperature": 0.1
|
"temperature": 0.1
|
||||||
},
|
},
|
||||||
"fallback": {
|
"fallback": {
|
||||||
"provider": "anthropic",
|
"provider": "anthropic",
|
||||||
"modelId": "claude-3-5-sonnet-20241022",
|
"modelId": "claude-3-5-sonnet-20241022",
|
||||||
"maxTokens": 8192,
|
"maxTokens": 8192,
|
||||||
"temperature": 0.2
|
"temperature": 0.2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"global": {
|
"global": {
|
||||||
"logLevel": "info",
|
"logLevel": "info",
|
||||||
"debug": false,
|
"debug": false,
|
||||||
"defaultNumTasks": 10,
|
"defaultNumTasks": 10,
|
||||||
"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",
|
||||||
"responseLanguage": "English",
|
"responseLanguage": "English",
|
||||||
"userId": "1234567890",
|
"userId": "1234567890",
|
||||||
"azureBaseURL": "https://your-endpoint.azure.com/",
|
"azureBaseURL": "https://your-endpoint.azure.com/",
|
||||||
"defaultTag": "master"
|
"defaultTag": "master"
|
||||||
},
|
},
|
||||||
"claudeCode": {}
|
"claudeCode": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"currentTag": "master",
|
"currentTag": "master",
|
||||||
"lastSwitched": "2025-08-27T21:03:20.550Z",
|
"lastSwitched": "2025-08-27T21:03:20.550Z",
|
||||||
"branchTagMapping": {
|
"branchTagMapping": {
|
||||||
"v017-adds": "v017-adds",
|
"v017-adds": "v017-adds",
|
||||||
"next": "next"
|
"next": "next"
|
||||||
},
|
},
|
||||||
"migrationNoticeShown": true
|
"migrationNoticeShown": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
||||||
"extends": ["../../biome.json"],
|
|
||||||
"files": {
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -93,7 +93,10 @@ export class ListTasksCommand extends Command {
|
|||||||
this.displayResults(result, options);
|
this.displayResults(result, options);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(chalk.red(`Error: ${error.message}`));
|
const msg = error?.getSanitizedDetails?.() ?? {
|
||||||
|
message: error?.message ?? String(error)
|
||||||
|
};
|
||||||
|
console.error(chalk.red(`Error: ${msg.message || 'Unexpected error'}`));
|
||||||
if (error.stack && process.env.DEBUG) {
|
if (error.stack && process.env.DEBUG) {
|
||||||
console.error(chalk.gray(error.stack));
|
console.error(chalk.gray(error.stack));
|
||||||
}
|
}
|
||||||
|
|||||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -77,7 +77,8 @@
|
|||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"supertest": "^7.1.0",
|
"supertest": "^7.1.0",
|
||||||
"tsup": "^8.5.0",
|
"tsup": "^8.5.0",
|
||||||
"tsx": "^4.16.2"
|
"tsx": "^4.16.2",
|
||||||
|
"typescript": "^5.9.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
@@ -24783,7 +24784,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.8.3",
|
"version": "5.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||||
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -136,6 +136,7 @@
|
|||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"supertest": "^7.1.0",
|
"supertest": "^7.1.0",
|
||||||
"tsup": "^8.5.0",
|
"tsup": "^8.5.0",
|
||||||
"tsx": "^4.16.2"
|
"tsx": "^4.16.2",
|
||||||
|
"typescript": "^5.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
||||||
"vcs": {
|
|
||||||
"enabled": true,
|
|
||||||
"clientKind": "git",
|
|
||||||
"useIgnoreFile": true,
|
|
||||||
"defaultBranch": "main"
|
|
||||||
},
|
|
||||||
"files": {
|
|
||||||
"include": ["src/**/*.ts", "tests/**/*.ts", "*.ts", "*.js", "*.json"],
|
|
||||||
"ignore": [
|
|
||||||
"**/node_modules",
|
|
||||||
"**/dist",
|
|
||||||
"**/.git",
|
|
||||||
"**/coverage",
|
|
||||||
"**/*.d.ts"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"formatWithErrors": false,
|
|
||||||
"indentStyle": "tab",
|
|
||||||
"indentWidth": 2,
|
|
||||||
"lineEnding": "lf",
|
|
||||||
"lineWidth": 100,
|
|
||||||
"attributePosition": "auto"
|
|
||||||
},
|
|
||||||
"organizeImports": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"enabled": true,
|
|
||||||
"rules": {
|
|
||||||
"recommended": true,
|
|
||||||
"style": {
|
|
||||||
"noNonNullAssertion": "off",
|
|
||||||
"useConst": "error",
|
|
||||||
"useImportType": "warn",
|
|
||||||
"useTemplate": "warn",
|
|
||||||
"noUselessElse": "warn",
|
|
||||||
"noVar": "error"
|
|
||||||
},
|
|
||||||
"correctness": {
|
|
||||||
"noUnusedVariables": "warn",
|
|
||||||
"noUnusedImports": "error",
|
|
||||||
"useExhaustiveDependencies": "warn"
|
|
||||||
},
|
|
||||||
"complexity": {
|
|
||||||
"noBannedTypes": "error",
|
|
||||||
"noForEach": "off",
|
|
||||||
"noStaticOnlyClass": "warn",
|
|
||||||
"noUselessConstructor": "error",
|
|
||||||
"noUselessTypeConstraint": "error",
|
|
||||||
"useArrowFunction": "off"
|
|
||||||
},
|
|
||||||
"suspicious": {
|
|
||||||
"noExplicitAny": "warn",
|
|
||||||
"noImplicitAnyLet": "error",
|
|
||||||
"noArrayIndexKey": "warn",
|
|
||||||
"noAsyncPromiseExecutor": "error",
|
|
||||||
"noDoubleEquals": "warn",
|
|
||||||
"noRedundantUseStrict": "error"
|
|
||||||
},
|
|
||||||
"security": {
|
|
||||||
"noGlobalEval": "error"
|
|
||||||
},
|
|
||||||
"performance": {
|
|
||||||
"noAccumulatingSpread": "warn",
|
|
||||||
"noDelete": "warn"
|
|
||||||
},
|
|
||||||
"a11y": {
|
|
||||||
"recommended": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"javascript": {
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"quoteStyle": "single",
|
|
||||||
"jsxQuoteStyle": "double",
|
|
||||||
"quoteProperties": "asNeeded",
|
|
||||||
"trailingCommas": "none",
|
|
||||||
"semicolons": "always",
|
|
||||||
"arrowParentheses": "always",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"bracketSameLine": false
|
|
||||||
},
|
|
||||||
"parser": {
|
|
||||||
"unsafeParameterDecoratorsEnabled": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"json": {
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"indentStyle": "tab",
|
|
||||||
"lineWidth": 100,
|
|
||||||
"trailingCommas": "none"
|
|
||||||
},
|
|
||||||
"parser": {
|
|
||||||
"allowComments": true,
|
|
||||||
"allowTrailingCommas": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,15 @@
|
|||||||
* This file exports all custom error types and error handling utilities
|
* This file exports all custom error types and error handling utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Export the main TaskMasterError class
|
||||||
|
export {
|
||||||
|
TaskMasterError,
|
||||||
|
ERROR_CODES,
|
||||||
|
type ErrorCode,
|
||||||
|
type ErrorContext,
|
||||||
|
type SerializableError
|
||||||
|
} from './task-master-error.js';
|
||||||
|
|
||||||
// Error implementations will be defined here
|
// Error implementations will be defined here
|
||||||
// export * from './task-errors.js';
|
// export * from './task-errors.js';
|
||||||
// export * from './storage-errors.js';
|
// export * from './storage-errors.js';
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ export class FileOperations {
|
|||||||
/**
|
/**
|
||||||
* Write JSON file with atomic operation and locking
|
* Write JSON file with atomic operation and locking
|
||||||
*/
|
*/
|
||||||
async writeJson(filePath: string, data: FileStorageData | any): Promise<void> {
|
async writeJson(
|
||||||
|
filePath: string,
|
||||||
|
data: FileStorageData | any
|
||||||
|
): Promise<void> {
|
||||||
// Use file locking to prevent concurrent writes
|
// Use file locking to prevent concurrent writes
|
||||||
const lockKey = filePath;
|
const lockKey = filePath;
|
||||||
const existingLock = this.fileLocks.get(lockKey);
|
const existingLock = this.fileLocks.get(lockKey);
|
||||||
@@ -109,7 +112,9 @@ export class FileOperations {
|
|||||||
try {
|
try {
|
||||||
await fs.mkdir(dirPath, { recursive: true });
|
await fs.mkdir(dirPath, { recursive: true });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
|
throw new Error(
|
||||||
|
`Failed to create directory ${dirPath}: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +138,9 @@ export class FileOperations {
|
|||||||
try {
|
try {
|
||||||
await fs.rename(oldPath, newPath);
|
await fs.rename(oldPath, newPath);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(`Failed to move file from ${oldPath} to ${newPath}: ${error.message}`);
|
throw new Error(
|
||||||
|
`Failed to move file from ${oldPath} to ${newPath}: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +151,9 @@ export class FileOperations {
|
|||||||
try {
|
try {
|
||||||
await fs.copyFile(srcPath, destPath);
|
await fs.copyFile(srcPath, destPath);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(`Failed to copy file from ${srcPath} to ${destPath}: ${error.message}`);
|
throw new Error(
|
||||||
|
`Failed to copy file from ${srcPath} to ${destPath}: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,4 +167,4 @@ export class FileOperations {
|
|||||||
}
|
}
|
||||||
this.fileLocks.clear();
|
this.fileLocks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,18 +44,18 @@ export class FileStorage implements IStorage {
|
|||||||
*/
|
*/
|
||||||
async getStats(): Promise<StorageStats> {
|
async getStats(): Promise<StorageStats> {
|
||||||
const filePath = this.pathResolver.getTasksPath();
|
const filePath = this.pathResolver.getTasksPath();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stats = await this.fileOps.getStats(filePath);
|
const stats = await this.fileOps.getStats(filePath);
|
||||||
const data = await this.fileOps.readJson(filePath);
|
const data = await this.fileOps.readJson(filePath);
|
||||||
const tags = this.formatHandler.extractTags(data);
|
const tags = this.formatHandler.extractTags(data);
|
||||||
|
|
||||||
let totalTasks = 0;
|
let totalTasks = 0;
|
||||||
const tagStats = tags.map((tag) => {
|
const tagStats = tags.map((tag) => {
|
||||||
const tasks = this.formatHandler.extractTasks(data, tag);
|
const tasks = this.formatHandler.extractTasks(data, tag);
|
||||||
const taskCount = tasks.length;
|
const taskCount = tasks.length;
|
||||||
totalTasks += taskCount;
|
totalTasks += taskCount;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tag,
|
tag,
|
||||||
taskCount,
|
taskCount,
|
||||||
@@ -136,7 +136,12 @@ export class FileStorage implements IStorage {
|
|||||||
const normalizedTasks = this.normalizeTaskIds(tasks);
|
const normalizedTasks = this.normalizeTaskIds(tasks);
|
||||||
|
|
||||||
// Update the specific tag in the existing data structure
|
// Update the specific tag in the existing data structure
|
||||||
if (this.formatHandler.detectFormat(existingData) === 'legacy' || Object.keys(existingData).some(key => key !== 'tasks' && key !== 'metadata')) {
|
if (
|
||||||
|
this.formatHandler.detectFormat(existingData) === 'legacy' ||
|
||||||
|
Object.keys(existingData).some(
|
||||||
|
(key) => key !== 'tasks' && key !== 'metadata'
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Legacy format - update/add the tag
|
// Legacy format - update/add the tag
|
||||||
existingData[resolvedTag] = {
|
existingData[resolvedTag] = {
|
||||||
tasks: normalizedTasks,
|
tasks: normalizedTasks,
|
||||||
@@ -152,7 +157,7 @@ export class FileStorage implements IStorage {
|
|||||||
// Convert to legacy format when adding non-master tags
|
// Convert to legacy format when adding non-master tags
|
||||||
const masterTasks = existingData.tasks || [];
|
const masterTasks = existingData.tasks || [];
|
||||||
const masterMetadata = existingData.metadata || metadata;
|
const masterMetadata = existingData.metadata || metadata;
|
||||||
|
|
||||||
existingData = {
|
existingData = {
|
||||||
master: {
|
master: {
|
||||||
tasks: masterTasks,
|
tasks: masterTasks,
|
||||||
@@ -177,11 +182,12 @@ export class FileStorage implements IStorage {
|
|||||||
...task,
|
...task,
|
||||||
id: String(task.id), // Task IDs are strings
|
id: String(task.id), // Task IDs are strings
|
||||||
dependencies: task.dependencies?.map((dep) => String(dep)) || [],
|
dependencies: task.dependencies?.map((dep) => String(dep)) || [],
|
||||||
subtasks: task.subtasks?.map((subtask) => ({
|
subtasks:
|
||||||
...subtask,
|
task.subtasks?.map((subtask) => ({
|
||||||
id: Number(subtask.id), // Subtask IDs are numbers
|
...subtask,
|
||||||
parentId: String(subtask.parentId) // Parent ID is string (Task ID)
|
id: Number(subtask.id), // Subtask IDs are numbers
|
||||||
})) || []
|
parentId: String(subtask.parentId) // Parent ID is string (Task ID)
|
||||||
|
})) || []
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,10 +292,10 @@ export class FileStorage implements IStorage {
|
|||||||
*/
|
*/
|
||||||
async deleteTag(tag: string): Promise<void> {
|
async deleteTag(tag: string): Promise<void> {
|
||||||
const filePath = this.pathResolver.getTasksPath();
|
const filePath = this.pathResolver.getTasksPath();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const existingData = await this.fileOps.readJson(filePath);
|
const existingData = await this.fileOps.readJson(filePath);
|
||||||
|
|
||||||
if (this.formatHandler.detectFormat(existingData) === 'legacy') {
|
if (this.formatHandler.detectFormat(existingData) === 'legacy') {
|
||||||
// Legacy format - remove the tag key
|
// Legacy format - remove the tag key
|
||||||
if (tag in existingData) {
|
if (tag in existingData) {
|
||||||
@@ -317,21 +323,21 @@ export class FileStorage implements IStorage {
|
|||||||
*/
|
*/
|
||||||
async renameTag(oldTag: string, newTag: string): Promise<void> {
|
async renameTag(oldTag: string, newTag: string): Promise<void> {
|
||||||
const filePath = this.pathResolver.getTasksPath();
|
const filePath = this.pathResolver.getTasksPath();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const existingData = await this.fileOps.readJson(filePath);
|
const existingData = await this.fileOps.readJson(filePath);
|
||||||
|
|
||||||
if (this.formatHandler.detectFormat(existingData) === 'legacy') {
|
if (this.formatHandler.detectFormat(existingData) === 'legacy') {
|
||||||
// Legacy format - rename the tag key
|
// Legacy format - rename the tag key
|
||||||
if (oldTag in existingData) {
|
if (oldTag in existingData) {
|
||||||
existingData[newTag] = existingData[oldTag];
|
existingData[newTag] = existingData[oldTag];
|
||||||
delete existingData[oldTag];
|
delete existingData[oldTag];
|
||||||
|
|
||||||
// Update metadata tags array
|
// Update metadata tags array
|
||||||
if (existingData[newTag].metadata) {
|
if (existingData[newTag].metadata) {
|
||||||
existingData[newTag].metadata.tags = [newTag];
|
existingData[newTag].metadata.tags = [newTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.fileOps.writeJson(filePath, existingData);
|
await this.fileOps.writeJson(filePath, existingData);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Tag ${oldTag} not found`);
|
throw new Error(`Tag ${oldTag} not found`);
|
||||||
@@ -340,14 +346,14 @@ export class FileStorage implements IStorage {
|
|||||||
// Convert standard format to legacy when renaming master
|
// Convert standard format to legacy when renaming master
|
||||||
const masterTasks = existingData.tasks || [];
|
const masterTasks = existingData.tasks || [];
|
||||||
const masterMetadata = existingData.metadata || {};
|
const masterMetadata = existingData.metadata || {};
|
||||||
|
|
||||||
const newData = {
|
const newData = {
|
||||||
[newTag]: {
|
[newTag]: {
|
||||||
tasks: masterTasks,
|
tasks: masterTasks,
|
||||||
metadata: { ...masterMetadata, tags: [newTag] }
|
metadata: { ...masterMetadata, tags: [newTag] }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.fileOps.writeJson(filePath, newData);
|
await this.fileOps.writeJson(filePath, newData);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Tag ${oldTag} not found in standard format`);
|
throw new Error(`Tag ${oldTag} not found in standard format`);
|
||||||
@@ -375,4 +381,4 @@ export class FileStorage implements IStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Export as default for convenience
|
// Export as default for convenience
|
||||||
export default FileStorage;
|
export default FileStorage;
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ export class FormatHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(data);
|
const keys = Object.keys(data);
|
||||||
|
|
||||||
// Check if this uses the legacy format with tag keys
|
// Check if this uses the legacy format with tag keys
|
||||||
// Legacy format has keys that are not 'tasks' or 'metadata'
|
// Legacy format has keys that are not 'tasks' or 'metadata'
|
||||||
const hasLegacyFormat = keys.some(key => key !== 'tasks' && key !== 'metadata');
|
const hasLegacyFormat = keys.some(
|
||||||
|
(key) => key !== 'tasks' && key !== 'metadata'
|
||||||
|
);
|
||||||
|
|
||||||
return hasLegacyFormat ? 'legacy' : 'standard';
|
return hasLegacyFormat ? 'legacy' : 'standard';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,11 +43,11 @@ export class FormatHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const format = this.detectFormat(data);
|
const format = this.detectFormat(data);
|
||||||
|
|
||||||
if (format === 'legacy') {
|
if (format === 'legacy') {
|
||||||
return this.extractTasksFromLegacy(data, tag);
|
return this.extractTasksFromLegacy(data, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.extractTasksFromStandard(data);
|
return this.extractTasksFromStandard(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +62,9 @@ export class FormatHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we're looking for 'master' tag but it doesn't exist, try the first available tag
|
// If we're looking for 'master' tag but it doesn't exist, try the first available tag
|
||||||
const availableKeys = Object.keys(data).filter(key => key !== 'tasks' && key !== 'metadata');
|
const availableKeys = Object.keys(data).filter(
|
||||||
|
(key) => key !== 'tasks' && key !== 'metadata'
|
||||||
|
);
|
||||||
if (tag === 'master' && availableKeys.length > 0) {
|
if (tag === 'master' && availableKeys.length > 0) {
|
||||||
const firstTag = availableKeys[0];
|
const firstTag = availableKeys[0];
|
||||||
const tagData = data[firstTag];
|
const tagData = data[firstTag];
|
||||||
@@ -86,18 +90,21 @@ export class FormatHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const format = this.detectFormat(data);
|
const format = this.detectFormat(data);
|
||||||
|
|
||||||
if (format === 'legacy') {
|
if (format === 'legacy') {
|
||||||
return this.extractMetadataFromLegacy(data, tag);
|
return this.extractMetadataFromLegacy(data, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.extractMetadataFromStandard(data);
|
return this.extractMetadataFromStandard(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract metadata from legacy format
|
* Extract metadata from legacy format
|
||||||
*/
|
*/
|
||||||
private extractMetadataFromLegacy(data: any, tag: string): TaskMetadata | null {
|
private extractMetadataFromLegacy(
|
||||||
|
data: any,
|
||||||
|
tag: string
|
||||||
|
): TaskMetadata | null {
|
||||||
if (tag in data) {
|
if (tag in data) {
|
||||||
const tagData = data[tag];
|
const tagData = data[tag];
|
||||||
// Generate metadata if not present in legacy format
|
// Generate metadata if not present in legacy format
|
||||||
@@ -108,7 +115,9 @@ export class FormatHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we're looking for 'master' tag but it doesn't exist, try the first available tag
|
// If we're looking for 'master' tag but it doesn't exist, try the first available tag
|
||||||
const availableKeys = Object.keys(data).filter(key => key !== 'tasks' && key !== 'metadata');
|
const availableKeys = Object.keys(data).filter(
|
||||||
|
(key) => key !== 'tasks' && key !== 'metadata'
|
||||||
|
);
|
||||||
if (tag === 'master' && availableKeys.length > 0) {
|
if (tag === 'master' && availableKeys.length > 0) {
|
||||||
const firstTag = availableKeys[0];
|
const firstTag = availableKeys[0];
|
||||||
const tagData = data[firstTag];
|
const tagData = data[firstTag];
|
||||||
@@ -137,13 +146,13 @@ export class FormatHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const format = this.detectFormat(data);
|
const format = this.detectFormat(data);
|
||||||
|
|
||||||
if (format === 'legacy') {
|
if (format === 'legacy') {
|
||||||
// Return all tag keys from legacy format
|
// Return all tag keys from legacy format
|
||||||
const keys = Object.keys(data);
|
const keys = Object.keys(data);
|
||||||
return keys.filter(key => key !== 'tasks' && key !== 'metadata');
|
return keys.filter((key) => key !== 'tasks' && key !== 'metadata');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard format - just has 'master' tag
|
// Standard format - just has 'master' tag
|
||||||
return ['master'];
|
return ['master'];
|
||||||
}
|
}
|
||||||
@@ -152,21 +161,21 @@ export class FormatHandler {
|
|||||||
* Convert tasks and metadata to the appropriate format for saving
|
* Convert tasks and metadata to the appropriate format for saving
|
||||||
*/
|
*/
|
||||||
convertToSaveFormat(
|
convertToSaveFormat(
|
||||||
tasks: Task[],
|
tasks: Task[],
|
||||||
metadata: TaskMetadata,
|
metadata: TaskMetadata,
|
||||||
existingData: any,
|
existingData: any,
|
||||||
tag: string
|
tag: string
|
||||||
): any {
|
): any {
|
||||||
const resolvedTag = tag || 'master';
|
const resolvedTag = tag || 'master';
|
||||||
|
|
||||||
// Normalize task IDs to strings
|
// Normalize task IDs to strings
|
||||||
const normalizedTasks = this.normalizeTasks(tasks);
|
const normalizedTasks = this.normalizeTasks(tasks);
|
||||||
|
|
||||||
// Check if existing file uses legacy format
|
// Check if existing file uses legacy format
|
||||||
if (existingData && this.detectFormat(existingData) === 'legacy') {
|
if (existingData && this.detectFormat(existingData) === 'legacy') {
|
||||||
return this.convertToLegacyFormat(normalizedTasks, metadata, resolvedTag);
|
return this.convertToLegacyFormat(normalizedTasks, metadata, resolvedTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use standard format for new files
|
// Use standard format for new files
|
||||||
return this.convertToStandardFormat(normalizedTasks, metadata, tag);
|
return this.convertToStandardFormat(normalizedTasks, metadata, tag);
|
||||||
}
|
}
|
||||||
@@ -175,8 +184,8 @@ export class FormatHandler {
|
|||||||
* Convert to legacy format
|
* Convert to legacy format
|
||||||
*/
|
*/
|
||||||
private convertToLegacyFormat(
|
private convertToLegacyFormat(
|
||||||
tasks: Task[],
|
tasks: Task[],
|
||||||
metadata: TaskMetadata,
|
metadata: TaskMetadata,
|
||||||
tag: string
|
tag: string
|
||||||
): any {
|
): any {
|
||||||
return {
|
return {
|
||||||
@@ -194,8 +203,8 @@ export class FormatHandler {
|
|||||||
* Convert to standard format
|
* Convert to standard format
|
||||||
*/
|
*/
|
||||||
private convertToStandardFormat(
|
private convertToStandardFormat(
|
||||||
tasks: Task[],
|
tasks: Task[],
|
||||||
metadata: TaskMetadata,
|
metadata: TaskMetadata,
|
||||||
tag?: string
|
tag?: string
|
||||||
): FileStorageData {
|
): FileStorageData {
|
||||||
return {
|
return {
|
||||||
@@ -215,11 +224,12 @@ export class FormatHandler {
|
|||||||
...task,
|
...task,
|
||||||
id: String(task.id), // Task IDs are strings
|
id: String(task.id), // Task IDs are strings
|
||||||
dependencies: task.dependencies?.map((dep) => String(dep)) || [],
|
dependencies: task.dependencies?.map((dep) => String(dep)) || [],
|
||||||
subtasks: task.subtasks?.map((subtask) => ({
|
subtasks:
|
||||||
...subtask,
|
task.subtasks?.map((subtask) => ({
|
||||||
id: Number(subtask.id), // Subtask IDs are numbers
|
...subtask,
|
||||||
parentId: String(subtask.parentId) // Parent ID is string (Task ID)
|
id: Number(subtask.id), // Subtask IDs are numbers
|
||||||
})) || []
|
parentId: String(subtask.parentId) // Parent ID is string (Task ID)
|
||||||
|
})) || []
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,4 +245,4 @@ export class FormatHandler {
|
|||||||
tags: [tag]
|
tags: [tag]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
* @fileoverview Exports for file storage components
|
* @fileoverview Exports for file storage components
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { FormatHandler, type FileStorageData, type FileFormat } from './format-handler.js';
|
export {
|
||||||
|
FormatHandler,
|
||||||
|
type FileStorageData,
|
||||||
|
type FileFormat
|
||||||
|
} from './format-handler.js';
|
||||||
export { FileOperations } from './file-operations.js';
|
export { FileOperations } from './file-operations.js';
|
||||||
export { PathResolver } from './path-resolver.js';
|
export { PathResolver } from './path-resolver.js';
|
||||||
|
|
||||||
// Main FileStorage class - primary export
|
// Main FileStorage class - primary export
|
||||||
export { FileStorage as default, FileStorage } from './file-storage.js';
|
export { FileStorage as default, FileStorage } from './file-storage.js';
|
||||||
|
|||||||
@@ -39,4 +39,4 @@ export class PathResolver {
|
|||||||
getTasksPath(): string {
|
getTasksPath(): string {
|
||||||
return this.tasksFilePath;
|
return this.tasksFilePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,12 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
"@/types": ["./src/types/index"],
|
"@/types": ["./src/types"],
|
||||||
"@/providers": ["./src/providers/index"],
|
"@/providers": ["./src/providers"],
|
||||||
"@/storage": ["./src/storage/index"],
|
"@/storage": ["./src/storage"],
|
||||||
"@/parser": ["./src/parser/index"],
|
"@/parser": ["./src/parser"],
|
||||||
"@/utils": ["./src/utils/index"],
|
"@/utils": ["./src/utils"],
|
||||||
"@/errors": ["./src/errors/index"]
|
"@/errors": ["./src/errors"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
// __dirname in ESM
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
@@ -8,6 +13,7 @@ export default defineConfig({
|
|||||||
include: [
|
include: [
|
||||||
'tests/**/*.test.ts',
|
'tests/**/*.test.ts',
|
||||||
'tests/**/*.spec.ts',
|
'tests/**/*.spec.ts',
|
||||||
|
'tests/{unit,integration,e2e}/**/*.{test,spec}.ts',
|
||||||
'src/**/*.test.ts',
|
'src/**/*.test.ts',
|
||||||
'src/**/*.spec.ts'
|
'src/**/*.spec.ts'
|
||||||
],
|
],
|
||||||
@@ -22,7 +28,7 @@ export default defineConfig({
|
|||||||
'**/*.test.ts',
|
'**/*.test.ts',
|
||||||
'**/*.spec.ts',
|
'**/*.spec.ts',
|
||||||
'**/*.d.ts',
|
'**/*.d.ts',
|
||||||
'**/index.ts'
|
'src/index.ts'
|
||||||
],
|
],
|
||||||
thresholds: {
|
thresholds: {
|
||||||
branches: 80,
|
branches: 80,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"types": ["node"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@@ -10,13 +12,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": "."
|
||||||
"paths": {
|
|
||||||
"@tm/core": ["packages/tm-core/src/index.ts"],
|
|
||||||
"@tm/core/*": ["packages/tm-core/src/*"],
|
|
||||||
"@tm/cli": ["apps/cli/src/index.ts"],
|
|
||||||
"@tm/cli/*": ["apps/cli/src/*"]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"tsx": {
|
"tsx": {
|
||||||
"tsconfig": {
|
"tsconfig": {
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ export default defineConfig({
|
|||||||
splitting: false,
|
splitting: false,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
clean: true,
|
clean: true,
|
||||||
shims: true,
|
|
||||||
dts: true,
|
|
||||||
bundle: true, // Bundle everything into one file
|
bundle: true, // Bundle everything into one file
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
// Handle TypeScript imports transparently
|
// Handle TypeScript imports transparently
|
||||||
|
|||||||
Reference in New Issue
Block a user