import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { homedir } from 'node:os'; import pc from 'picocolors'; export function resolvePath(raw: string): string { if (raw.startsWith('~/') || raw === '~') { return resolve(homedir(), raw.slice(2)); } return resolve(raw); } export function ensureWorkspace(rawPath: string): string { const path = resolvePath(rawPath); if (!existsSync(path)) { mkdirSync(path, { recursive: true }); } return path; } export function syncTemplates(workspacePath: string): string[] { // Get project root relative to this file const currentFile = fileURLToPath(import.meta.url); const srcDir = dirname(currentFile); const projectRoot = resolve(srcDir, '..'); const templatesDir = resolve(projectRoot, 'templates'); if (!existsSync(templatesDir)) { throw new Error(`Templates directory not found at ${templatesDir}`); } const created: string[] = []; function copyTemplate(src: string, dest: string) { if (existsSync(dest)) return; mkdirSync(dirname(dest), { recursive: true }); const content = readFileSync(src, 'utf8'); writeFileSync(dest, content, 'utf8'); created.push(dest.slice(workspacePath.length + 1)); } function copyDir(srcDir: string, destDir: string) { if (!existsSync(srcDir)) return; const entries = readdirSync(srcDir, { withFileTypes: true }); for (const entry of entries) { const srcPath = join(srcDir, entry.name); const destPath = join(destDir, entry.name); if (entry.isDirectory()) { copyDir(srcPath, destPath); } else if (entry.name.endsWith('.md')) { copyTemplate(srcPath, destPath); } } } copyDir(templatesDir, workspacePath); // Create empty HISTORY.md const historyPath = join(workspacePath, 'memory', 'HISTORY.md'); if (!existsSync(historyPath)) { mkdirSync(dirname(historyPath), { recursive: true }); writeFileSync(historyPath, '# Conversation History\n\n', 'utf8'); created.push('memory/HISTORY.md'); } // Create skills directory const skillsPath = join(workspacePath, 'skills'); if (!existsSync(skillsPath)) { mkdirSync(skillsPath, { recursive: true }); } return created; } export function checkWorkspaceEmpty(path: string): void { if (!existsSync(path)) return; const entries = readdirSync(path); if (entries.length > 0) { throw new Error(pc.red(`Directory not empty: ${path}`)); } }