workflow-agent-cli 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -359,7 +359,10 @@ var WORKFLOW_SCRIPTS = {
359
359
  "workflow:generate-instructions": "workflow-agent generate-instructions",
360
360
  // Template Management
361
361
  "workflow:update-templates": "workflow-agent update-templates",
362
- "workflow:update-templates:force": "workflow-agent update-templates --force"
362
+ "workflow:update-templates:force": "workflow-agent update-templates --force",
363
+ // Document Validation
364
+ "workflow:docs:validate": "workflow-agent docs:validate",
365
+ "workflow:docs:validate:fix": "workflow-agent docs:validate --fix"
363
366
  };
364
367
  var SCRIPT_CATEGORIES = {
365
368
  "Core Commands": [
@@ -409,6 +412,10 @@ var SCRIPT_CATEGORIES = {
409
412
  "Template Management": [
410
413
  "workflow:update-templates",
411
414
  "workflow:update-templates:force"
415
+ ],
416
+ "Document Validation": [
417
+ "workflow:docs:validate",
418
+ "workflow:docs:validate:fix"
412
419
  ]
413
420
  };
414
421
  var TOTAL_SCRIPTS = Object.keys(WORKFLOW_SCRIPTS).length;
@@ -692,4 +699,4 @@ export {
692
699
  installMandatoryTemplates,
693
700
  updateTemplates
694
701
  };
695
- //# sourceMappingURL=chunk-2WUPMAH3.js.map
702
+ //# sourceMappingURL=chunk-Y5N3JJTU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scripts/copilot-instructions-generator.ts","../src/scripts/workflow-scripts.ts","../src/scripts/template-installer.ts","../src/templates/metadata.ts"],"sourcesContent":["/**\n * Copilot Instructions Generator\n *\n * Generates .github/copilot-instructions.md from the project's guidelines directory.\n * This file serves as the Single Source of Truth for AI agents (GitHub Copilot, Claude, etc.)\n * when working on the codebase.\n *\n * Features:\n * - Reads all markdown files from guidelines/\n * - Extracts key rules and summaries from each guideline\n * - Loads project config from workflow.config.json\n * - Preserves custom user content between markers\n * - Provides links to full guideline documents\n */\n\nimport { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from \"fs\";\nimport { join, basename } from \"path\";\n\n// Markers for custom user content that should be preserved on regeneration\nconst CUSTOM_START_MARKER = \"<!-- CUSTOM START -->\";\nconst CUSTOM_END_MARKER = \"<!-- CUSTOM END -->\";\nconst GENERATED_MARKER = \"<!-- AUTO-GENERATED BY WORKFLOW-AGENT - DO NOT EDIT ABOVE THIS LINE -->\";\n\ninterface WorkflowConfig {\n projectName?: string;\n scopes?: Array<{ name: string; description: string; emoji?: string }>;\n enforcement?: string;\n language?: string;\n}\n\ninterface GuidelineSummary {\n filename: string;\n title: string;\n description: string;\n keyRules: string[];\n}\n\n/**\n * Extract title from markdown content (first H1)\n */\nfunction extractTitle(content: string): string {\n const match = content.match(/^#\\s+(.+)$/m);\n return match ? match[1].trim() : \"Untitled\";\n}\n\n/**\n * Extract description from markdown content (first paragraph after title)\n */\nfunction extractDescription(content: string): string {\n // Look for content after the first heading, before the next heading or section\n const lines = content.split(\"\\n\");\n let foundTitle = false;\n let description = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"# \")) {\n foundTitle = true;\n continue;\n }\n if (foundTitle) {\n // Skip empty lines and blockquotes at start\n if (line.trim() === \"\" || line.startsWith(\">\")) {\n if (description) break; // End if we already have content\n continue;\n }\n // Stop at next heading or horizontal rule\n if (line.startsWith(\"#\") || line.startsWith(\"---\") || line.startsWith(\"##\")) {\n break;\n }\n description += line.trim() + \" \";\n // Take only first meaningful paragraph\n if (description.length > 150) break;\n }\n }\n\n return description.trim().slice(0, 200) + (description.length > 200 ? \"...\" : \"\");\n}\n\n/**\n * Extract key rules from markdown content\n * Looks for lists, bold text, and important patterns\n */\nfunction extractKeyRules(content: string, maxRules: number = 5): string[] {\n const rules: string[] = [];\n\n // Pattern 1: Look for \"MUST\", \"NEVER\", \"ALWAYS\", \"REQUIRED\" in bold or emphasized\n const emphasisPatterns = [\n /\\*\\*(?:MUST|NEVER|ALWAYS|REQUIRED)[^*]+\\*\\*/gi,\n /(?:^|\\n)\\s*[-*]\\s+\\*\\*[^*]+\\*\\*/gm,\n ];\n\n for (const pattern of emphasisPatterns) {\n const matches = content.match(pattern);\n if (matches) {\n for (const match of matches.slice(0, 2)) {\n const cleaned = match.replace(/\\*\\*/g, \"\").replace(/^[-*]\\s*/, \"\").trim();\n if (cleaned.length > 10 && cleaned.length < 150 && !rules.includes(cleaned)) {\n rules.push(cleaned);\n }\n }\n }\n }\n\n // Pattern 2: Look for numbered or bulleted rules under headings containing \"Rules\", \"Requirements\", \"Guidelines\"\n const rulesSectionMatch = content.match(/##\\s+(?:.*(?:Rules?|Requirements?|Guidelines?|Standards?)[^\\n]*)\\n([\\s\\S]*?)(?=\\n##|\\n#|$)/i);\n if (rulesSectionMatch) {\n const section = rulesSectionMatch[1];\n const listItems = section.match(/^\\s*[-*\\d.]+\\s+(.+)$/gm);\n if (listItems) {\n for (const item of listItems.slice(0, 3)) {\n const cleaned = item.replace(/^[-*\\d.]+\\s*/, \"\").trim();\n if (cleaned.length > 10 && cleaned.length < 150 && !rules.includes(cleaned)) {\n rules.push(cleaned);\n }\n }\n }\n }\n\n // Pattern 3: Look for key points under \"Important\", \"Critical\", \"Key\"\n const importantMatch = content.match(/(?:Important|Critical|Key|Essential)[:\\s]+([^\\n]+)/gi);\n if (importantMatch) {\n for (const match of importantMatch.slice(0, 2)) {\n const cleaned = match.replace(/^(?:Important|Critical|Key|Essential)[:\\s]+/i, \"\").trim();\n if (cleaned.length > 10 && cleaned.length < 150 && !rules.includes(cleaned)) {\n rules.push(cleaned);\n }\n }\n }\n\n // Fallback: Get first few list items if we don't have enough rules\n if (rules.length < 2) {\n const listItems = content.match(/^\\s*[-*]\\s+(.+)$/gm);\n if (listItems) {\n for (const item of listItems.slice(0, 3)) {\n const cleaned = item.replace(/^[-*]\\s*/, \"\").trim();\n if (cleaned.length > 15 && cleaned.length < 150 && !rules.includes(cleaned)) {\n rules.push(cleaned);\n }\n }\n }\n }\n\n return rules.slice(0, maxRules);\n}\n\n/**\n * Parse a guideline markdown file and extract summary\n */\nfunction parseGuideline(filePath: string): GuidelineSummary | null {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const filename = basename(filePath);\n\n // Skip template example and non-guideline files\n if (filename.startsWith(\"_\") || filename === \"Guidelines.md\") {\n return null;\n }\n\n return {\n filename,\n title: extractTitle(content),\n description: extractDescription(content),\n keyRules: extractKeyRules(content),\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Load workflow config from project root\n */\nfunction loadWorkflowConfig(projectRoot: string): WorkflowConfig | null {\n const configPath = join(projectRoot, \"workflow.config.json\");\n if (!existsSync(configPath)) {\n return null;\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n return JSON.parse(content) as WorkflowConfig;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract preserved custom content from existing file\n */\nfunction extractCustomContent(existingContent: string): string | null {\n const startIndex = existingContent.indexOf(CUSTOM_START_MARKER);\n const endIndex = existingContent.indexOf(CUSTOM_END_MARKER);\n\n if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {\n return existingContent.slice(\n startIndex + CUSTOM_START_MARKER.length,\n endIndex\n ).trim();\n }\n\n return null;\n}\n\n/**\n * Generate the copilot-instructions.md content\n */\nfunction generateInstructionsContent(\n config: WorkflowConfig | null,\n guidelines: GuidelineSummary[],\n customContent: string | null\n): string {\n const projectName = config?.projectName || \"this project\";\n const scopes = config?.scopes || [];\n\n let content = `# Copilot Instructions for ${projectName}\n\n> **This file is the Single Source of Truth for AI agents working on this codebase.**\n> It is auto-generated from the \\`guidelines/\\` directory by workflow-agent-cli.\n> Last generated: ${new Date().toISOString().split(\"T\")[0]}\n\n${GENERATED_MARKER}\n\n## Project Overview\n\n`;\n\n if (config) {\n content += `- **Project Name**: ${projectName}\\n`;\n content += `- **Enforcement Level**: ${config.enforcement || \"strict\"}\\n`;\n if (scopes.length > 0) {\n content += `- **Available Scopes**: ${scopes.map(s => `\\`${s.name}\\``).join(\", \")}\\n`;\n }\n content += \"\\n\";\n }\n\n // Add scope reference if available\n if (scopes.length > 0) {\n content += `### Valid Scopes for Commits and Branches\n\n| Scope | Description |\n|-------|-------------|\n`;\n for (const scope of scopes.slice(0, 15)) {\n content += `| \\`${scope.name}\\` | ${scope.description} |\\n`;\n }\n if (scopes.length > 15) {\n content += `| ... | See workflow.config.json for all ${scopes.length} scopes |\\n`;\n }\n content += \"\\n\";\n }\n\n // Add guidelines summaries\n if (guidelines.length > 0) {\n content += `## Guidelines Summary\n\nThe following guidelines govern development on this project. **Read the linked documents for full details.**\n\n`;\n\n // Group by importance (mandatory templates first)\n const mandatoryFiles = [\n \"AGENT_EDITING_INSTRUCTIONS.md\",\n \"BRANCHING_STRATEGY.md\",\n \"TESTING_STRATEGY.md\",\n \"SINGLE_SOURCE_OF_TRUTH.md\",\n \"PATTERN_ANALYSIS_WORKFLOW.md\",\n \"SELF_IMPROVEMENT_MANDATE.md\",\n ];\n\n const sortedGuidelines = [...guidelines].sort((a, b) => {\n const aIndex = mandatoryFiles.indexOf(a.filename);\n const bIndex = mandatoryFiles.indexOf(b.filename);\n if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex;\n if (aIndex !== -1) return -1;\n if (bIndex !== -1) return 1;\n return a.title.localeCompare(b.title);\n });\n\n for (const guideline of sortedGuidelines) {\n content += `### ${guideline.title}\n\n📄 [See full details](../guidelines/${guideline.filename})\n\n${guideline.description}\n\n`;\n if (guideline.keyRules.length > 0) {\n content += `**Key Rules:**\n`;\n for (const rule of guideline.keyRules) {\n content += `- ${rule}\\n`;\n }\n content += \"\\n\";\n }\n }\n }\n\n // Add quick reference section\n content += `## Quick Reference\n\n### Branch Naming Convention\n\\`\\`\\`\n<type>/<scope>/<short-description>\n\\`\\`\\`\n\n**Types**: \\`feature\\`, \\`fix\\`, \\`chore\\`, \\`docs\\`, \\`refactor\\`, \\`test\\`, \\`perf\\`\n\n### Commit Message Format\n\\`\\`\\`\n<type>(<scope>): <description>\n\n[optional body]\n\n[optional footer]\n\\`\\`\\`\n\n### Before Making Changes\n1. Read the relevant guideline document in \\`guidelines/\\`\n2. Check for existing patterns in \\`workflow:solution:search\\`\n3. Create an implementation plan for multi-file changes\n4. Ensure tests are added for new functionality\n\n### Before Committing\n1. Run \\`pnpm run workflow:verify\\` to validate all changes\n2. Ensure branch name follows convention\n3. Ensure commit message follows conventional commits format\n\n`;\n\n // Add custom content section\n content += `## Project-Specific Instructions\n\n${CUSTOM_START_MARKER}\n${customContent || `\n<!-- \nAdd your project-specific instructions here.\nThis section will be preserved when the file is regenerated.\n\nExamples:\n- Specific coding patterns unique to this project\n- Custom review requirements\n- Domain-specific terminology\n- Team-specific workflows\n-->\n`}\n${CUSTOM_END_MARKER}\n\n---\n\n*This file was generated by [workflow-agent-cli](https://www.npmjs.com/package/workflow-agent-cli). Run \\`pnpm run workflow:generate-instructions\\` to regenerate.*\n`;\n\n return content;\n}\n\n/**\n * Result of generating copilot instructions\n */\nexport interface GenerateResult {\n success: boolean;\n filePath: string | null;\n guidelinesCount: number;\n isNew: boolean;\n preservedCustomContent: boolean;\n error?: string;\n}\n\n/**\n * Generate .github/copilot-instructions.md from guidelines directory\n *\n * @param projectRoot - Root directory of the project\n * @param options - Generation options\n * @returns Result of the generation\n */\nexport function generateCopilotInstructions(\n projectRoot: string,\n options: { force?: boolean; silent?: boolean } = {}\n): GenerateResult {\n const { force = false, silent = false } = options;\n\n const guidelinesDir = join(projectRoot, \"guidelines\");\n const githubDir = join(projectRoot, \".github\");\n const outputPath = join(githubDir, \"copilot-instructions.md\");\n\n // Check if guidelines directory exists\n if (!existsSync(guidelinesDir)) {\n if (!silent) {\n // Guidelines don't exist yet - skip silently during postinstall\n }\n return {\n success: false,\n filePath: null,\n guidelinesCount: 0,\n isNew: false,\n preservedCustomContent: false,\n error: \"No guidelines directory found. Run 'workflow init' first.\",\n };\n }\n\n // Read all markdown files from guidelines\n const files = readdirSync(guidelinesDir).filter(f => f.endsWith(\".md\"));\n if (files.length === 0) {\n return {\n success: false,\n filePath: null,\n guidelinesCount: 0,\n isNew: false,\n preservedCustomContent: false,\n error: \"No markdown files found in guidelines directory.\",\n };\n }\n\n // Parse each guideline\n const guidelines: GuidelineSummary[] = [];\n for (const file of files) {\n const summary = parseGuideline(join(guidelinesDir, file));\n if (summary) {\n guidelines.push(summary);\n }\n }\n\n // Load workflow config\n const config = loadWorkflowConfig(projectRoot);\n\n // Check for existing file and extract custom content\n let customContent: string | null = null;\n let isNew = true;\n\n if (existsSync(outputPath)) {\n isNew = false;\n const existingContent = readFileSync(outputPath, \"utf-8\");\n customContent = extractCustomContent(existingContent);\n }\n\n // Generate the content\n const content = generateInstructionsContent(config, guidelines, customContent);\n\n // Ensure .github directory exists\n if (!existsSync(githubDir)) {\n mkdirSync(githubDir, { recursive: true });\n }\n\n // Write the file\n writeFileSync(outputPath, content, \"utf-8\");\n\n return {\n success: true,\n filePath: outputPath,\n guidelinesCount: guidelines.length,\n isNew,\n preservedCustomContent: customContent !== null,\n };\n}\n\n/**\n * Check if copilot instructions need regeneration\n * (e.g., guidelines have been modified since last generation)\n */\nexport function needsRegeneration(projectRoot: string): boolean {\n const guidelinesDir = join(projectRoot, \"guidelines\");\n const outputPath = join(projectRoot, \".github\", \"copilot-instructions.md\");\n\n if (!existsSync(outputPath)) {\n return existsSync(guidelinesDir);\n }\n\n // For now, always regenerate to ensure latest content\n // Future: could compare file modification times\n return true;\n}\n","/**\n * Shared workflow scripts definition\n * Used by postinstall.ts and setup.ts to ensure consistency\n */\n\nexport const WORKFLOW_SCRIPTS = {\n // Core Commands\n \"workflow:init\": \"workflow-agent init\",\n \"workflow:validate\": \"workflow-agent validate\",\n \"workflow:config\": \"workflow-agent config\",\n \"workflow:suggest\": \"workflow-agent suggest\",\n \"workflow:setup\": \"workflow-agent setup\",\n \"workflow:doctor\": \"workflow-agent doctor\",\n\n // Scope Commands\n \"workflow:scope:create\": \"workflow-agent scope:create\",\n \"workflow:scope:migrate\": \"workflow-agent scope:migrate\",\n\n // Verification & Auto-Setup\n \"workflow:verify\": \"workflow-agent verify\",\n \"workflow:verify:fix\": \"workflow-agent verify --fix\",\n \"workflow:auto-setup\": \"workflow-agent auto-setup\",\n\n // Learning System Commands\n \"workflow:learn\": \"workflow-agent learn:list\",\n \"workflow:learn:record\": \"workflow-agent learn:record\",\n \"workflow:learn:list\": \"workflow-agent learn:list\",\n \"workflow:learn:apply\": \"workflow-agent learn:apply\",\n \"workflow:learn:sync\": \"workflow-agent learn:sync\",\n \"workflow:learn:config\": \"workflow-agent learn:config\",\n \"workflow:learn:deprecate\": \"workflow-agent learn:deprecate\",\n \"workflow:learn:stats\": \"workflow-agent learn:stats\",\n\n // Solution Pattern Commands\n \"workflow:solution\": \"workflow-agent solution:list\",\n \"workflow:solution:capture\": \"workflow-agent solution:capture\",\n \"workflow:solution:search\": \"workflow-agent solution:search\",\n \"workflow:solution:list\": \"workflow-agent solution:list\",\n \"workflow:solution:apply\": \"workflow-agent solution:apply\",\n \"workflow:solution:deprecate\": \"workflow-agent solution:deprecate\",\n \"workflow:solution:stats\": \"workflow-agent solution:stats\",\n\n // Advisory Board Commands\n \"workflow:advisory\": \"workflow-agent advisory\",\n \"workflow:advisory:quick\": \"workflow-agent advisory --depth quick\",\n \"workflow:advisory:standard\": \"workflow-agent advisory --depth standard\",\n \"workflow:advisory:comprehensive\":\n \"workflow-agent advisory --depth comprehensive\",\n \"workflow:advisory:executive\": \"workflow-agent advisory --depth executive\",\n \"workflow:advisory:ci\": \"workflow-agent advisory --ci\",\n\n // AI Agent Instructions\n \"workflow:generate-instructions\": \"workflow-agent generate-instructions\",\n\n // Template Management\n \"workflow:update-templates\": \"workflow-agent update-templates\",\n \"workflow:update-templates:force\": \"workflow-agent update-templates --force\",\n\n // Document Validation\n \"workflow:docs:validate\": \"workflow-agent docs:validate\",\n \"workflow:docs:validate:fix\": \"workflow-agent docs:validate --fix\",\n} as const;\n\nexport type WorkflowScriptName = keyof typeof WORKFLOW_SCRIPTS;\n\n/**\n * Script categories for organized console output\n */\nexport const SCRIPT_CATEGORIES = {\n \"Core Commands\": [\n \"workflow:init\",\n \"workflow:validate\",\n \"workflow:config\",\n \"workflow:suggest\",\n \"workflow:setup\",\n \"workflow:doctor\",\n ],\n \"Scope Commands\": [\"workflow:scope:create\", \"workflow:scope:migrate\"],\n Verification: [\n \"workflow:verify\",\n \"workflow:verify:fix\",\n \"workflow:auto-setup\",\n ],\n \"Learning System\": [\n \"workflow:learn\",\n \"workflow:learn:record\",\n \"workflow:learn:list\",\n \"workflow:learn:apply\",\n \"workflow:learn:sync\",\n \"workflow:learn:config\",\n \"workflow:learn:deprecate\",\n \"workflow:learn:stats\",\n ],\n \"Solution Patterns\": [\n \"workflow:solution\",\n \"workflow:solution:capture\",\n \"workflow:solution:search\",\n \"workflow:solution:list\",\n \"workflow:solution:apply\",\n \"workflow:solution:deprecate\",\n \"workflow:solution:stats\",\n ],\n \"Advisory Board\": [\n \"workflow:advisory\",\n \"workflow:advisory:quick\",\n \"workflow:advisory:standard\",\n \"workflow:advisory:comprehensive\",\n \"workflow:advisory:executive\",\n \"workflow:advisory:ci\",\n ],\n \"AI Agent Instructions\": [\n \"workflow:generate-instructions\",\n ],\n \"Template Management\": [\n \"workflow:update-templates\",\n \"workflow:update-templates:force\",\n ],\n \"Document Validation\": [\n \"workflow:docs:validate\",\n \"workflow:docs:validate:fix\",\n ],\n} as const;\n\nexport const TOTAL_SCRIPTS = Object.keys(WORKFLOW_SCRIPTS).length;\n","/**\n * Silent template installer for postinstall and non-interactive contexts\n *\n * This module provides functions to copy mandatory templates without\n * user interaction, suitable for use in postinstall scripts.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"fs\";\nimport { readdirSync } from \"fs\";\nimport { join, basename } from \"path\";\nimport { getMandatoryTemplateFilenames } from \"../templates/metadata.js\";\n\nexport interface InstallTemplatesOptions {\n /** Force overwrite existing files */\n force?: boolean;\n /** Skip if guidelines directory already exists */\n skipIfExists?: boolean;\n /** Silent mode - no console output */\n silent?: boolean;\n /** Only install mandatory templates (default: true) */\n mandatoryOnly?: boolean;\n}\n\nexport interface InstallTemplatesResult {\n success: boolean;\n installed: string[];\n skipped: string[];\n updated: string[];\n errors: string[];\n guidelinesExisted: boolean;\n}\n\n/**\n * Get project name from package.json or directory name\n */\nfunction getProjectName(projectRoot: string): string {\n try {\n const pkgPath = join(projectRoot, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.name || basename(projectRoot);\n }\n } catch {\n // Ignore errors, fall back to directory name\n }\n return basename(projectRoot);\n}\n\n/**\n * Simple template variable substitution using {{variable}} syntax\n */\nfunction renderTemplate(\n template: string,\n context: Record<string, string>,\n): string {\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key) => {\n return context[key] ?? match;\n });\n}\n\n/**\n * Build default template context from project info\n * Uses generic defaults when no workflow.config.json exists\n */\nfunction buildDefaultContext(projectRoot: string): Record<string, string> {\n const projectName = getProjectName(projectRoot);\n\n return {\n projectName,\n framework: \"unknown\",\n scopes: \"feat, fix, docs, refactor, test, chore\",\n scopeList: `- **feat** - New features\n- **fix** - Bug fixes\n- **docs** - Documentation\n- **refactor** - Code refactoring\n- **test** - Testing\n- **chore** - Maintenance`,\n pathStructure: \"N/A\",\n enforcement: \"strict\",\n year: new Date().getFullYear().toString(),\n };\n}\n\n/**\n * Find the templates directory relative to this module\n * Works in both development and installed contexts\n */\nexport function findTemplatesDirectory(callerDirname: string): string | null {\n // When installed: dist/scripts/template-installer.js -> ../../templates\n // Try multiple possible locations\n const possiblePaths = [\n join(callerDirname, \"../../templates\"),\n join(callerDirname, \"../templates\"),\n join(callerDirname, \"templates\"),\n ];\n\n for (const templatePath of possiblePaths) {\n if (existsSync(templatePath)) {\n return templatePath;\n }\n }\n\n return null;\n}\n\n/**\n * Install mandatory templates to a project's guidelines directory\n * Designed for non-interactive use (postinstall, CI, etc.)\n */\nexport function installMandatoryTemplates(\n projectRoot: string,\n templatesDir: string,\n options: InstallTemplatesOptions = {},\n): InstallTemplatesResult {\n const {\n force = false,\n skipIfExists = true,\n silent = false,\n mandatoryOnly = true,\n } = options;\n\n const result: InstallTemplatesResult = {\n success: true,\n installed: [],\n skipped: [],\n updated: [],\n errors: [],\n guidelinesExisted: false,\n };\n\n const guidelinesDir = join(projectRoot, \"guidelines\");\n result.guidelinesExisted = existsSync(guidelinesDir);\n\n // Skip if guidelines exists and skipIfExists is true\n if (result.guidelinesExisted && skipIfExists && !force) {\n if (!silent) {\n console.log(\" Guidelines directory already exists, skipping templates\");\n }\n return result;\n }\n\n // Get list of templates to install\n const mandatoryFiles = getMandatoryTemplateFilenames();\n\n // Check templates directory exists\n if (!existsSync(templatesDir)) {\n result.success = false;\n result.errors.push(`Templates directory not found: ${templatesDir}`);\n return result;\n }\n\n // Get available template files\n let availableFiles: string[];\n try {\n availableFiles = readdirSync(templatesDir).filter((f) => f.endsWith(\".md\"));\n } catch (error) {\n result.success = false;\n result.errors.push(`Failed to read templates directory: ${error}`);\n return result;\n }\n\n // Determine which files to install\n const filesToInstall = mandatoryOnly\n ? availableFiles.filter((f) => mandatoryFiles.includes(f))\n : availableFiles;\n\n if (filesToInstall.length === 0) {\n result.success = false;\n result.errors.push(\"No template files found to install\");\n return result;\n }\n\n // Build template context\n const context = buildDefaultContext(projectRoot);\n\n // Create guidelines directory\n try {\n mkdirSync(guidelinesDir, { recursive: true });\n } catch (error) {\n result.success = false;\n result.errors.push(`Failed to create guidelines directory: ${error}`);\n return result;\n }\n\n // Copy each template\n for (const filename of filesToInstall) {\n const sourcePath = join(templatesDir, filename);\n const destPath = join(guidelinesDir, filename);\n\n const fileExists = existsSync(destPath);\n\n // Skip if file exists and not forcing\n if (fileExists && !force) {\n result.skipped.push(filename);\n continue;\n }\n\n try {\n const template = readFileSync(sourcePath, \"utf-8\");\n const rendered = renderTemplate(template, context);\n writeFileSync(destPath, rendered, \"utf-8\");\n\n if (fileExists) {\n result.updated.push(filename);\n } else {\n result.installed.push(filename);\n }\n } catch (error) {\n result.errors.push(`Failed to install ${filename}: ${error}`);\n }\n }\n\n // Log results if not silent\n if (!silent) {\n if (result.installed.length > 0) {\n console.log(\n `\\n✓ Installed ${result.installed.length} guideline templates:`,\n );\n for (const file of result.installed) {\n console.log(` - ${file}`);\n }\n }\n if (result.updated.length > 0) {\n console.log(`\\n✓ Updated ${result.updated.length} guideline templates:`);\n for (const file of result.updated) {\n console.log(` - ${file}`);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Update templates - reinstall templates with option to force or skip existing\n */\nexport function updateTemplates(\n projectRoot: string,\n templatesDir: string,\n options: { force?: boolean; silent?: boolean } = {},\n): InstallTemplatesResult {\n return installMandatoryTemplates(projectRoot, templatesDir, {\n ...options,\n skipIfExists: false, // Don't skip - we want to update\n mandatoryOnly: false, // Install all templates during update\n });\n}\n","/**\n * Template metadata defining mandatory vs optional guidelines\n * and their associated validators for enforcement\n *\n * @fileoverview This module defines which guidelines are mandatory for projects\n * using the workflow agent. Mandatory guidelines MUST be present and cannot be\n * skipped during project initialization.\n *\n * TODO: Ensure all new templates have associated unit tests in metadata.test.ts\n */\n\nexport type TemplateCategory = \"workflow\" | \"documentation\" | \"development\";\n\nexport type ValidatorType =\n | \"branch-name\"\n | \"commit-message\"\n | \"pr-title\"\n | \"implementation-plan\"\n | \"test-coverage\"\n | \"file-exists\";\n\nexport interface TemplateMetadata {\n /** Template filename */\n filename: string;\n /** Human-readable name */\n displayName: string;\n /** Whether this template is mandatory (cannot be skipped during init) */\n mandatory: boolean;\n /** Category for grouping */\n category: TemplateCategory;\n /** Associated validators that enforce this template's rules */\n validators: ValidatorType[];\n /** Brief description of what this template covers */\n description: string;\n}\n\n/**\n * Metadata for all available templates\n * Templates marked as mandatory will be auto-generated during init\n * and checked by the doctor command\n */\nexport const templateMetadata: Record<string, TemplateMetadata> = {\n \"AGENT_EDITING_INSTRUCTIONS.md\": {\n filename: \"AGENT_EDITING_INSTRUCTIONS.md\",\n displayName: \"Agent Editing Instructions\",\n mandatory: true,\n category: \"workflow\",\n validators: [\"implementation-plan\"],\n description:\n \"Core rules for AI agents: implementation plans, coding standards, architecture\",\n },\n \"BRANCHING_STRATEGY.md\": {\n filename: \"BRANCHING_STRATEGY.md\",\n displayName: \"Branching Strategy\",\n mandatory: true,\n category: \"workflow\",\n validators: [\"branch-name\", \"pr-title\"],\n description:\n \"Git branch naming conventions, PR requirements, merge policies\",\n },\n \"TESTING_STRATEGY.md\": {\n filename: \"TESTING_STRATEGY.md\",\n displayName: \"Testing Strategy\",\n mandatory: true,\n category: \"development\",\n validators: [\"test-coverage\"],\n description:\n \"Testing pyramid, Vitest/Playwright patterns, when tests are required\",\n },\n \"SELF_IMPROVEMENT_MANDATE.md\": {\n filename: \"SELF_IMPROVEMENT_MANDATE.md\",\n displayName: \"Self-Improvement Mandate\",\n mandatory: true,\n category: \"workflow\",\n validators: [],\n description: \"Continuous improvement tracking, changelog requirements\",\n },\n \"PATTERN_ANALYSIS_WORKFLOW.md\": {\n filename: \"PATTERN_ANALYSIS_WORKFLOW.md\",\n displayName: \"Pattern Analysis Workflow\",\n mandatory: true,\n category: \"workflow\",\n validators: [],\n description:\n \"AI agent workflow for analyzing codebases, extracting patterns, and updating the central pattern store\",\n },\n \"SINGLE_SOURCE_OF_TRUTH.md\": {\n filename: \"SINGLE_SOURCE_OF_TRUTH.md\",\n displayName: \"Single Source of Truth\",\n mandatory: true,\n category: \"workflow\",\n validators: [],\n description:\n \"Canonical code locations, service patterns, avoiding duplication\",\n },\n \"COMPONENT_LIBRARY.md\": {\n filename: \"COMPONENT_LIBRARY.md\",\n displayName: \"Component Library\",\n mandatory: false,\n category: \"development\",\n validators: [],\n description: \"UI component patterns, design tokens, decision tree\",\n },\n \"DEPLOYMENT_STRATEGY.md\": {\n filename: \"DEPLOYMENT_STRATEGY.md\",\n displayName: \"Deployment Strategy\",\n mandatory: false,\n category: \"development\",\n validators: [],\n description: \"Deployment workflow, environments, migrations, rollback\",\n },\n \"LIBRARY_INVENTORY.md\": {\n filename: \"LIBRARY_INVENTORY.md\",\n displayName: \"Library Inventory\",\n mandatory: false,\n category: \"development\",\n validators: [],\n description: \"Dependency catalog, approved libraries, new library process\",\n },\n \"SCOPE_CREATION_WORKFLOW.md\": {\n filename: \"SCOPE_CREATION_WORKFLOW.md\",\n displayName: \"Scope Creation Workflow\",\n mandatory: false,\n category: \"workflow\",\n validators: [],\n description: \"Workflow for AI agents creating custom scopes\",\n },\n \"CUSTOM_SCOPE_TEMPLATE.md\": {\n filename: \"CUSTOM_SCOPE_TEMPLATE.md\",\n displayName: \"Custom Scope Template\",\n mandatory: false,\n category: \"workflow\",\n validators: [],\n description: \"Template for defining custom scope packages\",\n },\n \"PROJECT_TEMPLATE_README.md\": {\n filename: \"PROJECT_TEMPLATE_README.md\",\n displayName: \"Project Template README\",\n mandatory: false,\n category: \"documentation\",\n validators: [],\n description: \"Meta-document describing project structure\",\n },\n \"Guidelines.md\": {\n filename: \"Guidelines.md\",\n displayName: \"Custom Guidelines\",\n mandatory: false,\n category: \"documentation\",\n validators: [],\n description: \"Placeholder for custom user guidelines\",\n },\n};\n\n/**\n * Get all mandatory templates\n */\nexport function getMandatoryTemplates(): TemplateMetadata[] {\n return Object.values(templateMetadata).filter((t) => t.mandatory);\n}\n\n/**\n * Get all optional templates\n */\nexport function getOptionalTemplates(): TemplateMetadata[] {\n return Object.values(templateMetadata).filter((t) => !t.mandatory);\n}\n\n/**\n * Get templates by category\n */\nexport function getTemplatesByCategory(\n category: TemplateCategory,\n): TemplateMetadata[] {\n return Object.values(templateMetadata).filter((t) => t.category === category);\n}\n\n/**\n * Get template metadata by filename\n */\nexport function getTemplateMetadata(\n filename: string,\n): TemplateMetadata | undefined {\n return templateMetadata[filename];\n}\n\n/**\n * Check if a template is mandatory\n */\nexport function isTemplateMandatory(filename: string): boolean {\n return templateMetadata[filename]?.mandatory ?? false;\n}\n\n/**\n * Get mandatory template filenames\n */\nexport function getMandatoryTemplateFilenames(): string[] {\n return getMandatoryTemplates().map((t) => t.filename);\n}\n"],"mappings":";AAeA,SAAS,cAAc,eAAe,YAAY,aAAa,iBAAiB;AAChF,SAAS,MAAM,gBAAgB;AAG/B,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AAmBzB,SAAS,aAAa,SAAyB;AAC7C,QAAM,QAAQ,QAAQ,MAAM,aAAa;AACzC,SAAO,QAAQ,MAAM,CAAC,EAAE,KAAK,IAAI;AACnC;AAKA,SAAS,mBAAmB,SAAyB;AAEnD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,MAAI,aAAa;AACjB,MAAI,cAAc;AAElB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,WAAW,IAAI,GAAG;AACzB,mBAAa;AACb;AAAA,IACF;AACA,QAAI,YAAY;AAEd,UAAI,KAAK,KAAK,MAAM,MAAM,KAAK,WAAW,GAAG,GAAG;AAC9C,YAAI,YAAa;AACjB;AAAA,MACF;AAEA,UAAI,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,KAAK,KAAK,KAAK,WAAW,IAAI,GAAG;AAC3E;AAAA,MACF;AACA,qBAAe,KAAK,KAAK,IAAI;AAE7B,UAAI,YAAY,SAAS,IAAK;AAAA,IAChC;AAAA,EACF;AAEA,SAAO,YAAY,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK,YAAY,SAAS,MAAM,QAAQ;AAChF;AAMA,SAAS,gBAAgB,SAAiB,WAAmB,GAAa;AACxE,QAAM,QAAkB,CAAC;AAGzB,QAAM,mBAAmB;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AAEA,aAAW,WAAW,kBAAkB;AACtC,UAAM,UAAU,QAAQ,MAAM,OAAO;AACrC,QAAI,SAAS;AACX,iBAAW,SAAS,QAAQ,MAAM,GAAG,CAAC,GAAG;AACvC,cAAM,UAAU,MAAM,QAAQ,SAAS,EAAE,EAAE,QAAQ,YAAY,EAAE,EAAE,KAAK;AACxE,YAAI,QAAQ,SAAS,MAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,GAAG;AAC3E,gBAAM,KAAK,OAAO;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,oBAAoB,QAAQ,MAAM,6FAA6F;AACrI,MAAI,mBAAmB;AACrB,UAAM,UAAU,kBAAkB,CAAC;AACnC,UAAM,YAAY,QAAQ,MAAM,wBAAwB;AACxD,QAAI,WAAW;AACb,iBAAW,QAAQ,UAAU,MAAM,GAAG,CAAC,GAAG;AACxC,cAAM,UAAU,KAAK,QAAQ,gBAAgB,EAAE,EAAE,KAAK;AACtD,YAAI,QAAQ,SAAS,MAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,GAAG;AAC3E,gBAAM,KAAK,OAAO;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,QAAQ,MAAM,sDAAsD;AAC3F,MAAI,gBAAgB;AAClB,eAAW,SAAS,eAAe,MAAM,GAAG,CAAC,GAAG;AAC9C,YAAM,UAAU,MAAM,QAAQ,gDAAgD,EAAE,EAAE,KAAK;AACvF,UAAI,QAAQ,SAAS,MAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,GAAG;AAC3E,cAAM,KAAK,OAAO;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,YAAY,QAAQ,MAAM,oBAAoB;AACpD,QAAI,WAAW;AACb,iBAAW,QAAQ,UAAU,MAAM,GAAG,CAAC,GAAG;AACxC,cAAM,UAAU,KAAK,QAAQ,YAAY,EAAE,EAAE,KAAK;AAClD,YAAI,QAAQ,SAAS,MAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,GAAG;AAC3E,gBAAM,KAAK,OAAO;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,MAAM,GAAG,QAAQ;AAChC;AAKA,SAAS,eAAe,UAA2C;AACjE,MAAI;AACF,UAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,UAAM,WAAW,SAAS,QAAQ;AAGlC,QAAI,SAAS,WAAW,GAAG,KAAK,aAAa,iBAAiB;AAC5D,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA,OAAO,aAAa,OAAO;AAAA,MAC3B,aAAa,mBAAmB,OAAO;AAAA,MACvC,UAAU,gBAAgB,OAAO;AAAA,IACnC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,mBAAmB,aAA4C;AACtE,QAAM,aAAa,KAAK,aAAa,sBAAsB;AAC3D,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,YAAY,OAAO;AAChD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,qBAAqB,iBAAwC;AACpE,QAAM,aAAa,gBAAgB,QAAQ,mBAAmB;AAC9D,QAAM,WAAW,gBAAgB,QAAQ,iBAAiB;AAE1D,MAAI,eAAe,MAAM,aAAa,MAAM,WAAW,YAAY;AACjE,WAAO,gBAAgB;AAAA,MACrB,aAAa,oBAAoB;AAAA,MACjC;AAAA,IACF,EAAE,KAAK;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,4BACP,QACA,YACA,eACQ;AACR,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,SAAS,QAAQ,UAAU,CAAC;AAElC,MAAI,UAAU,8BAA8B,WAAW;AAAA;AAAA;AAAA;AAAA,qBAIrC,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA;AAAA,EAExD,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAMhB,MAAI,QAAQ;AACV,eAAW,uBAAuB,WAAW;AAAA;AAC7C,eAAW,4BAA4B,OAAO,eAAe,QAAQ;AAAA;AACrE,QAAI,OAAO,SAAS,GAAG;AACrB,iBAAW,2BAA2B,OAAO,IAAI,OAAK,KAAK,EAAE,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,IACnF;AACA,eAAW;AAAA,EACb;AAGA,MAAI,OAAO,SAAS,GAAG;AACrB,eAAW;AAAA;AAAA;AAAA;AAAA;AAKX,eAAW,SAAS,OAAO,MAAM,GAAG,EAAE,GAAG;AACvC,iBAAW,OAAO,MAAM,IAAI,QAAQ,MAAM,WAAW;AAAA;AAAA,IACvD;AACA,QAAI,OAAO,SAAS,IAAI;AACtB,iBAAW,4CAA4C,OAAO,MAAM;AAAA;AAAA,IACtE;AACA,eAAW;AAAA,EACb;AAGA,MAAI,WAAW,SAAS,GAAG;AACzB,eAAW;AAAA;AAAA;AAAA;AAAA;AAOX,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,mBAAmB,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,GAAG,MAAM;AACtD,YAAM,SAAS,eAAe,QAAQ,EAAE,QAAQ;AAChD,YAAM,SAAS,eAAe,QAAQ,EAAE,QAAQ;AAChD,UAAI,WAAW,MAAM,WAAW,GAAI,QAAO,SAAS;AACpD,UAAI,WAAW,GAAI,QAAO;AAC1B,UAAI,WAAW,GAAI,QAAO;AAC1B,aAAO,EAAE,MAAM,cAAc,EAAE,KAAK;AAAA,IACtC,CAAC;AAED,eAAW,aAAa,kBAAkB;AACxC,iBAAW,OAAO,UAAU,KAAK;AAAA;AAAA,6CAED,UAAU,QAAQ;AAAA;AAAA,EAEtD,UAAU,WAAW;AAAA;AAAA;AAGjB,UAAI,UAAU,SAAS,SAAS,GAAG;AACjC,mBAAW;AAAA;AAEX,mBAAW,QAAQ,UAAU,UAAU;AACrC,qBAAW,KAAK,IAAI;AAAA;AAAA,QACtB;AACA,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAGA,aAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCX,aAAW;AAAA;AAAA,EAEX,mBAAmB;AAAA,EACnB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAWlB;AAAA,EACC,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOjB,SAAO;AACT;AAqBO,SAAS,4BACd,aACA,UAAiD,CAAC,GAClC;AAChB,QAAM,EAAE,QAAQ,OAAO,SAAS,MAAM,IAAI;AAE1C,QAAM,gBAAgB,KAAK,aAAa,YAAY;AACpD,QAAM,YAAY,KAAK,aAAa,SAAS;AAC7C,QAAM,aAAa,KAAK,WAAW,yBAAyB;AAG5D,MAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,QAAI,CAAC,QAAQ;AAAA,IAEb;AACA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,wBAAwB;AAAA,MACxB,OAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,QAAQ,YAAY,aAAa,EAAE,OAAO,OAAK,EAAE,SAAS,KAAK,CAAC;AACtE,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,wBAAwB;AAAA,MACxB,OAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,aAAiC,CAAC;AACxC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,eAAe,KAAK,eAAe,IAAI,CAAC;AACxD,QAAI,SAAS;AACX,iBAAW,KAAK,OAAO;AAAA,IACzB;AAAA,EACF;AAGA,QAAM,SAAS,mBAAmB,WAAW;AAG7C,MAAI,gBAA+B;AACnC,MAAI,QAAQ;AAEZ,MAAI,WAAW,UAAU,GAAG;AAC1B,YAAQ;AACR,UAAM,kBAAkB,aAAa,YAAY,OAAO;AACxD,oBAAgB,qBAAqB,eAAe;AAAA,EACtD;AAGA,QAAM,UAAU,4BAA4B,QAAQ,YAAY,aAAa;AAG7E,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AAGA,gBAAc,YAAY,SAAS,OAAO;AAE1C,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,iBAAiB,WAAW;AAAA,IAC5B;AAAA,IACA,wBAAwB,kBAAkB;AAAA,EAC5C;AACF;;;AC/bO,IAAM,mBAAmB;AAAA;AAAA,EAE9B,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA;AAAA,EAGnB,yBAAyB;AAAA,EACzB,0BAA0B;AAAA;AAAA,EAG1B,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA;AAAA,EAGvB,kBAAkB;AAAA,EAClB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,4BAA4B;AAAA,EAC5B,wBAAwB;AAAA;AAAA,EAGxB,qBAAqB;AAAA,EACrB,6BAA6B;AAAA,EAC7B,4BAA4B;AAAA,EAC5B,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA;AAAA,EAG3B,qBAAqB;AAAA,EACrB,2BAA2B;AAAA,EAC3B,8BAA8B;AAAA,EAC9B,mCACE;AAAA,EACF,+BAA+B;AAAA,EAC/B,wBAAwB;AAAA;AAAA,EAGxB,kCAAkC;AAAA;AAAA,EAGlC,6BAA6B;AAAA,EAC7B,mCAAmC;AAAA;AAAA,EAGnC,0BAA0B;AAAA,EAC1B,8BAA8B;AAChC;AAOO,IAAM,oBAAoB;AAAA,EAC/B,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,kBAAkB,CAAC,yBAAyB,wBAAwB;AAAA,EACpE,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,qBAAqB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,kBAAkB;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,yBAAyB;AAAA,IACvB;AAAA,EACF;AAAA,EACA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,EACF;AAAA,EACA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,gBAAgB,OAAO,KAAK,gBAAgB,EAAE;;;ACpH3D,SAAS,cAAAA,aAAY,gBAAAC,eAAc,iBAAAC,gBAAe,aAAAC,kBAAiB;AACnE,SAAS,eAAAC,oBAAmB;AAC5B,SAAS,QAAAC,OAAM,YAAAC,iBAAgB;;;ACgCxB,IAAM,mBAAqD;AAAA,EAChE,iCAAiC;AAAA,IAC/B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC,qBAAqB;AAAA,IAClC,aACE;AAAA,EACJ;AAAA,EACA,yBAAyB;AAAA,IACvB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC,eAAe,UAAU;AAAA,IACtC,aACE;AAAA,EACJ;AAAA,EACA,uBAAuB;AAAA,IACrB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC,eAAe;AAAA,IAC5B,aACE;AAAA,EACJ;AAAA,EACA,+BAA+B;AAAA,IAC7B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,gCAAgC;AAAA,IAC9B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aACE;AAAA,EACJ;AAAA,EACA,6BAA6B;AAAA,IAC3B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aACE;AAAA,EACJ;AAAA,EACA,wBAAwB;AAAA,IACtB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,0BAA0B;AAAA,IACxB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,wBAAwB;AAAA,IACtB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,8BAA8B;AAAA,IAC5B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,4BAA4B;AAAA,IAC1B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,8BAA8B;AAAA,IAC5B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,iBAAiB;AAAA,IACf,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY,CAAC;AAAA,IACb,aAAa;AAAA,EACf;AACF;AAKO,SAAS,wBAA4C;AAC1D,SAAO,OAAO,OAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS;AAClE;AAqCO,SAAS,gCAA0C;AACxD,SAAO,sBAAsB,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AACtD;;;ADlKA,SAAS,eAAe,aAA6B;AACnD,MAAI;AACF,UAAM,UAAUC,MAAK,aAAa,cAAc;AAChD,QAAIC,YAAW,OAAO,GAAG;AACvB,YAAM,MAAM,KAAK,MAAMC,cAAa,SAAS,OAAO,CAAC;AACrD,aAAO,IAAI,QAAQC,UAAS,WAAW;AAAA,IACzC;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAOA,UAAS,WAAW;AAC7B;AAKA,SAAS,eACP,UACA,SACQ;AACR,SAAO,SAAS,QAAQ,kBAAkB,CAAC,OAAO,QAAQ;AACxD,WAAO,QAAQ,GAAG,KAAK;AAAA,EACzB,CAAC;AACH;AAMA,SAAS,oBAAoB,aAA6C;AACxE,QAAM,cAAc,eAAe,WAAW;AAE9C,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMX,eAAe;AAAA,IACf,aAAa;AAAA,IACb,OAAM,oBAAI,KAAK,GAAE,YAAY,EAAE,SAAS;AAAA,EAC1C;AACF;AAMO,SAAS,uBAAuB,eAAsC;AAG3E,QAAM,gBAAgB;AAAA,IACpBH,MAAK,eAAe,iBAAiB;AAAA,IACrCA,MAAK,eAAe,cAAc;AAAA,IAClCA,MAAK,eAAe,WAAW;AAAA,EACjC;AAEA,aAAW,gBAAgB,eAAe;AACxC,QAAIC,YAAW,YAAY,GAAG;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,0BACd,aACA,cACA,UAAmC,CAAC,GACZ;AACxB,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,SAAS;AAAA,IACT,gBAAgB;AAAA,EAClB,IAAI;AAEJ,QAAM,SAAiC;AAAA,IACrC,SAAS;AAAA,IACT,WAAW,CAAC;AAAA,IACZ,SAAS,CAAC;AAAA,IACV,SAAS,CAAC;AAAA,IACV,QAAQ,CAAC;AAAA,IACT,mBAAmB;AAAA,EACrB;AAEA,QAAM,gBAAgBD,MAAK,aAAa,YAAY;AACpD,SAAO,oBAAoBC,YAAW,aAAa;AAGnD,MAAI,OAAO,qBAAqB,gBAAgB,CAAC,OAAO;AACtD,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI,2DAA2D;AAAA,IACzE;AACA,WAAO;AAAA,EACT;AAGA,QAAM,iBAAiB,8BAA8B;AAGrD,MAAI,CAACA,YAAW,YAAY,GAAG;AAC7B,WAAO,UAAU;AACjB,WAAO,OAAO,KAAK,kCAAkC,YAAY,EAAE;AACnE,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,qBAAiBG,aAAY,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5E,SAAS,OAAO;AACd,WAAO,UAAU;AACjB,WAAO,OAAO,KAAK,uCAAuC,KAAK,EAAE;AACjE,WAAO;AAAA,EACT;AAGA,QAAM,iBAAiB,gBACnB,eAAe,OAAO,CAAC,MAAM,eAAe,SAAS,CAAC,CAAC,IACvD;AAEJ,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO,UAAU;AACjB,WAAO,OAAO,KAAK,oCAAoC;AACvD,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,oBAAoB,WAAW;AAG/C,MAAI;AACF,IAAAC,WAAU,eAAe,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C,SAAS,OAAO;AACd,WAAO,UAAU;AACjB,WAAO,OAAO,KAAK,0CAA0C,KAAK,EAAE;AACpE,WAAO;AAAA,EACT;AAGA,aAAW,YAAY,gBAAgB;AACrC,UAAM,aAAaL,MAAK,cAAc,QAAQ;AAC9C,UAAM,WAAWA,MAAK,eAAe,QAAQ;AAE7C,UAAM,aAAaC,YAAW,QAAQ;AAGtC,QAAI,cAAc,CAAC,OAAO;AACxB,aAAO,QAAQ,KAAK,QAAQ;AAC5B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,WAAWC,cAAa,YAAY,OAAO;AACjD,YAAM,WAAW,eAAe,UAAU,OAAO;AACjD,MAAAI,eAAc,UAAU,UAAU,OAAO;AAEzC,UAAI,YAAY;AACd,eAAO,QAAQ,KAAK,QAAQ;AAAA,MAC9B,OAAO;AACL,eAAO,UAAU,KAAK,QAAQ;AAAA,MAChC;AAAA,IACF,SAAS,OAAO;AACd,aAAO,OAAO,KAAK,qBAAqB,QAAQ,KAAK,KAAK,EAAE;AAAA,IAC9D;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ;AACX,QAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,cAAQ;AAAA,QACN;AAAA,mBAAiB,OAAO,UAAU,MAAM;AAAA,MAC1C;AACA,iBAAW,QAAQ,OAAO,WAAW;AACnC,gBAAQ,IAAI,SAAS,IAAI,EAAE;AAAA,MAC7B;AAAA,IACF;AACA,QAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,cAAQ,IAAI;AAAA,iBAAe,OAAO,QAAQ,MAAM,uBAAuB;AACvE,iBAAW,QAAQ,OAAO,SAAS;AACjC,gBAAQ,IAAI,SAAS,IAAI,EAAE;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,gBACd,aACA,cACA,UAAiD,CAAC,GAC1B;AACxB,SAAO,0BAA0B,aAAa,cAAc;AAAA,IAC1D,GAAG;AAAA,IACH,cAAc;AAAA;AAAA,IACd,eAAe;AAAA;AAAA,EACjB,CAAC;AACH;","names":["existsSync","readFileSync","writeFileSync","mkdirSync","readdirSync","join","basename","join","existsSync","readFileSync","basename","readdirSync","mkdirSync","writeFileSync"]}
@@ -0,0 +1,386 @@
1
+ // src/validators/index.ts
2
+ import didYouMean from "didyoumean2";
3
+ import { readdir } from "fs/promises";
4
+ import { join } from "path";
5
+
6
+ // src/validators/document-references.ts
7
+ import { existsSync } from "fs";
8
+ import { readFile } from "fs/promises";
9
+ import { resolve, dirname } from "path";
10
+ import fg from "fast-glob";
11
+ var LINK_PATTERNS = {
12
+ // Inline links: [text](path)
13
+ inline: /\[([^\]]+)\]\(([^)]+)\)/g,
14
+ // Images: ![alt](path)
15
+ image: /!\[([^\]]*)\]\(([^)]+)\)/g,
16
+ // Reference-style: [text]: path
17
+ reference: /^\[([^\]]+)\]:\s*(.+)$/gm
18
+ };
19
+ function isExternalUrl(path) {
20
+ return /^https?:\/\//i.test(path);
21
+ }
22
+ function isAnchorLink(path) {
23
+ return path.startsWith("#");
24
+ }
25
+ function resolveReferencePath(sourcePath, targetPath, projectRoot) {
26
+ const pathWithoutAnchor = targetPath.split("#")[0];
27
+ if (!pathWithoutAnchor) return targetPath;
28
+ const sourceDir = dirname(sourcePath);
29
+ if (pathWithoutAnchor.startsWith("/")) {
30
+ return resolve(projectRoot, pathWithoutAnchor.slice(1));
31
+ }
32
+ return resolve(sourceDir, pathWithoutAnchor);
33
+ }
34
+ async function extractReferences(filePath, _projectRoot) {
35
+ const content = await readFile(filePath, "utf-8");
36
+ const references = [];
37
+ const lines = content.split("\n");
38
+ lines.forEach((line, index) => {
39
+ let match2;
40
+ const inlineRegex = new RegExp(LINK_PATTERNS.inline);
41
+ while ((match2 = inlineRegex.exec(line)) !== null) {
42
+ const targetPath = match2[2].trim();
43
+ if (isExternalUrl(targetPath) || isAnchorLink(targetPath)) {
44
+ continue;
45
+ }
46
+ references.push({
47
+ file: filePath,
48
+ line: index + 1,
49
+ column: match2.index + 1,
50
+ rawLink: match2[0],
51
+ targetPath,
52
+ type: "link"
53
+ });
54
+ }
55
+ });
56
+ lines.forEach((line, index) => {
57
+ let match2;
58
+ const imageRegex = new RegExp(LINK_PATTERNS.image);
59
+ while ((match2 = imageRegex.exec(line)) !== null) {
60
+ const targetPath = match2[2].trim();
61
+ if (isExternalUrl(targetPath)) {
62
+ continue;
63
+ }
64
+ references.push({
65
+ file: filePath,
66
+ line: index + 1,
67
+ column: match2.index + 1,
68
+ rawLink: match2[0],
69
+ targetPath,
70
+ type: "image"
71
+ });
72
+ }
73
+ });
74
+ let match;
75
+ const referenceRegex = new RegExp(LINK_PATTERNS.reference);
76
+ while ((match = referenceRegex.exec(content)) !== null) {
77
+ const targetPath = match[2].trim();
78
+ if (isExternalUrl(targetPath) || isAnchorLink(targetPath)) {
79
+ continue;
80
+ }
81
+ const beforeMatch = content.substring(0, match.index);
82
+ const lineNumber = beforeMatch.split("\n").length;
83
+ references.push({
84
+ file: filePath,
85
+ line: lineNumber,
86
+ column: match.index - beforeMatch.lastIndexOf("\n"),
87
+ rawLink: match[0],
88
+ targetPath,
89
+ type: "reference"
90
+ });
91
+ }
92
+ return references;
93
+ }
94
+ async function findSimilarFiles(targetPath, projectRoot) {
95
+ const parts = targetPath.split("/");
96
+ const filename = parts[parts.length - 1];
97
+ const basename = filename.split(".")[0];
98
+ const ext = filename.includes(".") ? filename.split(".").pop() : "";
99
+ const patterns = [
100
+ `**/${filename}`,
101
+ // Exact filename match anywhere
102
+ ext ? `**/${basename}*.${ext}` : `**/${basename}*`,
103
+ // Similar basename
104
+ ext ? `**/*${basename}*.${ext}` : `**/*${basename}*`
105
+ // Contains basename
106
+ ];
107
+ const foundFiles = /* @__PURE__ */ new Set();
108
+ for (const pattern of patterns) {
109
+ try {
110
+ const matches = await fg(pattern, {
111
+ cwd: projectRoot,
112
+ ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"],
113
+ absolute: false,
114
+ onlyFiles: true
115
+ });
116
+ matches.forEach((file) => foundFiles.add(file));
117
+ if (foundFiles.size >= 10) break;
118
+ } catch (error) {
119
+ }
120
+ }
121
+ return Array.from(foundFiles).slice(0, 10);
122
+ }
123
+ async function scanDocumentReferences(projectPath, options) {
124
+ const patterns = options?.patterns || ["**/*.md"];
125
+ const ignore = options?.ignore || [
126
+ "**/node_modules/**",
127
+ "**/.git/**",
128
+ "**/dist/**",
129
+ "**/build/**"
130
+ ];
131
+ const files = await fg(patterns, {
132
+ cwd: projectPath,
133
+ ignore,
134
+ absolute: true,
135
+ onlyFiles: true
136
+ });
137
+ const allReferences = [];
138
+ for (const file of files) {
139
+ try {
140
+ const references = await extractReferences(file, projectPath);
141
+ allReferences.push(...references);
142
+ } catch (error) {
143
+ continue;
144
+ }
145
+ }
146
+ return allReferences;
147
+ }
148
+ async function validateDocumentReferences(projectPath, options) {
149
+ const errors = [];
150
+ const brokenReferences = [];
151
+ try {
152
+ const references = await scanDocumentReferences(projectPath, options);
153
+ const scannedFiles = new Set(references.map((ref) => ref.file)).size;
154
+ for (const ref of references) {
155
+ try {
156
+ const resolvedPath = resolveReferencePath(
157
+ ref.file,
158
+ ref.targetPath,
159
+ projectPath
160
+ );
161
+ if (!existsSync(resolvedPath)) {
162
+ const suggestions = await findSimilarFiles(ref.targetPath, projectPath);
163
+ brokenReferences.push({
164
+ ...ref,
165
+ suggestions
166
+ });
167
+ }
168
+ } catch (error) {
169
+ const suggestions = await findSimilarFiles(ref.targetPath, projectPath);
170
+ brokenReferences.push({
171
+ ...ref,
172
+ suggestions
173
+ });
174
+ }
175
+ }
176
+ return {
177
+ valid: brokenReferences.length === 0,
178
+ scannedFiles,
179
+ totalReferences: references.length,
180
+ brokenReferences,
181
+ errors
182
+ };
183
+ } catch (error) {
184
+ errors.push(
185
+ error instanceof Error ? error.message : "Unknown error during validation"
186
+ );
187
+ return {
188
+ valid: false,
189
+ scannedFiles: 0,
190
+ totalReferences: 0,
191
+ brokenReferences: [],
192
+ errors
193
+ };
194
+ }
195
+ }
196
+ async function applyReferenceFix(filePath, oldLink, newPath) {
197
+ const content = await readFile(filePath, "utf-8");
198
+ const updatedContent = content.replace(oldLink, (match) => {
199
+ if (match.startsWith("![")) {
200
+ return match.replace(/\(([^)]+)\)/, `(${newPath})`);
201
+ } else if (match.startsWith("[") && match.includes("](")) {
202
+ return match.replace(/\(([^)]+)\)/, `(${newPath})`);
203
+ } else {
204
+ return match.replace(/:\s*(.+)$/, `: ${newPath}`);
205
+ }
206
+ });
207
+ const fs = await import("fs/promises");
208
+ await fs.writeFile(filePath, updatedContent, "utf-8");
209
+ }
210
+
211
+ // src/validators/index.ts
212
+ var customScopesCache = null;
213
+ var cacheTimestamp = 0;
214
+ var CACHE_TTL = 5 * 60 * 1e3;
215
+ async function discoverCustomScopes(workspacePath = process.cwd()) {
216
+ const now = Date.now();
217
+ if (customScopesCache && now - cacheTimestamp < CACHE_TTL) {
218
+ return customScopesCache;
219
+ }
220
+ const discoveredScopes = [];
221
+ try {
222
+ const workspaceLocations = [join(workspacePath, "packages"), workspacePath];
223
+ for (const location of workspaceLocations) {
224
+ try {
225
+ const entries = await readdir(location, { withFileTypes: true });
226
+ for (const entry of entries) {
227
+ if (entry.isDirectory() && entry.name.startsWith("scopes-")) {
228
+ const indexPath = join(location, entry.name, "src", "index.ts");
229
+ try {
230
+ const module = await import(indexPath);
231
+ const scopes = module.scopes || module.default?.scopes;
232
+ if (Array.isArray(scopes)) {
233
+ discoveredScopes.push(...scopes);
234
+ }
235
+ } catch {
236
+ }
237
+ }
238
+ }
239
+ } catch {
240
+ }
241
+ }
242
+ customScopesCache = discoveredScopes;
243
+ cacheTimestamp = now;
244
+ } catch (error) {
245
+ console.warn("Warning: Error discovering custom scopes:", error);
246
+ }
247
+ return discoveredScopes;
248
+ }
249
+ function invalidateCustomScopesCache() {
250
+ customScopesCache = null;
251
+ cacheTimestamp = 0;
252
+ }
253
+ async function getAllScopes(config, workspacePath) {
254
+ const configScopes = config.scopes;
255
+ const customScopes = await discoverCustomScopes(workspacePath);
256
+ const scopeMap = /* @__PURE__ */ new Map();
257
+ for (const scope of configScopes) {
258
+ scopeMap.set(scope.name, scope);
259
+ }
260
+ for (const scope of customScopes) {
261
+ if (!scopeMap.has(scope.name)) {
262
+ scopeMap.set(scope.name, scope);
263
+ }
264
+ }
265
+ return Array.from(scopeMap.values());
266
+ }
267
+ async function validateBranchName(branchName, config, workspacePath) {
268
+ const branchTypes = config.branchTypes || [
269
+ "feature",
270
+ "bugfix",
271
+ "hotfix",
272
+ "chore",
273
+ "refactor",
274
+ "docs",
275
+ "test"
276
+ ];
277
+ const allScopes = await getAllScopes(config, workspacePath);
278
+ const scopes = allScopes.map((s) => s.name);
279
+ const branchPattern = /^([a-z]+)\/([a-z0-9-]+)\/([a-z0-9-]+)$/;
280
+ const match = branchName.match(branchPattern);
281
+ if (!match) {
282
+ return {
283
+ valid: false,
284
+ error: `Branch name must follow format: <type>/<scope>/<description> (e.g., feature/auth/add-login)`,
285
+ suggestion: `Current: ${branchName}. All parts must be lowercase alphanumeric with hyphens.`
286
+ };
287
+ }
288
+ const [, type, scope, description] = match;
289
+ if (!branchTypes.includes(type)) {
290
+ const suggestion = didYouMean(type, branchTypes);
291
+ return {
292
+ valid: false,
293
+ error: `Invalid branch type '${type}'. Must be one of: ${branchTypes.join(", ")}`,
294
+ suggestion: suggestion ? `Did you mean '${suggestion}'?` : void 0
295
+ };
296
+ }
297
+ if (!scopes.includes(scope)) {
298
+ const suggestion = didYouMean(scope, scopes);
299
+ const scopeList = scopes.slice(0, 5).join(", ") + (scopes.length > 5 ? "..." : "");
300
+ return {
301
+ valid: false,
302
+ error: `Invalid scope '${scope}'. Must be one of: ${scopeList}`,
303
+ suggestion: suggestion ? `Did you mean '${suggestion}'?` : void 0
304
+ };
305
+ }
306
+ if (description.length < 3) {
307
+ return {
308
+ valid: false,
309
+ error: `Branch description '${description}' is too short (minimum 3 characters)`
310
+ };
311
+ }
312
+ return { valid: true };
313
+ }
314
+ async function validateCommitMessage(message, config, workspacePath) {
315
+ const conventionalTypes = config.conventionalTypes || [
316
+ "feat",
317
+ "fix",
318
+ "refactor",
319
+ "chore",
320
+ "docs",
321
+ "test",
322
+ "perf",
323
+ "style"
324
+ ];
325
+ const allScopes = await getAllScopes(config, workspacePath);
326
+ const scopes = allScopes.map((s) => s.name);
327
+ const commitPattern = /^([a-z]+)(?:\(([a-z0-9-]+)\))?: (.+)$/;
328
+ const match = message.match(commitPattern);
329
+ if (!match) {
330
+ return {
331
+ valid: false,
332
+ error: `Commit message must follow conventional commits format: <type>(<scope>): <description>`,
333
+ suggestion: `Example: feat(auth): add login validation`
334
+ };
335
+ }
336
+ const [, type, scope, description] = match;
337
+ if (!conventionalTypes.includes(type)) {
338
+ const suggestion = didYouMean(type, conventionalTypes);
339
+ return {
340
+ valid: false,
341
+ error: `Invalid commit type '${type}'. Must be one of: ${conventionalTypes.join(", ")}`,
342
+ suggestion: suggestion ? `Did you mean '${suggestion}'?` : void 0
343
+ };
344
+ }
345
+ if (scope && !scopes.includes(scope)) {
346
+ const suggestion = didYouMean(scope, scopes);
347
+ const scopeList = scopes.slice(0, 5).join(", ") + (scopes.length > 5 ? "..." : "");
348
+ return {
349
+ valid: false,
350
+ error: `Invalid scope '${scope}'. Must be one of: ${scopeList}`,
351
+ suggestion: suggestion ? `Did you mean '${suggestion}'?` : void 0
352
+ };
353
+ }
354
+ if (description.length < 10) {
355
+ return {
356
+ valid: false,
357
+ error: `Commit description is too short (minimum 10 characters)`,
358
+ suggestion: `Be more descriptive about what changed`
359
+ };
360
+ }
361
+ if (description[0] !== description[0].toLowerCase()) {
362
+ return {
363
+ valid: false,
364
+ error: `Commit description must start with lowercase letter`,
365
+ suggestion: `Change '${description}' to '${description[0].toLowerCase()}${description.slice(1)}'`
366
+ };
367
+ }
368
+ return { valid: true };
369
+ }
370
+ async function validatePRTitle(title, config, workspacePath) {
371
+ return validateCommitMessage(title, config, workspacePath);
372
+ }
373
+
374
+ export {
375
+ findSimilarFiles,
376
+ scanDocumentReferences,
377
+ validateDocumentReferences,
378
+ applyReferenceFix,
379
+ discoverCustomScopes,
380
+ invalidateCustomScopesCache,
381
+ getAllScopes,
382
+ validateBranchName,
383
+ validateCommitMessage,
384
+ validatePRTitle
385
+ };
386
+ //# sourceMappingURL=chunk-ZLDJ2OGO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validators/index.ts","../src/validators/document-references.ts"],"sourcesContent":["import didYouMean from \"didyoumean2\";\nimport type { WorkflowConfig, BranchType, Scope } from \"../config/index.js\";\nimport { readdir } from \"fs/promises\";\nimport { join } from \"path\";\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n suggestion?: string;\n}\n\n// Export document reference validation\nexport {\n validateDocumentReferences,\n scanDocumentReferences,\n findSimilarFiles,\n applyReferenceFix,\n type DocumentReference,\n type BrokenReference,\n type DocumentValidationResult,\n} from \"./document-references.js\";\n\n// Cache for discovered custom scopes\nlet customScopesCache: Scope[] | null = null;\nlet cacheTimestamp: number = 0;\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Discovers custom scope packages in the workspace and node_modules\n * @param workspacePath Path to workspace root\n * @returns Array of discovered scopes\n */\nexport async function discoverCustomScopes(\n workspacePath: string = process.cwd(),\n): Promise<Scope[]> {\n // Check cache validity\n const now = Date.now();\n if (customScopesCache && now - cacheTimestamp < CACHE_TTL) {\n return customScopesCache;\n }\n\n const discoveredScopes: Scope[] = [];\n\n try {\n // Search for custom scope packages in workspace\n const workspaceLocations = [join(workspacePath, \"packages\"), workspacePath];\n\n for (const location of workspaceLocations) {\n try {\n const entries = await readdir(location, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.startsWith(\"scopes-\")) {\n const indexPath = join(location, entry.name, \"src\", \"index.ts\");\n try {\n const module = await import(indexPath);\n const scopes = module.scopes || module.default?.scopes;\n\n if (Array.isArray(scopes)) {\n discoveredScopes.push(...scopes);\n }\n } catch {\n // Silently skip packages that can't be loaded\n }\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n\n // Update cache\n customScopesCache = discoveredScopes;\n cacheTimestamp = now;\n } catch (error) {\n // Return empty array on error\n console.warn(\"Warning: Error discovering custom scopes:\", error);\n }\n\n return discoveredScopes;\n}\n\n/**\n * Invalidates the custom scopes cache (useful after config changes)\n */\nexport function invalidateCustomScopesCache(): void {\n customScopesCache = null;\n cacheTimestamp = 0;\n}\n\n/**\n * Gets all available scopes including custom discovered ones\n * @param config Workflow configuration\n * @param workspacePath Optional workspace path\n * @returns Combined array of scopes\n */\nexport async function getAllScopes(\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<Scope[]> {\n const configScopes = config.scopes;\n const customScopes = await discoverCustomScopes(workspacePath);\n\n // Merge and deduplicate by name\n const scopeMap = new Map<string, Scope>();\n\n // Config scopes take precedence\n for (const scope of configScopes) {\n scopeMap.set(scope.name, scope);\n }\n\n // Add custom scopes that don't conflict\n for (const scope of customScopes) {\n if (!scopeMap.has(scope.name)) {\n scopeMap.set(scope.name, scope);\n }\n }\n\n return Array.from(scopeMap.values());\n}\n\nexport async function validateBranchName(\n branchName: string,\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<ValidationResult> {\n const branchTypes = config.branchTypes || [\n \"feature\",\n \"bugfix\",\n \"hotfix\",\n \"chore\",\n \"refactor\",\n \"docs\",\n \"test\",\n ];\n const allScopes = await getAllScopes(config, workspacePath);\n const scopes = allScopes.map((s) => s.name);\n\n // Expected format: <type>/<scope>/<description>\n const branchPattern = /^([a-z]+)\\/([a-z0-9-]+)\\/([a-z0-9-]+)$/;\n const match = branchName.match(branchPattern);\n\n if (!match) {\n return {\n valid: false,\n error: `Branch name must follow format: <type>/<scope>/<description> (e.g., feature/auth/add-login)`,\n suggestion: `Current: ${branchName}. All parts must be lowercase alphanumeric with hyphens.`,\n };\n }\n\n const [, type, scope, description] = match;\n\n // Validate type\n if (!branchTypes.includes(type as BranchType)) {\n const suggestion = didYouMean(type, branchTypes);\n return {\n valid: false,\n error: `Invalid branch type '${type}'. Must be one of: ${branchTypes.join(\", \")}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate scope\n if (!scopes.includes(scope)) {\n const suggestion = didYouMean(scope, scopes);\n const scopeList =\n scopes.slice(0, 5).join(\", \") + (scopes.length > 5 ? \"...\" : \"\");\n return {\n valid: false,\n error: `Invalid scope '${scope}'. Must be one of: ${scopeList}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate description (not empty, meaningful)\n if (description.length < 3) {\n return {\n valid: false,\n error: `Branch description '${description}' is too short (minimum 3 characters)`,\n };\n }\n\n return { valid: true };\n}\n\nexport async function validateCommitMessage(\n message: string,\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<ValidationResult> {\n const conventionalTypes = config.conventionalTypes || [\n \"feat\",\n \"fix\",\n \"refactor\",\n \"chore\",\n \"docs\",\n \"test\",\n \"perf\",\n \"style\",\n ];\n const allScopes = await getAllScopes(config, workspacePath);\n const scopes = allScopes.map((s) => s.name);\n\n // Expected format: <type>(<scope>): <description>\n const commitPattern = /^([a-z]+)(?:\\(([a-z0-9-]+)\\))?: (.+)$/;\n const match = message.match(commitPattern);\n\n if (!match) {\n return {\n valid: false,\n error: `Commit message must follow conventional commits format: <type>(<scope>): <description>`,\n suggestion: `Example: feat(auth): add login validation`,\n };\n }\n\n const [, type, scope, description] = match;\n\n // Validate type\n if (!conventionalTypes.includes(type as any)) {\n const suggestion = didYouMean(type, conventionalTypes);\n return {\n valid: false,\n error: `Invalid commit type '${type}'. Must be one of: ${conventionalTypes.join(\", \")}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate scope (optional but recommended)\n if (scope && !scopes.includes(scope)) {\n const suggestion = didYouMean(scope, scopes);\n const scopeList =\n scopes.slice(0, 5).join(\", \") + (scopes.length > 5 ? \"...\" : \"\");\n return {\n valid: false,\n error: `Invalid scope '${scope}'. Must be one of: ${scopeList}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate description\n if (description.length < 10) {\n return {\n valid: false,\n error: `Commit description is too short (minimum 10 characters)`,\n suggestion: `Be more descriptive about what changed`,\n };\n }\n\n if (description[0] !== description[0].toLowerCase()) {\n return {\n valid: false,\n error: `Commit description must start with lowercase letter`,\n suggestion: `Change '${description}' to '${description[0].toLowerCase()}${description.slice(1)}'`,\n };\n }\n\n return { valid: true };\n}\n\nexport async function validatePRTitle(\n title: string,\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<ValidationResult> {\n // PR titles follow same format as commit messages\n return validateCommitMessage(title, config, workspacePath);\n}\n","/**\n * Document reference validator for checking broken markdown links\n * and file references in documentation\n */\n\nimport { existsSync } from \"fs\";\nimport { readFile } from \"fs/promises\";\nimport { resolve, dirname } from \"path\";\nimport fg from \"fast-glob\";\n\nexport interface DocumentReference {\n /** Source file containing the reference */\n file: string;\n /** Line number (1-indexed) */\n line: number;\n /** Column position (1-indexed) */\n column: number;\n /** Original markdown link text (e.g., \"[text](path)\") */\n rawLink: string;\n /** Extracted target path */\n targetPath: string;\n /** Type of reference */\n type: \"link\" | \"image\" | \"reference\";\n}\n\nexport interface BrokenReference extends DocumentReference {\n /** Possible correct paths (from glob matching) */\n suggestions: string[];\n}\n\nexport interface DocumentValidationResult {\n valid: boolean;\n scannedFiles: number;\n totalReferences: number;\n brokenReferences: BrokenReference[];\n errors: string[];\n}\n\n/**\n * Regex patterns for detecting markdown links\n */\nconst LINK_PATTERNS = {\n // Inline links: [text](path)\n inline: /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n // Images: ![alt](path)\n image: /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g,\n // Reference-style: [text]: path\n reference: /^\\[([^\\]]+)\\]:\\s*(.+)$/gm,\n};\n\n/**\n * Check if a URL is external (http/https)\n */\nfunction isExternalUrl(path: string): boolean {\n return /^https?:\\/\\//i.test(path);\n}\n\n/**\n * Check if a path is an anchor link (starts with #)\n */\nfunction isAnchorLink(path: string): boolean {\n return path.startsWith(\"#\");\n}\n\n/**\n * Resolve a relative path from a source file\n */\nfunction resolveReferencePath(\n sourcePath: string,\n targetPath: string,\n projectRoot: string,\n): string {\n // Remove any anchor fragments\n const pathWithoutAnchor = targetPath.split(\"#\")[0];\n if (!pathWithoutAnchor) return targetPath;\n\n const sourceDir = dirname(sourcePath);\n\n // If absolute path from project root, resolve from project root\n if (pathWithoutAnchor.startsWith(\"/\")) {\n return resolve(projectRoot, pathWithoutAnchor.slice(1));\n }\n\n // Otherwise resolve relative to source file\n return resolve(sourceDir, pathWithoutAnchor);\n}\n\n/**\n * Extract all document references from a markdown file\n */\nexport async function extractReferences(\n filePath: string,\n _projectRoot: string,\n): Promise<DocumentReference[]> {\n const content = await readFile(filePath, \"utf-8\");\n const references: DocumentReference[] = [];\n const lines = content.split(\"\\n\");\n\n // Process inline links [text](path)\n lines.forEach((line, index) => {\n let match;\n const inlineRegex = new RegExp(LINK_PATTERNS.inline);\n while ((match = inlineRegex.exec(line)) !== null) {\n const targetPath = match[2].trim();\n\n // Skip external URLs and anchor-only links\n if (isExternalUrl(targetPath) || isAnchorLink(targetPath)) {\n continue;\n }\n\n references.push({\n file: filePath,\n line: index + 1,\n column: match.index + 1,\n rawLink: match[0],\n targetPath,\n type: \"link\",\n });\n }\n });\n\n // Process images ![alt](path)\n lines.forEach((line, index) => {\n let match;\n const imageRegex = new RegExp(LINK_PATTERNS.image);\n while ((match = imageRegex.exec(line)) !== null) {\n const targetPath = match[2].trim();\n\n // Skip external URLs\n if (isExternalUrl(targetPath)) {\n continue;\n }\n\n references.push({\n file: filePath,\n line: index + 1,\n column: match.index + 1,\n rawLink: match[0],\n targetPath,\n type: \"image\",\n });\n }\n });\n\n // Process reference-style links [ref]: path\n let match;\n const referenceRegex = new RegExp(LINK_PATTERNS.reference);\n while ((match = referenceRegex.exec(content)) !== null) {\n const targetPath = match[2].trim();\n\n // Skip external URLs and anchor-only links\n if (isExternalUrl(targetPath) || isAnchorLink(targetPath)) {\n continue;\n }\n\n // Find line number\n const beforeMatch = content.substring(0, match.index);\n const lineNumber = beforeMatch.split(\"\\n\").length;\n\n references.push({\n file: filePath,\n line: lineNumber,\n column: match.index - beforeMatch.lastIndexOf(\"\\n\"),\n rawLink: match[0],\n targetPath,\n type: \"reference\",\n });\n }\n\n return references;\n}\n\n/**\n * Find similar files using glob patterns\n */\nexport async function findSimilarFiles(\n targetPath: string,\n projectRoot: string,\n): Promise<string[]> {\n // Extract filename and extension\n const parts = targetPath.split(\"/\");\n const filename = parts[parts.length - 1];\n const basename = filename.split(\".\")[0];\n const ext = filename.includes(\".\") ? filename.split(\".\").pop() : \"\";\n\n // Try multiple glob patterns for finding similar files\n const patterns = [\n `**/${filename}`, // Exact filename match anywhere\n ext ? `**/${basename}*.${ext}` : `**/${basename}*`, // Similar basename\n ext ? `**/*${basename}*.${ext}` : `**/*${basename}*`, // Contains basename\n ];\n\n const foundFiles = new Set<string>();\n\n for (const pattern of patterns) {\n try {\n const matches = await fg(pattern, {\n cwd: projectRoot,\n ignore: [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\", \"**/build/**\"],\n absolute: false,\n onlyFiles: true,\n });\n\n matches.forEach((file) => foundFiles.add(file));\n\n // Limit suggestions to 10 files\n if (foundFiles.size >= 10) break;\n } catch (error) {\n // Continue with next pattern\n }\n }\n\n return Array.from(foundFiles).slice(0, 10);\n}\n\n/**\n * Scan all markdown files in a project for references\n */\nexport async function scanDocumentReferences(\n projectPath: string,\n options?: { patterns?: string[]; ignore?: string[] },\n): Promise<DocumentReference[]> {\n const patterns = options?.patterns || [\"**/*.md\"];\n const ignore = options?.ignore || [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/dist/**\",\n \"**/build/**\",\n ];\n\n const files = await fg(patterns, {\n cwd: projectPath,\n ignore,\n absolute: true,\n onlyFiles: true,\n });\n\n const allReferences: DocumentReference[] = [];\n\n for (const file of files) {\n try {\n const references = await extractReferences(file, projectPath);\n allReferences.push(...references);\n } catch (error) {\n // Skip files that can't be read\n continue;\n }\n }\n\n return allReferences;\n}\n\n/**\n * Validate document references and find broken links\n */\nexport async function validateDocumentReferences(\n projectPath: string,\n options?: { patterns?: string[]; ignore?: string[] },\n): Promise<DocumentValidationResult> {\n const errors: string[] = [];\n const brokenReferences: BrokenReference[] = [];\n\n try {\n // Scan all references\n const references = await scanDocumentReferences(projectPath, options);\n\n // Count unique files scanned\n const scannedFiles = new Set(references.map((ref) => ref.file)).size;\n\n // Check each reference\n for (const ref of references) {\n try {\n const resolvedPath = resolveReferencePath(\n ref.file,\n ref.targetPath,\n projectPath,\n );\n\n // Check if the file exists\n if (!existsSync(resolvedPath)) {\n // Find similar files for suggestions\n const suggestions = await findSimilarFiles(ref.targetPath, projectPath);\n\n brokenReferences.push({\n ...ref,\n suggestions,\n });\n }\n } catch (error) {\n // Path resolution error\n const suggestions = await findSimilarFiles(ref.targetPath, projectPath);\n brokenReferences.push({\n ...ref,\n suggestions,\n });\n }\n }\n\n return {\n valid: brokenReferences.length === 0,\n scannedFiles,\n totalReferences: references.length,\n brokenReferences,\n errors,\n };\n } catch (error) {\n errors.push(\n error instanceof Error ? error.message : \"Unknown error during validation\",\n );\n\n return {\n valid: false,\n scannedFiles: 0,\n totalReferences: 0,\n brokenReferences: [],\n errors,\n };\n }\n}\n\n/**\n * Apply a fix to a broken reference\n */\nexport async function applyReferenceFix(\n filePath: string,\n oldLink: string,\n newPath: string,\n): Promise<void> {\n const content = await readFile(filePath, \"utf-8\");\n\n // Replace the old link with the new path\n const updatedContent = content.replace(oldLink, (match) => {\n // Extract the link text and replace only the path part\n if (match.startsWith(\"![\")) {\n // Image: ![alt](oldPath) -> ![alt](newPath)\n return match.replace(/\\(([^)]+)\\)/, `(${newPath})`);\n } else if (match.startsWith(\"[\") && match.includes(\"](\")) {\n // Inline link: [text](oldPath) -> [text](newPath)\n return match.replace(/\\(([^)]+)\\)/, `(${newPath})`);\n } else {\n // Reference-style: [ref]: oldPath -> [ref]: newPath\n return match.replace(/:\\s*(.+)$/, `: ${newPath}`);\n }\n });\n\n const fs = await import(\"fs/promises\");\n await fs.writeFile(filePath, updatedContent, \"utf-8\");\n}\n"],"mappings":"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","names":["match"]}