Features: - 30+ Custom Skills (cognitive, development, UI/UX, autonomous agents) - RalphLoop autonomous agent integration - Multi-AI consultation (Qwen) - Agent management system with sync capabilities - Custom hooks for session management - MCP servers integration - Plugin marketplace setup - Comprehensive installation script Components: - Skills: always-use-superpowers, ralph, brainstorming, ui-ux-pro-max, etc. - Agents: 100+ agents across engineering, marketing, product, etc. - Hooks: session-start-superpowers, qwen-consult, ralph-auto-trigger - Commands: /brainstorm, /write-plan, /execute-plan - MCP Servers: zai-mcp-server, web-search-prime, web-reader, zread - Binaries: ralphloop wrapper Installation: ./supercharge.sh
126 lines
3.4 KiB
TypeScript
126 lines
3.4 KiB
TypeScript
import { getBasename, stripWrappers } from '../shell.ts';
|
|
|
|
import { hasRecursiveForceFlags } from './rm-flags.ts';
|
|
|
|
const REASON_FIND_DELETE = 'find -delete permanently removes files. Use -print first to preview.';
|
|
|
|
export function analyzeFind(tokens: readonly string[]): string | null {
|
|
// Check for -delete outside of -exec/-execdir blocks
|
|
if (findHasDelete(tokens.slice(1))) {
|
|
return REASON_FIND_DELETE;
|
|
}
|
|
|
|
// Check all -exec and -execdir blocks for dangerous commands
|
|
for (let i = 0; i < tokens.length; i++) {
|
|
const token = tokens[i];
|
|
if (token === '-exec' || token === '-execdir') {
|
|
const execTokens = tokens.slice(i + 1);
|
|
const semicolonIdx = execTokens.indexOf(';');
|
|
const plusIdx = execTokens.indexOf('+');
|
|
// If no terminator found, shell-quote may have parsed it as an operator
|
|
// In that case, treat the rest of the tokens as the exec command
|
|
const endIdx =
|
|
semicolonIdx !== -1 && plusIdx !== -1
|
|
? Math.min(semicolonIdx, plusIdx)
|
|
: semicolonIdx !== -1
|
|
? semicolonIdx
|
|
: plusIdx !== -1
|
|
? plusIdx
|
|
: execTokens.length; // No terminator - use all remaining tokens
|
|
|
|
let execCommand = execTokens.slice(0, endIdx);
|
|
// Strip wrappers (env, sudo, command)
|
|
execCommand = stripWrappers(execCommand);
|
|
if (execCommand.length > 0) {
|
|
let head = getBasename(execCommand[0] ?? '');
|
|
// Handle busybox wrapper
|
|
if (head === 'busybox' && execCommand.length > 1) {
|
|
execCommand = execCommand.slice(1);
|
|
head = getBasename(execCommand[0] ?? '');
|
|
}
|
|
if (head === 'rm' && hasRecursiveForceFlags(execCommand)) {
|
|
return 'find -exec rm -rf is dangerous. Use explicit file list instead.';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check if find command has -delete action (not as argument to another option).
|
|
* Handles cases like "find -name -delete" where -delete is a filename pattern.
|
|
*/
|
|
export function findHasDelete(tokens: readonly string[]): boolean {
|
|
let i = 0;
|
|
let insideExec = false;
|
|
let execDepth = 0;
|
|
|
|
while (i < tokens.length) {
|
|
const token = tokens[i];
|
|
if (!token) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// Track -exec/-execdir blocks
|
|
if (token === '-exec' || token === '-execdir') {
|
|
insideExec = true;
|
|
execDepth++;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// End of -exec block
|
|
if (insideExec && (token === ';' || token === '+')) {
|
|
execDepth--;
|
|
if (execDepth === 0) {
|
|
insideExec = false;
|
|
}
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// Skip -delete inside -exec blocks
|
|
if (insideExec) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// Options that take an argument - skip the next token
|
|
if (
|
|
token === '-name' ||
|
|
token === '-iname' ||
|
|
token === '-path' ||
|
|
token === '-ipath' ||
|
|
token === '-regex' ||
|
|
token === '-iregex' ||
|
|
token === '-type' ||
|
|
token === '-user' ||
|
|
token === '-group' ||
|
|
token === '-perm' ||
|
|
token === '-size' ||
|
|
token === '-mtime' ||
|
|
token === '-ctime' ||
|
|
token === '-atime' ||
|
|
token === '-newer' ||
|
|
token === '-printf' ||
|
|
token === '-fprint' ||
|
|
token === '-fprintf'
|
|
) {
|
|
i += 2; // Skip option and its argument
|
|
continue;
|
|
}
|
|
|
|
// Found -delete outside of -exec and not as an argument
|
|
if (token === '-delete') {
|
|
return true;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return false;
|
|
}
|