workflow-agent-cli 2.23.0 → 2.23.1
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.
- package/README.md +15 -15
- package/dist/{auto-fix-7WAESKYO.js → auto-fix-WGRYEFLJ.js} +2 -2
- package/dist/{chunk-IWFUJ2DS.js → chunk-DWHIY7R4.js} +20 -9
- package/dist/chunk-DWHIY7R4.js.map +1 -0
- package/dist/{chunk-M6RHIUSM.js → chunk-K42R54II.js} +147 -54
- package/dist/chunk-K42R54II.js.map +1 -0
- package/dist/{chunk-XGS2VFBP.js → chunk-KJFRMHQU.js} +783 -18
- package/dist/chunk-KJFRMHQU.js.map +1 -0
- package/dist/{chunk-YELUGXOM.js → chunk-NPJJZHJG.js} +1 -1
- package/dist/chunk-NPJJZHJG.js.map +1 -0
- package/dist/{chunk-UWJ2ZGEI.js → chunk-OH6TK27N.js} +3 -3
- package/dist/{chunk-D36IFZUZ.js → chunk-PLVOGB6Y.js} +15 -4
- package/dist/chunk-PLVOGB6Y.js.map +1 -0
- package/dist/{chunk-ZLDJ2OGO.js → chunk-WK7D2AVV.js} +11 -3
- package/dist/{chunk-ZLDJ2OGO.js.map → chunk-WK7D2AVV.js.map} +1 -1
- package/dist/cli/index.js +771 -285
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/index.d.ts +78 -56
- package/dist/index.js +22 -10
- package/dist/scripts/postinstall.js +1 -1
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/sync-HHQM3GKR.js +7 -0
- package/dist/validators/index.js +1 -1
- package/dist/verify-OIHY7SGG.js +8 -0
- package/package.json +2 -2
- package/dist/chunk-3ADL5QDN.js +0 -396
- package/dist/chunk-3ADL5QDN.js.map +0 -1
- package/dist/chunk-D36IFZUZ.js.map +0 -1
- package/dist/chunk-IWFUJ2DS.js.map +0 -1
- package/dist/chunk-M6RHIUSM.js.map +0 -1
- package/dist/chunk-XGS2VFBP.js.map +0 -1
- package/dist/chunk-YELUGXOM.js.map +0 -1
- package/dist/sync-6T5TD4QS.js +0 -7
- package/dist/verify-TX6LFMI6.js +0 -8
- /package/dist/{auto-fix-7WAESKYO.js.map → auto-fix-WGRYEFLJ.js.map} +0 -0
- /package/dist/{chunk-UWJ2ZGEI.js.map → chunk-OH6TK27N.js.map} +0 -0
- /package/dist/{sync-6T5TD4QS.js.map → sync-HHQM3GKR.js.map} +0 -0
- /package/dist/{verify-TX6LFMI6.js.map → verify-OIHY7SGG.js.map} +0 -0
|
@@ -1 +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: \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 \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 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":";AAAA,OAAO,gBAAgB;AAEvB,SAAS,eAAe;AACxB,SAAS,YAAY;;;ACErB,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,SAAS,eAAe;AACjC,OAAO,QAAQ;AAiCf,IAAM,gBAAgB;AAAA;AAAA,EAEpB,QAAQ;AAAA;AAAA,EAER,OAAO;AAAA;AAAA,EAEP,WAAW;AACb;AAKA,SAAS,cAAc,MAAuB;AAC5C,SAAO,gBAAgB,KAAK,IAAI;AAClC;AAKA,SAAS,aAAa,MAAuB;AAC3C,SAAO,KAAK,WAAW,GAAG;AAC5B;AAKA,SAAS,qBACP,YACA,YACA,aACQ;AAER,QAAM,oBAAoB,WAAW,MAAM,GAAG,EAAE,CAAC;AACjD,MAAI,CAAC,kBAAmB,QAAO;AAE/B,QAAM,YAAY,QAAQ,UAAU;AAGpC,MAAI,kBAAkB,WAAW,GAAG,GAAG;AACrC,WAAO,QAAQ,aAAa,kBAAkB,MAAM,CAAC,CAAC;AAAA,EACxD;AAGA,SAAO,QAAQ,WAAW,iBAAiB;AAC7C;AAKA,eAAsB,kBACpB,UACA,cAC8B;AAC9B,QAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,QAAM,aAAkC,CAAC;AACzC,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAGhC,QAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,QAAIA;AACJ,UAAM,cAAc,IAAI,OAAO,cAAc,MAAM;AACnD,YAAQA,SAAQ,YAAY,KAAK,IAAI,OAAO,MAAM;AAChD,YAAM,aAAaA,OAAM,CAAC,EAAE,KAAK;AAGjC,UAAI,cAAc,UAAU,KAAK,aAAa,UAAU,GAAG;AACzD;AAAA,MACF;AAEA,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA,QACd,QAAQA,OAAM,QAAQ;AAAA,QACtB,SAASA,OAAM,CAAC;AAAA,QAChB;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAGD,QAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,QAAIA;AACJ,UAAM,aAAa,IAAI,OAAO,cAAc,KAAK;AACjD,YAAQA,SAAQ,WAAW,KAAK,IAAI,OAAO,MAAM;AAC/C,YAAM,aAAaA,OAAM,CAAC,EAAE,KAAK;AAGjC,UAAI,cAAc,UAAU,GAAG;AAC7B;AAAA,MACF;AAEA,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA,QACd,QAAQA,OAAM,QAAQ;AAAA,QACtB,SAASA,OAAM,CAAC;AAAA,QAChB;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAGD,MAAI;AACJ,QAAM,iBAAiB,IAAI,OAAO,cAAc,SAAS;AACzD,UAAQ,QAAQ,eAAe,KAAK,OAAO,OAAO,MAAM;AACtD,UAAM,aAAa,MAAM,CAAC,EAAE,KAAK;AAGjC,QAAI,cAAc,UAAU,KAAK,aAAa,UAAU,GAAG;AACzD;AAAA,IACF;AAGA,UAAM,cAAc,QAAQ,UAAU,GAAG,MAAM,KAAK;AACpD,UAAM,aAAa,YAAY,MAAM,IAAI,EAAE;AAE3C,eAAW,KAAK;AAAA,MACd,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ,MAAM,QAAQ,YAAY,YAAY,IAAI;AAAA,MAClD,SAAS,MAAM,CAAC;AAAA,MAChB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKA,eAAsB,iBACpB,YACA,aACmB;AAEnB,QAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,QAAM,WAAW,MAAM,MAAM,SAAS,CAAC;AACvC,QAAM,WAAW,SAAS,MAAM,GAAG,EAAE,CAAC;AACtC,QAAM,MAAM,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI,IAAI;AAGjE,QAAM,WAAW;AAAA,IACf,MAAM,QAAQ;AAAA;AAAA,IACd,MAAM,MAAM,QAAQ,KAAK,GAAG,KAAK,MAAM,QAAQ;AAAA;AAAA,IAC/C,MAAM,OAAO,QAAQ,KAAK,GAAG,KAAK,OAAO,QAAQ;AAAA;AAAA,EACnD;AAEA,QAAM,aAAa,oBAAI,IAAY;AAEnC,aAAW,WAAW,UAAU;AAC9B,QAAI;AACF,YAAM,UAAU,MAAM,GAAG,SAAS;AAAA,QAChC,KAAK;AAAA,QACL,QAAQ,CAAC,sBAAsB,cAAc,cAAc,aAAa;AAAA,QACxE,UAAU;AAAA,QACV,WAAW;AAAA,MACb,CAAC;AAED,cAAQ,QAAQ,CAAC,SAAS,WAAW,IAAI,IAAI,CAAC;AAG9C,UAAI,WAAW,QAAQ,GAAI;AAAA,IAC7B,SAAS,OAAO;AAAA,IAEhB;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,UAAU,EAAE,MAAM,GAAG,EAAE;AAC3C;AAKA,eAAsB,uBACpB,aACA,SAC8B;AAC9B,QAAM,WAAW,SAAS,YAAY,CAAC,SAAS;AAChD,QAAM,SAAS,SAAS,UAAU;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,GAAG,UAAU;AAAA,IAC/B,KAAK;AAAA,IACL;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,EACb,CAAC;AAED,QAAM,gBAAqC,CAAC;AAE5C,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAM,aAAa,MAAM,kBAAkB,MAAM,WAAW;AAC5D,oBAAc,KAAK,GAAG,UAAU;AAAA,IAClC,SAAS,OAAO;AAEd;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,2BACpB,aACA,SACmC;AACnC,QAAM,SAAmB,CAAC;AAC1B,QAAM,mBAAsC,CAAC;AAE7C,MAAI;AAEF,UAAM,aAAa,MAAM,uBAAuB,aAAa,OAAO;AAGpE,UAAM,eAAe,IAAI,IAAI,WAAW,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE;AAGhE,eAAW,OAAO,YAAY;AAC5B,UAAI;AACF,cAAM,eAAe;AAAA,UACnB,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ;AAAA,QACF;AAGA,YAAI,CAAC,WAAW,YAAY,GAAG;AAE7B,gBAAM,cAAc,MAAM,iBAAiB,IAAI,YAAY,WAAW;AAEtE,2BAAiB,KAAK;AAAA,YACpB,GAAG;AAAA,YACH;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,SAAS,OAAO;AAEd,cAAM,cAAc,MAAM,iBAAiB,IAAI,YAAY,WAAW;AACtE,yBAAiB,KAAK;AAAA,UACpB,GAAG;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,MACL,OAAO,iBAAiB,WAAW;AAAA,MACnC;AAAA,MACA,iBAAiB,WAAW;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,MACP,cAAc;AAAA,MACd,iBAAiB;AAAA,MACjB,kBAAkB,CAAC;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;AAKA,eAAsB,kBACpB,UACA,SACA,SACe;AACf,QAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAGhD,QAAM,iBAAiB,QAAQ,QAAQ,SAAS,CAAC,UAAU;AAEzD,QAAI,MAAM,WAAW,IAAI,GAAG;AAE1B,aAAO,MAAM,QAAQ,eAAe,IAAI,OAAO,GAAG;AAAA,IACpD,WAAW,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,IAAI,GAAG;AAExD,aAAO,MAAM,QAAQ,eAAe,IAAI,OAAO,GAAG;AAAA,IACpD,OAAO;AAEL,aAAO,MAAM,QAAQ,aAAa,KAAK,OAAO,EAAE;AAAA,IAClD;AAAA,EACF,CAAC;AAED,QAAM,KAAK,MAAM,OAAO,aAAa;AACrC,QAAM,GAAG,UAAU,UAAU,gBAAgB,OAAO;AACtD;;;ADpUA,IAAI,oBAAoC;AACxC,IAAI,iBAAyB;AAC7B,IAAM,YAAY,IAAI,KAAK;AAO3B,eAAsB,qBACpB,gBAAwB,QAAQ,IAAI,GAClB;AAElB,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,qBAAqB,MAAM,iBAAiB,WAAW;AACzD,WAAO;AAAA,EACT;AAEA,QAAM,mBAA4B,CAAC;AAEnC,MAAI;AAEF,UAAM,qBAAqB,CAAC,KAAK,eAAe,UAAU,GAAG,aAAa;AAE1E,eAAW,YAAY,oBAAoB;AACzC,UAAI;AACF,cAAM,UAAU,MAAM,QAAQ,UAAU,EAAE,eAAe,KAAK,CAAC;AAE/D,mBAAW,SAAS,SAAS;AAC3B,cAAI,MAAM,YAAY,KAAK,MAAM,KAAK,WAAW,SAAS,GAAG;AAC3D,kBAAM,YAAY,KAAK,UAAU,MAAM,MAAM,OAAO,UAAU;AAC9D,gBAAI;AACF,oBAAM,SAAS,MAAM,OAAO;AAC5B,oBAAM,SAAS,OAAO,UAAU,OAAO,SAAS;AAEhD,kBAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,iCAAiB,KAAK,GAAG,MAAM;AAAA,cACjC;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,wBAAoB;AACpB,qBAAiB;AAAA,EACnB,SAAS,OAAO;AAEd,YAAQ,KAAK,6CAA6C,KAAK;AAAA,EACjE;AAEA,SAAO;AACT;AAKO,SAAS,8BAAoC;AAClD,sBAAoB;AACpB,mBAAiB;AACnB;AAQA,eAAsB,aACpB,QACA,eACkB;AAClB,QAAM,eAAe,OAAO;AAC5B,QAAM,eAAe,MAAM,qBAAqB,aAAa;AAG7D,QAAM,WAAW,oBAAI,IAAmB;AAGxC,aAAW,SAAS,cAAc;AAChC,aAAS,IAAI,MAAM,MAAM,KAAK;AAAA,EAChC;AAGA,aAAW,SAAS,cAAc;AAChC,QAAI,CAAC,SAAS,IAAI,MAAM,IAAI,GAAG;AAC7B,eAAS,IAAI,MAAM,MAAM,KAAK;AAAA,IAChC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AACrC;AAEA,eAAsB,mBACpB,YACA,QACA,eAC2B;AAC3B,QAAM,cAAc,OAAO,eAAe;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,YAAY,MAAM,aAAa,QAAQ,aAAa;AAC1D,QAAM,SAAS,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI;AAG1C,QAAM,gBAAgB;AACtB,QAAM,QAAQ,WAAW,MAAM,aAAa;AAE5C,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,YAAY,YAAY,UAAU;AAAA,IACpC;AAAA,EACF;AAEA,QAAM,CAAC,EAAE,MAAM,OAAO,WAAW,IAAI;AAGrC,MAAI,CAAC,YAAY,SAAS,IAAkB,GAAG;AAC7C,UAAM,aAAa,WAAW,MAAM,WAAW;AAC/C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,wBAAwB,IAAI,sBAAsB,YAAY,KAAK,IAAI,CAAC;AAAA,MAC/E,YAAY,aAAa,iBAAiB,UAAU,OAAO;AAAA,IAC7D;AAAA,EACF;AAGA,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,UAAM,aAAa,WAAW,OAAO,MAAM;AAC3C,UAAM,YACJ,OAAO,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,KAAK,OAAO,SAAS,IAAI,QAAQ;AAC/D,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,kBAAkB,KAAK,sBAAsB,SAAS;AAAA,MAC7D,YAAY,aAAa,iBAAiB,UAAU,OAAO;AAAA,IAC7D;AAAA,EACF;AAGA,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,uBAAuB,WAAW;AAAA,IAC3C;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAEA,eAAsB,sBACpB,SACA,QACA,eAC2B;AAC3B,QAAM,oBAAoB,OAAO,qBAAqB;AAAA,IACpD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,YAAY,MAAM,aAAa,QAAQ,aAAa;AAC1D,QAAM,SAAS,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI;AAG1C,QAAM,gBAAgB;AACtB,QAAM,QAAQ,QAAQ,MAAM,aAAa;AAEzC,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,CAAC,EAAE,MAAM,OAAO,WAAW,IAAI;AAGrC,MAAI,CAAC,kBAAkB,SAAS,IAAW,GAAG;AAC5C,UAAM,aAAa,WAAW,MAAM,iBAAiB;AACrD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,wBAAwB,IAAI,sBAAsB,kBAAkB,KAAK,IAAI,CAAC;AAAA,MACrF,YAAY,aAAa,iBAAiB,UAAU,OAAO;AAAA,IAC7D;AAAA,EACF;AAGA,MAAI,SAAS,CAAC,OAAO,SAAS,KAAK,GAAG;AACpC,UAAM,aAAa,WAAW,OAAO,MAAM;AAC3C,UAAM,YACJ,OAAO,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,KAAK,OAAO,SAAS,IAAI,QAAQ;AAC/D,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,kBAAkB,KAAK,sBAAsB,SAAS;AAAA,MAC7D,YAAY,aAAa,iBAAiB,UAAU,OAAO;AAAA,IAC7D;AAAA,EACF;AAGA,MAAI,YAAY,SAAS,IAAI;AAC3B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,YAAY;AAAA,IACd;AAAA,EACF;AAEA,MAAI,YAAY,CAAC,MAAM,YAAY,CAAC,EAAE,YAAY,GAAG;AACnD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,YAAY,WAAW,WAAW,SAAS,YAAY,CAAC,EAAE,YAAY,CAAC,GAAG,YAAY,MAAM,CAAC,CAAC;AAAA,IAChG;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAEA,eAAsB,gBACpB,OACA,QACA,eAC2B;AAE3B,SAAO,sBAAsB,OAAO,QAAQ,aAAa;AAC3D;","names":["match"]}
|
|
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: \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 \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: [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/dist/**\",\n \"**/build/**\",\n ],\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(\n ref.targetPath,\n projectPath,\n );\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\n ? error.message\n : \"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 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"]}
|