From b07b18e181dfdffd4e817f5daed28557a1a14633 Mon Sep 17 00:00:00 2001 From: chenhg5 Date: Thu, 5 Mar 2026 14:43:51 +0800 Subject: [PATCH] feat(engine): add project-level command disabling support Implement disabled commands functionality for projects, including config parsing, engine integration, and i18n support. Allows disabling specific commands per-project via config. generated by llmgit Co-Authored-By: Claude --- cmd/cc-connect/main.go | 8 ++++++++ config.example.toml | 2 ++ config/config.go | 9 +++++---- core/engine.go | 23 +++++++++++++++++++++++ core/i18n.go | 8 ++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/cmd/cc-connect/main.go b/cmd/cc-connect/main.go index b5ae4be..47da323 100644 --- a/cmd/cc-connect/main.go +++ b/cmd/cc-connect/main.go @@ -210,6 +210,11 @@ func main() { engine.SetBannedWords(cfg.BannedWords) } + // Wire disabled commands (project-level) + if len(proj.DisabledCommands) > 0 { + engine.SetDisabledCommands(proj.DisabledCommands) + } + // Wire display truncation settings { dcfg := core.DisplayCfg{ @@ -577,6 +582,9 @@ func reloadConfig(configPath, projName string, engine *core.Engine) (*core.Confi // Reload banned words engine.SetBannedWords(cfg.BannedWords) + // Reload disabled commands + engine.SetDisabledCommands(proj.DisabledCommands) + slog.Info("config reloaded", "project", projName) return result, nil } diff --git a/config.example.toml b/config.example.toml index d3873df..803c78f 100644 --- a/config.example.toml +++ b/config.example.toml @@ -183,6 +183,8 @@ level = "info" # debug, info, warn, error [[projects]] name = "my-backend" # quiet = true # Override global quiet for this project / 覆盖全局静默模式 +# disabled_commands = ["restart", "upgrade", "cron"] # Disable specific commands for this project +# # 禁用该项目的指定命令 [projects.agent] type = "claudecode" diff --git a/config/config.go b/config/config.go index f2ffca0..88416a3 100644 --- a/config/config.go +++ b/config/config.go @@ -52,10 +52,11 @@ type SpeechConfig struct { // ProjectConfig binds one agent (with a specific work_dir) to one or more platforms. type ProjectConfig struct { - Name string `toml:"name"` - Agent AgentConfig `toml:"agent"` - Platforms []PlatformConfig `toml:"platforms"` - Quiet *bool `toml:"quiet,omitempty"` // project-level quiet mode; overrides global setting + Name string `toml:"name"` + Agent AgentConfig `toml:"agent"` + Platforms []PlatformConfig `toml:"platforms"` + Quiet *bool `toml:"quiet,omitempty"` // project-level quiet mode; overrides global setting + DisabledCommands []string `toml:"disabled_commands,omitempty"` // commands to disable for this project (e.g. ["restart", "upgrade"]) } type AgentConfig struct { diff --git a/core/engine.go b/core/engine.go index 4dd6140..b9ee37c 100644 --- a/core/engine.go +++ b/core/engine.go @@ -142,6 +142,8 @@ type Engine struct { bannedWords []string bannedMu sync.RWMutex + disabledCmds map[string]bool + // Interactive agent session management interactiveMu sync.Mutex interactiveStates map[string]*interactiveState // key = sessionKey @@ -296,6 +298,22 @@ func (e *Engine) ClearAliases() { e.aliases = make(map[string]string) } +// SetDisabledCommands sets the list of command IDs that are disabled for this project. +func (e *Engine) SetDisabledCommands(cmds []string) { + m := make(map[string]bool, len(cmds)) + for _, c := range cmds { + c = strings.ToLower(strings.TrimPrefix(c, "/")) + // Resolve alias names to canonical IDs + id := matchPrefix(c, builtinCommands) + if id != "" { + m[id] = true + } else { + m[c] = true + } + } + e.disabledCmds = m +} + // SetBannedWords replaces the banned words list. func (e *Engine) SetBannedWords(words []string) { e.bannedMu.Lock() @@ -1042,6 +1060,11 @@ func (e *Engine) handleCommand(p Platform, msg *Message, raw string) bool { cmdID := matchPrefix(cmd, builtinCommands) + if cmdID != "" && e.disabledCmds[cmdID] { + e.reply(p, msg.ReplyCtx, fmt.Sprintf(e.i18n.T(MsgCommandDisabled), "/"+cmdID)) + return true + } + switch cmdID { case "new": e.cmdNew(p, msg, args) diff --git a/core/i18n.go b/core/i18n.go index ea892bf..32132ff 100644 --- a/core/i18n.go +++ b/core/i18n.go @@ -281,6 +281,7 @@ const ( MsgDeleteNotSupported MsgKey = "delete_not_supported" MsgBannedWordBlocked MsgKey = "banned_word_blocked" + MsgCommandDisabled MsgKey = "command_disabled" ) var messages = map[MsgKey]map[Language]string{ @@ -1529,6 +1530,13 @@ var messages = map[MsgKey]map[Language]string{ LangJapanese: "⚠️ 禁止ワードが含まれているため、メッセージがブロックされました。", LangSpanish: "⚠️ Su mensaje fue bloqueado porque contiene una palabra prohibida.", }, + MsgCommandDisabled: { + LangEnglish: "🚫 Command `%s` is disabled for this project.", + LangChinese: "🚫 命令 `%s` 在当前项目中已被禁用。", + LangTraditionalChinese: "🚫 命令 `%s` 在當前專案中已被停用。", + LangJapanese: "🚫 コマンド `%s` はこのプロジェクトで無効化されています。", + LangSpanish: "🚫 El comando `%s` está deshabilitado para este proyecto.", + }, } func (i *I18n) T(key MsgKey) string {