@kb-labs/commit-core 2.119.0-canary.e120bc0ee → 2.119.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analyzer/file-summary.ts","../../src/storage/plan-storage.ts"],"names":["group"],"mappings":"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","file":"index.js","sourcesContent":["/**\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 * Commit plan storage in .kb/commit/\n */\n\n/* eslint-disable no-await-in-loop -- Sequential file operations required for plan cleanup and history management */\n\nimport { readFile, writeFile, mkdir, rm, readdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport type { CommitPlan, ApplyResult, GitStatusSnapshot } from '@kb-labs/commit-contracts';\nimport { CommitPlanSchema, GitStatusSnapshotSchema } from '@kb-labs/commit-contracts';\nimport { getFileSummaries } from '../analyzer/file-summary';\n\nconst COMMIT_DIR = '.kb/commit';\nconst PLANS_DIR = 'plans';\nconst CURRENT_DIR = 'current';\nconst HISTORY_DIR = 'history';\nconst PLAN_FILE = 'plan.json';\nconst STATUS_FILE = 'status.json';\nconst RESULT_FILE = 'result.json';\nconst MAX_HISTORY_ENTRIES = 30; // Keep last 30 history entries\n\n/**\n * Normalize scope string for use in file paths\n * @example \"@kb-labs/mind\" -> \"@kb-labs-mind\"\n * @example \"packages/core/**\" -> \"packages-core\"\n */\nfunction normalizeScopeForPath(scope: string): string {\n return scope\n .replace(/\\//g, '-')\n .replace(/\\*/g, '')\n .replace(/\\./g, '-')\n .replace(/:/g, '-');\n}\n\n/**\n * Get path to commit storage directory\n */\nexport function getCommitStoragePath(cwd: string): string {\n return join(cwd, COMMIT_DIR);\n}\n\n/**\n * Get path to scope-specific plan directory\n */\nexport function getScopePlanDir(cwd: string, scope: string = 'root'): string {\n const scopeDir = normalizeScopeForPath(scope);\n return join(cwd, COMMIT_DIR, PLANS_DIR, scopeDir);\n}\n\n/**\n * Get path to current plan file for a scope\n */\nexport function getCurrentPlanPath(cwd: string, scope: string = 'root'): string {\n return join(getScopePlanDir(cwd, scope), CURRENT_DIR, PLAN_FILE);\n}\n\n/**\n * Get path to current status file for a scope\n */\nexport function getCurrentStatusPath(cwd: string, scope: string = 'root'): string {\n return join(getScopePlanDir(cwd, scope), CURRENT_DIR, STATUS_FILE);\n}\n\n/**\n * Save commit plan to storage\n */\nexport async function savePlan(cwd: string, plan: CommitPlan, scope: string = 'root'): Promise<void> {\n const planPath = getCurrentPlanPath(cwd, scope);\n const statusPath = getCurrentStatusPath(cwd, scope);\n\n // Ensure directory exists\n await mkdir(dirname(planPath), { recursive: true });\n\n // Save plan\n await writeFile(planPath, JSON.stringify(plan, null, 2));\n\n // Save status snapshot — use git status already captured in the plan\n const status = plan.gitStatus;\n const allFiles = [...status.staged, ...status.unstaged, ...status.untracked];\n const summaries = await getFileSummaries(cwd, allFiles);\n\n const snapshot: GitStatusSnapshot = {\n schemaVersion: '1.0',\n createdAt: new Date().toISOString(),\n status,\n summaries,\n };\n\n await writeFile(statusPath, JSON.stringify(snapshot, null, 2));\n}\n\n/**\n * Load current commit plan from storage\n */\nexport async function loadPlan(cwd: string, scope: string = 'root'): Promise<CommitPlan | null> {\n const planPath = getCurrentPlanPath(cwd, scope);\n\n try {\n const content = await readFile(planPath, 'utf-8');\n const data = JSON.parse(content);\n\n // Validate with schema\n const result = CommitPlanSchema.safeParse(data);\n if (!result.success) {\n throw new Error(\n `Commit plan is corrupted and cannot be loaded. Run 'kb commit:generate' to create a new plan.\\n` +\n result.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n'),\n );\n }\n\n return result.data;\n } catch (err) {\n if (err instanceof SyntaxError) {\n throw new Error(\n `Commit plan file is not valid JSON. Run 'kb commit:generate' to create a new plan.`,\n );\n }\n // Re-throw corruption errors (thrown above) and file-not-found returns null\n if (err instanceof Error && err.message.startsWith('Commit plan is corrupted')) {\n throw err;\n }\n return null;\n }\n}\n\n/**\n * Load current status snapshot from storage\n */\nexport async function loadStatus(cwd: string, scope: string = 'root'): Promise<GitStatusSnapshot | null> {\n const statusPath = getCurrentStatusPath(cwd, scope);\n\n try {\n const content = await readFile(statusPath, 'utf-8');\n const data = JSON.parse(content);\n\n const result = GitStatusSnapshotSchema.safeParse(data);\n if (!result.success) {\n return null;\n }\n\n return result.data;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if a plan exists\n * Returns false for missing plans; throws for corrupted plans so the caller can surface the error.\n */\nexport async function hasPlan(cwd: string, scope: string = 'root'): Promise<boolean> {\n const plan = await loadPlan(cwd, scope);\n return plan !== null;\n}\n\n/**\n * Clear current commit plan\n */\nexport async function clearPlan(cwd: string, scope: string = 'root'): Promise<void> {\n const currentDir = join(getScopePlanDir(cwd, scope), CURRENT_DIR);\n\n try {\n await rm(currentDir, { recursive: true, force: true });\n } catch {\n // Ignore errors if directory doesn't exist\n }\n}\n\n/**\n * Save plan and result to history\n */\nexport async function saveToHistory(\n cwd: string,\n plan: CommitPlan,\n result: ApplyResult,\n scope: string = 'root'\n): Promise<void> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const historyDir = join(getScopePlanDir(cwd, scope), HISTORY_DIR, timestamp);\n\n await mkdir(historyDir, { recursive: true });\n\n await writeFile(join(historyDir, PLAN_FILE), JSON.stringify(plan, null, 2));\n await writeFile(join(historyDir, RESULT_FILE), JSON.stringify(result, null, 2));\n\n // Clean old history entries after saving new one\n await cleanOldHistory(cwd, scope);\n}\n\n/**\n * List history entries\n */\nexport async function listHistory(\n cwd: string,\n scope: string = 'root'\n): Promise<Array<{ timestamp: string; path: string }>> {\n const historyDir = join(getScopePlanDir(cwd, scope), HISTORY_DIR);\n\n try {\n const entries = await readdir(historyDir, { withFileTypes: true });\n\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => ({\n timestamp: e.name,\n path: join(historyDir, e.name),\n }))\n .sort((a, b) => b.timestamp.localeCompare(a.timestamp)); // Newest first\n } catch {\n return [];\n }\n}\n\n/**\n * Clean old history entries, keeping only the most recent N entries\n */\nexport async function cleanOldHistory(\n cwd: string,\n scope: string = 'root',\n maxEntries: number = MAX_HISTORY_ENTRIES\n): Promise<void> {\n const entries = await listHistory(cwd, scope);\n\n // If we have more entries than the limit, delete the oldest ones\n if (entries.length > maxEntries) {\n const toDelete = entries.slice(maxEntries); // Keep first N (newest), delete rest\n\n for (const entry of toDelete) {\n try {\n await rm(entry.path, { recursive: true, force: true });\n } catch {\n // Ignore errors if directory doesn't exist or can't be deleted\n }\n }\n }\n}\n\n/**\n * Initialize storage directory structure\n */\nexport async function initStorage(cwd: string, scope: string = 'root'): Promise<void> {\n const dirs = [\n join(getScopePlanDir(cwd, scope), CURRENT_DIR),\n join(getScopePlanDir(cwd, scope), HISTORY_DIR),\n ];\n\n for (const dir of dirs) {\n await mkdir(dir, { recursive: true });\n }\n}\n\n/**\n * List all scopes with plans\n */\nexport async function listScopes(cwd: string): Promise<string[]> {\n const plansDir = join(cwd, COMMIT_DIR, PLANS_DIR);\n\n try {\n const entries = await readdir(plansDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n } catch {\n return [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/file-summary.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/storage/plan-storage.ts"],"names":["group"],"mappings":"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","file":"index.js","sourcesContent":["/**\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 * Commit plan storage in .kb/commit/\n */\n\n/* eslint-disable no-await-in-loop -- Sequential file operations required for plan cleanup and history management */\n\nimport { readFile, writeFile, mkdir, rm, readdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport type { CommitPlan, ApplyResult, GitStatusSnapshot } from '@kb-labs/commit-contracts';\nimport { CommitPlanSchema, GitStatusSnapshotSchema } from '@kb-labs/commit-contracts';\nimport { getFileSummaries } from '../analyzer/file-summary';\n\nconst COMMIT_DIR = '.kb/commit';\nconst PLANS_DIR = 'plans';\nconst CURRENT_DIR = 'current';\nconst HISTORY_DIR = 'history';\nconst PLAN_FILE = 'plan.json';\nconst STATUS_FILE = 'status.json';\nconst RESULT_FILE = 'result.json';\nconst MAX_HISTORY_ENTRIES = 30; // Keep last 30 history entries\n\n/**\n * Normalize scope string for use in file paths\n * @example \"@kb-labs/mind\" -> \"@kb-labs-mind\"\n * @example \"packages/core/**\" -> \"packages-core\"\n */\nfunction normalizeScopeForPath(scope: string): string {\n return scope\n .replace(/\\//g, '-')\n .replace(/\\*/g, '')\n .replace(/\\./g, '-')\n .replace(/:/g, '-');\n}\n\n/**\n * Get path to commit storage directory\n */\nexport function getCommitStoragePath(cwd: string): string {\n return join(cwd, COMMIT_DIR);\n}\n\n/**\n * Get path to scope-specific plan directory\n */\nexport function getScopePlanDir(cwd: string, scope: string = 'root'): string {\n const scopeDir = normalizeScopeForPath(scope);\n return join(cwd, COMMIT_DIR, PLANS_DIR, scopeDir);\n}\n\n/**\n * Get path to current plan file for a scope\n */\nexport function getCurrentPlanPath(cwd: string, scope: string = 'root'): string {\n return join(getScopePlanDir(cwd, scope), CURRENT_DIR, PLAN_FILE);\n}\n\n/**\n * Get path to current status file for a scope\n */\nexport function getCurrentStatusPath(cwd: string, scope: string = 'root'): string {\n return join(getScopePlanDir(cwd, scope), CURRENT_DIR, STATUS_FILE);\n}\n\n/**\n * Save commit plan to storage\n */\nexport async function savePlan(cwd: string, plan: CommitPlan, scope: string = 'root'): Promise<void> {\n const planPath = getCurrentPlanPath(cwd, scope);\n const statusPath = getCurrentStatusPath(cwd, scope);\n\n // Ensure directory exists\n await mkdir(dirname(planPath), { recursive: true });\n\n // Save plan\n await writeFile(planPath, JSON.stringify(plan, null, 2));\n\n // Save status snapshot — use git status already captured in the plan\n const status = plan.gitStatus;\n const allFiles = [...status.staged, ...status.unstaged, ...status.untracked];\n const summaries = await getFileSummaries(cwd, allFiles);\n\n const snapshot: GitStatusSnapshot = {\n schemaVersion: '1.0',\n createdAt: new Date().toISOString(),\n status,\n summaries,\n };\n\n await writeFile(statusPath, JSON.stringify(snapshot, null, 2));\n}\n\n/**\n * Load current commit plan from storage\n */\nexport async function loadPlan(cwd: string, scope: string = 'root'): Promise<CommitPlan | null> {\n const planPath = getCurrentPlanPath(cwd, scope);\n\n try {\n const content = await readFile(planPath, 'utf-8');\n const data = JSON.parse(content);\n\n // Validate with schema\n const result = CommitPlanSchema.safeParse(data);\n if (!result.success) {\n throw new Error(\n `Commit plan is corrupted and cannot be loaded. Run 'kb commit:generate' to create a new plan.\\n` +\n result.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n'),\n );\n }\n\n return result.data;\n } catch (err) {\n if (err instanceof SyntaxError) {\n throw new Error(\n `Commit plan file is not valid JSON. Run 'kb commit:generate' to create a new plan.`,\n );\n }\n // Re-throw corruption errors (thrown above) and file-not-found returns null\n if (err instanceof Error && err.message.startsWith('Commit plan is corrupted')) {\n throw err;\n }\n return null;\n }\n}\n\n/**\n * Load current status snapshot from storage\n */\nexport async function loadStatus(cwd: string, scope: string = 'root'): Promise<GitStatusSnapshot | null> {\n const statusPath = getCurrentStatusPath(cwd, scope);\n\n try {\n const content = await readFile(statusPath, 'utf-8');\n const data = JSON.parse(content);\n\n const result = GitStatusSnapshotSchema.safeParse(data);\n if (!result.success) {\n return null;\n }\n\n return result.data;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if a plan exists\n * Returns false for missing plans; throws for corrupted plans so the caller can surface the error.\n */\nexport async function hasPlan(cwd: string, scope: string = 'root'): Promise<boolean> {\n const plan = await loadPlan(cwd, scope);\n return plan !== null;\n}\n\n/**\n * Clear current commit plan\n */\nexport async function clearPlan(cwd: string, scope: string = 'root'): Promise<void> {\n const currentDir = join(getScopePlanDir(cwd, scope), CURRENT_DIR);\n\n try {\n await rm(currentDir, { recursive: true, force: true });\n } catch {\n // Ignore errors if directory doesn't exist\n }\n}\n\n/**\n * Save plan and result to history\n */\nexport async function saveToHistory(\n cwd: string,\n plan: CommitPlan,\n result: ApplyResult,\n scope: string = 'root'\n): Promise<void> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const historyDir = join(getScopePlanDir(cwd, scope), HISTORY_DIR, timestamp);\n\n await mkdir(historyDir, { recursive: true });\n\n await writeFile(join(historyDir, PLAN_FILE), JSON.stringify(plan, null, 2));\n await writeFile(join(historyDir, RESULT_FILE), JSON.stringify(result, null, 2));\n\n // Clean old history entries after saving new one\n await cleanOldHistory(cwd, scope);\n}\n\n/**\n * List history entries\n */\nexport async function listHistory(\n cwd: string,\n scope: string = 'root'\n): Promise<Array<{ timestamp: string; path: string }>> {\n const historyDir = join(getScopePlanDir(cwd, scope), HISTORY_DIR);\n\n try {\n const entries = await readdir(historyDir, { withFileTypes: true });\n\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => ({\n timestamp: e.name,\n path: join(historyDir, e.name),\n }))\n .sort((a, b) => b.timestamp.localeCompare(a.timestamp)); // Newest first\n } catch {\n return [];\n }\n}\n\n/**\n * Clean old history entries, keeping only the most recent N entries\n */\nexport async function cleanOldHistory(\n cwd: string,\n scope: string = 'root',\n maxEntries: number = MAX_HISTORY_ENTRIES\n): Promise<void> {\n const entries = await listHistory(cwd, scope);\n\n // If we have more entries than the limit, delete the oldest ones\n if (entries.length > maxEntries) {\n const toDelete = entries.slice(maxEntries); // Keep first N (newest), delete rest\n\n for (const entry of toDelete) {\n try {\n await rm(entry.path, { recursive: true, force: true });\n } catch {\n // Ignore errors if directory doesn't exist or can't be deleted\n }\n }\n }\n}\n\n/**\n * Initialize storage directory structure\n */\nexport async function initStorage(cwd: string, scope: string = 'root'): Promise<void> {\n const dirs = [\n join(getScopePlanDir(cwd, scope), CURRENT_DIR),\n join(getScopePlanDir(cwd, scope), HISTORY_DIR),\n ];\n\n for (const dir of dirs) {\n await mkdir(dir, { recursive: true });\n }\n}\n\n/**\n * List all scopes with plans\n */\nexport async function listScopes(cwd: string): Promise<string[]> {\n const plansDir = join(cwd, COMMIT_DIR, PLANS_DIR);\n\n try {\n const entries = await readdir(plansDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n } catch {\n return [];\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analyzer/git-status.ts","../../src/validator/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Git status analysis\n */\n\nimport { simpleGit, type SimpleGit, type StatusResult } from \"simple-git\";\nimport type { GitStatus } from \"@kb-labs/commit-contracts\";\n\n/**\n * Get current git status (staged, unstaged, untracked files).\n * cwd must already point to the resolved scope directory.\n */\nexport async function getGitStatus(cwd: string): Promise<GitStatus> {\n // --ignore-submodules=all: exclude submodule pointer drift in worktrees\n const git: SimpleGit = simpleGit(cwd);\n const status: StatusResult = await git.status(['--ignore-submodules=all']);\n\n return {\n staged: status.staged.filter((f) => !shouldIgnoreFile(f)),\n unstaged: [...status.modified, ...status.deleted]\n .filter((f) => !status.staged.includes(f))\n .filter((f) => !shouldIgnoreFile(f)),\n untracked: status.not_added.filter((f) => !shouldIgnoreFile(f)),\n };\n}\n\nconst IGNORED_SEGMENTS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".turbo\",\n \"coverage\",\n]);\n\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n * Uses exact path segment matching to avoid false positives like \"my-dist/file.ts\".\n */\nfunction shouldIgnoreFile(file: string): boolean {\n return file.split(\"/\").some((segment) => IGNORED_SEGMENTS.has(segment));\n}\n\n/**\n * Get all changed files (staged + unstaged + untracked)\n * Filters out node_modules and other build artifacts\n */\nexport function getAllChangedFiles(status: GitStatus): string[] {\n const allFiles = [\n ...new Set([...status.staged, ...status.unstaged, ...status.untracked]),\n ];\n return allFiles.filter((file) => !shouldIgnoreFile(file));\n}\n\n/**\n * Check if there are any changes\n */\nexport function hasChanges(status: GitStatus): boolean {\n return (\n status.staged.length > 0 ||\n status.unstaged.length > 0 ||\n status.untracked.length > 0\n );\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n const git: SimpleGit = simpleGit(cwd);\n const branch = await git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch.trim();\n}\n\n/**\n * Check if branch is protected (main/master)\n */\nexport function isProtectedBranch(branch: string): boolean {\n const protectedBranches = [\n \"main\",\n \"master\",\n \"develop\",\n \"release\",\n \"production\",\n ];\n return protectedBranches.includes(branch.toLowerCase());\n}\n","/**\n * Commit plan validation — shared by applier, REST handlers, CLI, and MCP.\n *\n * Centralizes checks that used to live only inside apply.ts, so that any\n * surface (Studio, CLI, MCP) can proactively report plan integrity/staleness\n * before the user attempts to apply, instead of only failing at apply time.\n *\n * @module @kb-labs/commit-core/validator\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CommitPlan } from \"@kb-labs/commit-contracts\";\nimport { useLogger } from \"@kb-labs/sdk\";\nimport { getGitStatus, getAllChangedFiles } from \"../analyzer/git-status\";\n\n/**\n * Group files by their git repository (root or nested)\n *\n * Supports nested git repositories: files with a first path segment that is\n * itself a git repo root are grouped under that nested repo instead of cwd.\n */\nexport function groupFilesByRepo(\n cwd: string,\n files: string[],\n): Map<string, { relativePath: string; originalPath: string }[]> {\n const filesByRepo = new Map<\n string,\n { relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n // Check if file is in a nested repo (first segment might be a git repo)\n const segments = file.split(\"/\");\n const potentialRepoDir = segments[0];\n\n // Handle edge case: empty file path or no segments\n if (!potentialRepoDir) {\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n const potentialRepoPath = join(cwd, potentialRepoDir);\n const potentialGitDir = join(potentialRepoPath, \".git\");\n\n // Check if it's actually a nested git repo\n const isNestedRepo = existsSync(potentialGitDir);\n\n if (isNestedRepo) {\n // Use nested repo as git root, strip first segment from path\n const relativePath = segments.slice(1).join(\"/\");\n const group = filesByRepo.get(potentialRepoPath) ?? [];\n group.push({ relativePath, originalPath: file });\n filesByRepo.set(potentialRepoPath, group);\n } else {\n // Use cwd as git root\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n }\n }\n\n return filesByRepo;\n}\n\n/**\n * Validate internal consistency of a commit plan: every commit has files and\n * a message, and no file appears in more than one commit.\n *\n * Returns an array of human-readable error strings (empty = valid).\n */\nexport function validatePlanIntegrity(plan: CommitPlan): string[] {\n const errors: string[] = [];\n const seenInCommit = new Map<string, string>();\n\n for (const commit of plan.commits) {\n if (commit.files.length === 0) {\n errors.push(`Commit ${commit.id} has no files`);\n continue;\n }\n\n if (!commit.message.trim()) {\n errors.push(`Commit ${commit.id} has empty message`);\n continue;\n }\n\n for (const file of commit.files) {\n const firstCommit = seenInCommit.get(file);\n if (firstCommit) {\n errors.push(\n `File appears in multiple commits: ${file} (first: ${firstCommit}, duplicate: ${commit.id})`,\n );\n } else {\n seenInCommit.set(file, commit.id);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Check if files in the plan have changed since plan generation.\n *\n * Only checks files that are part of the plan, ignoring other changes in the\n * repo — cheap even on a large repo. Used both proactively (status handlers,\n * before the user attempts Apply) and as the last-second guard inside\n * applyCommitPlan.\n */\nexport async function checkPlanStaleness(\n cwd: string,\n plan: CommitPlan,\n scope?: string,\n): Promise<{ isStale: boolean; reason: string }> {\n const logger = useLogger();\n const planFiles = new Set(plan.commits.flatMap((c) => c.files));\n\n await logger.debug(\"checkPlanStaleness: start\", {\n scope,\n cwd,\n planFiles: [...planFiles],\n });\n\n // If no files in plan, nothing to check\n if (planFiles.size === 0) {\n return { isStale: false, reason: \"\" };\n }\n\n // Determine which repo(s) we need to check\n const filesByRepo = groupFilesByRepo(cwd, [...planFiles]);\n\n // Check each repo for staleness\n for (const [repoPath, fileInfos] of filesByRepo) {\n // Get current git status from the repo\n // Note: repoPath already points to the correct git repository root\n const currentStatus = await getGitStatus(repoPath);\n const currentFiles = new Set(getAllChangedFiles(currentStatus));\n\n await logger.debug(\"checkPlanStaleness: repo status\", {\n repoPath,\n staged: currentStatus.staged,\n unstaged: currentStatus.unstaged,\n untracked: currentStatus.untracked,\n expected: fileInfos.map((f) => f.relativePath),\n });\n\n // Check that all expected files are still changed\n for (const { relativePath, originalPath } of fileInfos) {\n if (!currentFiles.has(relativePath)) {\n await logger.warn(\"checkPlanStaleness: file not in current changes\", {\n scope,\n repoPath,\n originalPath,\n relativePath,\n currentFiles: [...currentFiles],\n });\n return {\n isStale: true,\n reason: `File no longer has changes: ${originalPath}. Regenerate plan or use --force.`,\n };\n }\n }\n }\n\n return { isStale: false, reason: \"\" };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/analyzer/git-status.ts","../../../home/runner/work/kb-labs/kb-labs/plugins/commit/core/src/validator/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Git status analysis\n */\n\nimport { simpleGit, type SimpleGit, type StatusResult } from \"simple-git\";\nimport type { GitStatus } from \"@kb-labs/commit-contracts\";\n\n/**\n * Get current git status (staged, unstaged, untracked files).\n * cwd must already point to the resolved scope directory.\n */\nexport async function getGitStatus(cwd: string): Promise<GitStatus> {\n // --ignore-submodules=all: exclude submodule pointer drift in worktrees\n const git: SimpleGit = simpleGit(cwd);\n const status: StatusResult = await git.status(['--ignore-submodules=all']);\n\n return {\n staged: status.staged.filter((f) => !shouldIgnoreFile(f)),\n unstaged: [...status.modified, ...status.deleted]\n .filter((f) => !status.staged.includes(f))\n .filter((f) => !shouldIgnoreFile(f)),\n untracked: status.not_added.filter((f) => !shouldIgnoreFile(f)),\n };\n}\n\nconst IGNORED_SEGMENTS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".turbo\",\n \"coverage\",\n]);\n\n/**\n * Check if file should be ignored (node_modules, dist, etc.)\n * Uses exact path segment matching to avoid false positives like \"my-dist/file.ts\".\n */\nfunction shouldIgnoreFile(file: string): boolean {\n return file.split(\"/\").some((segment) => IGNORED_SEGMENTS.has(segment));\n}\n\n/**\n * Get all changed files (staged + unstaged + untracked)\n * Filters out node_modules and other build artifacts\n */\nexport function getAllChangedFiles(status: GitStatus): string[] {\n const allFiles = [\n ...new Set([...status.staged, ...status.unstaged, ...status.untracked]),\n ];\n return allFiles.filter((file) => !shouldIgnoreFile(file));\n}\n\n/**\n * Check if there are any changes\n */\nexport function hasChanges(status: GitStatus): boolean {\n return (\n status.staged.length > 0 ||\n status.unstaged.length > 0 ||\n status.untracked.length > 0\n );\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n const git: SimpleGit = simpleGit(cwd);\n const branch = await git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch.trim();\n}\n\n/**\n * Check if branch is protected (main/master)\n */\nexport function isProtectedBranch(branch: string): boolean {\n const protectedBranches = [\n \"main\",\n \"master\",\n \"develop\",\n \"release\",\n \"production\",\n ];\n return protectedBranches.includes(branch.toLowerCase());\n}\n","/**\n * Commit plan validation — shared by applier, REST handlers, CLI, and MCP.\n *\n * Centralizes checks that used to live only inside apply.ts, so that any\n * surface (Studio, CLI, MCP) can proactively report plan integrity/staleness\n * before the user attempts to apply, instead of only failing at apply time.\n *\n * @module @kb-labs/commit-core/validator\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CommitPlan } from \"@kb-labs/commit-contracts\";\nimport { useLogger } from \"@kb-labs/sdk\";\nimport { getGitStatus, getAllChangedFiles } from \"../analyzer/git-status\";\n\n/**\n * Group files by their git repository (root or nested)\n *\n * Supports nested git repositories: files with a first path segment that is\n * itself a git repo root are grouped under that nested repo instead of cwd.\n */\nexport function groupFilesByRepo(\n cwd: string,\n files: string[],\n): Map<string, { relativePath: string; originalPath: string }[]> {\n const filesByRepo = new Map<\n string,\n { relativePath: string; originalPath: string }[]\n >();\n\n for (const file of files) {\n // Check if file is in a nested repo (first segment might be a git repo)\n const segments = file.split(\"/\");\n const potentialRepoDir = segments[0];\n\n // Handle edge case: empty file path or no segments\n if (!potentialRepoDir) {\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n continue;\n }\n\n const potentialRepoPath = join(cwd, potentialRepoDir);\n const potentialGitDir = join(potentialRepoPath, \".git\");\n\n // Check if it's actually a nested git repo\n const isNestedRepo = existsSync(potentialGitDir);\n\n if (isNestedRepo) {\n // Use nested repo as git root, strip first segment from path\n const relativePath = segments.slice(1).join(\"/\");\n const group = filesByRepo.get(potentialRepoPath) ?? [];\n group.push({ relativePath, originalPath: file });\n filesByRepo.set(potentialRepoPath, group);\n } else {\n // Use cwd as git root\n const group = filesByRepo.get(cwd) ?? [];\n group.push({ relativePath: file, originalPath: file });\n filesByRepo.set(cwd, group);\n }\n }\n\n return filesByRepo;\n}\n\n/**\n * Validate internal consistency of a commit plan: every commit has files and\n * a message, and no file appears in more than one commit.\n *\n * Returns an array of human-readable error strings (empty = valid).\n */\nexport function validatePlanIntegrity(plan: CommitPlan): string[] {\n const errors: string[] = [];\n const seenInCommit = new Map<string, string>();\n\n for (const commit of plan.commits) {\n if (commit.files.length === 0) {\n errors.push(`Commit ${commit.id} has no files`);\n continue;\n }\n\n if (!commit.message.trim()) {\n errors.push(`Commit ${commit.id} has empty message`);\n continue;\n }\n\n for (const file of commit.files) {\n const firstCommit = seenInCommit.get(file);\n if (firstCommit) {\n errors.push(\n `File appears in multiple commits: ${file} (first: ${firstCommit}, duplicate: ${commit.id})`,\n );\n } else {\n seenInCommit.set(file, commit.id);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Check if files in the plan have changed since plan generation.\n *\n * Only checks files that are part of the plan, ignoring other changes in the\n * repo — cheap even on a large repo. Used both proactively (status handlers,\n * before the user attempts Apply) and as the last-second guard inside\n * applyCommitPlan.\n */\nexport async function checkPlanStaleness(\n cwd: string,\n plan: CommitPlan,\n scope?: string,\n): Promise<{ isStale: boolean; reason: string }> {\n const logger = useLogger();\n const planFiles = new Set(plan.commits.flatMap((c) => c.files));\n\n await logger.debug(\"checkPlanStaleness: start\", {\n scope,\n cwd,\n planFiles: [...planFiles],\n });\n\n // If no files in plan, nothing to check\n if (planFiles.size === 0) {\n return { isStale: false, reason: \"\" };\n }\n\n // Determine which repo(s) we need to check\n const filesByRepo = groupFilesByRepo(cwd, [...planFiles]);\n\n // Check each repo for staleness\n for (const [repoPath, fileInfos] of filesByRepo) {\n // Get current git status from the repo\n // Note: repoPath already points to the correct git repository root\n const currentStatus = await getGitStatus(repoPath);\n const currentFiles = new Set(getAllChangedFiles(currentStatus));\n\n await logger.debug(\"checkPlanStaleness: repo status\", {\n repoPath,\n staged: currentStatus.staged,\n unstaged: currentStatus.unstaged,\n untracked: currentStatus.untracked,\n expected: fileInfos.map((f) => f.relativePath),\n });\n\n // Check that all expected files are still changed\n for (const { relativePath, originalPath } of fileInfos) {\n if (!currentFiles.has(relativePath)) {\n await logger.warn(\"checkPlanStaleness: file not in current changes\", {\n scope,\n repoPath,\n originalPath,\n relativePath,\n currentFiles: [...currentFiles],\n });\n return {\n isStale: true,\n reason: `File no longer has changes: ${originalPath}. Regenerate plan or use --force.`,\n };\n }\n }\n }\n\n return { isStale: false, reason: \"\" };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kb-labs/commit-core",
|
|
3
3
|
"description": "Core business logic for KB Labs Commit plugin - git analysis, plan generation, and commit application.",
|
|
4
|
-
"version": "2.119.0
|
|
4
|
+
"version": "2.119.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"globby": "^11.0.0",
|
|
42
42
|
"minimatch": "^10.0.1",
|
|
43
43
|
"simple-git": "^3.36.0",
|
|
44
|
-
"@kb-labs/commit-contracts": "2.119.0
|
|
44
|
+
"@kb-labs/commit-contracts": "2.119.0",
|
|
45
45
|
"@kb-labs/sdk": "2.115.4"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"tsup": "^8.5.0",
|
|
51
51
|
"typescript": "^5.6.3",
|
|
52
52
|
"vitest": "^3.2.6",
|
|
53
|
-
"@kb-labs/devkit": "2.119.0
|
|
53
|
+
"@kb-labs/devkit": "2.119.0"
|
|
54
54
|
},
|
|
55
55
|
"engines": {
|
|
56
|
-
"node": ">=
|
|
56
|
+
"node": ">=24.0.0",
|
|
57
57
|
"pnpm": ">=9.0.0"
|
|
58
58
|
},
|
|
59
59
|
"sideEffects": false,
|