takt/src/resources/index.ts
nrslib 5265cc0059 feat: Builtin管理をバンドル埋め込み方式に移行し、/ejectコマンドを追加 (#4)
- ローダーがユーザーファイル優先、なければdist/resources/からbuiltinを読む方式に変更
- /ejectコマンドを追加(builtinを~/.takt/にコピーしてカスタマイズ可能に)
- /refresh-builtinを簡素化(ejectへの移行案内)
- config.yamlにdisabled_builtinsフィールドを追加
- ワークフローYAMLをrules形式に統一
2026-01-30 20:03:38 +09:00

106 lines
3.3 KiB
TypeScript

/**
* Embedded resources for takt
*
* Contains default workflow definitions and resource paths.
* Resources are organized into:
* - resources/global/{lang}/workflows/ - Builtin workflows (loaded via fallback)
* - resources/global/{lang}/agents/ - Builtin agents (loaded via fallback)
* - resources/project/ - Project-level template files (.gitignore)
*/
import { readFileSync, readdirSync, existsSync, statSync, mkdirSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import type { Language } from '../models/types.js';
/**
* Get the resources directory path
* Supports both development (src/) and production (dist/) environments
*/
export function getResourcesDir(): string {
const currentDir = dirname(fileURLToPath(import.meta.url));
// From src/resources or dist/resources, go up to project root then into resources/
return join(currentDir, '..', '..', 'resources');
}
/**
* Get the global resources directory path (resources/global/)
*/
export function getGlobalResourcesDir(): string {
return join(getResourcesDir(), 'global');
}
/**
* Get the project resources directory path (resources/project/)
*/
export function getProjectResourcesDir(): string {
return join(getResourcesDir(), 'project');
}
/**
* Get the language-specific global resources directory path (resources/global/{lang}/)
*/
export function getLanguageResourcesDir(lang: Language): string {
return join(getGlobalResourcesDir(), lang);
}
/**
* Copy project resources directory to .takt in project.
* Only copies files that don't exist in target (e.g., .gitignore).
*/
export function copyProjectResourcesToDir(targetDir: string): void {
const resourcesDir = getProjectResourcesDir();
if (!existsSync(resourcesDir)) {
return;
}
copyDirRecursive(resourcesDir, targetDir, {
renameMap: { dotgitignore: '.gitignore' },
});
}
/** 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[];
/** Rename files during copy (source name → dest name) */
renameMap?: Record<string, string>;
}
/**
* Recursively copy directory contents.
* @param overwrite - If false (default), skips files that already exist in target.
* If true, overwrites existing files.
*/
function copyDirRecursive(srcDir: string, destDir: string, options: CopyOptions = {}): void {
const { skipDirs = [], overwrite = false, copiedFiles, renameMap } = options;
if (!existsSync(destDir)) {
mkdirSync(destDir, { recursive: true });
}
for (const entry of readdirSync(srcDir)) {
if (SKIP_FILES.includes(entry)) continue;
if (skipDirs.includes(entry)) continue;
const srcPath = join(srcDir, entry);
const destName = renameMap?.[entry] ?? entry;
const destPath = join(destDir, destName);
const stat = statSync(srcPath);
if (stat.isDirectory()) {
copyDirRecursive(srcPath, destPath, { overwrite, copiedFiles });
} else if (overwrite || !existsSync(destPath)) {
const content = readFileSync(srcPath);
writeFileSync(destPath, content);
copiedFiles?.push(destPath);
}
}
}