@kb-labs/commit-core 2.118.2 → 2.119.0-canary.0b4b21a55
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/dist/analyzer/index.d.ts +9 -1
- package/dist/analyzer/index.js +4 -1
- package/dist/analyzer/index.js.map +1 -1
- package/dist/applier/index.js.map +1 -1
- package/dist/generator/index.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/validator/index.js.map +1 -1
- package/package.json +4 -4
package/dist/analyzer/index.d.ts
CHANGED
|
@@ -43,6 +43,14 @@ declare class SecretsDetectedError extends Error {
|
|
|
43
43
|
readonly secretMatches: SecretMatch[];
|
|
44
44
|
constructor(matches: SecretMatch[], message: string);
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Structural check for SecretsDetectedError, safe across subpath-export
|
|
48
|
+
* boundaries (`./analyzer` vs `./generator`). tsup bundles each entry
|
|
49
|
+
* independently, so `error instanceof SecretsDetectedError` fails when the
|
|
50
|
+
* throw site and the catch site pull the class from different subpaths —
|
|
51
|
+
* they end up with two distinct class objects despite identical source.
|
|
52
|
+
*/
|
|
53
|
+
declare function isSecretsDetectedError(error: unknown): error is SecretsDetectedError;
|
|
46
54
|
/**
|
|
47
55
|
* Detailed information about a detected secret
|
|
48
56
|
*/
|
|
@@ -87,4 +95,4 @@ declare function detectSecretsWithLocation(diffs: Map<string, string>): SecretMa
|
|
|
87
95
|
*/
|
|
88
96
|
declare function formatSecretsReport(matches: SecretMatch[]): string;
|
|
89
97
|
|
|
90
|
-
export { type PackageInfo, type ResolvedScope, type SecretMatch, SecretsDetectedError, containsSecrets, detectSecretFiles, detectSecretsInDiffs, detectSecretsWithLocation, formatSecretsReport, formatSecretsWarning, isSecretFile, matchesScope, resolveScope };
|
|
98
|
+
export { type PackageInfo, type ResolvedScope, type SecretMatch, SecretsDetectedError, containsSecrets, detectSecretFiles, detectSecretsInDiffs, detectSecretsWithLocation, formatSecretsReport, formatSecretsWarning, isSecretFile, isSecretsDetectedError, matchesScope, resolveScope };
|
package/dist/analyzer/index.js
CHANGED
|
@@ -396,6 +396,9 @@ var SecretsDetectedError = class _SecretsDetectedError extends Error {
|
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
398
|
};
|
|
399
|
+
function isSecretsDetectedError(error) {
|
|
400
|
+
return error instanceof Error && error.name === "SecretsDetectedError" && Array.isArray(error.secretMatches);
|
|
401
|
+
}
|
|
399
402
|
var SECRET_FILE_PATTERNS = [
|
|
400
403
|
// Environment files
|
|
401
404
|
".env",
|
|
@@ -580,6 +583,6 @@ function formatSecretsReport(matches) {
|
|
|
580
583
|
return lines.join("\n");
|
|
581
584
|
}
|
|
582
585
|
|
|
583
|
-
export { SecretsDetectedError, containsSecrets, detectCommitStyle, detectSecretFiles, detectSecretsInDiffs, detectSecretsWithLocation, formatFileSummary, formatSecretsReport, formatSecretsWarning, getAllChangedFiles, getCurrentBranch, getFileDiff, getFileDiffs, getFileSummaries, getGitStatus, getRecentCommits, hasChanges, isProtectedBranch, isSecretFile, matchesScope, resolveScope };
|
|
586
|
+
export { SecretsDetectedError, containsSecrets, detectCommitStyle, detectSecretFiles, detectSecretsInDiffs, detectSecretsWithLocation, formatFileSummary, formatSecretsReport, formatSecretsWarning, getAllChangedFiles, getCurrentBranch, getFileDiff, getFileDiffs, getFileSummaries, getGitStatus, getRecentCommits, hasChanges, isProtectedBranch, isSecretFile, isSecretsDetectedError, matchesScope, resolveScope };
|
|
584
587
|
//# sourceMappingURL=index.js.map
|
|
585
588
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analyzer/git-status.ts","../../src/analyzer/file-summary.ts","../../src/analyzer/file-diff.ts","../../src/analyzer/recent-commits.ts","../../src/analyzer/scope-resolver.ts","../../src/analyzer/secrets-detector.ts"],"names":["simpleGit","group"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Git status analysis\n */\n\nimport { simpleGit, type SimpleGit, type StatusResult } from \"simple-git\";\nimport type { GitStatus } from \"@kb-labs/commit-contracts\";\n\n/**\n * Get current git status (staged, unstaged, untracked files).\n * cwd must already point to the resolved scope directory.\n */\nexport async function getGitStatus(cwd: string): Promise<GitStatus> {\n // --ignore-submodules=all: exclude submodule pointer drift in worktrees\n const git: SimpleGit = simpleGit(cwd);\n const status: StatusResult = await git.status(['--ignore-submodules=all']);\n\n return {\n staged: status.staged.filter((f) => !shouldIgnoreFile(f)),\n unstaged: [...status.modified, ...status.deleted]\n .filter((f) => !status.staged.includes(f))\n .filter((f) => !shouldIgnoreFile(f)),\n untracked: status.not_added.filter((f) => !shouldIgnoreFile(f)),\n };\n}\n\nconst IGNORED_SEGMENTS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".turbo\",\n \"coverage\",\n]);\n\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n * Uses exact path segment matching to avoid false positives like \"my-dist/file.ts\".\n */\nfunction shouldIgnoreFile(file: string): boolean {\n return file.split(\"/\").some((segment) => IGNORED_SEGMENTS.has(segment));\n}\n\n/**\n * Get all changed files (staged + unstaged + untracked)\n * Filters out node_modules and other build artifacts\n */\nexport function getAllChangedFiles(status: GitStatus): string[] {\n const allFiles = [\n ...new Set([...status.staged, ...status.unstaged, ...status.untracked]),\n ];\n return allFiles.filter((file) => !shouldIgnoreFile(file));\n}\n\n/**\n * Check if there are any changes\n */\nexport function hasChanges(status: GitStatus): boolean {\n return (\n status.staged.length > 0 ||\n status.unstaged.length > 0 ||\n status.untracked.length > 0\n );\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n const git: SimpleGit = simpleGit(cwd);\n const branch = await git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch.trim();\n}\n\n/**\n * Check if branch is protected (main/master)\n */\nexport function isProtectedBranch(branch: string): boolean {\n const protectedBranches = [\n \"main\",\n \"master\",\n \"develop\",\n \"release\",\n \"production\",\n ];\n return protectedBranches.includes(branch.toLowerCase());\n}\n","/**\n * File summary extraction\n */\n\n/* eslint-disable no-await-in-loop -- Sequential git operations required: must check status, read diffs, compute summaries one-by-one for each file */\n\nimport {\n simpleGit,\n type SimpleGit,\n type DiffResultTextFile,\n type DiffResultBinaryFile,\n} from \"simple-git\";\nimport type { FileSummary } from \"@kb-labs/commit-contracts\";\n\n/**\n * Type guard for text file diff result\n */\nfunction isTextFile(\n file: DiffResultTextFile | DiffResultBinaryFile,\n): file is DiffResultTextFile {\n return !file.binary;\n}\n\n/**\n * Check if file is truly new (doesn't exist in current HEAD commit)\n * Returns true if file doesn't exist in HEAD (the current committed state)\n * Returns false if file exists in HEAD (even if modified/deleted locally)\n * Supports nested git repositories\n */\nasync function isNewFile(cwd: string, filePath: string): Promise<boolean> {\n try {\n // Find the git repository for this file (walks up directory tree)\n const repo = await findGitRepo(cwd, filePath);\n\n if (!repo) {\n // No git repo found - treat as new file\n return true;\n }\n\n const git = simpleGit(repo.repoPath);\n\n // Check if file exists in HEAD (current commit), not in history\n // git ls-tree HEAD -- <file>\n // Returns tree entry if file exists in HEAD, empty if file is new\n // This correctly handles modified/unstaged files (they exist in HEAD = not new)\n const result = await git.raw([\"ls-tree\", \"HEAD\", \"--\", repo.relativePath]);\n\n // If no output, file doesn't exist in HEAD → truly new file\n // If has output, file exists in HEAD → not new (modified/unstaged)\n return result.trim().length === 0;\n } catch {\n // On error, assume it's new (safer to say \"new\" than claim it existed)\n return true;\n }\n}\n\n/**\n * Get file summaries with diff stats for given files\n * Supports nested git repositories by grouping files per repo\n */\n// eslint-disable-next-line sonarjs/cognitive-complexity -- File summary extraction: groups files by repo, handles nested git repos, computes diff stats, processes staged/unstaged/untracked files\nexport async function getFileSummaries(\n cwd: string,\n files: string[],\n): Promise<FileSummary[]> {\n if (files.length === 0) {\n return [];\n }\n\n const summaries: FileSummary[] = [];\n\n // Group files by repository (walks up directory tree to find .git)\n const filesByRepo = new Map<\n string,\n { repoPath: string; relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n const repo = await findGitRepo(cwd, file);\n\n if (!repo) {\n // No git repo found - treat as file in cwd\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ repoPath: cwd, relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n // Group by repository path\n const group = filesByRepo.get(repo.repoPath) ?? [];\n group.push({\n repoPath: repo.repoPath,\n relativePath: repo.relativePath,\n originalPath: file,\n });\n filesByRepo.set(repo.repoPath, group);\n }\n\n // Get diff summaries for each repo separately\n for (const [repoPath, fileInfos] of filesByRepo) {\n const git: SimpleGit = simpleGit(repoPath);\n const relativePaths = fileInfos.map((f) => f.relativePath);\n\n try {\n // Try staged diff first\n const stagedDiff = await git.diffSummary([\n \"--cached\",\n \"--\",\n ...relativePaths,\n ]);\n\n // Then try unstaged diff (working tree changes)\n const unstagedDiff = await git.diffSummary([\"--\", ...relativePaths]);\n\n // Combine results (prefer staged if file appears in both)\n const allDiffFiles = new Map<\n string,\n DiffResultTextFile | DiffResultBinaryFile\n >();\n for (const file of unstagedDiff.files) {\n allDiffFiles.set(file.file, file);\n }\n for (const file of stagedDiff.files) {\n // Staged takes priority\n allDiffFiles.set(file.file, file);\n }\n\n // Process diff results\n for (const file of allDiffFiles.values()) {\n // Find original path (with repo prefix)\n const fileInfo = fileInfos.find((f) => f.relativePath === file.file);\n if (!fileInfo) {\n continue;\n }\n\n // Check if file is truly new (using repoPath as cwd)\n const isNew = await isNewFile(repoPath, file.file);\n\n // Handle both text and binary files\n if (isTextFile(file)) {\n summaries.push({\n path: fileInfo.originalPath,\n status: mapDiffStatus(file.insertions, file.deletions),\n additions: file.insertions,\n deletions: file.deletions,\n binary: false,\n isNewFile: isNew,\n });\n } else {\n // Binary file\n summaries.push({\n path: fileInfo.originalPath,\n status: \"modified\",\n additions: 0,\n deletions: 0,\n binary: true,\n isNewFile: isNew,\n });\n }\n }\n\n // Add any missing files from this repo (untracked - not in git yet)\n const processedPaths = new Set(\n Array.from(allDiffFiles.values()).map((f) => f.file),\n );\n const missingInRepo = fileInfos.filter(\n (f) => !processedPaths.has(f.relativePath),\n );\n\n for (const fileInfo of missingInRepo) {\n // Untracked files are always new (never existed in git)\n summaries.push({\n path: fileInfo.originalPath,\n status: \"added\",\n additions: 0,\n deletions: 0,\n binary: false,\n isNewFile: true,\n });\n }\n } catch {\n // Fallback: create basic summaries for this repo's files\n for (const fileInfo of fileInfos) {\n summaries.push({\n path: fileInfo.originalPath,\n status: \"modified\",\n additions: 0,\n deletions: 0,\n binary: false,\n isNewFile: false, // Conservative assumption\n });\n }\n }\n }\n\n return summaries;\n}\n\n/**\n * Map insertions/deletions to status\n */\nfunction mapDiffStatus(\n insertions: number,\n deletions: number,\n): FileSummary[\"status\"] {\n // Simple heuristic: if only insertions, likely added; if only deletions, likely deleted\n if (deletions === 0 && insertions > 0) {\n return \"added\";\n }\n if (insertions === 0 && deletions > 0) {\n return \"deleted\";\n }\n return \"modified\";\n}\n\n/**\n * Get a short summary string for display\n */\nexport function formatFileSummary(summary: FileSummary): string {\n const stats = summary.binary\n ? \"binary\"\n : `+${summary.additions}/-${summary.deletions}`;\n return `${summary.path} (${summary.status}, ${stats})`;\n}\n\n/**\n * Get diff content for specific files\n * Used when LLM requests more context (Phase 2 escalation)\n * Supports nested git repositories (files with prefix like 'kb-labs-sdk/...')\n */\n// eslint-disable-next-line sonarjs/cognitive-complexity -- Diff extraction: groups files by nested repo, handles staged/unstaged diffs, processes binary files, merges results\nexport async function getFileDiffs(\n cwd: string,\n files: string[],\n): Promise<Map<string, string>> {\n if (files.length === 0) {\n return new Map();\n }\n\n const diffs = new Map<string, string>();\n\n // Group files by repository (walks up directory tree to find .git)\n const filesByRepo = new Map<\n string,\n { repoPath: string; relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n const repo = await findGitRepo(cwd, file);\n\n if (!repo) {\n // No git repo found - treat as file in cwd\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ repoPath: cwd, relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n // Group by repository path\n const group = filesByRepo.get(repo.repoPath) ?? [];\n group.push({\n repoPath: repo.repoPath,\n relativePath: repo.relativePath,\n originalPath: file,\n });\n filesByRepo.set(repo.repoPath, group);\n }\n\n // Get diffs for each repo\n for (const [repoPath, fileInfos] of filesByRepo) {\n const git: SimpleGit = simpleGit(repoPath);\n\n for (const { relativePath, originalPath } of fileInfos) {\n try {\n // Try staged diff first, then unstaged\n let diff = await git.diff([\"--cached\", \"--\", relativePath]);\n if (!diff) {\n diff = await git.diff([\"--\", relativePath]);\n }\n if (!diff) {\n // For untracked files, try to read content\n diff = await git.show([`:${relativePath}`]).catch(() => \"\");\n }\n if (diff) {\n diffs.set(originalPath, diff);\n }\n } catch {\n // Skip files that can't be diffed\n }\n }\n }\n\n return diffs;\n}\n\n/**\n * Helper to check if path exists asynchronously\n */\nasync function existsAsync(path: string): Promise<boolean> {\n try {\n const { existsSync } = await import(\"node:fs\");\n return existsSync(path);\n } catch {\n return false;\n }\n}\n\n/**\n * Find the git repository root for a given file path.\n * Walks up the directory tree to find the nearest .git directory.\n *\n * @param basePath - Base path to start searching from (e.g., /Users/user/project)\n * @param filePath - Relative file path (e.g., kb-labs-commit/packages/core/src/index.ts)\n * @returns Object with repoPath (absolute) and relativePath (relative to repo), or null if no repo found\n *\n * @example\n * // For nested repo:\n * findGitRepo('/Users/user/kb-labs', 'kb-labs-commit/packages/core/src/index.ts')\n * // Returns: { repoPath: '/Users/user/kb-labs/kb-labs-commit', relativePath: 'packages/core/src/index.ts' }\n *\n * // For file in root repo:\n * findGitRepo('/Users/user/kb-labs', 'src/index.ts')\n * // Returns: { repoPath: '/Users/user/kb-labs', relativePath: 'src/index.ts' }\n */\nasync function findGitRepo(\n basePath: string,\n filePath: string,\n): Promise<{ repoPath: string; relativePath: string } | null> {\n const segments = filePath.split(\"/\");\n\n // Try progressively shorter paths (walk up the tree)\n // For 'kb-labs-commit/packages/core/src/index.ts', try:\n // 1. basePath/kb-labs-commit/packages/core/src\n // 2. basePath/kb-labs-commit/packages/core\n // 3. basePath/kb-labs-commit/packages\n // 4. basePath/kb-labs-commit\n // 5. basePath (fallback to root)\n\n for (let i = segments.length - 1; i > 0; i--) {\n const potentialRepoSegments = segments.slice(0, i);\n const potentialRepoPath = `${basePath}/${potentialRepoSegments.join(\"/\")}`;\n const gitDir = `${potentialRepoPath}/.git`;\n\n if (await existsAsync(gitDir)) {\n // Found a .git directory - this is the repo root\n const relativePath = segments.slice(i).join(\"/\");\n return { repoPath: potentialRepoPath, relativePath };\n }\n }\n\n // No nested repo found, check if basePath itself is a git repo\n if (await existsAsync(`${basePath}/.git`)) {\n return { repoPath: basePath, relativePath: filePath };\n }\n\n // No git repo found at all\n return null;\n}\n","/**\n * File diff utilities\n */\n\nimport { simpleGit } from \"simple-git\";\n\nexport interface FileDiff {\n diff: string;\n additions: number;\n deletions: number;\n}\n\n/**\n * Get diff for a specific file\n */\nexport async function getFileDiff(\n cwd: string,\n filePath: string,\n): Promise<FileDiff> {\n const git = simpleGit(cwd);\n\n // Get diff for the specific file\n // git diff HEAD -- <file>\n const diffOutput = await git.diff([\"HEAD\", \"--\", filePath]);\n\n // Count additions and deletions\n const additions = (diffOutput.match(/^\\+(?!\\+)/gm) || []).length;\n const deletions = (diffOutput.match(/^-(?!-)/gm) || []).length;\n\n return {\n diff: diffOutput,\n additions,\n deletions,\n };\n}\n","/**\n * Recent commits analysis for style matching\n */\n\nimport { simpleGit, type SimpleGit } from \"simple-git\";\n\n/**\n * Get recent commit messages for style reference\n *\n * @param cwd - Working directory\n * @param count - Number of commits to retrieve (default: 10)\n * @returns Array of commit messages (subject line only)\n */\nexport async function getRecentCommits(\n cwd: string,\n count: number = 10,\n): Promise<string[]> {\n const git: SimpleGit = simpleGit(cwd);\n\n try {\n const log = await git.log({\n maxCount: count,\n format: {\n message: \"%s\", // Subject line only\n },\n });\n\n return log.all.map((commit) => commit.message);\n } catch {\n // Return empty array if no commits or error\n return [];\n }\n}\n\n/**\n * Detect the commit style from recent commits\n *\n * @returns Detected style hints\n */\nexport function detectCommitStyle(commits: string[]): {\n usesConventional: boolean;\n commonScopes: string[];\n avgLength: number;\n} {\n if (commits.length === 0) {\n return {\n usesConventional: false,\n commonScopes: [],\n avgLength: 50,\n };\n }\n\n // Check for conventional commit pattern: type(scope)?: message\n const conventionalPattern =\n /^(feat|fix|docs|style|refactor|test|chore|ci|perf|build)(\\([^)]+\\))?!?:/i;\n const conventionalMatches = commits.filter((c) =>\n conventionalPattern.test(c),\n );\n const usesConventional = conventionalMatches.length >= commits.length * 0.5;\n\n // Extract scopes\n const scopePattern = /^\\w+\\(([^)]+)\\)/;\n const scopes = commits\n .map((c) => {\n const match = c.match(scopePattern);\n return match ? match[1] : null;\n })\n .filter((s): s is string => s !== null);\n\n // Count scope frequency\n const scopeCounts = scopes.reduce<Record<string, number>>((acc, scope) => {\n acc[scope] = (acc[scope] || 0) + 1;\n return acc;\n }, {});\n\n // Get top 5 most common scopes\n const commonScopes = Object.entries(scopeCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([scope]) => scope);\n\n // Calculate average message length\n const avgLength = Math.round(\n commits.reduce((sum, c) => sum + c.length, 0) / commits.length,\n );\n\n return {\n usesConventional,\n commonScopes,\n avgLength,\n };\n}\n","/**\n * Scope resolver - converts package names to file path patterns\n * Supports:\n * 1. Exact package name: @kb-labs/core, my-package\n * 2. Wildcard pattern: @kb-labs/core-*, packages/*\n * 3. Path pattern: packages/core/src/**\n */\n\n/* eslint-disable no-await-in-loop -- Sequential file reads required for package.json discovery in nested directories */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport globby from \"globby\";\n\nexport interface ResolvedScope {\n /** Original scope string */\n original: string;\n /** Type of scope */\n type: \"package-name\" | \"wildcard\" | \"path-pattern\";\n /** Resolved package paths (directories) */\n packagePaths: string[];\n /** Pattern to filter files (glob or regex) */\n filePattern?: string;\n}\n\nexport interface PackageInfo {\n name: string;\n path: string;\n}\n\n/**\n * Resolve scope to package paths and file pattern\n */\nexport async function resolveScope(\n cwd: string,\n scope: string,\n): Promise<ResolvedScope> {\n // Detect scope type\n const isExactPackageName =\n !scope.includes(\"*\") && (scope.startsWith(\"@\") || !scope.includes(\"/\"));\n const isWildcardPackageName =\n scope.includes(\"*\") && (scope.startsWith(\"@\") || !scope.includes(\"/\"));\n const _isPathPattern = scope.includes(\"/\") && !scope.startsWith(\"@\");\n\n if (isExactPackageName) {\n // Exact package name: @kb-labs/core\n const packages = await discoverPackages(cwd);\n const matched = packages.filter((pkg) => pkg.name === scope);\n\n return {\n original: scope,\n type: \"package-name\",\n packagePaths: matched.map((p) => p.path),\n filePattern:\n matched.length > 0 ? createGlobPattern(cwd, matched) : undefined,\n };\n }\n\n if (isWildcardPackageName) {\n // Wildcard package name: @kb-labs/core-*\n const packages = await discoverPackages(cwd);\n const regex = createPackageNameRegex(scope);\n const matched = packages.filter((pkg) => regex.test(pkg.name));\n\n return {\n original: scope,\n type: \"wildcard\",\n packagePaths: matched.map((p) => p.path),\n filePattern:\n matched.length > 0 ? createGlobPattern(cwd, matched) : undefined,\n };\n }\n\n // Path pattern: packages/core/** or src/**/*.ts\n // If the scope is a plain directory path (no glob chars), append /** to match all files inside\n const filePattern = !scope.includes(\"*\") && !scope.includes(\"?\")\n ? `${scope}/**`\n : scope;\n return {\n original: scope,\n type: \"path-pattern\",\n packagePaths: [],\n filePattern,\n };\n}\n\n/**\n * Check if a file matches the resolved scope\n */\nexport function matchesScope(\n filePath: string,\n resolvedScope: ResolvedScope,\n): boolean {\n // For package-based scopes, check if file is within any package path\n if (resolvedScope.packagePaths.length > 0) {\n return resolvedScope.packagePaths.some((pkgPath) => {\n // Normalize both paths for comparison\n const normalizedFile = filePath.replace(/\\\\/g, \"/\");\n const normalizedPkg = pkgPath.replace(/\\\\/g, \"/\");\n return (\n normalizedFile.startsWith(normalizedPkg + \"/\") ||\n normalizedFile === normalizedPkg\n );\n });\n }\n\n // For path patterns, use minimatch (caller should handle this)\n return true;\n}\n\n/**\n * Discover all packages in workspace\n */\nasync function discoverPackages(cwd: string): Promise<PackageInfo[]> {\n const packages: PackageInfo[] = [];\n\n // Find all package.json files\n const packageJsonPaths = await globby(\"**/package.json\", {\n cwd,\n absolute: true,\n onlyFiles: true,\n ignore: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.git/**\",\n \"**/.*/**\",\n ],\n });\n\n for (const packageJsonPath of packageJsonPaths) {\n try {\n const packagePath = join(packageJsonPath, \"..\");\n const content = await readFile(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(content);\n\n // Skip packages without name\n if (!packageJson.name) {\n continue;\n }\n\n packages.push({\n name: packageJson.name,\n path: relative(cwd, packagePath) || \".\",\n });\n } catch {\n // Skip invalid package.json\n }\n }\n\n return packages;\n}\n\n/**\n * Create regex from wildcard package name pattern\n * @kb-labs/core-* -> /^@kb-labs\\/core-.*$/\n */\nfunction createPackageNameRegex(pattern: string): RegExp {\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape special chars\n .replace(/\\*/g, \".*\"); // Convert * to .*\n return new RegExp(`^${escaped}$`);\n}\n\n/**\n * Create glob pattern from package paths\n */\nfunction createGlobPattern(cwd: string, packages: PackageInfo[]): string {\n if (packages.length === 1 && packages[0]) {\n return `${packages[0].path}/**`;\n }\n // Multiple packages: {pkg1,pkg2}/**\n const paths = packages.map((p) => p.path).join(\",\");\n return `{${paths}}/**`;\n}\n","/**\n * Secrets detection module\n * Detects files that likely contain secrets and should not be sent to LLM\n */\n\nimport { minimatch } from \"minimatch\";\n\n/**\n * Custom error class for secrets detection\n * This error should NEVER be caught and fallback to heuristics\n */\nexport class SecretsDetectedError extends Error {\n public readonly secretMatches: SecretMatch[];\n\n constructor(matches: SecretMatch[], message: string) {\n super(message);\n this.name = 'SecretsDetectedError';\n this.secretMatches = matches;\n\n // Maintain proper stack trace for where error was thrown (V8 only)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SecretsDetectedError);\n }\n }\n}\n\n/**\n * Detailed information about a detected secret\n */\nexport interface SecretMatch {\n file: string;\n line: number;\n column: number;\n pattern: string; // Which pattern matched\n patternName: string; // Human-readable pattern name\n snippet: string; // Code snippet (with context)\n matchedText: string; // The actual matched text (truncated if long)\n}\n\n/**\n * File patterns that likely contain secrets\n * Based on common secret file conventions\n */\nconst SECRET_FILE_PATTERNS = [\n // Environment files\n \".env\",\n \".env.*\",\n \"*.env\",\n \".envrc\",\n\n // NPM/Node\n \".npmrc\",\n \".yarnrc\",\n \".yarnrc.yml\",\n\n // SSH/GPG keys\n \"*.key\",\n \"*.pem\",\n \"*.p12\",\n \"*.pfx\",\n \"id_rsa\",\n \"id_dsa\",\n \"id_ecdsa\",\n \"id_ed25519\",\n \"*.pub\",\n\n // AWS\n \".aws/**\",\n \"credentials\",\n\n // Docker\n \".docker/config.json\",\n\n // Git credentials\n \".git-credentials\",\n \".netrc\",\n\n // Service account files\n \"*-service-account.json\",\n \"*-serviceaccount.json\",\n \"service-account*.json\",\n \"serviceaccount*.json\",\n\n // Kubernetes\n \"kubeconfig\",\n \"*.kubeconfig\",\n\n // Terraform\n \"*.tfvars\",\n \"terraform.tfstate\",\n \"terraform.tfstate.backup\",\n\n // Other common secrets\n \"secrets.yml\",\n \"secrets.yaml\",\n \"secret.yml\",\n \"secret.yaml\",\n \"passwords.txt\",\n \"password.txt\",\n];\n\n/**\n * Content patterns that indicate secrets (with human-readable names)\n */\nconst SECRET_CONTENT_PATTERNS: Array<{ pattern: RegExp; name: string }> = [\n // API keys/tokens\n { pattern: /api[_-]?key[s]?['\":\\s]*[a-zA-Z0-9_-]{20,}/i, name: 'API Key' },\n { pattern: /auth[_-]?token[s]?['\":\\s]*[a-zA-Z0-9_-]{20,}/i, name: 'Auth Token' },\n { pattern: /access[_-]?token[s]?['\":\\s]*[a-zA-Z0-9_-]{20,}/i, name: 'Access Token' },\n\n // AWS\n { pattern: /AKIA[0-9A-Z]{16}/, name: 'AWS Access Key ID' },\n { pattern: /aws[_-]?secret[_-]?access[_-]?key/i, name: 'AWS Secret Access Key' },\n\n // NPM\n { pattern: /\\/\\/registry\\.npmjs\\.org\\/:_authToken=/, name: 'NPM Auth Token' },\n { pattern: /npm_[A-Za-z0-9]{30,}/, name: 'NPM Token' },\n\n // GitHub\n { pattern: /gh[pousr]_[A-Za-z0-9_]{36,}/, name: 'GitHub Token' },\n\n // Slack\n { pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[A-Za-z0-9]{24,}/, name: 'Slack Token' },\n\n // Private keys\n { pattern: /-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY-----/, name: 'Private Key' },\n\n // Passwords\n { pattern: /password['\":\\s]*['\"]/i, name: 'Password' },\n\n // Generic patterns\n { pattern: /secret['\":\\s]*['\"]/i, name: 'Secret' },\n];\n\n/**\n * Check if file path matches secret file patterns\n */\nexport function isSecretFile(filePath: string): boolean {\n // Normalize path separators\n const normalizedPath = filePath.replace(/\\\\/g, \"/\");\n\n return SECRET_FILE_PATTERNS.some((pattern) =>\n minimatch(normalizedPath, pattern, { matchBase: true }),\n );\n}\n\n/**\n * Check if file content contains secret patterns\n */\nexport function containsSecrets(content: string): boolean {\n return SECRET_CONTENT_PATTERNS.some(({ pattern }) => pattern.test(content));\n}\n\n/**\n * Scan files for potential secrets\n * Returns list of files that likely contain secrets\n */\nexport function detectSecretFiles(files: string[]): string[] {\n return files.filter(isSecretFile);\n}\n\n/**\n * Scan file diffs for secrets\n * Returns map of files that contain secrets\n */\nexport function detectSecretsInDiffs(\n diffs: Map<string, string>,\n): Map<string, string> {\n const secretDiffs = new Map<string, string>();\n\n for (const [file, diff] of diffs.entries()) {\n if (containsSecrets(diff)) {\n secretDiffs.set(file, diff);\n }\n }\n\n return secretDiffs;\n}\n\n/**\n * Create error message for detected secrets\n */\nexport function formatSecretsWarning(secretFiles: string[]): string {\n const count = secretFiles.length;\n const filesList = secretFiles.map((f) => ` - ${f}`).join(\"\\n\");\n\n return [\n `🚨 CRITICAL SECURITY ERROR: Detected ${count} file(s) with potential secrets:`,\n filesList,\n \"\",\n \"⛔️ COMMIT GENERATION ABORTED\",\n \"\",\n \"These files contain sensitive data (API keys, tokens, credentials)\",\n \"that MUST NOT be committed to git or sent to LLM.\",\n \"\",\n \"✅ Actions to fix:\",\n \" 1. Add these files to .gitignore\",\n \" 2. Remove secrets from the files (use environment variables instead)\",\n \" 3. If already committed, use git filter-branch or BFG to remove from history\",\n \"\",\n \"⚠️ If you already ran commit:generate before, the secrets may have been\",\n \"sent to OpenAI. Rotate your credentials immediately.\",\n ].join(\"\\n\");\n}\n\n/**\n * Detect secrets in file diffs with exact location information\n * Returns array of SecretMatch with file, line, column, pattern info\n */\nexport function detectSecretsWithLocation(\n diffs: Map<string, string>,\n): SecretMatch[] {\n const matches: SecretMatch[] = [];\n\n for (const [file, diff] of diffs.entries()) {\n const lines = diff.split('\\n');\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex] ?? '';\n\n for (const { pattern, name } of SECRET_CONTENT_PATTERNS) {\n // Reset regex lastIndex for global patterns\n pattern.lastIndex = 0;\n\n const match = pattern.exec(line);\n if (match) {\n const matchedText = match[0];\n const column = match.index;\n\n // Create snippet with context (20 chars before, full match, 20 chars after)\n const snippetStart = Math.max(0, column - 20);\n const snippetEnd = Math.min(line.length, column + matchedText.length + 20);\n const snippet = line.substring(snippetStart, snippetEnd);\n\n // Truncate matched text if very long (max 50 chars)\n const displayText = matchedText.length > 50\n ? matchedText.substring(0, 50) + '...'\n : matchedText;\n\n matches.push({\n file,\n line: lineIndex + 1, // 1-based line numbers\n column: column + 1, // 1-based column numbers\n pattern: pattern.source,\n patternName: name,\n snippet: snippet.trim(),\n matchedText: displayText,\n });\n }\n }\n }\n }\n\n return matches;\n}\n\n/**\n * Format detailed secrets report with locations\n */\nexport function formatSecretsReport(matches: SecretMatch[]): string {\n const count = matches.length;\n const fileCount = new Set(matches.map(m => m.file)).size;\n\n const lines = [\n `🚨 CRITICAL SECURITY ERROR: Detected ${count} potential secret(s) in ${fileCount} file(s)`,\n \"\",\n \"⛔️ COMMIT GENERATION BLOCKED\",\n \"\",\n ];\n\n // Group by file\n const byFile = new Map<string, SecretMatch[]>();\n for (const match of matches) {\n const existing = byFile.get(match.file) ?? [];\n existing.push(match);\n byFile.set(match.file, existing);\n }\n\n // Format each file's matches\n for (const [file, fileMatches] of byFile.entries()) {\n lines.push(`📄 ${file}:`);\n for (const match of fileMatches) {\n lines.push(` Line ${match.line}:${match.column} - ${match.patternName}`);\n lines.push(` Pattern: ${match.pattern.substring(0, 60)}${match.pattern.length > 60 ? '...' : ''}`);\n lines.push(` Matched: ${match.matchedText}`);\n lines.push(` Context: ...${match.snippet}...`);\n lines.push('');\n }\n }\n\n lines.push(\n \"🔒 These files contain sensitive data (API keys, tokens, credentials)\",\n \"that MUST NOT be committed to git or sent to LLM.\",\n \"\",\n \"✅ Actions to fix:\",\n \" 1. Review each match above - some may be false positives (e.g., examples in comments)\",\n \" 2. If real secrets: Add files to .gitignore and remove secrets (use env vars)\",\n \" 3. If false positives: Use --allow-secrets flag to proceed with confirmation\",\n \"\",\n \"⚠️ If secrets were already sent to LLM in previous runs, rotate credentials immediately!\",\n );\n\n return lines.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/git-status.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/file-summary.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/file-diff.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/recent-commits.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/scope-resolver.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/secrets-detector.ts"],"names":["simpleGit","group"],"mappings":";;;;;;;AAWA,eAAsB,aAAa,GAAA,EAAiC;AAElE,EAAA,MAAM,GAAA,GAAiB,UAAU,GAAG,CAAA;AACpC,EAAA,MAAM,SAAuB,MAAM,GAAA,CAAI,MAAA,CAAO,CAAC,yBAAyB,CAAC,CAAA;AAEzE,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,OAAO,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,gBAAA,CAAiB,CAAC,CAAC,CAAA;AAAA,IACxD,QAAA,EAAU,CAAC,GAAG,MAAA,CAAO,QAAA,EAAU,GAAG,MAAA,CAAO,OAAO,CAAA,CAC7C,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,MAAA,CAAO,MAAA,CAAO,QAAA,CAAS,CAAC,CAAC,CAAA,CACxC,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,gBAAA,CAAiB,CAAC,CAAC,CAAA;AAAA,IACrC,SAAA,EAAW,OAAO,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,gBAAA,CAAiB,CAAC,CAAC;AAAA,GAChE;AACF;AAEA,IAAM,gBAAA,uBAAuB,GAAA,CAAI;AAAA,EAC/B,cAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC,CAAA;AAMD,SAAS,iBAAiB,IAAA,EAAuB;AAC/C,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,IAAA,CAAK,CAAC,OAAA,KAAY,gBAAA,CAAiB,GAAA,CAAI,OAAO,CAAC,CAAA;AACxE;AAMO,SAAS,mBAAmB,MAAA,EAA6B;AAC9D,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,mBAAG,IAAI,GAAA,CAAI,CAAC,GAAG,MAAA,CAAO,MAAA,EAAQ,GAAG,MAAA,CAAO,QAAA,EAAU,GAAG,MAAA,CAAO,SAAS,CAAC;AAAA,GACxE;AACA,EAAA,OAAO,SAAS,MAAA,CAAO,CAAC,SAAS,CAAC,gBAAA,CAAiB,IAAI,CAAC,CAAA;AAC1D;AAKO,SAAS,WAAW,MAAA,EAA4B;AACrD,EAAA,OACE,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAA,IACvB,MAAA,CAAO,SAAS,MAAA,GAAS,CAAA,IACzB,MAAA,CAAO,SAAA,CAAU,MAAA,GAAS,CAAA;AAE9B;AAKA,eAAsB,iBAAiB,GAAA,EAA8B;AACnE,EAAA,MAAM,GAAA,GAAiB,UAAU,GAAG,CAAA;AACpC,EAAA,MAAM,SAAS,MAAM,GAAA,CAAI,SAAS,CAAC,cAAA,EAAgB,MAAM,CAAC,CAAA;AAC1D,EAAA,OAAO,OAAO,IAAA,EAAK;AACrB;AAKO,SAAS,kBAAkB,MAAA,EAAyB;AACzD,EAAA,MAAM,iBAAA,GAAoB;AAAA,IACxB,MAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO,iBAAA,CAAkB,QAAA,CAAS,MAAA,CAAO,WAAA,EAAa,CAAA;AACxD;ACrEA,SAAS,WACP,IAAA,EAC4B;AAC5B,EAAA,OAAO,CAAC,IAAA,CAAK,MAAA;AACf;AAQA,eAAe,SAAA,CAAU,KAAa,QAAA,EAAoC;AACxE,EAAA,IAAI;AAEF,IAAA,MAAM,IAAA,GAAO,MAAM,WAAA,CAAY,GAAA,EAAK,QAAQ,CAAA;AAE5C,IAAA,IAAI,CAAC,IAAA,EAAM;AAET,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,MAAM,GAAA,GAAMA,SAAAA,CAAU,IAAA,CAAK,QAAQ,CAAA;AAMnC,IAAA,MAAM,MAAA,GAAS,MAAM,GAAA,CAAI,GAAA,CAAI,CAAC,WAAW,MAAA,EAAQ,IAAA,EAAM,IAAA,CAAK,YAAY,CAAC,CAAA;AAIzE,IAAA,OAAO,MAAA,CAAO,IAAA,EAAK,CAAE,MAAA,KAAW,CAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAOA,eAAsB,gBAAA,CACpB,KACA,KAAA,EACwB;AACxB,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,YAA2B,EAAC;AAGlC,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAGtB;AAEF,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,IAAA,GAAO,MAAM,WAAA,CAAY,GAAA,EAAK,IAAI,CAAA;AAExC,IAAA,IAAI,CAAC,IAAA,EAAM;AAET,MAAA,MAAMC,MAAAA,GAAQ,WAAA,CAAY,GAAA,CAAI,GAAG,KAAK,EAAC;AACvC,MAAAA,MAAAA,CAAM,KAAK,EAAE,QAAA,EAAU,KAAK,YAAA,EAAc,IAAA,EAAM,YAAA,EAAc,IAAA,EAAM,CAAA;AACpE,MAAA,WAAA,CAAY,GAAA,CAAI,KAAKA,MAAK,CAAA;AAC1B,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,QAAQ,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,QAAQ,KAAK,EAAC;AACjD,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,YAAA,EAAc;AAAA,KACf,CAAA;AACD,IAAA,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,QAAA,EAAU,KAAK,CAAA;AAAA,EACtC;AAGA,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,SAAS,CAAA,IAAK,WAAA,EAAa;AAC/C,IAAA,MAAM,GAAA,GAAiBD,UAAU,QAAQ,CAAA;AACzC,IAAA,MAAM,gBAAgB,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,YAAY,CAAA;AAEzD,IAAA,IAAI;AAEF,MAAA,MAAM,UAAA,GAAa,MAAM,GAAA,CAAI,WAAA,CAAY;AAAA,QACvC,UAAA;AAAA,QACA,IAAA;AAAA,QACA,GAAG;AAAA,OACJ,CAAA;AAGD,MAAA,MAAM,YAAA,GAAe,MAAM,GAAA,CAAI,WAAA,CAAY,CAAC,IAAA,EAAM,GAAG,aAAa,CAAC,CAAA;AAGnE,MAAA,MAAM,YAAA,uBAAmB,GAAA,EAGvB;AACF,MAAA,KAAA,MAAW,IAAA,IAAQ,aAAa,KAAA,EAAO;AACrC,QAAA,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,IAAI,CAAA;AAAA,MAClC;AACA,MAAA,KAAA,MAAW,IAAA,IAAQ,WAAW,KAAA,EAAO;AAEnC,QAAA,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,IAAI,CAAA;AAAA,MAClC;AAGA,MAAA,KAAA,MAAW,IAAA,IAAQ,YAAA,CAAa,MAAA,EAAO,EAAG;AAExC,QAAA,MAAM,QAAA,GAAW,UAAU,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,YAAA,KAAiB,KAAK,IAAI,CAAA;AACnE,QAAA,IAAI,CAAC,QAAA,EAAU;AACb,UAAA;AAAA,QACF;AAGA,QAAA,MAAM,KAAA,GAAQ,MAAM,SAAA,CAAU,QAAA,EAAU,KAAK,IAAI,CAAA;AAGjD,QAAA,IAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACpB,UAAA,SAAA,CAAU,IAAA,CAAK;AAAA,YACb,MAAM,QAAA,CAAS,YAAA;AAAA,YACf,MAAA,EAAQ,aAAA,CAAc,IAAA,CAAK,UAAA,EAAY,KAAK,SAAS,CAAA;AAAA,YACrD,WAAW,IAAA,CAAK,UAAA;AAAA,YAChB,WAAW,IAAA,CAAK,SAAA;AAAA,YAChB,MAAA,EAAQ,KAAA;AAAA,YACR,SAAA,EAAW;AAAA,WACZ,CAAA;AAAA,QACH,CAAA,MAAO;AAEL,UAAA,SAAA,CAAU,IAAA,CAAK;AAAA,YACb,MAAM,QAAA,CAAS,YAAA;AAAA,YACf,MAAA,EAAQ,UAAA;AAAA,YACR,SAAA,EAAW,CAAA;AAAA,YACX,SAAA,EAAW,CAAA;AAAA,YACX,MAAA,EAAQ,IAAA;AAAA,YACR,SAAA,EAAW;AAAA,WACZ,CAAA;AAAA,QACH;AAAA,MACF;AAGA,MAAA,MAAM,iBAAiB,IAAI,GAAA;AAAA,QACzB,KAAA,CAAM,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI;AAAA,OACrD;AACA,MAAA,MAAM,gBAAgB,SAAA,CAAU,MAAA;AAAA,QAC9B,CAAC,CAAA,KAAM,CAAC,cAAA,CAAe,GAAA,CAAI,EAAE,YAAY;AAAA,OAC3C;AAEA,MAAA,KAAA,MAAW,YAAY,aAAA,EAAe;AAEpC,QAAA,SAAA,CAAU,IAAA,CAAK;AAAA,UACb,MAAM,QAAA,CAAS,YAAA;AAAA,UACf,MAAA,EAAQ,OAAA;AAAA,UACR,SAAA,EAAW,CAAA;AAAA,UACX,SAAA,EAAW,CAAA;AAAA,UACX,MAAA,EAAQ,KAAA;AAAA,UACR,SAAA,EAAW;AAAA,SACZ,CAAA;AAAA,MACH;AAAA,IACF,CAAA,CAAA,MAAQ;AAEN,MAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,QAAA,SAAA,CAAU,IAAA,CAAK;AAAA,UACb,MAAM,QAAA,CAAS,YAAA;AAAA,UACf,MAAA,EAAQ,UAAA;AAAA,UACR,SAAA,EAAW,CAAA;AAAA,UACX,SAAA,EAAW,CAAA;AAAA,UACX,MAAA,EAAQ,KAAA;AAAA,UACR,SAAA,EAAW;AAAA;AAAA,SACZ,CAAA;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,SAAA;AACT;AAKA,SAAS,aAAA,CACP,YACA,SAAA,EACuB;AAEvB,EAAA,IAAI,SAAA,KAAc,CAAA,IAAK,UAAA,GAAa,CAAA,EAAG;AACrC,IAAA,OAAO,OAAA;AAAA,EACT;AACA,EAAA,IAAI,UAAA,KAAe,CAAA,IAAK,SAAA,GAAY,CAAA,EAAG;AACrC,IAAA,OAAO,SAAA;AAAA,EACT;AACA,EAAA,OAAO,UAAA;AACT;AAKO,SAAS,kBAAkB,OAAA,EAA8B;AAC9D,EAAA,MAAM,KAAA,GAAQ,QAAQ,MAAA,GAClB,QAAA,GACA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAA,EAAK,OAAA,CAAQ,SAAS,CAAA,CAAA;AAC/C,EAAA,OAAO,GAAG,OAAA,CAAQ,IAAI,KAAK,OAAA,CAAQ,MAAM,KAAK,KAAK,CAAA,CAAA,CAAA;AACrD;AAQA,eAAsB,YAAA,CACpB,KACA,KAAA,EAC8B;AAC9B,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,2BAAW,GAAA,EAAI;AAAA,EACjB;AAEA,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAoB;AAGtC,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAGtB;AAEF,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,IAAA,GAAO,MAAM,WAAA,CAAY,GAAA,EAAK,IAAI,CAAA;AAExC,IAAA,IAAI,CAAC,IAAA,EAAM;AAET,MAAA,MAAMC,MAAAA,GAAQ,WAAA,CAAY,GAAA,CAAI,GAAG,KAAK,EAAC;AACvC,MAAAA,MAAAA,CAAM,KAAK,EAAE,QAAA,EAAU,KAAK,YAAA,EAAc,IAAA,EAAM,YAAA,EAAc,IAAA,EAAM,CAAA;AACpE,MAAA,WAAA,CAAY,GAAA,CAAI,KAAKA,MAAK,CAAA;AAC1B,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,QAAQ,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,QAAQ,KAAK,EAAC;AACjD,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,YAAA,EAAc;AAAA,KACf,CAAA;AACD,IAAA,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,QAAA,EAAU,KAAK,CAAA;AAAA,EACtC;AAGA,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,SAAS,CAAA,IAAK,WAAA,EAAa;AAC/C,IAAA,MAAM,GAAA,GAAiBD,UAAU,QAAQ,CAAA;AAEzC,IAAA,KAAA,MAAW,EAAE,YAAA,EAAc,YAAA,EAAa,IAAK,SAAA,EAAW;AACtD,MAAA,IAAI;AAEF,QAAA,IAAI,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,CAAK,CAAC,UAAA,EAAY,IAAA,EAAM,YAAY,CAAC,CAAA;AAC1D,QAAA,IAAI,CAAC,IAAA,EAAM;AACT,UAAA,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,CAAK,CAAC,IAAA,EAAM,YAAY,CAAC,CAAA;AAAA,QAC5C;AACA,QAAA,IAAI,CAAC,IAAA,EAAM;AAET,UAAA,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,CAAK,CAAC,CAAA,CAAA,EAAI,YAAY,CAAA,CAAE,CAAC,CAAA,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAAA,QAC5D;AACA,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,KAAA,CAAM,GAAA,CAAI,cAAc,IAAI,CAAA;AAAA,QAC9B;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAKA,eAAe,YAAY,IAAA,EAAgC;AACzD,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,UAAA,EAAW,GAAI,MAAM,OAAO,IAAS,CAAA;AAC7C,IAAA,OAAO,WAAW,IAAI,CAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAmBA,eAAe,WAAA,CACb,UACA,QAAA,EAC4D;AAC5D,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAUnC,EAAA,KAAA,IAAS,IAAI,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG,CAAA,GAAI,GAAG,CAAA,EAAA,EAAK;AAC5C,IAAA,MAAM,qBAAA,GAAwB,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACjD,IAAA,MAAM,oBAAoB,CAAA,EAAG,QAAQ,IAAI,qBAAA,CAAsB,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AACxE,IAAA,MAAM,MAAA,GAAS,GAAG,iBAAiB,CAAA,KAAA,CAAA;AAEnC,IAAA,IAAI,MAAM,WAAA,CAAY,MAAM,CAAA,EAAG;AAE7B,MAAA,MAAM,eAAe,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,CAAE,KAAK,GAAG,CAAA;AAC/C,MAAA,OAAO,EAAE,QAAA,EAAU,iBAAA,EAAmB,YAAA,EAAa;AAAA,IACrD;AAAA,EACF;AAGA,EAAA,IAAI,MAAM,WAAA,CAAY,CAAA,EAAG,QAAQ,OAAO,CAAA,EAAG;AACzC,IAAA,OAAO,EAAE,QAAA,EAAU,QAAA,EAAU,YAAA,EAAc,QAAA,EAAS;AAAA,EACtD;AAGA,EAAA,OAAO,IAAA;AACT;ACtVA,eAAsB,WAAA,CACpB,KACA,QAAA,EACmB;AACnB,EAAA,MAAM,GAAA,GAAMA,UAAU,GAAG,CAAA;AAIzB,EAAA,MAAM,UAAA,GAAa,MAAM,GAAA,CAAI,IAAA,CAAK,CAAC,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAC,CAAA;AAG1D,EAAA,MAAM,aAAa,UAAA,CAAW,KAAA,CAAM,aAAa,CAAA,IAAK,EAAC,EAAG,MAAA;AAC1D,EAAA,MAAM,aAAa,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAK,EAAC,EAAG,MAAA;AAExD,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IACN,SAAA;AAAA,IACA;AAAA,GACF;AACF;ACrBA,eAAsB,gBAAA,CACpB,GAAA,EACA,KAAA,GAAgB,EAAA,EACG;AACnB,EAAA,MAAM,GAAA,GAAiBA,UAAU,GAAG,CAAA;AAEpC,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,GAAA,CAAI,GAAA,CAAI;AAAA,MACxB,QAAA,EAAU,KAAA;AAAA,MACV,MAAA,EAAQ;AAAA,QACN,OAAA,EAAS;AAAA;AAAA;AACX,KACD,CAAA;AAED,IAAA,OAAO,IAAI,GAAA,CAAI,GAAA,CAAI,CAAC,MAAA,KAAW,OAAO,OAAO,CAAA;AAAA,EAC/C,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAOO,SAAS,kBAAkB,OAAA,EAIhC;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,OAAO;AAAA,MACL,gBAAA,EAAkB,KAAA;AAAA,MAClB,cAAc,EAAC;AAAA,MACf,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AAGA,EAAA,MAAM,mBAAA,GACJ,0EAAA;AACF,EAAA,MAAM,sBAAsB,OAAA,CAAQ,MAAA;AAAA,IAAO,CAAC,CAAA,KAC1C,mBAAA,CAAoB,IAAA,CAAK,CAAC;AAAA,GAC5B;AACA,EAAA,MAAM,gBAAA,GAAmB,mBAAA,CAAoB,MAAA,IAAU,OAAA,CAAQ,MAAA,GAAS,GAAA;AAGxE,EAAA,MAAM,YAAA,GAAe,iBAAA;AACrB,EAAA,MAAM,MAAA,GAAS,OAAA,CACZ,GAAA,CAAI,CAAC,CAAA,KAAM;AACV,IAAA,MAAM,KAAA,GAAQ,CAAA,CAAE,KAAA,CAAM,YAAY,CAAA;AAClC,IAAA,OAAO,KAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA;AAAA,EAC5B,CAAC,CAAA,CACA,MAAA,CAAO,CAAC,CAAA,KAAmB,MAAM,IAAI,CAAA;AAGxC,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,MAAA,CAA+B,CAAC,KAAK,KAAA,KAAU;AACxE,IAAA,GAAA,CAAI,KAAK,CAAA,GAAA,CAAK,GAAA,CAAI,KAAK,KAAK,CAAA,IAAK,CAAA;AACjC,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,CAC5C,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,CAAC,CAAA,GAAI,EAAE,CAAC,CAAC,CAAA,CAC1B,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CACV,GAAA,CAAI,CAAC,CAAC,KAAK,CAAA,KAAM,KAAK,CAAA;AAGzB,EAAA,MAAM,YAAY,IAAA,CAAK,KAAA;AAAA,IACrB,OAAA,CAAQ,MAAA,CAAO,CAAC,GAAA,EAAK,CAAA,KAAM,MAAM,CAAA,CAAE,MAAA,EAAQ,CAAC,CAAA,GAAI,OAAA,CAAQ;AAAA,GAC1D;AAEA,EAAA,OAAO;AAAA,IACL,gBAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF;AC1DA,eAAsB,YAAA,CACpB,KACA,KAAA,EACwB;AAExB,EAAA,MAAM,kBAAA,GACJ,CAAC,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,KAAM,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,KAAA,CAAM,SAAS,GAAG,CAAA,CAAA;AACvE,EAAA,MAAM,qBAAA,GACJ,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,KAAM,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,CAAA;AACtE,EAAuB,MAAM,QAAA,CAAS,GAAG,KAAK,CAAC,KAAA,CAAM,WAAW,GAAG;AAEnE,EAAA,IAAI,kBAAA,EAAoB;AAEtB,IAAA,MAAM,QAAA,GAAW,MAAM,gBAAA,CAAiB,GAAG,CAAA;AAC3C,IAAA,MAAM,UAAU,QAAA,CAAS,MAAA,CAAO,CAAC,GAAA,KAAQ,GAAA,CAAI,SAAS,KAAK,CAAA;AAE3D,IAAA,OAAO;AAAA,MACL,QAAA,EAAU,KAAA;AAAA,MACV,IAAA,EAAM,cAAA;AAAA,MACN,cAAc,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,MACvC,aACE,OAAA,CAAQ,MAAA,GAAS,IAAI,iBAAA,CAAkB,GAAA,EAAK,OAAO,CAAA,GAAI;AAAA,KAC3D;AAAA,EACF;AAEA,EAAA,IAAI,qBAAA,EAAuB;AAEzB,IAAA,MAAM,QAAA,GAAW,MAAM,gBAAA,CAAiB,GAAG,CAAA;AAC3C,IAAA,MAAM,KAAA,GAAQ,uBAAuB,KAAK,CAAA;AAC1C,IAAA,MAAM,OAAA,GAAU,SAAS,MAAA,CAAO,CAAC,QAAQ,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAA;AAE7D,IAAA,OAAO;AAAA,MACL,QAAA,EAAU,KAAA;AAAA,MACV,IAAA,EAAM,UAAA;AAAA,MACN,cAAc,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,MACvC,aACE,OAAA,CAAQ,MAAA,GAAS,IAAI,iBAAA,CAAkB,GAAA,EAAK,OAAO,CAAA,GAAI;AAAA,KAC3D;AAAA,EACF;AAIA,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,IAAK,CAAC,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,GAC3D,CAAA,EAAG,KAAK,CAAA,GAAA,CAAA,GACR,KAAA;AACJ,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,KAAA;AAAA,IACV,IAAA,EAAM,cAAA;AAAA,IACN,cAAc,EAAC;AAAA,IACf;AAAA,GACF;AACF;AAKO,SAAS,YAAA,CACd,UACA,aAAA,EACS;AAET,EAAA,IAAI,aAAA,CAAc,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG;AACzC,IAAA,OAAO,aAAA,CAAc,YAAA,CAAa,IAAA,CAAK,CAAC,OAAA,KAAY;AAElD,MAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAClD,MAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAChD,MAAA,OACE,cAAA,CAAe,UAAA,CAAW,aAAA,GAAgB,GAAG,KAC7C,cAAA,KAAmB,aAAA;AAAA,IAEvB,CAAC,CAAA;AAAA,EACH;AAGA,EAAA,OAAO,IAAA;AACT;AAKA,eAAe,iBAAiB,GAAA,EAAqC;AACnE,EAAA,MAAM,WAA0B,EAAC;AAGjC,EAAA,MAAM,gBAAA,GAAmB,MAAM,MAAA,CAAO,iBAAA,EAAmB;AAAA,IACvD,GAAA;AAAA,IACA,QAAA,EAAU,IAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,MAAA,EAAQ;AAAA,MACN,oBAAA;AAAA,MACA,YAAA;AAAA,MACA,aAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA;AACF,GACD,CAAA;AAED,EAAA,KAAA,MAAW,mBAAmB,gBAAA,EAAkB;AAC9C,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,GAAc,IAAA,CAAK,eAAA,EAAiB,IAAI,CAAA;AAC9C,MAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,eAAA,EAAiB,OAAO,CAAA;AACvD,MAAA,MAAM,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAGtC,MAAA,IAAI,CAAC,YAAY,IAAA,EAAM;AACrB,QAAA;AAAA,MACF;AAEA,MAAA,QAAA,CAAS,IAAA,CAAK;AAAA,QACZ,MAAM,WAAA,CAAY,IAAA;AAAA,QAClB,IAAA,EAAM,QAAA,CAAS,GAAA,EAAK,WAAW,CAAA,IAAK;AAAA,OACrC,CAAA;AAAA,IACH,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAMA,SAAS,uBAAuB,OAAA,EAAyB;AACvD,EAAA,MAAM,OAAA,GAAU,QACb,OAAA,CAAQ,oBAAA,EAAsB,MAAM,CAAA,CACpC,OAAA,CAAQ,OAAO,IAAI,CAAA;AACtB,EAAA,OAAO,IAAI,MAAA,CAAO,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,CAAG,CAAA;AAClC;AAKA,SAAS,iBAAA,CAAkB,KAAa,QAAA,EAAiC;AACvE,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,IAAK,QAAA,CAAS,CAAC,CAAA,EAAG;AACxC,IAAA,OAAO,CAAA,EAAG,QAAA,CAAS,CAAC,CAAA,CAAE,IAAI,CAAA,GAAA,CAAA;AAAA,EAC5B;AAEA,EAAA,MAAM,KAAA,GAAQ,SAAS,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,IAAI,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAClD,EAAA,OAAO,IAAI,KAAK,CAAA,IAAA,CAAA;AAClB;ACnKO,IAAM,oBAAA,GAAN,MAAM,qBAAA,SAA6B,KAAA,CAAM;AAAA,EAC9B,aAAA;AAAA,EAEhB,WAAA,CAAY,SAAwB,OAAA,EAAiB;AACnD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AACZ,IAAA,IAAA,CAAK,aAAA,GAAgB,OAAA;AAGrB,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,MAAM,qBAAoB,CAAA;AAAA,IACpD;AAAA,EACF;AACF;AASO,SAAS,uBAAuB,KAAA,EAA+C;AACpF,EAAA,OACE,KAAA,YAAiB,SACjB,KAAA,CAAM,IAAA,KAAS,0BACf,KAAA,CAAM,OAAA,CAAS,MAA+B,aAAa,CAAA;AAE/D;AAmBA,IAAM,oBAAA,GAAuB;AAAA;AAAA,EAE3B,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA;AAAA,EAGA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,aAAA;AAAA;AAAA,EAGA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,OAAA;AAAA;AAAA,EAGA,SAAA;AAAA,EACA,aAAA;AAAA;AAAA,EAGA,qBAAA;AAAA;AAAA,EAGA,kBAAA;AAAA,EACA,QAAA;AAAA;AAAA,EAGA,wBAAA;AAAA,EACA,uBAAA;AAAA,EACA,uBAAA;AAAA,EACA,sBAAA;AAAA;AAAA,EAGA,YAAA;AAAA,EACA,cAAA;AAAA;AAAA,EAGA,UAAA;AAAA,EACA,mBAAA;AAAA,EACA,0BAAA;AAAA;AAAA,EAGA,aAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA;AACF,CAAA;AAKA,IAAM,uBAAA,GAAoE;AAAA;AAAA,EAExE,EAAE,OAAA,EAAS,4CAAA,EAA8C,IAAA,EAAM,SAAA,EAAU;AAAA,EACzE,EAAE,OAAA,EAAS,+CAAA,EAAiD,IAAA,EAAM,YAAA,EAAa;AAAA,EAC/E,EAAE,OAAA,EAAS,iDAAA,EAAmD,IAAA,EAAM,cAAA,EAAe;AAAA;AAAA,EAGnF,EAAE,OAAA,EAAS,kBAAA,EAAoB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACzD,EAAE,OAAA,EAAS,oCAAA,EAAsC,IAAA,EAAM,uBAAA,EAAwB;AAAA;AAAA,EAG/E,EAAE,OAAA,EAAS,wCAAA,EAA0C,IAAA,EAAM,gBAAA,EAAiB;AAAA,EAC5E,EAAE,OAAA,EAAS,sBAAA,EAAwB,IAAA,EAAM,WAAA,EAAY;AAAA;AAAA,EAGrD,EAAE,OAAA,EAAS,6BAAA,EAA+B,IAAA,EAAM,cAAA,EAAe;AAAA;AAAA,EAG/D,EAAE,OAAA,EAAS,uDAAA,EAAyD,IAAA,EAAM,aAAA,EAAc;AAAA;AAAA,EAGxF,EAAE,OAAA,EAAS,sDAAA,EAAwD,IAAA,EAAM,aAAA,EAAc;AAAA;AAAA,EAGvF,EAAE,OAAA,EAAS,uBAAA,EAAyB,IAAA,EAAM,UAAA,EAAW;AAAA;AAAA,EAGrD,EAAE,OAAA,EAAS,qBAAA,EAAuB,IAAA,EAAM,QAAA;AAC1C,CAAA;AAKO,SAAS,aAAa,QAAA,EAA2B;AAEtD,EAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAElD,EAAA,OAAO,oBAAA,CAAqB,IAAA;AAAA,IAAK,CAAC,YAChC,SAAA,CAAU,cAAA,EAAgB,SAAS,EAAE,SAAA,EAAW,MAAM;AAAA,GACxD;AACF;AAKO,SAAS,gBAAgB,OAAA,EAA0B;AACxD,EAAA,OAAO,uBAAA,CAAwB,KAAK,CAAC,EAAE,SAAQ,KAAM,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAC,CAAA;AAC5E;AAMO,SAAS,kBAAkB,KAAA,EAA2B;AAC3D,EAAA,OAAO,KAAA,CAAM,OAAO,YAAY,CAAA;AAClC;AAMO,SAAS,qBACd,KAAA,EACqB;AACrB,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAE5C,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,IAAI,CAAA,IAAK,KAAA,CAAM,SAAQ,EAAG;AAC1C,IAAA,IAAI,eAAA,CAAgB,IAAI,CAAA,EAAG;AACzB,MAAA,WAAA,CAAY,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,IAC5B;AAAA,EACF;AAEA,EAAA,OAAO,WAAA;AACT;AAKO,SAAS,qBAAqB,WAAA,EAA+B;AAClE,EAAA,MAAM,QAAQ,WAAA,CAAY,MAAA;AAC1B,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,OAAO,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAE9D,EAAA,OAAO;AAAA,IACL,+CAAwC,KAAK,CAAA,gCAAA,CAAA;AAAA,IAC7C,SAAA;AAAA,IACA,EAAA;AAAA,IACA,wCAAA;AAAA,IACA,EAAA;AAAA,IACA,oEAAA;AAAA,IACA,mDAAA;AAAA,IACA,EAAA;AAAA,IACA,wBAAA;AAAA,IACA,oCAAA;AAAA,IACA,wEAAA;AAAA,IACA,gFAAA;AAAA,IACA,EAAA;AAAA,IACA,oFAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI,CAAA;AACb;AAMO,SAAS,0BACd,KAAA,EACe;AACf,EAAA,MAAM,UAAyB,EAAC;AAEhC,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,IAAI,CAAA,IAAK,KAAA,CAAM,SAAQ,EAAG;AAC1C,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAE7B,IAAA,KAAA,IAAS,SAAA,GAAY,CAAA,EAAG,SAAA,GAAY,KAAA,CAAM,QAAQ,SAAA,EAAA,EAAa;AAC7D,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,SAAS,CAAA,IAAK,EAAA;AAEjC,MAAA,KAAA,MAAW,EAAE,OAAA,EAAS,IAAA,EAAK,IAAK,uBAAA,EAAyB;AAEvD,QAAA,OAAA,CAAQ,SAAA,GAAY,CAAA;AAEpB,QAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAC/B,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,MAAM,WAAA,GAAc,MAAM,CAAC,CAAA;AAC3B,UAAA,MAAM,SAAS,KAAA,CAAM,KAAA;AAGrB,UAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,EAAE,CAAA;AAC5C,UAAA,MAAM,UAAA,GAAa,KAAK,GAAA,CAAI,IAAA,CAAK,QAAQ,MAAA,GAAS,WAAA,CAAY,SAAS,EAAE,CAAA;AACzE,UAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,YAAA,EAAc,UAAU,CAAA;AAGvD,UAAA,MAAM,WAAA,GAAc,YAAY,MAAA,GAAS,EAAA,GACrC,YAAY,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA,GAAI,KAAA,GAC/B,WAAA;AAEJ,UAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,YACX,IAAA;AAAA,YACA,MAAM,SAAA,GAAY,CAAA;AAAA;AAAA,YAClB,QAAQ,MAAA,GAAS,CAAA;AAAA;AAAA,YACjB,SAAS,OAAA,CAAQ,MAAA;AAAA,YACjB,WAAA,EAAa,IAAA;AAAA,YACb,OAAA,EAAS,QAAQ,IAAA,EAAK;AAAA,YACtB,WAAA,EAAa;AAAA,WACd,CAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,oBAAoB,OAAA,EAAgC;AAClE,EAAA,MAAM,QAAQ,OAAA,CAAQ,MAAA;AACtB,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,IAAI,CAAC,CAAA,CAAE,IAAA;AAEpD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,CAAA,4CAAA,EAAwC,KAAK,CAAA,wBAAA,EAA2B,SAAS,CAAA,QAAA,CAAA;AAAA,IACjF,EAAA;AAAA,IACA,wCAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA2B;AAC9C,EAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,IAAA,MAAM,WAAW,MAAA,CAAO,GAAA,CAAI,KAAA,CAAM,IAAI,KAAK,EAAC;AAC5C,IAAA,QAAA,CAAS,KAAK,KAAK,CAAA;AACnB,IAAA,MAAA,CAAO,GAAA,CAAI,KAAA,CAAM,IAAA,EAAM,QAAQ,CAAA;AAAA,EACjC;AAGA,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,WAAW,CAAA,IAAK,MAAA,CAAO,SAAQ,EAAG;AAClD,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AACxB,IAAA,KAAA,MAAW,SAAS,WAAA,EAAa;AAC/B,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,OAAA,EAAU,KAAA,CAAM,IAAI,CAAA,CAAA,EAAI,MAAM,MAAM,CAAA,GAAA,EAAM,KAAA,CAAM,WAAW,CAAA,CAAE,CAAA;AACxE,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,EAAG,EAAE,CAAC,CAAA,EAAG,MAAM,OAAA,CAAQ,MAAA,GAAS,EAAA,GAAK,KAAA,GAAQ,EAAE,CAAA,CAAE,CAAA;AACpG,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,KAAA,CAAM,WAAW,CAAA,CAAE,CAAA;AAC9C,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,gBAAA,EAAmB,KAAA,CAAM,OAAO,CAAA,GAAA,CAAK,CAAA;AAChD,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,IACf;AAAA,EACF;AAEA,EAAA,KAAA,CAAM,IAAA;AAAA,IACJ,8EAAA;AAAA,IACA,mDAAA;AAAA,IACA,EAAA;AAAA,IACA,wBAAA;AAAA,IACA,yFAAA;AAAA,IACA,iFAAA;AAAA,IACA,gFAAA;AAAA,IACA,EAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB","file":"index.js","sourcesContent":["/**\n * Git status analysis\n */\n\nimport { simpleGit, type SimpleGit, type StatusResult } from \"simple-git\";\nimport type { GitStatus } from \"@kb-labs/commit-contracts\";\n\n/**\n * Get current git status (staged, unstaged, untracked files).\n * cwd must already point to the resolved scope directory.\n */\nexport async function getGitStatus(cwd: string): Promise<GitStatus> {\n // --ignore-submodules=all: exclude submodule pointer drift in worktrees\n const git: SimpleGit = simpleGit(cwd);\n const status: StatusResult = await git.status(['--ignore-submodules=all']);\n\n return {\n staged: status.staged.filter((f) => !shouldIgnoreFile(f)),\n unstaged: [...status.modified, ...status.deleted]\n .filter((f) => !status.staged.includes(f))\n .filter((f) => !shouldIgnoreFile(f)),\n untracked: status.not_added.filter((f) => !shouldIgnoreFile(f)),\n };\n}\n\nconst IGNORED_SEGMENTS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".turbo\",\n \"coverage\",\n]);\n\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n * Uses exact path segment matching to avoid false positives like \"my-dist/file.ts\".\n */\nfunction shouldIgnoreFile(file: string): boolean {\n return file.split(\"/\").some((segment) => IGNORED_SEGMENTS.has(segment));\n}\n\n/**\n * Get all changed files (staged + unstaged + untracked)\n * Filters out node_modules and other build artifacts\n */\nexport function getAllChangedFiles(status: GitStatus): string[] {\n const allFiles = [\n ...new Set([...status.staged, ...status.unstaged, ...status.untracked]),\n ];\n return allFiles.filter((file) => !shouldIgnoreFile(file));\n}\n\n/**\n * Check if there are any changes\n */\nexport function hasChanges(status: GitStatus): boolean {\n return (\n status.staged.length > 0 ||\n status.unstaged.length > 0 ||\n status.untracked.length > 0\n );\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n const git: SimpleGit = simpleGit(cwd);\n const branch = await git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch.trim();\n}\n\n/**\n * Check if branch is protected (main/master)\n */\nexport function isProtectedBranch(branch: string): boolean {\n const protectedBranches = [\n \"main\",\n \"master\",\n \"develop\",\n \"release\",\n \"production\",\n ];\n return protectedBranches.includes(branch.toLowerCase());\n}\n","/**\n * File summary extraction\n */\n\n/* eslint-disable no-await-in-loop -- Sequential git operations required: must check status, read diffs, compute summaries one-by-one for each file */\n\nimport {\n simpleGit,\n type SimpleGit,\n type DiffResultTextFile,\n type DiffResultBinaryFile,\n} from \"simple-git\";\nimport type { FileSummary } from \"@kb-labs/commit-contracts\";\n\n/**\n * Type guard for text file diff result\n */\nfunction isTextFile(\n file: DiffResultTextFile | DiffResultBinaryFile,\n): file is DiffResultTextFile {\n return !file.binary;\n}\n\n/**\n * Check if file is truly new (doesn't exist in current HEAD commit)\n * Returns true if file doesn't exist in HEAD (the current committed state)\n * Returns false if file exists in HEAD (even if modified/deleted locally)\n * Supports nested git repositories\n */\nasync function isNewFile(cwd: string, filePath: string): Promise<boolean> {\n try {\n // Find the git repository for this file (walks up directory tree)\n const repo = await findGitRepo(cwd, filePath);\n\n if (!repo) {\n // No git repo found - treat as new file\n return true;\n }\n\n const git = simpleGit(repo.repoPath);\n\n // Check if file exists in HEAD (current commit), not in history\n // git ls-tree HEAD -- <file>\n // Returns tree entry if file exists in HEAD, empty if file is new\n // This correctly handles modified/unstaged files (they exist in HEAD = not new)\n const result = await git.raw([\"ls-tree\", \"HEAD\", \"--\", repo.relativePath]);\n\n // If no output, file doesn't exist in HEAD → truly new file\n // If has output, file exists in HEAD → not new (modified/unstaged)\n return result.trim().length === 0;\n } catch {\n // On error, assume it's new (safer to say \"new\" than claim it existed)\n return true;\n }\n}\n\n/**\n * Get file summaries with diff stats for given files\n * Supports nested git repositories by grouping files per repo\n */\n// eslint-disable-next-line sonarjs/cognitive-complexity -- File summary extraction: groups files by repo, handles nested git repos, computes diff stats, processes staged/unstaged/untracked files\nexport async function getFileSummaries(\n cwd: string,\n files: string[],\n): Promise<FileSummary[]> {\n if (files.length === 0) {\n return [];\n }\n\n const summaries: FileSummary[] = [];\n\n // Group files by repository (walks up directory tree to find .git)\n const filesByRepo = new Map<\n string,\n { repoPath: string; relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n const repo = await findGitRepo(cwd, file);\n\n if (!repo) {\n // No git repo found - treat as file in cwd\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ repoPath: cwd, relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n // Group by repository path\n const group = filesByRepo.get(repo.repoPath) ?? [];\n group.push({\n repoPath: repo.repoPath,\n relativePath: repo.relativePath,\n originalPath: file,\n });\n filesByRepo.set(repo.repoPath, group);\n }\n\n // Get diff summaries for each repo separately\n for (const [repoPath, fileInfos] of filesByRepo) {\n const git: SimpleGit = simpleGit(repoPath);\n const relativePaths = fileInfos.map((f) => f.relativePath);\n\n try {\n // Try staged diff first\n const stagedDiff = await git.diffSummary([\n \"--cached\",\n \"--\",\n ...relativePaths,\n ]);\n\n // Then try unstaged diff (working tree changes)\n const unstagedDiff = await git.diffSummary([\"--\", ...relativePaths]);\n\n // Combine results (prefer staged if file appears in both)\n const allDiffFiles = new Map<\n string,\n DiffResultTextFile | DiffResultBinaryFile\n >();\n for (const file of unstagedDiff.files) {\n allDiffFiles.set(file.file, file);\n }\n for (const file of stagedDiff.files) {\n // Staged takes priority\n allDiffFiles.set(file.file, file);\n }\n\n // Process diff results\n for (const file of allDiffFiles.values()) {\n // Find original path (with repo prefix)\n const fileInfo = fileInfos.find((f) => f.relativePath === file.file);\n if (!fileInfo) {\n continue;\n }\n\n // Check if file is truly new (using repoPath as cwd)\n const isNew = await isNewFile(repoPath, file.file);\n\n // Handle both text and binary files\n if (isTextFile(file)) {\n summaries.push({\n path: fileInfo.originalPath,\n status: mapDiffStatus(file.insertions, file.deletions),\n additions: file.insertions,\n deletions: file.deletions,\n binary: false,\n isNewFile: isNew,\n });\n } else {\n // Binary file\n summaries.push({\n path: fileInfo.originalPath,\n status: \"modified\",\n additions: 0,\n deletions: 0,\n binary: true,\n isNewFile: isNew,\n });\n }\n }\n\n // Add any missing files from this repo (untracked - not in git yet)\n const processedPaths = new Set(\n Array.from(allDiffFiles.values()).map((f) => f.file),\n );\n const missingInRepo = fileInfos.filter(\n (f) => !processedPaths.has(f.relativePath),\n );\n\n for (const fileInfo of missingInRepo) {\n // Untracked files are always new (never existed in git)\n summaries.push({\n path: fileInfo.originalPath,\n status: \"added\",\n additions: 0,\n deletions: 0,\n binary: false,\n isNewFile: true,\n });\n }\n } catch {\n // Fallback: create basic summaries for this repo's files\n for (const fileInfo of fileInfos) {\n summaries.push({\n path: fileInfo.originalPath,\n status: \"modified\",\n additions: 0,\n deletions: 0,\n binary: false,\n isNewFile: false, // Conservative assumption\n });\n }\n }\n }\n\n return summaries;\n}\n\n/**\n * Map insertions/deletions to status\n */\nfunction mapDiffStatus(\n insertions: number,\n deletions: number,\n): FileSummary[\"status\"] {\n // Simple heuristic: if only insertions, likely added; if only deletions, likely deleted\n if (deletions === 0 && insertions > 0) {\n return \"added\";\n }\n if (insertions === 0 && deletions > 0) {\n return \"deleted\";\n }\n return \"modified\";\n}\n\n/**\n * Get a short summary string for display\n */\nexport function formatFileSummary(summary: FileSummary): string {\n const stats = summary.binary\n ? \"binary\"\n : `+${summary.additions}/-${summary.deletions}`;\n return `${summary.path} (${summary.status}, ${stats})`;\n}\n\n/**\n * Get diff content for specific files\n * Used when LLM requests more context (Phase 2 escalation)\n * Supports nested git repositories (files with prefix like 'kb-labs-sdk/...')\n */\n// eslint-disable-next-line sonarjs/cognitive-complexity -- Diff extraction: groups files by nested repo, handles staged/unstaged diffs, processes binary files, merges results\nexport async function getFileDiffs(\n cwd: string,\n files: string[],\n): Promise<Map<string, string>> {\n if (files.length === 0) {\n return new Map();\n }\n\n const diffs = new Map<string, string>();\n\n // Group files by repository (walks up directory tree to find .git)\n const filesByRepo = new Map<\n string,\n { repoPath: string; relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n const repo = await findGitRepo(cwd, file);\n\n if (!repo) {\n // No git repo found - treat as file in cwd\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ repoPath: cwd, relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n // Group by repository path\n const group = filesByRepo.get(repo.repoPath) ?? [];\n group.push({\n repoPath: repo.repoPath,\n relativePath: repo.relativePath,\n originalPath: file,\n });\n filesByRepo.set(repo.repoPath, group);\n }\n\n // Get diffs for each repo\n for (const [repoPath, fileInfos] of filesByRepo) {\n const git: SimpleGit = simpleGit(repoPath);\n\n for (const { relativePath, originalPath } of fileInfos) {\n try {\n // Try staged diff first, then unstaged\n let diff = await git.diff([\"--cached\", \"--\", relativePath]);\n if (!diff) {\n diff = await git.diff([\"--\", relativePath]);\n }\n if (!diff) {\n // For untracked files, try to read content\n diff = await git.show([`:${relativePath}`]).catch(() => \"\");\n }\n if (diff) {\n diffs.set(originalPath, diff);\n }\n } catch {\n // Skip files that can't be diffed\n }\n }\n }\n\n return diffs;\n}\n\n/**\n * Helper to check if path exists asynchronously\n */\nasync function existsAsync(path: string): Promise<boolean> {\n try {\n const { existsSync } = await import(\"node:fs\");\n return existsSync(path);\n } catch {\n return false;\n }\n}\n\n/**\n * Find the git repository root for a given file path.\n * Walks up the directory tree to find the nearest .git directory.\n *\n * @param basePath - Base path to start searching from (e.g., /Users/user/project)\n * @param filePath - Relative file path (e.g., kb-labs-commit/packages/core/src/index.ts)\n * @returns Object with repoPath (absolute) and relativePath (relative to repo), or null if no repo found\n *\n * @example\n * // For nested repo:\n * findGitRepo('/Users/user/kb-labs', 'kb-labs-commit/packages/core/src/index.ts')\n * // Returns: { repoPath: '/Users/user/kb-labs/kb-labs-commit', relativePath: 'packages/core/src/index.ts' }\n *\n * // For file in root repo:\n * findGitRepo('/Users/user/kb-labs', 'src/index.ts')\n * // Returns: { repoPath: '/Users/user/kb-labs', relativePath: 'src/index.ts' }\n */\nasync function findGitRepo(\n basePath: string,\n filePath: string,\n): Promise<{ repoPath: string; relativePath: string } | null> {\n const segments = filePath.split(\"/\");\n\n // Try progressively shorter paths (walk up the tree)\n // For 'kb-labs-commit/packages/core/src/index.ts', try:\n // 1. basePath/kb-labs-commit/packages/core/src\n // 2. basePath/kb-labs-commit/packages/core\n // 3. basePath/kb-labs-commit/packages\n // 4. basePath/kb-labs-commit\n // 5. basePath (fallback to root)\n\n for (let i = segments.length - 1; i > 0; i--) {\n const potentialRepoSegments = segments.slice(0, i);\n const potentialRepoPath = `${basePath}/${potentialRepoSegments.join(\"/\")}`;\n const gitDir = `${potentialRepoPath}/.git`;\n\n if (await existsAsync(gitDir)) {\n // Found a .git directory - this is the repo root\n const relativePath = segments.slice(i).join(\"/\");\n return { repoPath: potentialRepoPath, relativePath };\n }\n }\n\n // No nested repo found, check if basePath itself is a git repo\n if (await existsAsync(`${basePath}/.git`)) {\n return { repoPath: basePath, relativePath: filePath };\n }\n\n // No git repo found at all\n return null;\n}\n","/**\n * File diff utilities\n */\n\nimport { simpleGit } from \"simple-git\";\n\nexport interface FileDiff {\n diff: string;\n additions: number;\n deletions: number;\n}\n\n/**\n * Get diff for a specific file\n */\nexport async function getFileDiff(\n cwd: string,\n filePath: string,\n): Promise<FileDiff> {\n const git = simpleGit(cwd);\n\n // Get diff for the specific file\n // git diff HEAD -- <file>\n const diffOutput = await git.diff([\"HEAD\", \"--\", filePath]);\n\n // Count additions and deletions\n const additions = (diffOutput.match(/^\\+(?!\\+)/gm) || []).length;\n const deletions = (diffOutput.match(/^-(?!-)/gm) || []).length;\n\n return {\n diff: diffOutput,\n additions,\n deletions,\n };\n}\n","/**\n * Recent commits analysis for style matching\n */\n\nimport { simpleGit, type SimpleGit } from \"simple-git\";\n\n/**\n * Get recent commit messages for style reference\n *\n * @param cwd - Working directory\n * @param count - Number of commits to retrieve (default: 10)\n * @returns Array of commit messages (subject line only)\n */\nexport async function getRecentCommits(\n cwd: string,\n count: number = 10,\n): Promise<string[]> {\n const git: SimpleGit = simpleGit(cwd);\n\n try {\n const log = await git.log({\n maxCount: count,\n format: {\n message: \"%s\", // Subject line only\n },\n });\n\n return log.all.map((commit) => commit.message);\n } catch {\n // Return empty array if no commits or error\n return [];\n }\n}\n\n/**\n * Detect the commit style from recent commits\n *\n * @returns Detected style hints\n */\nexport function detectCommitStyle(commits: string[]): {\n usesConventional: boolean;\n commonScopes: string[];\n avgLength: number;\n} {\n if (commits.length === 0) {\n return {\n usesConventional: false,\n commonScopes: [],\n avgLength: 50,\n };\n }\n\n // Check for conventional commit pattern: type(scope)?: message\n const conventionalPattern =\n /^(feat|fix|docs|style|refactor|test|chore|ci|perf|build)(\\([^)]+\\))?!?:/i;\n const conventionalMatches = commits.filter((c) =>\n conventionalPattern.test(c),\n );\n const usesConventional = conventionalMatches.length >= commits.length * 0.5;\n\n // Extract scopes\n const scopePattern = /^\\w+\\(([^)]+)\\)/;\n const scopes = commits\n .map((c) => {\n const match = c.match(scopePattern);\n return match ? match[1] : null;\n })\n .filter((s): s is string => s !== null);\n\n // Count scope frequency\n const scopeCounts = scopes.reduce<Record<string, number>>((acc, scope) => {\n acc[scope] = (acc[scope] || 0) + 1;\n return acc;\n }, {});\n\n // Get top 5 most common scopes\n const commonScopes = Object.entries(scopeCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([scope]) => scope);\n\n // Calculate average message length\n const avgLength = Math.round(\n commits.reduce((sum, c) => sum + c.length, 0) / commits.length,\n );\n\n return {\n usesConventional,\n commonScopes,\n avgLength,\n };\n}\n","/**\n * Scope resolver - converts package names to file path patterns\n * Supports:\n * 1. Exact package name: @kb-labs/core, my-package\n * 2. Wildcard pattern: @kb-labs/core-*, packages/*\n * 3. Path pattern: packages/core/src/**\n */\n\n/* eslint-disable no-await-in-loop -- Sequential file reads required for package.json discovery in nested directories */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport globby from \"globby\";\n\nexport interface ResolvedScope {\n /** Original scope string */\n original: string;\n /** Type of scope */\n type: \"package-name\" | \"wildcard\" | \"path-pattern\";\n /** Resolved package paths (directories) */\n packagePaths: string[];\n /** Pattern to filter files (glob or regex) */\n filePattern?: string;\n}\n\nexport interface PackageInfo {\n name: string;\n path: string;\n}\n\n/**\n * Resolve scope to package paths and file pattern\n */\nexport async function resolveScope(\n cwd: string,\n scope: string,\n): Promise<ResolvedScope> {\n // Detect scope type\n const isExactPackageName =\n !scope.includes(\"*\") && (scope.startsWith(\"@\") || !scope.includes(\"/\"));\n const isWildcardPackageName =\n scope.includes(\"*\") && (scope.startsWith(\"@\") || !scope.includes(\"/\"));\n const _isPathPattern = scope.includes(\"/\") && !scope.startsWith(\"@\");\n\n if (isExactPackageName) {\n // Exact package name: @kb-labs/core\n const packages = await discoverPackages(cwd);\n const matched = packages.filter((pkg) => pkg.name === scope);\n\n return {\n original: scope,\n type: \"package-name\",\n packagePaths: matched.map((p) => p.path),\n filePattern:\n matched.length > 0 ? createGlobPattern(cwd, matched) : undefined,\n };\n }\n\n if (isWildcardPackageName) {\n // Wildcard package name: @kb-labs/core-*\n const packages = await discoverPackages(cwd);\n const regex = createPackageNameRegex(scope);\n const matched = packages.filter((pkg) => regex.test(pkg.name));\n\n return {\n original: scope,\n type: \"wildcard\",\n packagePaths: matched.map((p) => p.path),\n filePattern:\n matched.length > 0 ? createGlobPattern(cwd, matched) : undefined,\n };\n }\n\n // Path pattern: packages/core/** or src/**/*.ts\n // If the scope is a plain directory path (no glob chars), append /** to match all files inside\n const filePattern = !scope.includes(\"*\") && !scope.includes(\"?\")\n ? `${scope}/**`\n : scope;\n return {\n original: scope,\n type: \"path-pattern\",\n packagePaths: [],\n filePattern,\n };\n}\n\n/**\n * Check if a file matches the resolved scope\n */\nexport function matchesScope(\n filePath: string,\n resolvedScope: ResolvedScope,\n): boolean {\n // For package-based scopes, check if file is within any package path\n if (resolvedScope.packagePaths.length > 0) {\n return resolvedScope.packagePaths.some((pkgPath) => {\n // Normalize both paths for comparison\n const normalizedFile = filePath.replace(/\\\\/g, \"/\");\n const normalizedPkg = pkgPath.replace(/\\\\/g, \"/\");\n return (\n normalizedFile.startsWith(normalizedPkg + \"/\") ||\n normalizedFile === normalizedPkg\n );\n });\n }\n\n // For path patterns, use minimatch (caller should handle this)\n return true;\n}\n\n/**\n * Discover all packages in workspace\n */\nasync function discoverPackages(cwd: string): Promise<PackageInfo[]> {\n const packages: PackageInfo[] = [];\n\n // Find all package.json files\n const packageJsonPaths = await globby(\"**/package.json\", {\n cwd,\n absolute: true,\n onlyFiles: true,\n ignore: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.git/**\",\n \"**/.*/**\",\n ],\n });\n\n for (const packageJsonPath of packageJsonPaths) {\n try {\n const packagePath = join(packageJsonPath, \"..\");\n const content = await readFile(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(content);\n\n // Skip packages without name\n if (!packageJson.name) {\n continue;\n }\n\n packages.push({\n name: packageJson.name,\n path: relative(cwd, packagePath) || \".\",\n });\n } catch {\n // Skip invalid package.json\n }\n }\n\n return packages;\n}\n\n/**\n * Create regex from wildcard package name pattern\n * @kb-labs/core-* -> /^@kb-labs\\/core-.*$/\n */\nfunction createPackageNameRegex(pattern: string): RegExp {\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape special chars\n .replace(/\\*/g, \".*\"); // Convert * to .*\n return new RegExp(`^${escaped}$`);\n}\n\n/**\n * Create glob pattern from package paths\n */\nfunction createGlobPattern(cwd: string, packages: PackageInfo[]): string {\n if (packages.length === 1 && packages[0]) {\n return `${packages[0].path}/**`;\n }\n // Multiple packages: {pkg1,pkg2}/**\n const paths = packages.map((p) => p.path).join(\",\");\n return `{${paths}}/**`;\n}\n","/**\n * Secrets detection module\n * Detects files that likely contain secrets and should not be sent to LLM\n */\n\nimport { minimatch } from \"minimatch\";\n\n/**\n * Custom error class for secrets detection\n * This error should NEVER be caught and fallback to heuristics\n */\nexport class SecretsDetectedError extends Error {\n public readonly secretMatches: SecretMatch[];\n\n constructor(matches: SecretMatch[], message: string) {\n super(message);\n this.name = 'SecretsDetectedError';\n this.secretMatches = matches;\n\n // Maintain proper stack trace for where error was thrown (V8 only)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SecretsDetectedError);\n }\n }\n}\n\n/**\n * Structural check for SecretsDetectedError, safe across subpath-export\n * boundaries (`./analyzer` vs `./generator`). tsup bundles each entry\n * independently, so `error instanceof SecretsDetectedError` fails when the\n * throw site and the catch site pull the class from different subpaths —\n * they end up with two distinct class objects despite identical source.\n */\nexport function isSecretsDetectedError(error: unknown): error is SecretsDetectedError {\n return (\n error instanceof Error &&\n error.name === 'SecretsDetectedError' &&\n Array.isArray((error as SecretsDetectedError).secretMatches)\n );\n}\n\n/**\n * Detailed information about a detected secret\n */\nexport interface SecretMatch {\n file: string;\n line: number;\n column: number;\n pattern: string; // Which pattern matched\n patternName: string; // Human-readable pattern name\n snippet: string; // Code snippet (with context)\n matchedText: string; // The actual matched text (truncated if long)\n}\n\n/**\n * File patterns that likely contain secrets\n * Based on common secret file conventions\n */\nconst SECRET_FILE_PATTERNS = [\n // Environment files\n \".env\",\n \".env.*\",\n \"*.env\",\n \".envrc\",\n\n // NPM/Node\n \".npmrc\",\n \".yarnrc\",\n \".yarnrc.yml\",\n\n // SSH/GPG keys\n \"*.key\",\n \"*.pem\",\n \"*.p12\",\n \"*.pfx\",\n \"id_rsa\",\n \"id_dsa\",\n \"id_ecdsa\",\n \"id_ed25519\",\n \"*.pub\",\n\n // AWS\n \".aws/**\",\n \"credentials\",\n\n // Docker\n \".docker/config.json\",\n\n // Git credentials\n \".git-credentials\",\n \".netrc\",\n\n // Service account files\n \"*-service-account.json\",\n \"*-serviceaccount.json\",\n \"service-account*.json\",\n \"serviceaccount*.json\",\n\n // Kubernetes\n \"kubeconfig\",\n \"*.kubeconfig\",\n\n // Terraform\n \"*.tfvars\",\n \"terraform.tfstate\",\n \"terraform.tfstate.backup\",\n\n // Other common secrets\n \"secrets.yml\",\n \"secrets.yaml\",\n \"secret.yml\",\n \"secret.yaml\",\n \"passwords.txt\",\n \"password.txt\",\n];\n\n/**\n * Content patterns that indicate secrets (with human-readable names)\n */\nconst SECRET_CONTENT_PATTERNS: Array<{ pattern: RegExp; name: string }> = [\n // API keys/tokens\n { pattern: /api[_-]?key[s]?['\":\\s]*[a-zA-Z0-9_-]{20,}/i, name: 'API Key' },\n { pattern: /auth[_-]?token[s]?['\":\\s]*[a-zA-Z0-9_-]{20,}/i, name: 'Auth Token' },\n { pattern: /access[_-]?token[s]?['\":\\s]*[a-zA-Z0-9_-]{20,}/i, name: 'Access Token' },\n\n // AWS\n { pattern: /AKIA[0-9A-Z]{16}/, name: 'AWS Access Key ID' },\n { pattern: /aws[_-]?secret[_-]?access[_-]?key/i, name: 'AWS Secret Access Key' },\n\n // NPM\n { pattern: /\\/\\/registry\\.npmjs\\.org\\/:_authToken=/, name: 'NPM Auth Token' },\n { pattern: /npm_[A-Za-z0-9]{30,}/, name: 'NPM Token' },\n\n // GitHub\n { pattern: /gh[pousr]_[A-Za-z0-9_]{36,}/, name: 'GitHub Token' },\n\n // Slack\n { pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[A-Za-z0-9]{24,}/, name: 'Slack Token' },\n\n // Private keys\n { pattern: /-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY-----/, name: 'Private Key' },\n\n // Passwords\n { pattern: /password['\":\\s]*['\"]/i, name: 'Password' },\n\n // Generic patterns\n { pattern: /secret['\":\\s]*['\"]/i, name: 'Secret' },\n];\n\n/**\n * Check if file path matches secret file patterns\n */\nexport function isSecretFile(filePath: string): boolean {\n // Normalize path separators\n const normalizedPath = filePath.replace(/\\\\/g, \"/\");\n\n return SECRET_FILE_PATTERNS.some((pattern) =>\n minimatch(normalizedPath, pattern, { matchBase: true }),\n );\n}\n\n/**\n * Check if file content contains secret patterns\n */\nexport function containsSecrets(content: string): boolean {\n return SECRET_CONTENT_PATTERNS.some(({ pattern }) => pattern.test(content));\n}\n\n/**\n * Scan files for potential secrets\n * Returns list of files that likely contain secrets\n */\nexport function detectSecretFiles(files: string[]): string[] {\n return files.filter(isSecretFile);\n}\n\n/**\n * Scan file diffs for secrets\n * Returns map of files that contain secrets\n */\nexport function detectSecretsInDiffs(\n diffs: Map<string, string>,\n): Map<string, string> {\n const secretDiffs = new Map<string, string>();\n\n for (const [file, diff] of diffs.entries()) {\n if (containsSecrets(diff)) {\n secretDiffs.set(file, diff);\n }\n }\n\n return secretDiffs;\n}\n\n/**\n * Create error message for detected secrets\n */\nexport function formatSecretsWarning(secretFiles: string[]): string {\n const count = secretFiles.length;\n const filesList = secretFiles.map((f) => ` - ${f}`).join(\"\\n\");\n\n return [\n `🚨 CRITICAL SECURITY ERROR: Detected ${count} file(s) with potential secrets:`,\n filesList,\n \"\",\n \"⛔️ COMMIT GENERATION ABORTED\",\n \"\",\n \"These files contain sensitive data (API keys, tokens, credentials)\",\n \"that MUST NOT be committed to git or sent to LLM.\",\n \"\",\n \"✅ Actions to fix:\",\n \" 1. Add these files to .gitignore\",\n \" 2. Remove secrets from the files (use environment variables instead)\",\n \" 3. If already committed, use git filter-branch or BFG to remove from history\",\n \"\",\n \"⚠️ If you already ran commit:generate before, the secrets may have been\",\n \"sent to OpenAI. Rotate your credentials immediately.\",\n ].join(\"\\n\");\n}\n\n/**\n * Detect secrets in file diffs with exact location information\n * Returns array of SecretMatch with file, line, column, pattern info\n */\nexport function detectSecretsWithLocation(\n diffs: Map<string, string>,\n): SecretMatch[] {\n const matches: SecretMatch[] = [];\n\n for (const [file, diff] of diffs.entries()) {\n const lines = diff.split('\\n');\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex] ?? '';\n\n for (const { pattern, name } of SECRET_CONTENT_PATTERNS) {\n // Reset regex lastIndex for global patterns\n pattern.lastIndex = 0;\n\n const match = pattern.exec(line);\n if (match) {\n const matchedText = match[0];\n const column = match.index;\n\n // Create snippet with context (20 chars before, full match, 20 chars after)\n const snippetStart = Math.max(0, column - 20);\n const snippetEnd = Math.min(line.length, column + matchedText.length + 20);\n const snippet = line.substring(snippetStart, snippetEnd);\n\n // Truncate matched text if very long (max 50 chars)\n const displayText = matchedText.length > 50\n ? matchedText.substring(0, 50) + '...'\n : matchedText;\n\n matches.push({\n file,\n line: lineIndex + 1, // 1-based line numbers\n column: column + 1, // 1-based column numbers\n pattern: pattern.source,\n patternName: name,\n snippet: snippet.trim(),\n matchedText: displayText,\n });\n }\n }\n }\n }\n\n return matches;\n}\n\n/**\n * Format detailed secrets report with locations\n */\nexport function formatSecretsReport(matches: SecretMatch[]): string {\n const count = matches.length;\n const fileCount = new Set(matches.map(m => m.file)).size;\n\n const lines = [\n `🚨 CRITICAL SECURITY ERROR: Detected ${count} potential secret(s) in ${fileCount} file(s)`,\n \"\",\n \"⛔️ COMMIT GENERATION BLOCKED\",\n \"\",\n ];\n\n // Group by file\n const byFile = new Map<string, SecretMatch[]>();\n for (const match of matches) {\n const existing = byFile.get(match.file) ?? [];\n existing.push(match);\n byFile.set(match.file, existing);\n }\n\n // Format each file's matches\n for (const [file, fileMatches] of byFile.entries()) {\n lines.push(`📄 ${file}:`);\n for (const match of fileMatches) {\n lines.push(` Line ${match.line}:${match.column} - ${match.patternName}`);\n lines.push(` Pattern: ${match.pattern.substring(0, 60)}${match.pattern.length > 60 ? '...' : ''}`);\n lines.push(` Matched: ${match.matchedText}`);\n lines.push(` Context: ...${match.snippet}...`);\n lines.push('');\n }\n }\n\n lines.push(\n \"🔒 These files contain sensitive data (API keys, tokens, credentials)\",\n \"that MUST NOT be committed to git or sent to LLM.\",\n \"\",\n \"✅ Actions to fix:\",\n \" 1. Review each match above - some may be false positives (e.g., examples in comments)\",\n \" 2. If real secrets: Add files to .gitignore and remove secrets (use env vars)\",\n \" 3. If false positives: Use --allow-secrets flag to proceed with confirmation\",\n \"\",\n \"⚠️ If secrets were already sent to LLM in previous runs, rotate credentials immediately!\",\n );\n\n return lines.join(\"\\n\");\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analyzer/git-status.ts","../../src/validator/index.ts","../../src/applier/apply.ts","../../src/applier/push.ts"],"names":["useLogger","simpleGit","cleanGitError"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Git status analysis\n */\n\nimport { simpleGit, type SimpleGit, type StatusResult } from \"simple-git\";\nimport type { GitStatus } from \"@kb-labs/commit-contracts\";\n\n/**\n * Get current git status (staged, unstaged, untracked files).\n * cwd must already point to the resolved scope directory.\n */\nexport async function getGitStatus(cwd: string): Promise<GitStatus> {\n // --ignore-submodules=all: exclude submodule pointer drift in worktrees\n const git: SimpleGit = simpleGit(cwd);\n const status: StatusResult = await git.status(['--ignore-submodules=all']);\n\n return {\n staged: status.staged.filter((f) => !shouldIgnoreFile(f)),\n unstaged: [...status.modified, ...status.deleted]\n .filter((f) => !status.staged.includes(f))\n .filter((f) => !shouldIgnoreFile(f)),\n untracked: status.not_added.filter((f) => !shouldIgnoreFile(f)),\n };\n}\n\nconst IGNORED_SEGMENTS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".turbo\",\n \"coverage\",\n]);\n\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n * Uses exact path segment matching to avoid false positives like \"my-dist/file.ts\".\n */\nfunction shouldIgnoreFile(file: string): boolean {\n return file.split(\"/\").some((segment) => IGNORED_SEGMENTS.has(segment));\n}\n\n/**\n * Get all changed files (staged + unstaged + untracked)\n * Filters out node_modules and other build artifacts\n */\nexport function getAllChangedFiles(status: GitStatus): string[] {\n const allFiles = [\n ...new Set([...status.staged, ...status.unstaged, ...status.untracked]),\n ];\n return allFiles.filter((file) => !shouldIgnoreFile(file));\n}\n\n/**\n * Check if there are any changes\n */\nexport function hasChanges(status: GitStatus): boolean {\n return (\n status.staged.length > 0 ||\n status.unstaged.length > 0 ||\n status.untracked.length > 0\n );\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n const git: SimpleGit = simpleGit(cwd);\n const branch = await git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch.trim();\n}\n\n/**\n * Check if branch is protected (main/master)\n */\nexport function isProtectedBranch(branch: string): boolean {\n const protectedBranches = [\n \"main\",\n \"master\",\n \"develop\",\n \"release\",\n \"production\",\n ];\n return protectedBranches.includes(branch.toLowerCase());\n}\n","/**\n * Commit plan validation — shared by applier, REST handlers, CLI, and MCP.\n *\n * Centralizes checks that used to live only inside apply.ts, so that any\n * surface (Studio, CLI, MCP) can proactively report plan integrity/staleness\n * before the user attempts to apply, instead of only failing at apply time.\n *\n * @module @kb-labs/commit-core/validator\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CommitPlan } from \"@kb-labs/commit-contracts\";\nimport { useLogger } from \"@kb-labs/sdk\";\nimport { getGitStatus, getAllChangedFiles } from \"../analyzer/git-status\";\n\n/**\n * Group files by their git repository (root or nested)\n *\n * Supports nested git repositories: files with a first path segment that is\n * itself a git repo root are grouped under that nested repo instead of cwd.\n */\nexport function groupFilesByRepo(\n cwd: string,\n files: string[],\n): Map<string, { relativePath: string; originalPath: string }[]> {\n const filesByRepo = new Map<\n string,\n { relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n // Check if file is in a nested repo (first segment might be a git repo)\n const segments = file.split(\"/\");\n const potentialRepoDir = segments[0];\n\n // Handle edge case: empty file path or no segments\n if (!potentialRepoDir) {\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n const potentialRepoPath = join(cwd, potentialRepoDir);\n const potentialGitDir = join(potentialRepoPath, \".git\");\n\n // Check if it's actually a nested git repo\n const isNestedRepo = existsSync(potentialGitDir);\n\n if (isNestedRepo) {\n // Use nested repo as git root, strip first segment from path\n const relativePath = segments.slice(1).join(\"/\");\n const group = filesByRepo.get(potentialRepoPath) ?? [];\n group.push({ relativePath, originalPath: file });\n filesByRepo.set(potentialRepoPath, group);\n } else {\n // Use cwd as git root\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n }\n }\n\n return filesByRepo;\n}\n\n/**\n * Validate internal consistency of a commit plan: every commit has files and\n * a message, and no file appears in more than one commit.\n *\n * Returns an array of human-readable error strings (empty = valid).\n */\nexport function validatePlanIntegrity(plan: CommitPlan): string[] {\n const errors: string[] = [];\n const seenInCommit = new Map<string, string>();\n\n for (const commit of plan.commits) {\n if (commit.files.length === 0) {\n errors.push(`Commit ${commit.id} has no files`);\n continue;\n }\n\n if (!commit.message.trim()) {\n errors.push(`Commit ${commit.id} has empty message`);\n continue;\n }\n\n for (const file of commit.files) {\n const firstCommit = seenInCommit.get(file);\n if (firstCommit) {\n errors.push(\n `File appears in multiple commits: ${file} (first: ${firstCommit}, duplicate: ${commit.id})`,\n );\n } else {\n seenInCommit.set(file, commit.id);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Check if files in the plan have changed since plan generation.\n *\n * Only checks files that are part of the plan, ignoring other changes in the\n * repo — cheap even on a large repo. Used both proactively (status handlers,\n * before the user attempts Apply) and as the last-second guard inside\n * applyCommitPlan.\n */\nexport async function checkPlanStaleness(\n cwd: string,\n plan: CommitPlan,\n scope?: string,\n): Promise<{ isStale: boolean; reason: string }> {\n const logger = useLogger();\n const planFiles = new Set(plan.commits.flatMap((c) => c.files));\n\n await logger.debug(\"checkPlanStaleness: start\", {\n scope,\n cwd,\n planFiles: [...planFiles],\n });\n\n // If no files in plan, nothing to check\n if (planFiles.size === 0) {\n return { isStale: false, reason: \"\" };\n }\n\n // Determine which repo(s) we need to check\n const filesByRepo = groupFilesByRepo(cwd, [...planFiles]);\n\n // Check each repo for staleness\n for (const [repoPath, fileInfos] of filesByRepo) {\n // Get current git status from the repo\n // Note: repoPath already points to the correct git repository root\n const currentStatus = await getGitStatus(repoPath);\n const currentFiles = new Set(getAllChangedFiles(currentStatus));\n\n await logger.debug(\"checkPlanStaleness: repo status\", {\n repoPath,\n staged: currentStatus.staged,\n unstaged: currentStatus.unstaged,\n untracked: currentStatus.untracked,\n expected: fileInfos.map((f) => f.relativePath),\n });\n\n // Check that all expected files are still changed\n for (const { relativePath, originalPath } of fileInfos) {\n if (!currentFiles.has(relativePath)) {\n await logger.warn(\"checkPlanStaleness: file not in current changes\", {\n scope,\n repoPath,\n originalPath,\n relativePath,\n currentFiles: [...currentFiles],\n });\n return {\n isStale: true,\n reason: `File no longer has changes: ${originalPath}. Regenerate plan or use --force.`,\n };\n }\n }\n }\n\n return { isStale: false, reason: \"\" };\n}\n","/**\n * Commit plan applier\n */\n\n/* eslint-disable no-await-in-loop -- Sequential git commits required: must stage files and commit one group at a time */\n\nimport { simpleGit, type SimpleGit } from \"simple-git\";\nimport type {\n CommitPlan,\n ApplyResult,\n CommitGroup,\n} from \"@kb-labs/commit-contracts\";\nimport type { ApplyOptions } from \"../types\";\nimport { validatePlanIntegrity, checkPlanStaleness, groupFilesByRepo } from \"../validator\";\nimport { useLogger } from \"@kb-labs/sdk\";\n\nconst GIT_HOOK_NAMES = [\n \"pre-commit\",\n \"commit-msg\",\n \"post-commit\",\n \"prepare-commit-msg\",\n \"pre-push\",\n] as const;\n\nfunction cleanGitError(message: string): string {\n return message\n .split(\"\\n\")\n .filter((line) => !line.trimStart().startsWith(\"hint:\"))\n .join(\"\\n\")\n .trim();\n}\n\nfunction formatCommitError(message: string): string {\n const cleaned = cleanGitError(message);\n const isHookError =\n /hook\\s+(failed|exited|returned)/i.test(cleaned) ||\n /husky/i.test(cleaned) ||\n /lint-staged/i.test(cleaned) ||\n GIT_HOOK_NAMES.some((h) => cleaned.includes(h));\n\n if (isHookError) {\n const match = cleaned.match(\n /(pre-commit|commit-msg|post-commit|prepare-commit-msg|pre-push)/i,\n );\n const hookName = match?.[1] ?? \"git hook\";\n return `${hookName} hook failed:\\n${cleaned}`;\n }\n return cleaned;\n}\n\n/**\n * Apply a commit plan - creates local git commits\n *\n * @param cwd - Working directory (repo root)\n * @param plan - Commit plan to apply\n * @param options - Apply options\n * @returns Apply result with created commit SHAs\n */\nexport async function applyCommitPlan(\n cwd: string,\n plan: CommitPlan,\n options?: ApplyOptions,\n): Promise<ApplyResult> {\n const appliedCommits: ApplyResult[\"appliedCommits\"] = [];\n const errors: string[] = [];\n const logger = useLogger();\n const planFileCount = plan.commits.reduce((sum, c) => sum + c.files.length, 0);\n\n await logger.info(\"apply: start\", {\n scope: options?.scope,\n cwd,\n commits: plan.commits.length,\n planFiles: planFileCount,\n force: !!options?.force,\n });\n\n // 0. Validate plan integrity before touching git state.\n const integrityErrors = validatePlanIntegrity(plan);\n if (integrityErrors.length > 0) {\n await logger.warn(\"apply: plan integrity failed\", { errors: integrityErrors });\n return {\n success: false,\n appliedCommits: [],\n errors: integrityErrors,\n };\n }\n\n // 1. Check for staleness (only for files in the plan, not entire repo)\n if (!options?.force) {\n const staleness = await checkPlanStaleness(cwd, plan, options?.scope);\n if (staleness.isStale) {\n return {\n success: false,\n appliedCommits: [],\n errors: [staleness.reason],\n };\n }\n }\n\n // 2. Apply each commit in order\n for (const commit of plan.commits) {\n try {\n const sha = await applyCommit(cwd, commit);\n appliedCommits.push({\n groupId: commit.id,\n sha,\n message: formatCommitMessage(commit),\n });\n } catch (error) {\n const raw = error instanceof Error ? error.message : String(error);\n errors.push(`Failed to apply commit ${commit.id}: ${formatCommitError(raw)}`);\n\n // Stop on first error - don't leave repo in inconsistent state\n break;\n }\n }\n\n return {\n success: errors.length === 0,\n appliedCommits,\n errors,\n };\n}\n\n/**\n * Apply a single commit\n * Supports nested git repositories - files with 'nested-repo/...' prefix will be committed in the nested repo\n */\nasync function applyCommit(cwd: string, commit: CommitGroup): Promise<string> {\n // Group files by repository (root vs nested)\n const filesByRepo = groupFilesByRepo(cwd, commit.files);\n\n // For now, we only support commits within a single repo\n // If files span multiple repos, we need to handle that differently\n const repos = Array.from(filesByRepo.keys());\n\n if (repos.length > 1) {\n throw new Error(\n \"Commit spans multiple repositories. Split into separate commits.\",\n );\n }\n\n const [repoPath, fileInfos] = Array.from(filesByRepo.entries())[0]!;\n const git: SimpleGit = simpleGit(repoPath);\n\n // Unstage all currently staged files so only plan files end up in the commit.\n // restore --staged requires HEAD; fall back to rm --cached for fresh repos.\n const statusBefore = await git.status();\n if (statusBefore.staged.length > 0) {\n const restored = await git\n .raw([\"restore\", \"--staged\", \"--\", ...statusBefore.staged])\n .then(() => true)\n .catch(() => false);\n if (!restored) {\n for (const f of statusBefore.staged) {\n await git.raw([\"rm\", \"--cached\", \"--\", f]).catch(() => {});\n }\n }\n }\n\n // Stage the files for this commit, handling gitignored-but-tracked files\n for (const { relativePath } of fileInfos) {\n try {\n await git.add(relativePath);\n } catch (addErr) {\n const msg = addErr instanceof Error ? addErr.message : String(addErr);\n if (msg.includes(\"ignored by one of your .gitignore files\")) {\n // File is inside a gitignored directory — check if it's tracked (previously committed)\n const isTracked = await git\n .raw([\"ls-files\", \"--error-unmatch\", relativePath])\n .then(() => true)\n .catch(() => false);\n if (isTracked) {\n await git.raw([\"add\", \"-f\", relativePath]);\n } else {\n throw new Error(\n `File is gitignored and untracked, cannot stage: ${relativePath}`,\n );\n }\n } else {\n throw new Error(cleanGitError(msg));\n }\n }\n }\n\n // Verify something was actually staged for this commit\n const statusAfterAdd = await git.status();\n const stagedSet = new Set(statusAfterAdd.staged);\n const anyStagedForCommit = fileInfos.some((fi) =>\n stagedSet.has(fi.relativePath),\n );\n if (!anyStagedForCommit) {\n throw new Error(\n `Nothing staged for this commit — files may already be committed or have no changes`,\n );\n }\n\n // Commit only the plan files (staging area contains exclusively those at this point)\n const message = formatCommitMessage(commit);\n const result = await git.commit(message);\n\n return result.commit;\n}\n\n/** Commit footer for branding */\nconst COMMIT_FOOTER = \"\\n\\n🤖 Generated by kb-labs-commit-plugin\";\n\n/**\n * Format commit message following conventional commits\n */\nexport function formatCommitMessage(\n commit: CommitGroup,\n options?: { includeFooter?: boolean },\n): string {\n const type = commit.type;\n const scope = commit.scope ? `(${commit.scope})` : \"\";\n const breaking = commit.breaking ? \"!\" : \"\";\n const subject = commit.message;\n\n let message = `${type}${scope}${breaking}: ${subject}`;\n\n // Add body if present\n if (commit.body) {\n message += `\\n\\n${commit.body}`;\n }\n\n // Add branding footer (default: true)\n if (options?.includeFooter !== false) {\n message += COMMIT_FOOTER;\n }\n\n return message;\n}\n","/**\n * Git push operations\n */\n\nimport { simpleGit, type SimpleGit } from 'simple-git';\nimport type { PushResult } from '@kb-labs/commit-contracts';\nimport type { PushOptions } from '../types';\nimport { getCurrentBranch, isProtectedBranch } from '../analyzer/git-status';\n\n/**\n * Push commits to remote repository\n *\n * @param cwd - Working directory (repo root)\n * @param options - Push options\n * @returns Push result\n */\nexport async function pushCommits(cwd: string, options?: PushOptions): Promise<PushResult> {\n const git: SimpleGit = simpleGit(cwd);\n const remote = options?.remote || 'origin';\n\n try {\n // Get current branch\n const branch = await getCurrentBranch(cwd);\n\n // Warn about protected branches with force push\n if (options?.force && isProtectedBranch(branch)) {\n return {\n success: false,\n remote,\n branch,\n commitsPushed: 0,\n error: `Refusing to force push to protected branch '${branch}'. This is dangerous and disabled by default.`,\n };\n }\n\n // Check how many commits ahead of remote\n const commitsToPush = await countCommitsToPush(git, remote, branch);\n\n if (commitsToPush === 0) {\n return {\n success: true,\n remote,\n branch,\n commitsPushed: 0,\n };\n }\n\n // Push to remote\n const pushOptions = options?.force ? ['--force'] : [];\n await git.push(remote, branch, pushOptions);\n\n return {\n success: true,\n remote,\n branch,\n commitsPushed: commitsToPush,\n };\n } catch (error) {\n const raw = error instanceof Error ? error.message : String(error);\n const message = cleanGitError(raw);\n const branch = await getCurrentBranch(cwd).catch(() => 'unknown');\n\n return {\n success: false,\n remote,\n branch,\n commitsPushed: 0,\n error: message,\n };\n }\n}\n\nfunction cleanGitError(message: string): string {\n return message\n .split('\\n')\n .filter((line) => !line.trimStart().startsWith('hint:'))\n .join('\\n')\n .trim();\n}\n\n/**\n * Count commits ahead of remote\n */\nasync function countCommitsToPush(\n git: SimpleGit,\n remote: string,\n branch: string\n): Promise<number> {\n try {\n // First fetch to ensure we have latest remote refs\n await git.fetch(remote, branch);\n\n // Count commits between remote and local\n const result = await git.raw([\n 'rev-list',\n '--count',\n `${remote}/${branch}..HEAD`,\n ]);\n\n return parseInt(result.trim(), 10) || 0;\n } catch {\n // If remote doesn't have the branch, all local commits need pushing\n try {\n const result = await git.raw(['rev-list', '--count', 'HEAD']);\n return parseInt(result.trim(), 10) || 0;\n } catch {\n return 0;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/git-status.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/validator/index.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/applier/apply.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/applier/push.ts"],"names":["useLogger","simpleGit","cleanGitError"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Git status analysis\n */\n\nimport { simpleGit, type SimpleGit, type StatusResult } from \"simple-git\";\nimport type { GitStatus } from \"@kb-labs/commit-contracts\";\n\n/**\n * Get current git status (staged, unstaged, untracked files).\n * cwd must already point to the resolved scope directory.\n */\nexport async function getGitStatus(cwd: string): Promise<GitStatus> {\n // --ignore-submodules=all: exclude submodule pointer drift in worktrees\n const git: SimpleGit = simpleGit(cwd);\n const status: StatusResult = await git.status(['--ignore-submodules=all']);\n\n return {\n staged: status.staged.filter((f) => !shouldIgnoreFile(f)),\n unstaged: [...status.modified, ...status.deleted]\n .filter((f) => !status.staged.includes(f))\n .filter((f) => !shouldIgnoreFile(f)),\n untracked: status.not_added.filter((f) => !shouldIgnoreFile(f)),\n };\n}\n\nconst IGNORED_SEGMENTS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".turbo\",\n \"coverage\",\n]);\n\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n * Uses exact path segment matching to avoid false positives like \"my-dist/file.ts\".\n */\nfunction shouldIgnoreFile(file: string): boolean {\n return file.split(\"/\").some((segment) => IGNORED_SEGMENTS.has(segment));\n}\n\n/**\n * Get all changed files (staged + unstaged + untracked)\n * Filters out node_modules and other build artifacts\n */\nexport function getAllChangedFiles(status: GitStatus): string[] {\n const allFiles = [\n ...new Set([...status.staged, ...status.unstaged, ...status.untracked]),\n ];\n return allFiles.filter((file) => !shouldIgnoreFile(file));\n}\n\n/**\n * Check if there are any changes\n */\nexport function hasChanges(status: GitStatus): boolean {\n return (\n status.staged.length > 0 ||\n status.unstaged.length > 0 ||\n status.untracked.length > 0\n );\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n const git: SimpleGit = simpleGit(cwd);\n const branch = await git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch.trim();\n}\n\n/**\n * Check if branch is protected (main/master)\n */\nexport function isProtectedBranch(branch: string): boolean {\n const protectedBranches = [\n \"main\",\n \"master\",\n \"develop\",\n \"release\",\n \"production\",\n ];\n return protectedBranches.includes(branch.toLowerCase());\n}\n","/**\n * Commit plan validation — shared by applier, REST handlers, CLI, and MCP.\n *\n * Centralizes checks that used to live only inside apply.ts, so that any\n * surface (Studio, CLI, MCP) can proactively report plan integrity/staleness\n * before the user attempts to apply, instead of only failing at apply time.\n *\n * @module @kb-labs/commit-core/validator\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CommitPlan } from \"@kb-labs/commit-contracts\";\nimport { useLogger } from \"@kb-labs/sdk\";\nimport { getGitStatus, getAllChangedFiles } from \"../analyzer/git-status\";\n\n/**\n * Group files by their git repository (root or nested)\n *\n * Supports nested git repositories: files with a first path segment that is\n * itself a git repo root are grouped under that nested repo instead of cwd.\n */\nexport function groupFilesByRepo(\n cwd: string,\n files: string[],\n): Map<string, { relativePath: string; originalPath: string }[]> {\n const filesByRepo = new Map<\n string,\n { relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n // Check if file is in a nested repo (first segment might be a git repo)\n const segments = file.split(\"/\");\n const potentialRepoDir = segments[0];\n\n // Handle edge case: empty file path or no segments\n if (!potentialRepoDir) {\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n const potentialRepoPath = join(cwd, potentialRepoDir);\n const potentialGitDir = join(potentialRepoPath, \".git\");\n\n // Check if it's actually a nested git repo\n const isNestedRepo = existsSync(potentialGitDir);\n\n if (isNestedRepo) {\n // Use nested repo as git root, strip first segment from path\n const relativePath = segments.slice(1).join(\"/\");\n const group = filesByRepo.get(potentialRepoPath) ?? [];\n group.push({ relativePath, originalPath: file });\n filesByRepo.set(potentialRepoPath, group);\n } else {\n // Use cwd as git root\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n }\n }\n\n return filesByRepo;\n}\n\n/**\n * Validate internal consistency of a commit plan: every commit has files and\n * a message, and no file appears in more than one commit.\n *\n * Returns an array of human-readable error strings (empty = valid).\n */\nexport function validatePlanIntegrity(plan: CommitPlan): string[] {\n const errors: string[] = [];\n const seenInCommit = new Map<string, string>();\n\n for (const commit of plan.commits) {\n if (commit.files.length === 0) {\n errors.push(`Commit ${commit.id} has no files`);\n continue;\n }\n\n if (!commit.message.trim()) {\n errors.push(`Commit ${commit.id} has empty message`);\n continue;\n }\n\n for (const file of commit.files) {\n const firstCommit = seenInCommit.get(file);\n if (firstCommit) {\n errors.push(\n `File appears in multiple commits: ${file} (first: ${firstCommit}, duplicate: ${commit.id})`,\n );\n } else {\n seenInCommit.set(file, commit.id);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Check if files in the plan have changed since plan generation.\n *\n * Only checks files that are part of the plan, ignoring other changes in the\n * repo — cheap even on a large repo. Used both proactively (status handlers,\n * before the user attempts Apply) and as the last-second guard inside\n * applyCommitPlan.\n */\nexport async function checkPlanStaleness(\n cwd: string,\n plan: CommitPlan,\n scope?: string,\n): Promise<{ isStale: boolean; reason: string }> {\n const logger = useLogger();\n const planFiles = new Set(plan.commits.flatMap((c) => c.files));\n\n await logger.debug(\"checkPlanStaleness: start\", {\n scope,\n cwd,\n planFiles: [...planFiles],\n });\n\n // If no files in plan, nothing to check\n if (planFiles.size === 0) {\n return { isStale: false, reason: \"\" };\n }\n\n // Determine which repo(s) we need to check\n const filesByRepo = groupFilesByRepo(cwd, [...planFiles]);\n\n // Check each repo for staleness\n for (const [repoPath, fileInfos] of filesByRepo) {\n // Get current git status from the repo\n // Note: repoPath already points to the correct git repository root\n const currentStatus = await getGitStatus(repoPath);\n const currentFiles = new Set(getAllChangedFiles(currentStatus));\n\n await logger.debug(\"checkPlanStaleness: repo status\", {\n repoPath,\n staged: currentStatus.staged,\n unstaged: currentStatus.unstaged,\n untracked: currentStatus.untracked,\n expected: fileInfos.map((f) => f.relativePath),\n });\n\n // Check that all expected files are still changed\n for (const { relativePath, originalPath } of fileInfos) {\n if (!currentFiles.has(relativePath)) {\n await logger.warn(\"checkPlanStaleness: file not in current changes\", {\n scope,\n repoPath,\n originalPath,\n relativePath,\n currentFiles: [...currentFiles],\n });\n return {\n isStale: true,\n reason: `File no longer has changes: ${originalPath}. Regenerate plan or use --force.`,\n };\n }\n }\n }\n\n return { isStale: false, reason: \"\" };\n}\n","/**\n * Commit plan applier\n */\n\n/* eslint-disable no-await-in-loop -- Sequential git commits required: must stage files and commit one group at a time */\n\nimport { simpleGit, type SimpleGit } from \"simple-git\";\nimport type {\n CommitPlan,\n ApplyResult,\n CommitGroup,\n} from \"@kb-labs/commit-contracts\";\nimport type { ApplyOptions } from \"../types\";\nimport { validatePlanIntegrity, checkPlanStaleness, groupFilesByRepo } from \"../validator\";\nimport { useLogger } from \"@kb-labs/sdk\";\n\nconst GIT_HOOK_NAMES = [\n \"pre-commit\",\n \"commit-msg\",\n \"post-commit\",\n \"prepare-commit-msg\",\n \"pre-push\",\n] as const;\n\nfunction cleanGitError(message: string): string {\n return message\n .split(\"\\n\")\n .filter((line) => !line.trimStart().startsWith(\"hint:\"))\n .join(\"\\n\")\n .trim();\n}\n\nfunction formatCommitError(message: string): string {\n const cleaned = cleanGitError(message);\n const isHookError =\n /hook\\s+(failed|exited|returned)/i.test(cleaned) ||\n /husky/i.test(cleaned) ||\n /lint-staged/i.test(cleaned) ||\n GIT_HOOK_NAMES.some((h) => cleaned.includes(h));\n\n if (isHookError) {\n const match = cleaned.match(\n /(pre-commit|commit-msg|post-commit|prepare-commit-msg|pre-push)/i,\n );\n const hookName = match?.[1] ?? \"git hook\";\n return `${hookName} hook failed:\\n${cleaned}`;\n }\n return cleaned;\n}\n\n/**\n * Apply a commit plan - creates local git commits\n *\n * @param cwd - Working directory (repo root)\n * @param plan - Commit plan to apply\n * @param options - Apply options\n * @returns Apply result with created commit SHAs\n */\nexport async function applyCommitPlan(\n cwd: string,\n plan: CommitPlan,\n options?: ApplyOptions,\n): Promise<ApplyResult> {\n const appliedCommits: ApplyResult[\"appliedCommits\"] = [];\n const errors: string[] = [];\n const logger = useLogger();\n const planFileCount = plan.commits.reduce((sum, c) => sum + c.files.length, 0);\n\n await logger.info(\"apply: start\", {\n scope: options?.scope,\n cwd,\n commits: plan.commits.length,\n planFiles: planFileCount,\n force: !!options?.force,\n });\n\n // 0. Validate plan integrity before touching git state.\n const integrityErrors = validatePlanIntegrity(plan);\n if (integrityErrors.length > 0) {\n await logger.warn(\"apply: plan integrity failed\", { errors: integrityErrors });\n return {\n success: false,\n appliedCommits: [],\n errors: integrityErrors,\n };\n }\n\n // 1. Check for staleness (only for files in the plan, not entire repo)\n if (!options?.force) {\n const staleness = await checkPlanStaleness(cwd, plan, options?.scope);\n if (staleness.isStale) {\n return {\n success: false,\n appliedCommits: [],\n errors: [staleness.reason],\n };\n }\n }\n\n // 2. Apply each commit in order\n for (const commit of plan.commits) {\n try {\n const sha = await applyCommit(cwd, commit);\n appliedCommits.push({\n groupId: commit.id,\n sha,\n message: formatCommitMessage(commit),\n });\n } catch (error) {\n const raw = error instanceof Error ? error.message : String(error);\n errors.push(`Failed to apply commit ${commit.id}: ${formatCommitError(raw)}`);\n\n // Stop on first error - don't leave repo in inconsistent state\n break;\n }\n }\n\n return {\n success: errors.length === 0,\n appliedCommits,\n errors,\n };\n}\n\n/**\n * Apply a single commit\n * Supports nested git repositories - files with 'nested-repo/...' prefix will be committed in the nested repo\n */\nasync function applyCommit(cwd: string, commit: CommitGroup): Promise<string> {\n // Group files by repository (root vs nested)\n const filesByRepo = groupFilesByRepo(cwd, commit.files);\n\n // For now, we only support commits within a single repo\n // If files span multiple repos, we need to handle that differently\n const repos = Array.from(filesByRepo.keys());\n\n if (repos.length > 1) {\n throw new Error(\n \"Commit spans multiple repositories. Split into separate commits.\",\n );\n }\n\n const [repoPath, fileInfos] = Array.from(filesByRepo.entries())[0]!;\n const git: SimpleGit = simpleGit(repoPath);\n\n // Unstage all currently staged files so only plan files end up in the commit.\n // restore --staged requires HEAD; fall back to rm --cached for fresh repos.\n const statusBefore = await git.status();\n if (statusBefore.staged.length > 0) {\n const restored = await git\n .raw([\"restore\", \"--staged\", \"--\", ...statusBefore.staged])\n .then(() => true)\n .catch(() => false);\n if (!restored) {\n for (const f of statusBefore.staged) {\n await git.raw([\"rm\", \"--cached\", \"--\", f]).catch(() => {});\n }\n }\n }\n\n // Stage the files for this commit, handling gitignored-but-tracked files\n for (const { relativePath } of fileInfos) {\n try {\n await git.add(relativePath);\n } catch (addErr) {\n const msg = addErr instanceof Error ? addErr.message : String(addErr);\n if (msg.includes(\"ignored by one of your .gitignore files\")) {\n // File is inside a gitignored directory — check if it's tracked (previously committed)\n const isTracked = await git\n .raw([\"ls-files\", \"--error-unmatch\", relativePath])\n .then(() => true)\n .catch(() => false);\n if (isTracked) {\n await git.raw([\"add\", \"-f\", relativePath]);\n } else {\n throw new Error(\n `File is gitignored and untracked, cannot stage: ${relativePath}`,\n );\n }\n } else {\n throw new Error(cleanGitError(msg));\n }\n }\n }\n\n // Verify something was actually staged for this commit\n const statusAfterAdd = await git.status();\n const stagedSet = new Set(statusAfterAdd.staged);\n const anyStagedForCommit = fileInfos.some((fi) =>\n stagedSet.has(fi.relativePath),\n );\n if (!anyStagedForCommit) {\n throw new Error(\n `Nothing staged for this commit — files may already be committed or have no changes`,\n );\n }\n\n // Commit only the plan files (staging area contains exclusively those at this point)\n const message = formatCommitMessage(commit);\n const result = await git.commit(message);\n\n return result.commit;\n}\n\n/** Commit footer for branding */\nconst COMMIT_FOOTER = \"\\n\\n🤖 Generated by kb-labs-commit-plugin\";\n\n/**\n * Format commit message following conventional commits\n */\nexport function formatCommitMessage(\n commit: CommitGroup,\n options?: { includeFooter?: boolean },\n): string {\n const type = commit.type;\n const scope = commit.scope ? `(${commit.scope})` : \"\";\n const breaking = commit.breaking ? \"!\" : \"\";\n const subject = commit.message;\n\n let message = `${type}${scope}${breaking}: ${subject}`;\n\n // Add body if present\n if (commit.body) {\n message += `\\n\\n${commit.body}`;\n }\n\n // Add branding footer (default: true)\n if (options?.includeFooter !== false) {\n message += COMMIT_FOOTER;\n }\n\n return message;\n}\n","/**\n * Git push operations\n */\n\nimport { simpleGit, type SimpleGit } from 'simple-git';\nimport type { PushResult } from '@kb-labs/commit-contracts';\nimport type { PushOptions } from '../types';\nimport { getCurrentBranch, isProtectedBranch } from '../analyzer/git-status';\n\n/**\n * Push commits to remote repository\n *\n * @param cwd - Working directory (repo root)\n * @param options - Push options\n * @returns Push result\n */\nexport async function pushCommits(cwd: string, options?: PushOptions): Promise<PushResult> {\n const git: SimpleGit = simpleGit(cwd);\n const remote = options?.remote || 'origin';\n\n try {\n // Get current branch\n const branch = await getCurrentBranch(cwd);\n\n // Warn about protected branches with force push\n if (options?.force && isProtectedBranch(branch)) {\n return {\n success: false,\n remote,\n branch,\n commitsPushed: 0,\n error: `Refusing to force push to protected branch '${branch}'. This is dangerous and disabled by default.`,\n };\n }\n\n // Check how many commits ahead of remote\n const commitsToPush = await countCommitsToPush(git, remote, branch);\n\n if (commitsToPush === 0) {\n return {\n success: true,\n remote,\n branch,\n commitsPushed: 0,\n };\n }\n\n // Push to remote\n const pushOptions = options?.force ? ['--force'] : [];\n await git.push(remote, branch, pushOptions);\n\n return {\n success: true,\n remote,\n branch,\n commitsPushed: commitsToPush,\n };\n } catch (error) {\n const raw = error instanceof Error ? error.message : String(error);\n const message = cleanGitError(raw);\n const branch = await getCurrentBranch(cwd).catch(() => 'unknown');\n\n return {\n success: false,\n remote,\n branch,\n commitsPushed: 0,\n error: message,\n };\n }\n}\n\nfunction cleanGitError(message: string): string {\n return message\n .split('\\n')\n .filter((line) => !line.trimStart().startsWith('hint:'))\n .join('\\n')\n .trim();\n}\n\n/**\n * Count commits ahead of remote\n */\nasync function countCommitsToPush(\n git: SimpleGit,\n remote: string,\n branch: string\n): Promise<number> {\n try {\n // First fetch to ensure we have latest remote refs\n await git.fetch(remote, branch);\n\n // Count commits between remote and local\n const result = await git.raw([\n 'rev-list',\n '--count',\n `${remote}/${branch}..HEAD`,\n ]);\n\n return parseInt(result.trim(), 10) || 0;\n } catch {\n // If remote doesn't have the branch, all local commits need pushing\n try {\n const result = await git.raw(['rev-list', '--count', 'HEAD']);\n return parseInt(result.trim(), 10) || 0;\n } catch {\n return 0;\n }\n }\n}\n"]}
|