package builtin // 命令分类器 -- 对 shell 命令进行语义分类. // // 将命令分为搜索,读取,列表,写入,静默操作和通用六大类. // 分类结果用于输出截断策略选择,权限提示和 UI 展示. // // 管道分类规则:管道中任一命令为写入或通用类型时,整条管道降级为该类型. // 中性命令(echo, printf, test 等)不影响管道分类. import ( "strings" ) // CommandClass 是命令的语义分类. type CommandClass string const ( ClassSearch CommandClass = "search" // 搜索命令:grep, rg, find, ag, ack ClassRead CommandClass = "read" // 读取命令:cat, head, tail, less, wc, jq ClassList CommandClass = "list" // 列表命令:ls, tree, du ClassWrite CommandClass = "write" // 写入操作 ClassSilent CommandClass = "silent" // 静默操作:mv, cp, rm, mkdir, touch, ln ClassGeneral CommandClass = "general" // 其他 ) // searchCommands 搜索类命令集合. var searchCommands = map[string]bool{ "find": true, "grep": true, "rg": true, "ag": true, "ack": true, "fd": true, "locate": true, "which": true, "whereis": true, } // readCommands 读取类命令集合. var readCommands = map[string]bool{ "cat": true, "head": true, "tail": true, "less": true, "more": true, "wc": true, "jq": true, "yq": true, "sort": true, "uniq": true, "cut": true, "awk": true, "sed": true, } // listCommands 列表类命令集合. var listCommands = map[string]bool{ "ls": true, "tree": true, "du": true, "df": true, "file": true, "stat": true, "lsof": true, "ps": true, "top": true, } // silentCommands 静默操作类命令集合. var silentCommands = map[string]bool{ "mv": true, "cp": true, "rm": true, "mkdir": true, "touch": true, "ln": true, "chmod": true, "chown": true, } // neutralCommands 语义中性命令,不影响管道分类. // 精妙之处(CLEVER): 中性命令不影响管道分类--`echo "hello" | grep foo` 应该被分类为 search // 而非 general,因为 echo 只是数据源.没有 neutral 概念的话,echo 会拉低管道的分类. var neutralCommands = map[string]bool{ "echo": true, "printf": true, "true": true, "false": true, "test": true, "[": true, "[[": true, } // ClassifyCommand 对单个命令名进行分类. // cmdName 应为命令的基本名称(不含路径和参数). func ClassifyCommand(cmdName string) CommandClass { // 去除路径前缀,如 /usr/bin/grep -> grep if idx := strings.LastIndex(cmdName, "/"); idx >= 0 { cmdName = cmdName[idx+1:] } if searchCommands[cmdName] { return ClassSearch } if readCommands[cmdName] { return ClassRead } if listCommands[cmdName] { return ClassList } if silentCommands[cmdName] { return ClassSilent } return ClassGeneral } // ClassifyPipeline 对管道命令序列进行分类. // commands 是管道中每个命令的名称列表. // 降级规则:管道中任一命令为 write/general 时,整条管道降级. // 中性命令被跳过. func ClassifyPipeline(commands []string) CommandClass { if len(commands) == 0 { return ClassGeneral } // 收集非中性命令的分类 var classes []CommandClass for _, cmd := range commands { cmd = strings.TrimSpace(cmd) if cmd == "" { continue } // 跳过中性命令 if neutralCommands[cmd] { continue } classes = append(classes, ClassifyCommand(cmd)) } // 如果所有命令都是中性的,返回 general if len(classes) == 0 { return ClassGeneral } // 降级逻辑:有 write 或 general 则整体降级 hasWrite := false hasGeneral := false hasSearch := false hasRead := false hasList := false hasSilent := false for _, c := range classes { switch c { case ClassWrite: hasWrite = true case ClassGeneral: hasGeneral = true case ClassSearch: hasSearch = true case ClassRead: hasRead = true case ClassList: hasList = true case ClassSilent: hasSilent = true } } // write 优先级最高 if hasWrite { return ClassWrite } // general 次之 if hasGeneral { return ClassGeneral } // 搜索优先于读取和列表 if hasSearch { return ClassSearch } if hasRead { return ClassRead } if hasList { return ClassList } if hasSilent { return ClassSilent } return ClassGeneral } // IsSearchOrRead 判断命令是否为搜索,读取或列表类. func IsSearchOrRead(cmd string) (isSearch bool, isRead bool, isList bool) { cmd = strings.TrimSpace(cmd) if cmd == "" { return false, false, false } // 去除路径前缀 if idx := strings.LastIndex(cmd, "/"); idx >= 0 { cmd = cmd[idx+1:] } return searchCommands[cmd], readCommands[cmd], listCommands[cmd] } // extractCommandName 从 shell 命令字符串中提取第一个命令名. // 处理环境变量前缀(如 VAR=val cmd)和 sudo/env 前缀. func extractCommandName(cmdStr string) string { cmdStr = strings.TrimSpace(cmdStr) if cmdStr == "" { return "" } parts := strings.Fields(cmdStr) for _, p := range parts { // 跳过环境变量赋值 (KEY=VALUE) if strings.Contains(p, "=") && !strings.HasPrefix(p, "-") { continue } // 跳过 sudo / env 前缀 if p == "sudo" || p == "env" { continue } // 去除路径 if idx := strings.LastIndex(p, "/"); idx >= 0 { p = p[idx+1:] } return p } return "" } // ClassifyShellCommand 对完整的 shell 命令字符串进行分类. // 支持管道解析:将命令按 | 分割,提取每段的命令名,再调用 ClassifyPipeline. func ClassifyShellCommand(shellCmd string) CommandClass { shellCmd = strings.TrimSpace(shellCmd) if shellCmd == "" { return ClassGeneral } // 按管道符分割 segments := strings.Split(shellCmd, "|") var cmdNames []string for _, seg := range segments { name := extractCommandName(seg) if name != "" { cmdNames = append(cmdNames, name) } } return ClassifyPipeline(cmdNames) }