UNPKG

@gonzui/claude-task-manager

Version:

Task management extension for Claude Code with archiving and history

454 lines • 17.9 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const commander_1 = require("commander"); const chalk_1 = __importDefault(require("chalk")); const TaskManager_1 = require("../lib/TaskManager"); const types_1 = require("../types"); const i18n_1 = require("../lib/i18n"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); // Read version from package.json const packageJsonPath = path.join(__dirname, '../../package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); const version = packageJson.version; const program = new commander_1.Command(); const taskManager = new TaskManager_1.TaskManager(); const i18n = i18n_1.I18n.getInstance(); // Initialize i18n synchronously at startup so command descriptions are // localized in --help. Commander builds these at module load, before the // async preAction hook (initI18n) ever runs. try { i18n.initSync(taskManager.getLanguageSync()); } catch { try { i18n.initSync('en'); } catch { // Locales unavailable; t() will fall back to returning keys. } } // Initialize i18n with system or config language async function initI18n() { try { const lang = await taskManager.getLanguage(); await i18n.init(lang); } catch { await i18n.init('en'); } } program .name('claude-task') .description('Claude Code Task Manager') .version(version) .hook('preAction', async () => { await initI18n(); }); program .command('init') .description(i18n.t('commands.init.description')) .option('--hooks', 'Configure Claude Code statusline and SessionStart hook in .claude/settings.json') .action(async (options) => { try { await taskManager.init({ hooks: options.hooks }); console.log(chalk_1.default.green(i18n.t('commands.init.success'))); console.log(chalk_1.default.gray(' Created: task.md, archive/, .claude-tasks/')); } catch (error) { handleError(error); } }); program .command('new [title]') .description(i18n.t('commands.new.description')) .option('-d, --description <description>', 'Task description') .option('-p, --priority <priority>', 'Task priority (low, medium, high)', 'medium') .option('--tags <tags>', 'Task tags (comma-separated)') .option('-n, --name <name>', 'Create the task under this name and switch to it (does not archive the current task)') .action(async (title, options) => { try { const taskOptions = { title: title || options.title, description: options.description, priority: options.priority, tags: options.tags ? options.tags.split(',').map((tag) => tag.trim()) : undefined, name: options.name }; const taskFile = await taskManager.createNewTask(taskOptions); console.log(chalk_1.default.green(i18n.t('commands.new.success', { title: taskOptions.title || 'New Task' }))); if (taskOptions.name) { const activeName = await taskManager.getActiveTaskName(); console.log(chalk_1.default.blue(i18n.t('commands.status.activeTask', { name: chalk_1.default.yellow(activeName || taskOptions.name) }))); } else if (taskOptions.title) { console.log(chalk_1.default.blue(i18n.t('commands.new.archiving'))); } if (taskOptions.priority && taskOptions.priority !== 'medium') { console.log(chalk_1.default.yellow(` Priority: ${taskOptions.priority.toUpperCase()}`)); } if (taskOptions.tags && taskOptions.tags.length > 0) { console.log(chalk_1.default.cyan(` Tags: ${taskOptions.tags.join(', ')}`)); } } catch (error) { handleError(error); } }); program .command('run') .description(i18n.t('commands.run.description')) .option('-v, --verbose', 'Verbose output') .option('-d, --debug', 'Debug output') .option('--no-edit-permission', 'Disable file edit permissions for Claude (default: edit permissions enabled)') .action(async (options) => { try { console.log(chalk_1.default.blue(i18n.t('commands.run.starting'))); const startTime = Date.now(); const result = await taskManager.runTask(options.verbose, options.debug, options.editPermission); const duration = Date.now() - startTime; if (result.success) { console.log(chalk_1.default.green(i18n.t('commands.run.success'))); if (options.verbose && result.output) { console.log(chalk_1.default.gray('Output:')); console.log(result.output); } } else { console.log(chalk_1.default.red(i18n.t('commands.run.error', { error: result.error || 'Unknown error' }))); } } catch (error) { handleError(error); } }); program .command('history') .description(i18n.t('commands.history.description')) .option('-l, --limit <number>', 'Limit number of results', '10') .option('--size', 'Show file sizes') .action(async (options) => { try { const history = await taskManager.getHistory(parseInt(options.limit)); if (history.length === 0) { console.log(chalk_1.default.yellow(i18n.t('commands.history.empty'))); return; } console.log(chalk_1.default.blue(i18n.t('commands.history.title'))); history.forEach((task) => { const sizeInfo = options.size && task.size ? ` (${formatBytes(task.size)})` : ''; console.log(i18n.t('commands.history.item', { date: task.date, title: task.title }) + chalk_1.default.gray(sizeInfo)); }); } catch (error) { handleError(error); } }); program .command('status') .description(i18n.t('commands.status.description')) .option('--short', 'One-line output for statusline embedding (e.g. Claude Code statusLine)') .action(async (options) => { try { if (options.short) { console.log(await taskManager.getShortStatus()); return; } const status = await taskManager.getStatus(); console.log(chalk_1.default.blue(i18n.t('commands.status.title'))); if (status.activeTaskName) { console.log(i18n.t('commands.status.activeTask', { name: chalk_1.default.yellow(status.activeTaskName) })); } console.log(status.currentTask ? i18n.t('commands.status.currentTask', { task: chalk_1.default.yellow(status.currentTask) }) : i18n.t('commands.status.noCurrentTask')); if (status.currentTaskSize) { console.log(`Task file size: ${chalk_1.default.gray(formatBytes(status.currentTaskSize))}`); } console.log(i18n.t('commands.status.archivedCount', { count: chalk_1.default.green(status.archivedCount.toString()) })); console.log(i18n.t('commands.status.totalExecutions', { count: chalk_1.default.cyan(status.totalExecutions.toString()) })); console.log(status.lastRun ? i18n.t('commands.status.lastRun', { time: chalk_1.default.gray(status.lastRun) }) : i18n.t('commands.status.noLastRun')); } catch (error) { handleError(error); } }); program .command('switch <name>') .description(i18n.t('commands.switch.description')) .option('-c, --create', 'Create the task if it does not exist yet') .action(async (name, options) => { try { const result = await taskManager.switchTask(name, { create: options.create }); if (result.created) { console.log(chalk_1.default.green(i18n.t('commands.switch.created', { name: result.name }))); } else if (result.previous === result.name) { console.log(chalk_1.default.yellow(i18n.t('commands.switch.already', { name: result.name }))); } else { console.log(chalk_1.default.green(i18n.t('commands.switch.success', { name: result.name }))); } console.log(chalk_1.default.gray(await taskManager.getShortStatus())); } catch (error) { handleError(error); } }); program .command('list') .alias('ls') .description(i18n.t('commands.list.description')) .action(async () => { try { const tasks = await taskManager.listTasks(); if (tasks.length === 0) { console.log(chalk_1.default.yellow(i18n.t('commands.list.empty'))); return; } console.log(chalk_1.default.blue(i18n.t('commands.list.title'))); for (const task of tasks) { const marker = task.active ? chalk_1.default.green('*') : ' '; const name = task.active ? chalk_1.default.green(task.name) : task.name; const progress = task.total > 0 ? chalk_1.default.gray(` — ${task.completed}/${task.total} (${task.percentage}%)`) : ''; console.log(`${marker} ${name}: ${task.title}${progress}`); } if (tasks.length > 1) { console.log(''); console.log(chalk_1.default.gray(i18n.t('commands.list.hint'))); } } catch (error) { handleError(error); } }); program .command('archive') .description(i18n.t('commands.archive.description')) .action(async () => { try { const archivedPath = await taskManager.archiveCurrentTask(); if (archivedPath) { console.log(chalk_1.default.green(i18n.t('commands.archive.success', { path: path.basename(archivedPath) }))); } else { console.log(chalk_1.default.yellow(i18n.t('commands.archive.noTask'))); } } catch (error) { handleError(error); } }); program .command('claude [prompt...]') .description(i18n.t('commands.claude.description')) .action(async (promptParts) => { try { console.log(chalk_1.default.yellow(i18n.t('commands.claude.deprecated'))); const prompt = promptParts.join(' '); if (!prompt) { // No prompt provided, just show task content console.log(chalk_1.default.blue('šŸ“‹ Executing task with Claude Code...')); const taskContent = await taskManager.getTaskContent(); console.log('\n' + taskContent); console.log('\nšŸ’” Please use the task content above in Claude Code.'); } else { // Prompt provided, execute with task context console.log(chalk_1.default.blue('šŸš€ Executing Claude Code with prompt...')); const taskContent = await taskManager.getTaskContent(); console.log('\n=== TASK CONTEXT ==='); console.log(taskContent); console.log('=== END TASK CONTEXT ===\n'); console.log('šŸ“ Your prompt:', chalk_1.default.yellow(prompt)); console.log('\nšŸ’” Please execute the above prompt with the task context in Claude Code.'); } // Update execution count await taskManager.recordExecution({ success: true, output: 'Claude Code execution initiated', timestamp: new Date().toISOString(), duration: 0 }); } catch (error) { handleError(error); } }); program .command('lang [language]') .description(i18n.t('commands.lang.description')) .action(async (language) => { try { if (!language) { const currentLang = await taskManager.getLanguage(); console.log(i18n.t('commands.lang.current', { lang: currentLang })); return; } if (language !== 'en' && language !== 'ja') { console.log(chalk_1.default.red(i18n.t('commands.lang.invalid'))); return; } await taskManager.setLanguage(language); await i18n.init(language); console.log(chalk_1.default.green(i18n.t('commands.lang.changed', { lang: language }))); } catch (error) { handleError(error); } }); program .command('progress') .description(i18n.t('commands.progress.description')) .action(async () => { try { const result = await taskManager.getProgress(); console.log(taskManager.formatProgress(result)); } catch (error) { handleError(error); } }); program .command('done <numbers...>') .description(i18n.t('commands.done.description')) .option('-u, --undo', 'Uncheck the given subtasks instead of completing them') .action(async (numbers, options) => { try { const indices = numbers .map((n) => parseInt(n, 10)) .filter((n) => !Number.isNaN(n)); if (indices.length === 0) { console.log(chalk_1.default.red(i18n.t('commands.done.noNumbers'))); return; } const completed = !options.undo; const { updated, invalid, result } = await taskManager.setTaskCompletion(indices, completed); if (updated.length > 0) { const key = completed ? 'commands.done.success' : 'commands.done.undone'; console.log(chalk_1.default.green(i18n.t(key, { numbers: updated.join(', ') }))); } if (invalid.length > 0) { console.log(chalk_1.default.yellow(i18n.t('commands.done.invalid', { numbers: invalid.join(', ') }))); } console.log(''); console.log(taskManager.formatProgress(result)); } catch (error) { handleError(error); } }); program .command('split') .description(i18n.t('commands.split.description')) .option('-c, --count <number>', 'Number of subtasks to generate') .action(async (options) => { try { console.log(chalk_1.default.blue(i18n.t('commands.split.analyzing'))); const count = options.count ? parseInt(options.count) : undefined; const result = await taskManager.splitTask(count); if (result.success) { console.log(chalk_1.default.green(i18n.t('commands.split.success', { count: result.subtasks.length }))); console.log(''); result.subtasks.forEach((task, index) => { console.log(chalk_1.default.gray(` ${index + 1}. ${task}`)); }); } else { console.log(chalk_1.default.red(i18n.t('commands.split.error', { error: result.error }))); } } catch (error) { handleError(error); } }); // Error handling function function handleError(error) { if (error instanceof types_1.ClaudeExecutionError) { console.error(chalk_1.default.red('Claude Execution Error:'), error.message); if (error.stderr) { console.error(chalk_1.default.gray('Claude stderr:'), error.stderr); } if (error.exitCode) { console.error(chalk_1.default.gray('Exit code:'), error.exitCode); } } else if (error instanceof types_1.FileSystemError) { console.error(chalk_1.default.red('File System Error:'), error.message); console.error(chalk_1.default.gray('Path:'), error.details?.path); console.error(chalk_1.default.gray('Operation:'), error.details?.operation); } else if (error instanceof types_1.TaskManagerError) { console.error(chalk_1.default.red('Task Manager Error:'), error.message); console.error(chalk_1.default.gray('Code:'), error.code); } else if (error instanceof Error) { console.error(chalk_1.default.red('Error:'), error.message); } else { console.error(chalk_1.default.red('Unknown error occurred')); } process.exit(1); } // Utility function to format bytes function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Handle unknown commands program.on('command:*', () => { console.error(chalk_1.default.red('Invalid command. See --help for available commands.')); process.exit(1); }); // Parse command line arguments program.parse(); // Show help if no command provided if (!process.argv.slice(2).length) { program.outputHelp(); } //# sourceMappingURL=claude-task.js.map