UNPKG

@gonzui/claude-task-manager

Version:

Task management extension for Claude Code with archiving and history

277 lines (256 loc) 12.1 kB
"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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CustomCommandGenerator = exports.SESSION_START_HOOK_ENTRY = exports.STATUS_LINE_ENTRY = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); /** * Claude Code integration entries shared by `init --hooks` (merged into * .claude/settings.json) and the plugin package (hooks/hooks.json), so both * distribution channels stay in sync. */ exports.STATUS_LINE_ENTRY = { type: 'command', command: 'claude-task status --short', padding: 1 }; exports.SESSION_START_HOOK_ENTRY = { type: 'command', command: 'claude-task status' }; class CustomCommandGenerator { constructor(workingDir, i18n) { this.workingDir = workingDir; this.i18n = i18n; } async createClaudeCustomCommand() { const claudeDir = path.join(this.workingDir, '.claude'); const claudeCommandsDir = path.join(claudeDir, 'commands'); try { if (await fs.pathExists(claudeDir)) { await fs.ensureDir(claudeCommandsDir); const taskCommandPath = path.join(claudeCommandsDir, 'task.md'); const taskCommandContent = this.generateCustomCommandContent(); await fs.writeFile(taskCommandPath, taskCommandContent); console.log(this.i18n.t('commands.init.customCommand')); } } catch (error) { console.warn('Could not create Claude custom command:', error); } } /** * Create a Claude Code Skill (.claude/skills/task/SKILL.md) so newer Claude * Code versions can discover task management automatically. Created only when * a .claude directory already exists, mirroring the slash command behavior. */ async createClaudeSkill() { const claudeDir = path.join(this.workingDir, '.claude'); const skillDir = path.join(claudeDir, 'skills', 'task'); try { if (await fs.pathExists(claudeDir)) { await fs.ensureDir(skillDir); const skillPath = path.join(skillDir, 'SKILL.md'); await fs.writeFile(skillPath, this.generateSkillContent()); console.log(this.i18n.t('commands.init.skill')); } } catch (error) { console.warn('Could not create Claude skill:', error); } } /** * Register the claude-task MCP server in the project's .mcp.json so Claude * Code can use schema-bound task tools. Created only when a .claude * directory already exists, mirroring the command/skill behavior. An * existing "claude-task" entry is left untouched so user edits survive * re-running init. */ async createMcpConfig() { const claudeDir = path.join(this.workingDir, '.claude'); const mcpConfigPath = path.join(this.workingDir, '.mcp.json'); try { if (!(await fs.pathExists(claudeDir))) { return; } let config = {}; if (await fs.pathExists(mcpConfigPath)) { config = await fs.readJson(mcpConfigPath); } if (config.mcpServers && config.mcpServers['claude-task']) { return; } config.mcpServers = { ...config.mcpServers, 'claude-task': { command: 'claude-task-mcp', args: [] } }; await fs.writeJson(mcpConfigPath, config, { spaces: 2 }); console.log(this.i18n.t('commands.init.mcpConfig')); } catch (error) { console.warn('Could not update .mcp.json:', error); } } /** * Wire claude-task into Claude Code's ambient integration points by merging * a `statusLine` entry and a `SessionStart` hook into * `.claude/settings.json`. Only invoked behind an explicit flag * (`claude-task init --hooks`); the user asked for it, so the .claude * directory is created if missing. Existing user settings are preserved: * a present `statusLine` is left untouched, and the hook is only appended * when no claude-task SessionStart hook exists yet. */ async createHooksConfig() { const claudeDir = path.join(this.workingDir, '.claude'); const settingsPath = path.join(claudeDir, 'settings.json'); try { await fs.ensureDir(claudeDir); let settings = {}; if (await fs.pathExists(settingsPath)) { settings = await fs.readJson(settingsPath); } if (!settings.statusLine) { settings.statusLine = { ...exports.STATUS_LINE_ENTRY }; } settings.hooks = settings.hooks || {}; const sessionStart = settings.hooks['SessionStart'] || []; const alreadyHooked = sessionStart.some((entry) => typeof entry.command === 'string' && entry.command.includes('claude-task')); if (!alreadyHooked) { sessionStart.push({ ...exports.SESSION_START_HOOK_ENTRY }); settings.hooks['SessionStart'] = sessionStart; } await fs.writeJson(settingsPath, settings, { spaces: 2 }); console.log(this.i18n.t('commands.init.hooksConfig')); } catch (error) { console.warn('Could not update .claude/settings.json:', error); } } generateSkillContent() { return `--- name: task description: Manage development tasks with the claude-task CLI. Use when the user wants to create, run, track, split, complete, or archive tasks, or asks about the current task / task progress. Tasks live in task.md. metadata: source: claude-task-manager --- # Task Management (claude-task) This skill drives the \`claude-task\` CLI, which manages a \`task.md\` file and an \`archive/\` history. Run the commands below with the Bash tool and report the output back to the user. ## When to use - The user wants to start, track, or finish a unit of work. - The user asks "what's the current task?", "how far along am I?", or similar. - The user mentions task.md, subtasks, or task history. ## Commands | Intent | Command | | --- | --- | | Create a task | \`claude-task new "<title>" [--priority high|medium|low] [--tags a,b]\` | | Create a task alongside the current one | \`claude-task new "<title>" --name <name>\` | | List all tasks | \`claude-task list\` | | Switch to another task | \`claude-task switch <name>\` (\`--create\` to start it) | | Show current task / counts | \`claude-task status\` | | Show subtask progress bar | \`claude-task progress\` | | Complete subtask(s) | \`claude-task done <n> [<n> ...]\` (use \`--undo\` to uncheck) | | Split a task into subtasks (AI) | \`claude-task split [-c <count>]\` | | Show history | \`claude-task history [--limit <n>]\` | | Archive current task | \`claude-task archive\` | ## Executing a task To actually do the work described in the current task, read \`@task.md\` and carry out the steps yourself. \`claude-task run\` exists, but inside a Claude Code session prefer reading \`@task.md\` directly and editing the relevant files. As you finish each subtask, mark it complete with \`claude-task done <n>\`. ## task.md vs your in-session todo list \`task.md\` is the **persistent, cross-session source of truth**; your in-session todo list is an **ephemeral working mirror** of it. To keep them from conflicting: - When you start working on the current task, run \`claude-task progress\` and seed your todo list from the unchecked subtasks (keep their numbering). - When you finish a subtask, persist it first with \`claude-task done <n>\`, then check off the mirrored todo. Never update only the in-session list. - Add or reword subtasks by editing the checkboxes in \`task.md\` (or via \`claude-task split\`), not only in the in-session list. - If the two ever disagree, \`task.md\` wins — re-seed your todo list from \`claude-task progress\`. ## Notes - Quote titles that contain spaces. - Subtask numbers in \`done\` match the order shown by \`claude-task progress\`. - One task is active at a time (\`task.md\`), but several named tasks can coexist: \`new --name <name>\` starts one without archiving, \`switch <name>\` changes the active one (state is saved on both sides), \`list\` shows them all. - \`new\` without \`--name\` keeps the classic behavior: it archives the current task and replaces it, reusing the active name. `; } generateCustomCommandContent() { return `--- description: Manage tasks with the claude-task CLI argument-hint: <new|list|switch|status|progress|done|split|history|archive|run> [options] allowed-tools: Bash(claude-task:*), Read, Edit --- # /task — Claude Task Manager The user ran: \`/task $ARGUMENTS\` ## Actions - \`/task new "<title>" [--priority high|medium|low] [--tags a,b]\` — create a task (archives the current one; add \`--name <name>\` to create it alongside instead) - \`/task list\` — list all tasks with their progress - \`/task switch <name>\` — make another task active (\`--create\` to start it) - \`/task status\` — show the current task and counts - \`/task progress\` — show the subtask progress bar - \`/task done <numbers...>\` — mark subtasks done (\`--undo\` to uncheck) - \`/task split [--count n]\` — break the task into subtasks (uses AI) - \`/task history [--limit n]\` — show task history - \`/task archive\` — archive the current task - \`/task run\` — execute the current task ## How to handle this invocation Pass arguments through **verbatim** — never rewrite, reorder, or drop flags. - **run** (or no argument): do NOT shell out to \`claude-task run\`. Instead read the current \`task.md\`, seed your in-session todo list from its unchecked checkboxes, and carry out the work yourself. As you finish each subtask, persist it with \`claude-task done <n>\` first, then check off the mirrored todo. \`task.md\` is the source of truth — if it and your todo list disagree, re-seed from \`claude-task progress\`. - **split**: run \`claude-task split $ARGUMENTS\`. It calls Claude in the background to generate subtasks, so it may take a moment — this is expected. - **everything else** (new / list / switch / status / progress / done / history / archive): run \`claude-task $ARGUMENTS\` with the Bash tool and report the output to the user. Quote titles that contain spaces. Subtask numbers for \`done\` match the order shown by \`/task progress\`.`; } } exports.CustomCommandGenerator = CustomCommandGenerator; //# sourceMappingURL=CustomCommandGenerator.js.map