UNPKG

@waltcow/claude-code-spec-workflow

Version:

Automated spec-driven workflow for Claude Code. Transforms feature ideas into complete implementations through Requirements → Design → Tasks → Implementation.

158 lines 7.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SpecWorkflowSetup = void 0; const fs_1 = require("fs"); const path_1 = require("path"); const commands_1 = require("./commands"); const templates_1 = require("./templates"); const claude_md_1 = require("./claude-md"); const scripts_1 = require("./scripts"); class SpecWorkflowSetup { constructor(projectRoot = process.cwd()) { this.projectRoot = projectRoot; this.claudeDir = (0, path_1.join)(projectRoot, '.claude'); this.commandsDir = (0, path_1.join)(this.claudeDir, 'commands'); this.specsDir = (0, path_1.join)(this.claudeDir, 'specs'); this.templatesDir = (0, path_1.join)(this.claudeDir, 'templates'); this.scriptsDir = (0, path_1.join)(this.claudeDir, 'scripts'); } async claudeDirectoryExists() { try { await fs_1.promises.access(this.claudeDir); return true; } catch { return false; } } async setupDirectories() { const directories = [ this.claudeDir, this.commandsDir, this.specsDir, this.templatesDir, this.scriptsDir ]; for (const dir of directories) { await fs_1.promises.mkdir(dir, { recursive: true }); } } async createSlashCommands() { const commands = { 'spec-create': (0, commands_1.getSpecCreateCommand)(), 'spec-requirements': (0, commands_1.getSpecRequirementsCommand)(), 'spec-design': (0, commands_1.getSpecDesignCommand)(), 'spec-tasks': (0, commands_1.getSpecTasksCommand)(), 'spec-execute': (0, commands_1.getSpecExecuteCommand)(), 'spec-status': (0, commands_1.getSpecStatusCommand)(), 'spec-list': (0, commands_1.getSpecListCommand)() }; for (const [commandName, commandContent] of Object.entries(commands)) { const commandFile = (0, path_1.join)(this.commandsDir, `${commandName}.md`); await fs_1.promises.writeFile(commandFile, commandContent, 'utf-8'); } } async createTemplates() { const templates = { 'requirements-template.md': (0, templates_1.getRequirementsTemplate)(), 'design-template.md': (0, templates_1.getDesignTemplate)(), 'tasks-template.md': (0, templates_1.getTasksTemplate)() }; for (const [templateName, templateContent] of Object.entries(templates)) { const templateFile = (0, path_1.join)(this.templatesDir, templateName); await fs_1.promises.writeFile(templateFile, templateContent, 'utf-8'); } } async createScripts() { const scripts = { 'generate-commands.bat': (0, scripts_1.getWindowsCommandGenerationScript)(), 'generate-commands.sh': (0, scripts_1.getUnixCommandGenerationScript)(), 'generate-commands-launcher.sh': (0, scripts_1.getOSDetectionScript)(), 'README.md': (0, scripts_1.getCommandGenerationInstructions)() }; for (const [scriptName, scriptContent] of Object.entries(scripts)) { const scriptFile = (0, path_1.join)(this.scriptsDir, scriptName); await fs_1.promises.writeFile(scriptFile, scriptContent, 'utf-8'); // Make shell scripts executable on Unix-like systems if (scriptName.endsWith('.sh')) { try { await fs_1.promises.chmod(scriptFile, 0o755); } catch (error) { // Ignore chmod errors on Windows console.warn(`Warning: Could not set execute permissions for ${scriptName}`); } } } } async createConfigFile() { const config = { spec_workflow: { version: '1.0.0', auto_create_directories: true, auto_reference_requirements: true, enforce_approval_workflow: true, default_feature_prefix: 'feature-', supported_formats: ['markdown', 'mermaid'] } }; const configFile = (0, path_1.join)(this.claudeDir, 'spec-config.json'); await fs_1.promises.writeFile(configFile, JSON.stringify(config, null, 2), 'utf-8'); } async createClaudeMd() { const claudeMdContent = (0, claude_md_1.getClaudeMdContent)(); const claudeMdFile = (0, path_1.join)(this.projectRoot, 'CLAUDE.md'); // Check if CLAUDE.md exists try { const existingContent = await fs_1.promises.readFile(claudeMdFile, 'utf-8'); if (!existingContent.includes('# Spec Workflow')) { // Append to existing file - preserve all existing content const separator = existingContent.trim().length > 0 ? '\n\n---\n\n' : ''; const updatedContent = existingContent.trim() + separator + claudeMdContent; await fs_1.promises.writeFile(claudeMdFile, updatedContent, 'utf-8'); } else { // Replace existing spec workflow section while preserving everything else const lines = existingContent.split('\n'); const startIndex = lines.findIndex(line => line.trim() === '# Spec Workflow'); if (startIndex !== -1) { // Find the end of the spec workflow section (next # header or end of file) let endIndex = lines.length; for (let i = startIndex + 1; i < lines.length; i++) { if (lines[i].startsWith('# ') && !lines[i].includes('Spec Workflow')) { endIndex = i; break; } } // Preserve content before and after the spec workflow section const beforeSection = lines.slice(0, startIndex).join('\n').trim(); const afterSection = lines.slice(endIndex).join('\n').trim(); // Reconstruct the file with preserved content let updatedContent = ''; if (beforeSection) { updatedContent += beforeSection + '\n\n'; } updatedContent += claudeMdContent; if (afterSection) { updatedContent += '\n\n' + afterSection; } await fs_1.promises.writeFile(claudeMdFile, updatedContent, 'utf-8'); } } } catch { // File doesn't exist, create it await fs_1.promises.writeFile(claudeMdFile, claudeMdContent, 'utf-8'); } } async runSetup() { await this.setupDirectories(); await this.createSlashCommands(); await this.createTemplates(); await this.createScripts(); await this.createConfigFile(); await this.createClaudeMd(); } } exports.SpecWorkflowSetup = SpecWorkflowSetup; //# sourceMappingURL=setup.js.map