/**
* Safety Warnings System
*
* Provides safety warnings and confirmations for destructive Git operations.
* Helps prevent accidental data loss by clearly communicating risks.
*/
export type WarningLevel = 'WARNING' | 'DANGER' | 'CRITICAL';
export interface SafetyWarning {
level: WarningLevel;
operation: string;
title: string;
message: string;
whatWillBeLost: string[];
suggestions: string[];
confirmationRequired: boolean;
alternatives?: string[];
}
export interface DestructiveOperationInfo {
operation: string;
riskLevel: WarningLevel;
irreversible: boolean;
dataLoss: boolean;
workLoss: boolean;
}
export class SafetyWarnings {
private static readonly DESTRUCTIVE_OPERATIONS: Record<string, DestructiveOperationInfo> = {
'git-reset-hard': {
operation: 'git reset --hard',
riskLevel: 'CRITICAL',
irreversible: true,
dataLoss: true,
workLoss: true
},
'git-reset-mixed': {
operation: 'git reset --mixed',
riskLevel: 'DANGER',
irreversible: false,
dataLoss: false,
workLoss: true
},
'delete-repository': {
operation: 'delete repository',
riskLevel: 'CRITICAL',
irreversible: true,
dataLoss: true,
workLoss: true
},
'delete-branch': {
operation: 'delete branch',
riskLevel: 'WARNING',
irreversible: true,
dataLoss: true,
workLoss: false
},
'delete-remote-branch': {
operation: 'delete remote branch',
riskLevel: 'DANGER',
irreversible: true,
dataLoss: true,
workLoss: false
},
'force-push': {
operation: 'force push',
riskLevel: 'CRITICAL',
irreversible: true,
dataLoss: true,
workLoss: false
},
'clean-untracked': {
operation: 'git clean -f',
riskLevel: 'DANGER',
irreversible: true,
dataLoss: true,
workLoss: true
}
};
/**
* Generate safety warning for a destructive operation
*/
public static generateWarning(operationType: string, context?: any): SafetyWarning {
const operationInfo = this.DESTRUCTIVE_OPERATIONS[operationType];
if (!operationInfo) {
throw new Error(`Unknown destructive operation: ${operationType}`);
}
return this.createWarningForOperation(operationType, operationInfo, context);
}
/**
* Check if an operation is destructive and requires warning
*/
public static isDestructiveOperation(operation: string): boolean {
return Object.keys(this.DESTRUCTIVE_OPERATIONS).includes(operation);
}
/**
* Get operation risk level
*/
public static getRiskLevel(operation: string): WarningLevel | null {
const operationInfo = this.DESTRUCTIVE_OPERATIONS[operation];
return operationInfo ? operationInfo.riskLevel : null;
}
/**
* Format warning message for display
*/
public static formatWarning(warning: SafetyWarning): string {
const levelEmoji = this.getLevelEmoji(warning.level);
const levelColor = this.getLevelColor(warning.level);
let message = `${levelEmoji} **${warning.title}**\n\n`;
message += `${warning.message}\n\n`;
if (warning.whatWillBeLost.length > 0) {
message += `**⚠️ What will be lost:**\n`;
warning.whatWillBeLost.forEach(item => {
message += ` • ${item}\n`;
});
message += '\n';
}
if (warning.suggestions.length > 0) {
message += `**💡 Suggestions:**\n`;
warning.suggestions.forEach(suggestion => {
message += ` • ${suggestion}\n`;
});
message += '\n';
}
if (warning.alternatives && warning.alternatives.length > 0) {
message += `**🔄 Safer alternatives:**\n`;
warning.alternatives.forEach(alternative => {
message += ` • ${alternative}\n`;
});
message += '\n';
}
if (warning.confirmationRequired) {
message += `**🚨 This operation requires explicit confirmation.**\n`;
}
return message;
}
/**
* Create warning for specific operation
*/
private static createWarningForOperation(
operationType: string,
operationInfo: DestructiveOperationInfo,
context?: any
): SafetyWarning {
switch (operationType) {
case 'git-reset-hard':
return {
level: 'CRITICAL',
operation: 'git reset --hard',
title: 'CRITICAL: Hard Reset Will Permanently Delete Changes',
message: 'This operation will permanently delete all uncommitted changes and reset your working directory to the specified commit.',
whatWillBeLost: [
'All uncommitted changes in tracked files',
'All staged changes',
'Any local modifications since the last commit'
],
suggestions: [
'Create a backup or stash your changes first',
'Use `git stash` to temporarily save your work',
'Check `git status` to see what will be lost',
'Consider using `git reset --soft` or `git reset --mixed` for safer alternatives'
],
confirmationRequired: true,
alternatives: [
'git reset --soft HEAD~1 (keeps changes staged)',
'git reset --mixed HEAD~1 (keeps changes unstaged)',
'git stash (saves changes temporarily)',
'git checkout -- <file> (reset specific files only)'
]
};
case 'git-reset-mixed':
return {
level: 'DANGER',
operation: 'git reset --mixed',
title: 'DANGER: Mixed Reset Will Unstage Changes',
message: 'This operation will unstage all staged changes but keep them in your working directory.',
whatWillBeLost: [
'All staged changes (will become unstaged)',
'Commit history will be modified'
],
suggestions: [
'Check what files are currently staged with `git status`',
'Ensure you want to unstage all changes',
'Consider committing changes first if they\'re important'
],
confirmationRequired: false,
alternatives: [
'git reset HEAD <file> (unstage specific files only)',
'git commit (commit staged changes)',
'git stash (save changes temporarily)'
]
};
case 'delete-repository':
return {
level: 'CRITICAL',
operation: 'delete repository',
title: 'CRITICAL: Repository Deletion is Permanent',
message: `This will permanently delete the repository "${context?.repo || 'unknown'}" and all its contents.`,
whatWillBeLost: [
'Entire repository and all files',
'All commit history',
'All branches and tags',
'All issues and pull requests',
'All project settings and configurations'
],
suggestions: [
'Create a backup of the repository first',
'Download/clone the repository locally',
'Archive the repository instead of deleting',
'Double-check you have the right repository'
],
confirmationRequired: true,
alternatives: [
'Archive repository (preserves data)',
'Make repository private (restricts access)',
'Transfer repository ownership',
'Clone/download repository before deletion'
]
};
case 'delete-branch':
return {
level: 'WARNING',
operation: 'delete branch',
title: 'WARNING: Branch Deletion May Cause Data Loss',
message: `This will permanently delete the branch "${context?.branch || 'unknown'}" and its commit history.`,
whatWillBeLost: [
'Branch and its commit history',
'Any unmerged commits on this branch'
],
suggestions: [
'Ensure the branch has been merged to main/master',
'Check for any unmerged commits with `git log main..branch-name`',
'Create a backup branch before deletion',
'Verify no one else is working on this branch'
],
confirmationRequired: true,
alternatives: [
'Merge branch to main first',
'Create backup branch before deletion',
'Archive branch instead of deleting'
]
};
case 'delete-remote-branch':
return {
level: 'DANGER',
operation: 'delete remote branch',
title: 'DANGER: Remote Branch Deletion Affects All Users',
message: `This will delete the remote branch "${context?.branch || 'unknown'}" for all repository users.`,
whatWillBeLost: [
'Remote branch access for all team members',
'Remote tracking branches',
'Any CI/CD configurations tied to this branch'
],
suggestions: [
'Coordinate with team members before deletion',
'Ensure no active pull requests depend on this branch',
'Check if branch is referenced in documentation',
'Verify CI/CD pipelines don\'t depend on this branch'
],
confirmationRequired: true,
alternatives: [
'Coordinate deletion with team first',
'Archive branch instead of deleting',
'Make branch read-only instead'
]
};
case 'force-push':
return {
level: 'CRITICAL',
operation: 'force push',
title: 'CRITICAL: Force Push Will Overwrite Remote History',
message: 'This will forcefully overwrite the remote branch history, potentially losing commits.',
whatWillBeLost: [
'Remote commit history may be lost',
'Other developers\' work may be overwritten',
'Any commits that exist only on the remote'
],
suggestions: [
'Coordinate with all team members before force pushing',
'Ensure no one else has pushed to the remote branch',
'Consider using `git push --force-with-lease` for safer force push',
'Create a backup branch before force pushing'
],
confirmationRequired: true,
alternatives: [
'git push --force-with-lease (safer force push)',
'git rebase -i (rebase instead of force push)',
'Coordinate with team and merge normally'
]
};
case 'clean-untracked':
return {
level: 'DANGER',
operation: 'git clean -f',
title: 'DANGER: Clean Will Permanently Delete Untracked Files',
message: 'This will permanently delete all untracked files and directories.',
whatWillBeLost: [
'All untracked files',
'All untracked directories',
'Any temporary files or build artifacts'
],
suggestions: [
'Review what will be deleted with `git clean -n` first',
'Backup important untracked files',
'Add important files to .gitignore before cleaning',
'Use `git clean -i` for interactive cleaning'
],
confirmationRequired: true,
alternatives: [
'git clean -n (dry run to see what would be deleted)',
'git clean -i (interactive mode)',
'git clean -d (include directories)',
'Manually delete specific files'
]
};
default:
return {
level: 'WARNING',
operation: operationType,
title: 'WARNING: Potentially Destructive Operation',
message: 'This operation may cause data loss or permanent changes.',
whatWillBeLost: ['Unknown data may be lost'],
suggestions: ['Review the operation carefully before proceeding'],
confirmationRequired: true
};
}
}
/**
* Get emoji for warning level
*/
private static getLevelEmoji(level: WarningLevel): string {
switch (level) {
case 'WARNING': return '⚠️';
case 'DANGER': return '🚨';
case 'CRITICAL': return '💥';
default: return '⚠️';
}
}
/**
* Get color indicator for warning level
*/
private static getLevelColor(level: WarningLevel): string {
switch (level) {
case 'WARNING': return 'yellow';
case 'DANGER': return 'orange';
case 'CRITICAL': return 'red';
default: return 'yellow';
}
}
}