Claude Code + Codex + Gemini 多模型协作工作流系统 ## 核心功能 - 6阶段开发工作流 (/ccg:dev) - 智能路由:前端→Gemini,后端→Codex - Prompt 增强 (Auggie MCP prompt-enhancer) - 双模型代码审查和技术分析 ## 命令列表 - /ccg:dev - 完整开发工作流 - /ccg:frontend - 前端任务 - /ccg:backend - 后端任务 - /ccg:review - 代码审查 - /ccg:analyze - 技术分析 - /ccg:enhance - Prompt 增强 - /ccg:commit - 智能提交 - /ccg:rollback - 交互式回滚 - /ccg:clean-branches - 清理分支 - /ccg:worktree - Worktree 管理 - /ccg:init - 项目初始化 ## 特性 - 预编译二进制(无需 Go 环境) - 专家系统提示词(Codex/Gemini) - 一键安装脚本
127 lines
2.7 KiB
Go
127 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
defaultWrapperName = "codeagent-wrapper"
|
|
legacyWrapperName = "codex-wrapper"
|
|
)
|
|
|
|
var executablePathFn = os.Executable
|
|
|
|
func normalizeWrapperName(path string) string {
|
|
if path == "" {
|
|
return ""
|
|
}
|
|
|
|
base := filepath.Base(path)
|
|
base = strings.TrimSuffix(base, ".exe") // tolerate Windows executables
|
|
|
|
switch base {
|
|
case defaultWrapperName, legacyWrapperName:
|
|
return base
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// currentWrapperName resolves the wrapper name based on the invoked binary.
|
|
// Only known names are honored to avoid leaking build/test binary names into logs.
|
|
func currentWrapperName() string {
|
|
if len(os.Args) == 0 {
|
|
return defaultWrapperName
|
|
}
|
|
|
|
if name := normalizeWrapperName(os.Args[0]); name != "" {
|
|
return name
|
|
}
|
|
|
|
execPath, err := executablePathFn()
|
|
if err == nil {
|
|
if name := normalizeWrapperName(execPath); name != "" {
|
|
return name
|
|
}
|
|
|
|
if resolved, err := filepath.EvalSymlinks(execPath); err == nil {
|
|
if name := normalizeWrapperName(resolved); name != "" {
|
|
return name
|
|
}
|
|
if alias := resolveAlias(execPath, resolved); alias != "" {
|
|
return alias
|
|
}
|
|
}
|
|
|
|
if alias := resolveAlias(execPath, ""); alias != "" {
|
|
return alias
|
|
}
|
|
}
|
|
|
|
return defaultWrapperName
|
|
}
|
|
|
|
// logPrefixes returns the set of accepted log name prefixes, including the
|
|
// current wrapper name and legacy aliases.
|
|
func logPrefixes() []string {
|
|
prefixes := []string{currentWrapperName(), defaultWrapperName, legacyWrapperName}
|
|
seen := make(map[string]struct{}, len(prefixes))
|
|
var unique []string
|
|
for _, prefix := range prefixes {
|
|
if prefix == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[prefix]; ok {
|
|
continue
|
|
}
|
|
seen[prefix] = struct{}{}
|
|
unique = append(unique, prefix)
|
|
}
|
|
return unique
|
|
}
|
|
|
|
// primaryLogPrefix returns the preferred filename prefix for log files.
|
|
// Defaults to the current wrapper name when available, otherwise falls back
|
|
// to the canonical default name.
|
|
func primaryLogPrefix() string {
|
|
prefixes := logPrefixes()
|
|
if len(prefixes) == 0 {
|
|
return defaultWrapperName
|
|
}
|
|
return prefixes[0]
|
|
}
|
|
|
|
func resolveAlias(execPath string, target string) string {
|
|
if execPath == "" {
|
|
return ""
|
|
}
|
|
|
|
dir := filepath.Dir(execPath)
|
|
for _, candidate := range []string{defaultWrapperName, legacyWrapperName} {
|
|
aliasPath := filepath.Join(dir, candidate)
|
|
info, err := os.Lstat(aliasPath)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if info.Mode()&os.ModeSymlink == 0 {
|
|
continue
|
|
}
|
|
|
|
resolved, err := filepath.EvalSymlinks(aliasPath)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if target != "" && resolved != target {
|
|
continue
|
|
}
|
|
|
|
if name := normalizeWrapperName(aliasPath); name != "" {
|
|
return name
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|