Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-filestorage-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"task-master-ai": patch
---

Fix FileStorage resource leaks by ensuring close() is called in try/finally blocks
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if close() actually does anything, but hey, why not!

118 changes: 69 additions & 49 deletions apps/cli/src/commands/export.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,13 @@ export class ExportCommand extends Command {
}

const fileStorage = new FileStorage(projectRoot);
await fileStorage.initialize();
const tagsResult = await fileStorage.getTagsWithStats();
let tagsResult;
try {
await fileStorage.initialize();
tagsResult = await fileStorage.getTagsWithStats();
} finally {
await fileStorage.close();
}

if (!tagsResult.tags || tagsResult.tags.length === 0) {
console.log(chalk.yellow('\nNo local tags found in tasks.json.\n'));
Expand Down Expand Up @@ -389,8 +394,13 @@ export class ExportCommand extends Command {

spinner = ora('Loading tasks...').start();
const fileStorage = new FileStorage(projectRoot);
await fileStorage.initialize();
const tasks = await fileStorage.loadTasks(options?.tag);
let tasks;
try {
await fileStorage.initialize();
tasks = await fileStorage.loadTasks(options?.tag);
} finally {
await fileStorage.close();
}
spinner.succeed(`${tasks.length} tasks`);

if (!tasks || tasks.length === 0) {
Expand Down Expand Up @@ -494,8 +504,13 @@ export class ExportCommand extends Command {
}

const fileStorage = new FileStorage(projectRoot);
await fileStorage.initialize();
const tasks = await fileStorage.loadTasks(options?.tag);
let tasks;
try {
await fileStorage.initialize();
tasks = await fileStorage.loadTasks(options?.tag);
} finally {
await fileStorage.close();
}

if (!tasks || tasks.length === 0) {
console.log(
Expand Down Expand Up @@ -715,57 +730,62 @@ export class ExportCommand extends Command {
return;
}
const fileStorage = new FileStorage(projectRoot);
await fileStorage.initialize();

const exportPromises: Promise<ExportResult>[] = tags.map(async (tag) => {
try {
// Load tasks from LOCAL FileStorage to count parent tasks vs subtasks
const tasks = await fileStorage.loadTasks(tag);
const parentTaskCount = tasks.length;
const subtaskCount = tasks.reduce(
(acc, t) => acc + (t.subtasks?.length || 0),
0
);
let results: ExportResult[];
try {
await fileStorage.initialize();
const exportPromises: Promise<ExportResult>[] = tags.map(async (tag) => {
try {
// Load tasks from LOCAL FileStorage to count parent tasks vs subtasks
const tasks = await fileStorage.loadTasks(tag);
const parentTaskCount = tasks.length;
const subtaskCount = tasks.reduce(
(acc, t) => acc + (t.subtasks?.length || 0),
0
);

const result =
await this.taskMasterCore!.integration.generateBriefFromTasks({
const result =
await this.taskMasterCore!.integration.generateBriefFromTasks({
tag,
options: {
generateTitle: true,
generateDescription: true,
preserveHierarchy: true,
preserveDependencies: true
}
});

if (result.success && result.brief) {
// Track exported tag
await this.trackExportedTag(tag, result.brief.id, result.brief.url);
return {
tag,
success: true,
brief: result.brief,
parentTaskCount,
subtaskCount
};
}
return {
tag,
options: {
generateTitle: true,
generateDescription: true,
preserveHierarchy: true,
preserveDependencies: true
}
});

if (result.success && result.brief) {
// Track exported tag
await this.trackExportedTag(tag, result.brief.id, result.brief.url);
success: false,
error: result.error?.message || 'Unknown error'
};
} catch (error: any) {
return {
tag,
success: true,
brief: result.brief,
parentTaskCount,
subtaskCount
success: false,
error: error.message || 'Export failed'
};
}
return {
tag,
success: false,
error: result.error?.message || 'Unknown error'
};
} catch (error: any) {
return {
tag,
success: false,
error: error.message || 'Export failed'
};
}
});
});

// Wait for all exports to complete
const results = await Promise.all(exportPromises);
spinner.stop();
// Wait for all exports to complete
results = await Promise.all(exportPromises);
spinner.stop();
} finally {
await fileStorage.close();
}

// Display results
const successful = results.filter((r) => r.success);
Expand Down
Loading
Loading