#!/usr/bin/env node /** * TAKT CLI - Task Agent Koordination Tool * * Usage: * takt {task} - Execute task with current workflow (continues session) * takt /run-tasks - Run all pending tasks from .takt/tasks/ * takt /switch - Switch workflow interactively * takt /clear - Clear agent conversation sessions (reset to initial state) * takt /help - Show help * takt /config - Select permission mode interactively */ import { Command } from 'commander'; import { resolve } from 'node:path'; import { initGlobalDirs, initProjectDirs, loadGlobalConfig, getEffectiveDebugConfig, } from './config/index.js'; import { clearAgentSessions, getCurrentWorkflow, isVerboseMode } from './config/paths.js'; import { info, error, success, setLogLevel } from './utils/ui.js'; import { initDebugLogger, createLogger, setVerboseConsole } from './utils/debug.js'; import { executeTask, runAllTasks, showHelp, switchWorkflow, switchConfig, addTask, refreshBuiltin, watchTasks, reviewTasks, } from './commands/index.js'; import { listWorkflows } from './config/workflowLoader.js'; import { selectOptionWithDefault, confirm } from './prompt/index.js'; import { createSharedClone, removeClone } from './task/worktree.js'; import { autoCommitAndPush } from './task/autoCommit.js'; import { summarizeTaskName } from './task/summarize.js'; import { DEFAULT_WORKFLOW_NAME } from './constants.js'; const log = createLogger('cli'); export interface WorktreeConfirmationResult { execCwd: string; isWorktree: boolean; } /** * Ask user whether to create a shared clone, and create one if confirmed. * Returns the execution directory and whether a clone was created. * Task name is summarized to English by AI for use in branch/clone names. */ export async function confirmAndCreateWorktree( cwd: string, task: string, ): Promise { const useWorktree = await confirm('Create worktree?', false); if (!useWorktree) { return { execCwd: cwd, isWorktree: false }; } // Summarize task name to English slug using AI info('Generating branch name...'); const taskSlug = await summarizeTaskName(task, { cwd }); const result = createSharedClone(cwd, { worktree: true, taskSlug, }); info(`Clone created: ${result.path} (branch: ${result.branch})`); return { execCwd: result.path, isWorktree: true }; } const program = new Command(); program .name('takt') .description('TAKT: Task Agent Koordination Tool') .version('0.1.0'); program .argument('[task]', 'Task to execute or slash command') .action(async (task) => { const cwd = resolve(process.cwd()); // Initialize global directories first await initGlobalDirs(); // Initialize project directories (.takt/) initProjectDirs(cwd); // Determine verbose mode and initialize logging const verbose = isVerboseMode(cwd); let debugConfig = getEffectiveDebugConfig(cwd); // verbose=true enables file logging automatically if (verbose && (!debugConfig || !debugConfig.enabled)) { debugConfig = { enabled: true }; } initDebugLogger(debugConfig, cwd); // Enable verbose console output (stderr) for debug logs if (verbose) { setVerboseConsole(true); setLogLevel('debug'); } else { const config = loadGlobalConfig(); setLogLevel(config.logLevel); } log.info('TAKT CLI starting', { version: '0.1.0', cwd, task: task || null, verbose, }); // Handle slash commands if (task?.startsWith('/')) { const parts = task.slice(1).split(/\s+/); const command = parts[0]?.toLowerCase() || ''; const args = parts.slice(1); switch (command) { case 'run-tasks': case 'run': { const workflow = getCurrentWorkflow(cwd); await runAllTasks(cwd, workflow); return; } case 'clear': clearAgentSessions(cwd); success('Agent sessions cleared'); return; case 'switch': case 'sw': await switchWorkflow(cwd, args[0]); return; case 'help': showHelp(); return; case 'config': await switchConfig(cwd, args[0]); return; case 'add-task': case 'add': await addTask(cwd, args); return; case 'refresh-builtin': await refreshBuiltin(); return; case 'watch': await watchTasks(cwd); return; case 'review-tasks': case 'review': await reviewTasks(cwd); return; default: error(`Unknown command: /${command}`); info('Available: /run-tasks (/run), /watch, /add-task (/add), /review-tasks (/review), /switch (/sw), /clear, /refresh-builtin, /help, /config'); process.exit(1); } } // Task execution if (task) { // Get available workflows and prompt user to select const availableWorkflows = listWorkflows(); const currentWorkflow = getCurrentWorkflow(cwd); let selectedWorkflow: string; if (availableWorkflows.length === 0) { // No workflows available, use default selectedWorkflow = DEFAULT_WORKFLOW_NAME; info(`No workflows found. Using default: ${selectedWorkflow}`); } else if (availableWorkflows.length === 1 && availableWorkflows[0]) { // Only one workflow, use it directly selectedWorkflow = availableWorkflows[0]; } else { // Multiple workflows, prompt user to select const options = availableWorkflows.map((name) => ({ label: name === currentWorkflow ? `${name} (current)` : name, value: name, })); // Use current workflow as default, fallback to DEFAULT_WORKFLOW_NAME const defaultWorkflow = availableWorkflows.includes(currentWorkflow) ? currentWorkflow : (availableWorkflows.includes(DEFAULT_WORKFLOW_NAME) ? DEFAULT_WORKFLOW_NAME : availableWorkflows[0] || DEFAULT_WORKFLOW_NAME); const selected = await selectOptionWithDefault( 'Select workflow:', options, defaultWorkflow ); if (selected === null) { info('Cancelled'); return; } selectedWorkflow = selected; } // Ask whether to create a worktree const { execCwd, isWorktree } = await confirmAndCreateWorktree(cwd, task); log.info('Starting task execution', { task, workflow: selectedWorkflow, worktree: isWorktree }); const taskSuccess = await executeTask(task, execCwd, selectedWorkflow, cwd); if (taskSuccess && isWorktree) { const commitResult = autoCommitAndPush(execCwd, task, cwd); if (commitResult.success && commitResult.commitHash) { success(`Auto-committed & pushed: ${commitResult.commitHash}`); } else if (!commitResult.success) { error(`Auto-commit failed: ${commitResult.message}`); } } // Remove clone after task completion (success or failure) if (isWorktree) { removeClone(execCwd); } if (!taskSuccess) { process.exit(1); } return; } // No task provided - show help showHelp(); }); program.parse();