@gonzui/claude-task-manager
Version:
Task management extension for Claude Code with archiving and history
454 lines ⢠17.9 kB
JavaScript
;
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