mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2025-06-27 00:29:58 +00:00
185 lines
5.6 KiB
JavaScript
185 lines
5.6 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import chalk from 'chalk';
|
|
import { log, findProjectRoot } from './utils.js';
|
|
import { getProjectName } from './config-manager.js';
|
|
import listTasks from './task-manager/list-tasks.js';
|
|
|
|
/**
|
|
* Creates a basic README structure if one doesn't exist
|
|
* @param {string} projectName - Name of the project
|
|
* @returns {string} - Basic README content
|
|
*/
|
|
function createBasicReadme(projectName) {
|
|
return `# ${projectName}
|
|
|
|
This project is managed using Task Master.
|
|
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Create UTM tracking URL for task-master.dev
|
|
* @param {string} projectRoot - The project root path
|
|
* @returns {string} - UTM tracked URL
|
|
*/
|
|
function createTaskMasterUrl(projectRoot) {
|
|
// Get the actual folder name from the project root path
|
|
const folderName = path.basename(projectRoot);
|
|
|
|
// Clean folder name for UTM (replace spaces/special chars with hyphens)
|
|
const cleanFolderName = folderName
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]/g, '-')
|
|
.replace(/-+/g, '-')
|
|
.replace(/^-|-$/g, '');
|
|
|
|
const utmParams = new URLSearchParams({
|
|
utm_source: 'github-readme',
|
|
utm_medium: 'readme-export',
|
|
utm_campaign: cleanFolderName || 'task-sync',
|
|
utm_content: 'task-export-link'
|
|
});
|
|
|
|
return `https://task-master.dev?${utmParams.toString()}`;
|
|
}
|
|
|
|
/**
|
|
* Create the start marker with metadata
|
|
* @param {Object} options - Export options
|
|
* @returns {string} - Formatted start marker
|
|
*/
|
|
function createStartMarker(options) {
|
|
const { timestamp, withSubtasks, status, projectRoot } = options;
|
|
|
|
// Format status filter text
|
|
const statusText = status
|
|
? `Status filter: ${status}`
|
|
: 'Status filter: none';
|
|
const subtasksText = withSubtasks ? 'with subtasks' : 'without subtasks';
|
|
|
|
// Create the export info content
|
|
const exportInfo =
|
|
`🎯 **Taskmaster Export** - ${timestamp}\n` +
|
|
`📋 Export: ${subtasksText} • ${statusText}\n` +
|
|
`🔗 Powered by [Task Master](${createTaskMasterUrl(projectRoot)})`;
|
|
|
|
// Create a markdown box using code blocks and emojis to mimic our UI style
|
|
const boxContent =
|
|
`<!-- TASKMASTER_EXPORT_START -->\n` +
|
|
`> ${exportInfo.split('\n').join('\n> ')}\n\n`;
|
|
|
|
return boxContent;
|
|
}
|
|
|
|
/**
|
|
* Create the end marker
|
|
* @returns {string} - Formatted end marker
|
|
*/
|
|
function createEndMarker() {
|
|
return (
|
|
`\n> 📋 **End of Taskmaster Export** - Tasks are synced from your project using the \`sync-readme\` command.\n` +
|
|
`<!-- TASKMASTER_EXPORT_END -->\n`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Syncs the current task list to README.md at the project root
|
|
* @param {string} projectRoot - Path to the project root directory
|
|
* @param {Object} options - Options for syncing
|
|
* @param {boolean} options.withSubtasks - Include subtasks in the output (default: false)
|
|
* @param {string} options.status - Filter by status (e.g., 'pending', 'done')
|
|
* @param {string} options.tasksPath - Custom path to tasks.json
|
|
* @returns {boolean} - True if sync was successful, false otherwise
|
|
*/
|
|
export async function syncTasksToReadme(projectRoot = null, options = {}) {
|
|
try {
|
|
const actualProjectRoot = projectRoot || findProjectRoot() || '.';
|
|
const { withSubtasks = false, status, tasksPath } = options;
|
|
|
|
// Get current tasks using the list-tasks functionality with markdown-readme format
|
|
const tasksOutput = await listTasks(
|
|
tasksPath ||
|
|
path.join(actualProjectRoot, '.taskmaster', 'tasks', 'tasks.json'),
|
|
status,
|
|
null,
|
|
withSubtasks,
|
|
'markdown-readme'
|
|
);
|
|
|
|
if (!tasksOutput) {
|
|
console.log(chalk.red('❌ Failed to generate task output'));
|
|
return false;
|
|
}
|
|
|
|
// Generate timestamp and metadata
|
|
const timestamp =
|
|
new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
|
|
const projectName = getProjectName(actualProjectRoot);
|
|
|
|
// Create the export markers with metadata
|
|
const startMarker = createStartMarker({
|
|
timestamp,
|
|
withSubtasks,
|
|
status,
|
|
projectRoot: actualProjectRoot
|
|
});
|
|
|
|
const endMarker = createEndMarker();
|
|
|
|
// Create the complete task section
|
|
const taskSection = startMarker + tasksOutput + endMarker;
|
|
|
|
// Read current README content
|
|
const readmePath = path.join(actualProjectRoot, 'README.md');
|
|
let readmeContent = '';
|
|
try {
|
|
readmeContent = fs.readFileSync(readmePath, 'utf8');
|
|
} catch (err) {
|
|
if (err.code === 'ENOENT') {
|
|
// Create basic README if it doesn't exist
|
|
readmeContent = createBasicReadme(projectName);
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
// Check if export markers exist and replace content between them
|
|
const startComment = '<!-- TASKMASTER_EXPORT_START -->';
|
|
const endComment = '<!-- TASKMASTER_EXPORT_END -->';
|
|
|
|
let updatedContent;
|
|
const startIndex = readmeContent.indexOf(startComment);
|
|
const endIndex = readmeContent.indexOf(endComment);
|
|
|
|
if (startIndex !== -1 && endIndex !== -1) {
|
|
// Replace existing task section
|
|
const beforeTasks = readmeContent.substring(0, startIndex);
|
|
const afterTasks = readmeContent.substring(endIndex + endComment.length);
|
|
updatedContent = beforeTasks + taskSection + afterTasks;
|
|
} else {
|
|
// Append to end of README
|
|
updatedContent = readmeContent + '\n' + taskSection;
|
|
}
|
|
|
|
// Write updated content to README
|
|
fs.writeFileSync(readmePath, updatedContent, 'utf8');
|
|
|
|
console.log(chalk.green('✅ Successfully synced tasks to README.md'));
|
|
console.log(
|
|
chalk.cyan(
|
|
`📋 Export details: ${withSubtasks ? 'with' : 'without'} subtasks${status ? `, status: ${status}` : ''}`
|
|
)
|
|
);
|
|
console.log(chalk.gray(`📍 Location: ${readmePath}`));
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.log(chalk.red('❌ Failed to sync tasks to README:'), error.message);
|
|
log('error', `README sync error: ${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export default syncTasksToReadme;
|