UNPKG

ccguard

Version:

Automated enforcement of net-negative LOC, complexity constraints, and quality standards for Claude code

163 lines 5.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BashCommandParser = void 0; class BashCommandParser { /** * Parse a bash command to identify file operations */ parseCommand(input) { const command = input.command.trim(); const operations = []; // Handle file deletion commands if (this.isDeleteCommand(command)) { const paths = this.extractPathsFromDelete(command); if (paths.length > 0) { operations.push({ type: 'delete', paths }); } } // Handle file creation commands if (this.isCreateCommand(command)) { const paths = this.extractPathsFromCreate(command); if (paths.length > 0) { operations.push({ type: 'create', paths }); } } // Handle file overwrite commands (echo >, cat >) if (this.isOverwriteCommand(command)) { const paths = this.extractPathsFromOverwrite(command); if (paths.length > 0) { operations.push({ type: 'overwrite', paths }); } } // Handle file append commands (echo >>, cat >>) if (this.isAppendCommand(command)) { const paths = this.extractPathsFromAppend(command); if (paths.length > 0) { operations.push({ type: 'modify', paths }); } } return operations; } isDeleteCommand(command) { // Match rm command with various flags return /^\s*rm\s+/.test(command); } isCreateCommand(command) { // Match touch command return /^\s*touch\s+/.test(command); } isOverwriteCommand(command) { // Match commands that overwrite files with single > // Avoid matching >> (append) return /(?:echo|cat|printf)\s+.*?>\s*(?!>)/.test(command) || />\s*(?!>)[^\s|;&]+/.test(command); } isAppendCommand(command) { // Match commands that append to files with >> return />>\s*[^\s|;&]+/.test(command); } extractPathsFromDelete(command) { const paths = []; // Remove rm command and flags let args = command.replace(/^\s*rm\s+/, ''); // Remove common flags (-r, -f, -rf, etc.) args = args.replace(/^(-[rfiv]+\s+)+/, ''); // Extract paths (simplified - doesn't handle all edge cases) const tokens = this.tokenizeCommand(args); for (const token of tokens) { // Skip if it's a flag if (token.startsWith('-')) continue; // Add as path if it doesn't contain special characters if (!this.isSpecialToken(token)) { paths.push(token); } } return paths; } extractPathsFromCreate(command) { const paths = []; // Remove touch command let args = command.replace(/^\s*touch\s+/, ''); // Extract paths const tokens = this.tokenizeCommand(args); for (const token of tokens) { // Skip if it's a flag if (token.startsWith('-')) continue; // Add as path if (!this.isSpecialToken(token)) { paths.push(token); } } return paths; } extractPathsFromOverwrite(command) { const paths = []; // Match pattern: > filename const match = command.match(/>\s*([^\s|;&]+)/); if (match && match[1]) { paths.push(match[1]); } return paths; } extractPathsFromAppend(command) { const paths = []; // Match pattern: >> filename const match = command.match(/>>\s*([^\s|;&]+)/); if (match && match[1]) { paths.push(match[1]); } return paths; } /** * Simple command tokenizer (doesn't handle all shell quoting rules) */ tokenizeCommand(command) { const tokens = []; let current = ''; let inQuote = false; let quoteChar = ''; for (let i = 0; i < command.length; i++) { const char = command[i]; if (!inQuote && (char === '"' || char === "'")) { inQuote = true; quoteChar = char; } else if (inQuote && char === quoteChar) { inQuote = false; if (current) { tokens.push(current); current = ''; } } else if (!inQuote && /\s/.test(char)) { if (current) { tokens.push(current); current = ''; } } else { current += char; } } if (current) { tokens.push(current); } return tokens; } isSpecialToken(token) { // Check if token contains shell special characters return /[|;&<>]/.test(token); } /** * Check if a command would affect files */ static wouldAffectFiles(input) { const parser = new BashCommandParser(); const operations = parser.parseCommand(input); return operations.length > 0; } } exports.BashCommandParser = BashCommandParser; //# sourceMappingURL=bashCommandParser.js.map