mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-05 02:30:26 +08:00
- Add --agent parameter for agent-based backend/model resolution - Add --prompt-file parameter for agent prompt injection - Add opencode backend support with JSON output parsing - Add yolo field in agent config for auto-enabling dangerous flags - claude: --dangerously-skip-permissions - codex: --dangerously-bypass-approvals-and-sandbox - Add develop agent for code development tasks - Add omo skill for multi-agent orchestration with Sisyphus coordinator - Bump version to 5.5.0 Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
210 lines
6.0 KiB
Go
210 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestResolveAgentConfig_Defaults(t *testing.T) {
|
|
home := t.TempDir()
|
|
t.Setenv("HOME", home)
|
|
t.Setenv("USERPROFILE", home)
|
|
|
|
// Test that default agents resolve correctly without config file
|
|
tests := []struct {
|
|
agent string
|
|
wantBackend string
|
|
wantModel string
|
|
wantPromptFile string
|
|
}{
|
|
{"sisyphus", "claude", "claude-sonnet-4-20250514", "~/.claude/skills/omo/references/sisyphus.md"},
|
|
{"oracle", "claude", "claude-sonnet-4-20250514", "~/.claude/skills/omo/references/oracle.md"},
|
|
{"librarian", "claude", "claude-sonnet-4-5-20250514", "~/.claude/skills/omo/references/librarian.md"},
|
|
{"explore", "opencode", "opencode/grok-code", "~/.claude/skills/omo/references/explore.md"},
|
|
{"frontend-ui-ux-engineer", "gemini", "gemini-3-pro-preview", "~/.claude/skills/omo/references/frontend-ui-ux-engineer.md"},
|
|
{"document-writer", "gemini", "gemini-3-flash-preview", "~/.claude/skills/omo/references/document-writer.md"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.agent, func(t *testing.T) {
|
|
backend, model, promptFile, _ := resolveAgentConfig(tt.agent)
|
|
if backend != tt.wantBackend {
|
|
t.Errorf("backend = %q, want %q", backend, tt.wantBackend)
|
|
}
|
|
if model != tt.wantModel {
|
|
t.Errorf("model = %q, want %q", model, tt.wantModel)
|
|
}
|
|
if promptFile != tt.wantPromptFile {
|
|
t.Errorf("promptFile = %q, want %q", promptFile, tt.wantPromptFile)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolveAgentConfig_UnknownAgent(t *testing.T) {
|
|
home := t.TempDir()
|
|
t.Setenv("HOME", home)
|
|
t.Setenv("USERPROFILE", home)
|
|
|
|
backend, model, promptFile, _ := resolveAgentConfig("unknown-agent")
|
|
if backend != "opencode" {
|
|
t.Errorf("unknown agent backend = %q, want %q", backend, "opencode")
|
|
}
|
|
if model != "opencode/grok-code" {
|
|
t.Errorf("unknown agent model = %q, want %q", model, "opencode/grok-code")
|
|
}
|
|
if promptFile != "" {
|
|
t.Errorf("unknown agent promptFile = %q, want empty", promptFile)
|
|
}
|
|
}
|
|
|
|
func TestLoadModelsConfig_NoFile(t *testing.T) {
|
|
home := "/nonexistent/path/that/does/not/exist"
|
|
t.Setenv("HOME", home)
|
|
t.Setenv("USERPROFILE", home)
|
|
|
|
cfg := loadModelsConfig()
|
|
if cfg.DefaultBackend != "opencode" {
|
|
t.Errorf("DefaultBackend = %q, want %q", cfg.DefaultBackend, "opencode")
|
|
}
|
|
if len(cfg.Agents) != 7 {
|
|
t.Errorf("len(Agents) = %d, want 7", len(cfg.Agents))
|
|
}
|
|
}
|
|
|
|
func TestLoadModelsConfig_WithFile(t *testing.T) {
|
|
// Create temp dir and config file
|
|
tmpDir := t.TempDir()
|
|
configDir := filepath.Join(tmpDir, ".codeagent")
|
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
configContent := `{
|
|
"default_backend": "claude",
|
|
"default_model": "claude-opus-4",
|
|
"agents": {
|
|
"custom-agent": {
|
|
"backend": "codex",
|
|
"model": "gpt-4o",
|
|
"description": "Custom agent"
|
|
}
|
|
}
|
|
}`
|
|
configPath := filepath.Join(configDir, "models.json")
|
|
if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Setenv("HOME", tmpDir)
|
|
t.Setenv("USERPROFILE", tmpDir)
|
|
|
|
cfg := loadModelsConfig()
|
|
|
|
if cfg.DefaultBackend != "claude" {
|
|
t.Errorf("DefaultBackend = %q, want %q", cfg.DefaultBackend, "claude")
|
|
}
|
|
if cfg.DefaultModel != "claude-opus-4" {
|
|
t.Errorf("DefaultModel = %q, want %q", cfg.DefaultModel, "claude-opus-4")
|
|
}
|
|
|
|
// Check custom agent
|
|
if agent, ok := cfg.Agents["custom-agent"]; !ok {
|
|
t.Error("custom-agent not found")
|
|
} else {
|
|
if agent.Backend != "codex" {
|
|
t.Errorf("custom-agent.Backend = %q, want %q", agent.Backend, "codex")
|
|
}
|
|
if agent.Model != "gpt-4o" {
|
|
t.Errorf("custom-agent.Model = %q, want %q", agent.Model, "gpt-4o")
|
|
}
|
|
}
|
|
|
|
// Check that defaults are merged
|
|
if _, ok := cfg.Agents["sisyphus"]; !ok {
|
|
t.Error("default agent sisyphus should be merged")
|
|
}
|
|
}
|
|
|
|
func TestLoadModelsConfig_InvalidJSON(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
configDir := filepath.Join(tmpDir, ".codeagent")
|
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Write invalid JSON
|
|
configPath := filepath.Join(configDir, "models.json")
|
|
if err := os.WriteFile(configPath, []byte("invalid json {"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Setenv("HOME", tmpDir)
|
|
t.Setenv("USERPROFILE", tmpDir)
|
|
|
|
cfg := loadModelsConfig()
|
|
// Should fall back to defaults
|
|
if cfg.DefaultBackend != "opencode" {
|
|
t.Errorf("invalid JSON should fallback, got DefaultBackend = %q", cfg.DefaultBackend)
|
|
}
|
|
}
|
|
|
|
func TestOpencodeBackend_BuildArgs(t *testing.T) {
|
|
backend := OpencodeBackend{}
|
|
|
|
t.Run("basic", func(t *testing.T) {
|
|
cfg := &Config{Mode: "new"}
|
|
got := backend.BuildArgs(cfg, "hello")
|
|
want := []string{"run", "--format", "json", "hello"}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("got %v, want %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("with model", func(t *testing.T) {
|
|
cfg := &Config{Mode: "new", Model: "opencode/grok-code"}
|
|
got := backend.BuildArgs(cfg, "task")
|
|
want := []string{"run", "-m", "opencode/grok-code", "--format", "json", "task"}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("got %v, want %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("resume mode", func(t *testing.T) {
|
|
cfg := &Config{Mode: "resume", SessionID: "ses_123", Model: "opencode/grok-code"}
|
|
got := backend.BuildArgs(cfg, "follow-up")
|
|
want := []string{"run", "-m", "opencode/grok-code", "-s", "ses_123", "--format", "json", "follow-up"}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("got %v, want %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("resume without session", func(t *testing.T) {
|
|
cfg := &Config{Mode: "resume"}
|
|
got := backend.BuildArgs(cfg, "task")
|
|
want := []string{"run", "--format", "json", "task"}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("got %v, want %v", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestOpencodeBackend_Interface(t *testing.T) {
|
|
backend := OpencodeBackend{}
|
|
|
|
if backend.Name() != "opencode" {
|
|
t.Errorf("Name() = %q, want %q", backend.Name(), "opencode")
|
|
}
|
|
if backend.Command() != "opencode" {
|
|
t.Errorf("Command() = %q, want %q", backend.Command(), "opencode")
|
|
}
|
|
}
|
|
|
|
func TestBackendRegistry_IncludesOpencode(t *testing.T) {
|
|
if _, ok := backendRegistry["opencode"]; !ok {
|
|
t.Error("backendRegistry should include opencode")
|
|
}
|
|
}
|