@kb-labs/commit-core 2.94.0 → 2.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,20 +14,17 @@ async function getGitStatus(cwd) {
14
14
  untracked: status.not_added.filter((f) => !shouldIgnoreFile(f))
15
15
  };
16
16
  }
17
+ var IGNORED_SEGMENTS = /* @__PURE__ */ new Set([
18
+ "node_modules",
19
+ ".git",
20
+ "dist",
21
+ "build",
22
+ ".next",
23
+ ".turbo",
24
+ "coverage"
25
+ ]);
17
26
  function shouldIgnoreFile(file) {
18
- const ignoredPaths = [
19
- "node_modules/",
20
- ".git/",
21
- "dist/",
22
- "build/",
23
- ".next/",
24
- ".turbo/",
25
- "coverage/",
26
- ".cache/",
27
- ".temp/",
28
- "tmp/"
29
- ];
30
- return ignoredPaths.some((path) => file.includes(path));
27
+ return file.split("/").some((segment) => IGNORED_SEGMENTS.has(segment));
31
28
  }
32
29
  function getAllChangedFiles(status) {
33
30
  const allFiles = [
@@ -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\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n */\nfunction shouldIgnoreFile(file: string): boolean {\n const ignoredPaths = [\n \"node_modules/\",\n \".git/\",\n \"dist/\",\n \"build/\",\n \".next/\",\n \".turbo/\",\n \"coverage/\",\n \".cache/\",\n \".temp/\",\n \"tmp/\",\n ];\n\n return ignoredPaths.some((path) => file.includes(path));\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":["../../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,6 +1,7 @@
1
1
  import { existsSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { simpleGit } from 'simple-git';
4
+ import { useLogger } from '@kb-labs/sdk';
4
5
 
5
6
  // src/applier/apply.ts
6
7
  async function getGitStatus(cwd) {
@@ -12,20 +13,17 @@ async function getGitStatus(cwd) {
12
13
  untracked: status.not_added.filter((f) => !shouldIgnoreFile(f))
13
14
  };
14
15
  }
16
+ var IGNORED_SEGMENTS = /* @__PURE__ */ new Set([
17
+ "node_modules",
18
+ ".git",
19
+ "dist",
20
+ "build",
21
+ ".next",
22
+ ".turbo",
23
+ "coverage"
24
+ ]);
15
25
  function shouldIgnoreFile(file) {
16
- const ignoredPaths = [
17
- "node_modules/",
18
- ".git/",
19
- "dist/",
20
- "build/",
21
- ".next/",
22
- ".turbo/",
23
- "coverage/",
24
- ".cache/",
25
- ".temp/",
26
- "tmp/"
27
- ];
28
- return ignoredPaths.some((path) => file.includes(path));
26
+ return file.split("/").some((segment) => IGNORED_SEGMENTS.has(segment));
29
27
  }
30
28
  function getAllChangedFiles(status) {
31
29
  const allFiles = [
@@ -48,13 +46,44 @@ function isProtectedBranch(branch) {
48
46
  ];
49
47
  return protectedBranches.includes(branch.toLowerCase());
50
48
  }
51
-
52
- // src/applier/apply.ts
49
+ var GIT_HOOK_NAMES = [
50
+ "pre-commit",
51
+ "commit-msg",
52
+ "post-commit",
53
+ "prepare-commit-msg",
54
+ "pre-push"
55
+ ];
56
+ function cleanGitError(message) {
57
+ return message.split("\n").filter((line) => !line.trimStart().startsWith("hint:")).join("\n").trim();
58
+ }
59
+ function formatCommitError(message) {
60
+ const cleaned = cleanGitError(message);
61
+ const isHookError = /hook\s+(failed|exited|returned)/i.test(cleaned) || /husky/i.test(cleaned) || /lint-staged/i.test(cleaned) || GIT_HOOK_NAMES.some((h) => cleaned.includes(h));
62
+ if (isHookError) {
63
+ const match = cleaned.match(
64
+ /(pre-commit|commit-msg|post-commit|prepare-commit-msg|pre-push)/i
65
+ );
66
+ const hookName = match?.[1] ?? "git hook";
67
+ return `${hookName} hook failed:
68
+ ${cleaned}`;
69
+ }
70
+ return cleaned;
71
+ }
53
72
  async function applyCommitPlan(cwd, plan, options) {
54
73
  const appliedCommits = [];
55
74
  const errors = [];
75
+ const logger = useLogger();
76
+ const planFileCount = plan.commits.reduce((sum, c) => sum + c.files.length, 0);
77
+ await logger.info("apply: start", {
78
+ scope: options?.scope,
79
+ cwd,
80
+ commits: plan.commits.length,
81
+ planFiles: planFileCount,
82
+ force: !!options?.force
83
+ });
56
84
  const integrityErrors = validatePlanIntegrity(plan);
57
85
  if (integrityErrors.length > 0) {
86
+ await logger.warn("apply: plan integrity failed", { errors: integrityErrors });
58
87
  return {
59
88
  success: false,
60
89
  appliedCommits: [],
@@ -80,8 +109,8 @@ async function applyCommitPlan(cwd, plan, options) {
80
109
  message: formatCommitMessage(commit)
81
110
  });
82
111
  } catch (error) {
83
- const message = error instanceof Error ? error.message : "Unknown error";
84
- errors.push(`Failed to apply commit ${commit.id}: ${message}`);
112
+ const raw = error instanceof Error ? error.message : String(error);
113
+ errors.push(`Failed to apply commit ${commit.id}: ${formatCommitError(raw)}`);
85
114
  break;
86
115
  }
87
116
  }
@@ -99,6 +128,10 @@ function validatePlanIntegrity(plan) {
99
128
  errors.push(`Commit ${commit.id} has no files`);
100
129
  continue;
101
130
  }
131
+ if (!commit.message.trim()) {
132
+ errors.push(`Commit ${commit.id} has empty message`);
133
+ continue;
134
+ }
102
135
  for (const file of commit.files) {
103
136
  const firstCommit = seenInCommit.get(file);
104
137
  if (firstCommit) {
@@ -122,9 +155,44 @@ async function applyCommit(cwd, commit) {
122
155
  }
123
156
  const [repoPath, fileInfos] = Array.from(filesByRepo.entries())[0];
124
157
  const git = simpleGit(repoPath);
125
- await git.reset(["--"]);
158
+ const statusBefore = await git.status();
159
+ if (statusBefore.staged.length > 0) {
160
+ const restored = await git.raw(["restore", "--staged", "--", ...statusBefore.staged]).then(() => true).catch(() => false);
161
+ if (!restored) {
162
+ for (const f of statusBefore.staged) {
163
+ await git.raw(["rm", "--cached", "--", f]).catch(() => {
164
+ });
165
+ }
166
+ }
167
+ }
126
168
  for (const { relativePath } of fileInfos) {
127
- await git.add(relativePath);
169
+ try {
170
+ await git.add(relativePath);
171
+ } catch (addErr) {
172
+ const msg = addErr instanceof Error ? addErr.message : String(addErr);
173
+ if (msg.includes("ignored by one of your .gitignore files")) {
174
+ const isTracked = await git.raw(["ls-files", "--error-unmatch", relativePath]).then(() => true).catch(() => false);
175
+ if (isTracked) {
176
+ await git.raw(["add", "-f", relativePath]);
177
+ } else {
178
+ throw new Error(
179
+ `File is gitignored and untracked, cannot stage: ${relativePath}`
180
+ );
181
+ }
182
+ } else {
183
+ throw new Error(cleanGitError(msg));
184
+ }
185
+ }
186
+ }
187
+ const statusAfterAdd = await git.status();
188
+ const stagedSet = new Set(statusAfterAdd.staged);
189
+ const anyStagedForCommit = fileInfos.some(
190
+ (fi) => stagedSet.has(fi.relativePath)
191
+ );
192
+ if (!anyStagedForCommit) {
193
+ throw new Error(
194
+ `Nothing staged for this commit \u2014 files may already be committed or have no changes`
195
+ );
128
196
  }
129
197
  const message = formatCommitMessage(commit);
130
198
  const result = await git.commit(message);
@@ -174,8 +242,14 @@ ${commit.body}`;
174
242
  }
175
243
  return message;
176
244
  }
177
- async function checkStaleness(cwd, plan, _scope) {
245
+ async function checkStaleness(cwd, plan, scope) {
246
+ const logger = useLogger();
178
247
  const planFiles = new Set(plan.commits.flatMap((c) => c.files));
248
+ await logger.debug("checkStaleness: start", {
249
+ scope,
250
+ cwd,
251
+ planFiles: [...planFiles]
252
+ });
179
253
  if (planFiles.size === 0) {
180
254
  return { isStale: false, reason: "" };
181
255
  }
@@ -183,8 +257,22 @@ async function checkStaleness(cwd, plan, _scope) {
183
257
  for (const [repoPath, fileInfos] of filesByRepo) {
184
258
  const currentStatus = await getGitStatus(repoPath);
185
259
  const currentFiles = new Set(getAllChangedFiles(currentStatus));
260
+ await logger.debug("checkStaleness: repo status", {
261
+ repoPath,
262
+ staged: currentStatus.staged,
263
+ unstaged: currentStatus.unstaged,
264
+ untracked: currentStatus.untracked,
265
+ expected: fileInfos.map((f) => f.relativePath)
266
+ });
186
267
  for (const { relativePath, originalPath } of fileInfos) {
187
268
  if (!currentFiles.has(relativePath)) {
269
+ await logger.warn("checkStaleness: file not in current changes", {
270
+ scope,
271
+ repoPath,
272
+ originalPath,
273
+ relativePath,
274
+ currentFiles: [...currentFiles]
275
+ });
188
276
  return {
189
277
  isStale: true,
190
278
  reason: `File no longer has changes: ${originalPath}. Regenerate plan or use --force.`
@@ -226,7 +314,8 @@ async function pushCommits(cwd, options) {
226
314
  commitsPushed: commitsToPush
227
315
  };
228
316
  } catch (error) {
229
- const message = error instanceof Error ? error.message : "Unknown error";
317
+ const raw = error instanceof Error ? error.message : String(error);
318
+ const message = cleanGitError2(raw);
230
319
  const branch = await getCurrentBranch(cwd).catch(() => "unknown");
231
320
  return {
232
321
  success: false,
@@ -237,6 +326,9 @@ async function pushCommits(cwd, options) {
237
326
  };
238
327
  }
239
328
  }
329
+ function cleanGitError2(message) {
330
+ return message.split("\n").filter((line) => !line.trimStart().startsWith("hint:")).join("\n").trim();
331
+ }
240
332
  async function countCommitsToPush(git, remote, branch) {
241
333
  try {
242
334
  await git.fetch(remote, branch);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/analyzer/git-status.ts","../../src/applier/apply.ts","../../src/applier/push.ts"],"names":["simpleGit"],"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\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n */\nfunction shouldIgnoreFile(file: string): boolean {\n const ignoredPaths = [\n \"node_modules/\",\n \".git/\",\n \"dist/\",\n \"build/\",\n \".next/\",\n \".turbo/\",\n \"coverage/\",\n \".cache/\",\n \".temp/\",\n \"tmp/\",\n ];\n\n return ignoredPaths.some((path) => file.includes(path));\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 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 { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\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 { getGitStatus, getAllChangedFiles } from \"../analyzer/git-status\";\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\n // 0. Validate plan integrity before touching git state.\n const integrityErrors = validatePlanIntegrity(plan);\n if (integrityErrors.length > 0) {\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 checkStaleness(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 message = error instanceof Error ? error.message : \"Unknown error\";\n errors.push(`Failed to apply commit ${commit.id}: ${message}`);\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\nfunction 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 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 * 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 // CRITICAL: Reset staging area first to ensure we only commit the specific files for this commit\n // Without this, git.commit() would commit ALL staged files, not just the ones we want\n await git.reset([\"--\"]);\n\n // Stage only the files for this specific commit (using paths relative to the repo)\n for (const { relativePath } of fileInfos) {\n await git.add(relativePath);\n }\n\n // Create commit with only the staged files\n const message = formatCommitMessage(commit);\n const result = await git.commit(message);\n\n return result.commit;\n}\n\n/**\n * Group files by their git repository (root or nested)\n */\nfunction 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/** 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/**\n * Check if files in the plan have changed since plan generation\n * Only checks files that are part of the plan, ignoring other changes in the repo\n */\nasync function checkStaleness(\n cwd: string,\n plan: CommitPlan,\n _scope?: string,\n): Promise<{ isStale: boolean; reason: string }> {\n const planFiles = new Set(plan.commits.flatMap((c) => c.files));\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 // Check that all expected files are still changed\n for (const { relativePath, originalPath } of fileInfos) {\n if (!currentFiles.has(relativePath)) {\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 * 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 message = error instanceof Error ? error.message : 'Unknown error';\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\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":["../../src/analyzer/git-status.ts","../../src/applier/apply.ts","../../src/applier/push.ts"],"names":["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 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 { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\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 { getGitStatus, getAllChangedFiles } from \"../analyzer/git-status\";\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 checkStaleness(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\nfunction 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 * 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/**\n * Group files by their git repository (root or nested)\n */\nfunction 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/** 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/**\n * Check if files in the plan have changed since plan generation\n * Only checks files that are part of the plan, ignoring other changes in the repo\n */\nasync function checkStaleness(\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(\"checkStaleness: 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(\"checkStaleness: 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(\"checkStaleness: 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 * 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"]}