add /refresh-builtin

This commit is contained in:
nrslib 2026-01-27 22:42:04 +09:00
parent 4671af954a
commit 7270b29044
5 changed files with 106 additions and 17 deletions

View File

@ -30,6 +30,7 @@ import {
switchWorkflow,
switchConfig,
addTask,
refreshBuiltin,
} from './commands/index.js';
import { listWorkflows } from './config/workflowLoader.js';
import { selectOptionWithDefault } from './prompt/index.js';
@ -109,9 +110,13 @@ program
await addTask(cwd, args);
return;
case 'refresh-builtin':
await refreshBuiltin();
return;
default:
error(`Unknown command: /${command}`);
info('Available: /run-tasks, /add-task, /switch, /clear, /help, /config');
info('Available: /run-tasks, /add-task, /switch, /clear, /refresh-builtin, /help, /config');
process.exit(1);
}
}

View File

@ -13,18 +13,20 @@ export function showHelp(): void {
console.log(`
Usage:
takt {task} Execute task with current workflow (continues session)
takt /run-tasks Run all pending tasks from .takt/tasks/
takt /add-task Add a new task (interactive, YAML format)
takt /switch Switch workflow interactively
takt /clear Clear agent conversation sessions (reset to initial state)
takt /help Show this help
takt {task} Execute task with current workflow (continues session)
takt /run-tasks Run all pending tasks from .takt/tasks/
takt /add-task Add a new task (interactive, YAML format)
takt /switch Switch workflow interactively
takt /clear Clear agent conversation sessions (reset to initial state)
takt /refresh-builtin Overwrite builtin agents/workflows with latest version
takt /help Show this help
Examples:
takt "Fix the bug in main.ts" # Execute task (continues session)
takt /add-task "認証機能を追加する" # Quick add task
takt /add-task # Interactive task creation
takt /clear # Clear sessions, start fresh
takt /refresh-builtin # Update builtin resources
takt /switch
takt /run-tasks

View File

@ -5,6 +5,7 @@
export { executeWorkflow, type WorkflowExecutionResult, type WorkflowExecutionOptions } from './workflowExecution.js';
export { executeTask, runAllTasks } from './taskExecution.js';
export { addTask } from './addTask.js';
export { refreshBuiltin } from './refreshBuiltin.js';
export { showHelp } from './help.js';
export { withAgentSession } from './session.js';
export { switchWorkflow } from './workflow.js';

View File

@ -0,0 +1,43 @@
/**
* /refresh-builtin command implementation
*
* Overwrites builtin workflow and agent files in ~/.takt/ with the latest
* embedded resources. Does NOT touch config.yaml or user-added files.
*/
import { getGlobalConfigDir } from '../config/paths.js';
import { getLanguage } from '../config/globalConfig.js';
import { forceRefreshLanguageResources } from '../resources/index.js';
import { header, success, info, error } from '../utils/ui.js';
import { createLogger } from '../utils/debug.js';
const log = createLogger('refresh-builtin');
/**
* Refresh builtin agents and workflows to latest version.
*/
export async function refreshBuiltin(): Promise<void> {
const globalDir = getGlobalConfigDir();
const lang = getLanguage();
header('Refresh Builtin Resources');
info(`Language: ${lang}`);
info(`Target: ${globalDir}`);
try {
const overwritten = forceRefreshLanguageResources(globalDir, lang);
log.info('Builtin resources refreshed', { count: overwritten.length, lang });
console.log();
success(`${overwritten.length} files refreshed.`);
for (const filePath of overwritten) {
info(`${filePath}`);
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
log.error('Failed to refresh builtin resources', { error: message });
error(`Failed to refresh: ${message}`);
}
}

View File

@ -55,7 +55,7 @@ export function copyGlobalResourcesToDir(targetDir: string): void {
return;
}
// Skip language directories (they are handled by copyLanguageResourcesToDir)
copyDirRecursive(resourcesDir, targetDir, ['en', 'ja']);
copyDirRecursive(resourcesDir, targetDir, { skipDirs: ['en', 'ja'] });
}
/**
@ -106,24 +106,62 @@ export function copyLanguageResourcesToDir(targetDir: string, lang: Language): v
}
}
/**
* Force-refresh language-specific resources (agents and workflows) to ~/.takt.
* Overwrites existing builtin files. Does NOT touch config.yaml.
*/
export function forceRefreshLanguageResources(targetDir: string, lang: Language): string[] {
const langDir = getLanguageResourcesDir(lang);
if (!existsSync(langDir)) {
throw new Error(`Language resources not found: ${langDir}`);
}
const copiedFiles: string[] = [];
const forceOptions = { overwrite: true, copiedFiles };
// Overwrite agents directory
const langAgentsDir = join(langDir, 'agents');
const targetAgentsDir = join(targetDir, 'agents');
if (existsSync(langAgentsDir)) {
copyDirRecursive(langAgentsDir, targetAgentsDir, forceOptions);
}
// Overwrite workflows directory
const langWorkflowsDir = join(langDir, 'workflows');
const targetWorkflowsDir = join(targetDir, 'workflows');
if (existsSync(langWorkflowsDir)) {
copyDirRecursive(langWorkflowsDir, targetWorkflowsDir, forceOptions);
}
return copiedFiles;
}
/** Files to skip during resource copy (OS-generated files) */
const SKIP_FILES = ['.DS_Store', 'Thumbs.db'];
interface CopyOptions {
/** Directory names to skip at the top level */
skipDirs?: string[];
/** Overwrite existing files (default: false) */
overwrite?: boolean;
/** Collect copied file paths into this array */
copiedFiles?: string[];
}
/**
* Recursively copy directory contents.
* Skips files that already exist in target.
* @param skipDirs - Directory names to skip at top level
* @param overwrite - If false (default), skips files that already exist in target.
* If true, overwrites existing files.
*/
function copyDirRecursive(srcDir: string, destDir: string, skipDirs: string[] = []): void {
function copyDirRecursive(srcDir: string, destDir: string, options: CopyOptions = {}): void {
const { skipDirs = [], overwrite = false, copiedFiles } = options;
if (!existsSync(destDir)) {
mkdirSync(destDir, { recursive: true });
}
for (const entry of readdirSync(srcDir)) {
// Skip OS-generated files
if (SKIP_FILES.includes(entry)) continue;
// Skip specified directories
if (skipDirs.includes(entry)) continue;
const srcPath = join(srcDir, entry);
@ -131,11 +169,11 @@ function copyDirRecursive(srcDir: string, destDir: string, skipDirs: string[] =
const stat = statSync(srcPath);
if (stat.isDirectory()) {
copyDirRecursive(srcPath, destPath);
} else if (!existsSync(destPath)) {
// Only copy if file doesn't exist
copyDirRecursive(srcPath, destPath, { overwrite, copiedFiles });
} else if (overwrite || !existsSync(destPath)) {
const content = readFileSync(srcPath);
writeFileSync(destPath, content);
copiedFiles?.push(destPath);
}
}
}