diff --git a/cmd/config/integrations.go b/cmd/config/integrations.go
index 9ef35d814..fcf596efd 100644
--- a/cmd/config/integrations.go
+++ b/cmd/config/integrations.go
@@ -41,10 +41,18 @@ type Editor interface {
// integrations is the registry of available integrations.
var integrations = map[string]Runner{
"claude": &Claude{},
- "clawdbot": &Clawdbot{},
+ "clawdbot": &Openclaw{},
"codex": &Codex{},
+ "moltbot": &Openclaw{},
"droid": &Droid{},
"opencode": &OpenCode{},
+ "openclaw": &Openclaw{},
+}
+
+// integrationAliases are hidden from the interactive selector but work as CLI arguments.
+var integrationAliases = map[string]bool{
+ "clawdbot": true,
+ "moltbot": true,
}
func selectIntegration() (string, error) {
@@ -55,6 +63,9 @@ func selectIntegration() (string, error) {
names := slices.Sorted(maps.Keys(integrations))
var items []selectItem
for _, name := range names {
+ if integrationAliases[name] {
+ continue
+ }
r := integrations[name]
description := r.String()
if conn, err := loadIntegration(name); err == nil && len(conn.Models) > 0 {
@@ -243,10 +254,10 @@ func LaunchCmd(checkServerHeartbeat func(cmd *cobra.Command, args []string) erro
Supported integrations:
claude Claude Code
- clawdbot Clawdbot
codex Codex
droid Droid
opencode OpenCode
+ openclaw OpenClaw (aliases: clawdbot, moltbot)
Examples:
ollama launch
diff --git a/cmd/config/clawdbot.go b/cmd/config/openclaw.go
similarity index 75%
rename from cmd/config/clawdbot.go
rename to cmd/config/openclaw.go
index faad1251c..904047247 100644
--- a/cmd/config/clawdbot.go
+++ b/cmd/config/openclaw.go
@@ -13,26 +13,32 @@ import (
"github.com/ollama/ollama/envconfig"
)
-type Clawdbot struct{}
+type Openclaw struct{}
-func (c *Clawdbot) String() string { return "Clawdbot" }
+func (c *Openclaw) String() string { return "OpenClaw" }
const ansiGreen = "\033[32m"
-func (c *Clawdbot) Run(model string) error {
- if _, err := exec.LookPath("clawdbot"); err != nil {
- return fmt.Errorf("clawdbot is not installed, install from https://docs.clawd.bot")
+func (c *Openclaw) Run(model string) error {
+ bin := "openclaw"
+ if _, err := exec.LookPath(bin); err != nil {
+ bin = "clawdbot"
+ if _, err := exec.LookPath(bin); err != nil {
+ return fmt.Errorf("openclaw is not installed, install from https://docs.openclaw.ai")
+ }
}
models := []string{model}
- if config, err := loadIntegration("clawdbot"); err == nil && len(config.Models) > 0 {
+ if config, err := loadIntegration("openclaw"); err == nil && len(config.Models) > 0 {
+ models = config.Models
+ } else if config, err := loadIntegration("clawdbot"); err == nil && len(config.Models) > 0 {
models = config.Models
}
if err := c.Edit(models); err != nil {
return fmt.Errorf("setup failed: %w", err)
}
- cmd := exec.Command("clawdbot", "gateway")
+ cmd := exec.Command(bin, "gateway")
cmd.Stdin = os.Stdin
// Capture output to detect "already running" message
@@ -42,22 +48,26 @@ func (c *Clawdbot) Run(model string) error {
err := cmd.Run()
if err != nil && strings.Contains(outputBuf.String(), "Gateway already running") {
- fmt.Fprintf(os.Stderr, "%sClawdbot has been configured with Ollama. Gateway is already running.%s\n", ansiGreen, ansiReset)
+ fmt.Fprintf(os.Stderr, "%sOpenClaw has been configured with Ollama. Gateway is already running.%s\n", ansiGreen, ansiReset)
return nil
}
return err
}
-func (c *Clawdbot) Paths() []string {
+func (c *Openclaw) Paths() []string {
home, _ := os.UserHomeDir()
- p := filepath.Join(home, ".clawdbot", "clawdbot.json")
+ p := filepath.Join(home, ".openclaw", "openclaw.json")
if _, err := os.Stat(p); err == nil {
return []string{p}
}
+ legacy := filepath.Join(home, ".clawdbot", "clawdbot.json")
+ if _, err := os.Stat(legacy); err == nil {
+ return []string{legacy}
+ }
return nil
}
-func (c *Clawdbot) Edit(models []string) error {
+func (c *Openclaw) Edit(models []string) error {
if len(models) == 0 {
return nil
}
@@ -67,7 +77,8 @@ func (c *Clawdbot) Edit(models []string) error {
return err
}
- configPath := filepath.Join(home, ".clawdbot", "clawdbot.json")
+ configPath := filepath.Join(home, ".openclaw", "openclaw.json")
+ legacyPath := filepath.Join(home, ".clawdbot", "clawdbot.json")
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
return err
}
@@ -76,6 +87,8 @@ func (c *Clawdbot) Edit(models []string) error {
config := make(map[string]any)
if data, err := os.ReadFile(configPath); err == nil {
_ = json.Unmarshal(data, &config)
+ } else if data, err := os.ReadFile(legacyPath); err == nil {
+ _ = json.Unmarshal(data, &config)
}
// Navigate/create: models.providers.ollama (preserving other providers)
@@ -167,15 +180,18 @@ func (c *Clawdbot) Edit(models []string) error {
return writeWithBackup(configPath, data)
}
-func (c *Clawdbot) Models() []string {
+func (c *Openclaw) Models() []string {
home, err := os.UserHomeDir()
if err != nil {
return nil
}
- config, err := readJSONFile(filepath.Join(home, ".clawdbot", "clawdbot.json"))
+ config, err := readJSONFile(filepath.Join(home, ".openclaw", "openclaw.json"))
if err != nil {
- return nil
+ config, err = readJSONFile(filepath.Join(home, ".clawdbot", "clawdbot.json"))
+ if err != nil {
+ return nil
+ }
}
modelsSection, _ := config["models"].(map[string]any)
diff --git a/cmd/config/clawdbot_test.go b/cmd/config/openclaw_test.go
similarity index 63%
rename from cmd/config/clawdbot_test.go
rename to cmd/config/openclaw_test.go
index 74c732a0c..1c862c246 100644
--- a/cmd/config/clawdbot_test.go
+++ b/cmd/config/openclaw_test.go
@@ -8,12 +8,12 @@ import (
"testing"
)
-func TestClawdbotIntegration(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawIntegration(t *testing.T) {
+ c := &Openclaw{}
t.Run("String", func(t *testing.T) {
- if got := c.String(); got != "Clawdbot" {
- t.Errorf("String() = %q, want %q", got, "Clawdbot")
+ if got := c.String(); got != "OpenClaw" {
+ t.Errorf("String() = %q, want %q", got, "OpenClaw")
}
})
@@ -26,13 +26,13 @@ func TestClawdbotIntegration(t *testing.T) {
})
}
-func TestClawdbotEdit(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEdit(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
cleanup := func() { os.RemoveAll(configDir) }
@@ -41,8 +41,8 @@ func TestClawdbotEdit(t *testing.T) {
if err := c.Edit([]string{"llama3.2"}); err != nil {
t.Fatal(err)
}
- assertClawdbotModelExists(t, configPath, "llama3.2")
- assertClawdbotPrimaryModel(t, configPath, "ollama/llama3.2")
+ assertOpenclawModelExists(t, configPath, "llama3.2")
+ assertOpenclawPrimaryModel(t, configPath, "ollama/llama3.2")
})
t.Run("multiple models - first is primary", func(t *testing.T) {
@@ -50,9 +50,9 @@ func TestClawdbotEdit(t *testing.T) {
if err := c.Edit([]string{"llama3.2", "mistral"}); err != nil {
t.Fatal(err)
}
- assertClawdbotModelExists(t, configPath, "llama3.2")
- assertClawdbotModelExists(t, configPath, "mistral")
- assertClawdbotPrimaryModel(t, configPath, "ollama/llama3.2")
+ assertOpenclawModelExists(t, configPath, "llama3.2")
+ assertOpenclawModelExists(t, configPath, "mistral")
+ assertOpenclawPrimaryModel(t, configPath, "ollama/llama3.2")
})
t.Run("preserve other providers", func(t *testing.T) {
@@ -127,8 +127,8 @@ func TestClawdbotEdit(t *testing.T) {
c.Edit([]string{"llama3.2", "mistral"})
c.Edit([]string{"llama3.2"})
- assertClawdbotModelExists(t, configPath, "llama3.2")
- assertClawdbotModelNotExists(t, configPath, "mistral")
+ assertOpenclawModelExists(t, configPath, "llama3.2")
+ assertOpenclawModelNotExists(t, configPath, "mistral")
})
t.Run("empty models is no-op", func(t *testing.T) {
@@ -169,12 +169,12 @@ func TestClawdbotEdit(t *testing.T) {
if err := c.Edit([]string{"llama3.2"}); err != nil {
t.Fatal(err)
}
- assertClawdbotModelExists(t, configPath, "llama3.2")
+ assertOpenclawModelExists(t, configPath, "llama3.2")
})
}
-func TestClawdbotModels(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawModels(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
@@ -185,9 +185,9 @@ func TestClawdbotModels(t *testing.T) {
})
t.Run("returns all ollama models", func(t *testing.T) {
- configDir := filepath.Join(tmpDir, ".clawdbot")
+ configDir := filepath.Join(tmpDir, ".openclaw")
os.MkdirAll(configDir, 0o755)
- os.WriteFile(filepath.Join(configDir, "clawdbot.json"), []byte(`{
+ os.WriteFile(filepath.Join(configDir, "openclaw.json"), []byte(`{
"models":{"providers":{"ollama":{"models":[
{"id":"llama3.2"},
{"id":"mistral"}
@@ -202,7 +202,7 @@ func TestClawdbotModels(t *testing.T) {
}
// Helper functions
-func assertClawdbotModelExists(t *testing.T, path, model string) {
+func assertOpenclawModelExists(t *testing.T, path, model string) {
t.Helper()
data, _ := os.ReadFile(path)
var cfg map[string]any
@@ -221,7 +221,7 @@ func assertClawdbotModelExists(t *testing.T, path, model string) {
t.Errorf("model %s not found", model)
}
-func assertClawdbotModelNotExists(t *testing.T, path, model string) {
+func assertOpenclawModelNotExists(t *testing.T, path, model string) {
t.Helper()
data, _ := os.ReadFile(path)
var cfg map[string]any
@@ -239,7 +239,7 @@ func assertClawdbotModelNotExists(t *testing.T, path, model string) {
}
}
-func assertClawdbotPrimaryModel(t *testing.T, path, expected string) {
+func assertOpenclawPrimaryModel(t *testing.T, path, expected string) {
t.Helper()
data, _ := os.ReadFile(path)
var cfg map[string]any
@@ -252,15 +252,15 @@ func assertClawdbotPrimaryModel(t *testing.T, path, expected string) {
}
}
-func TestClawdbotPaths(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawPaths(t *testing.T) {
+ c := &Openclaw{}
t.Run("returns path when config exists", func(t *testing.T) {
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
+ configDir := filepath.Join(tmpDir, ".openclaw")
os.MkdirAll(configDir, 0o755)
- os.WriteFile(filepath.Join(configDir, "clawdbot.json"), []byte(`{}`), 0o644)
+ os.WriteFile(filepath.Join(configDir, "openclaw.json"), []byte(`{}`), 0o644)
paths := c.Paths()
if len(paths) != 1 {
@@ -277,12 +277,12 @@ func TestClawdbotPaths(t *testing.T) {
})
}
-func TestClawdbotModelsEdgeCases(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawModelsEdgeCases(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
cleanup := func() { os.RemoveAll(configDir) }
t.Run("corrupted JSON returns nil", func(t *testing.T) {
@@ -340,11 +340,11 @@ func TestClawdbotModelsEdgeCases(t *testing.T) {
})
}
-func TestClawdbotEditSchemaFields(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEditSchemaFields(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configPath := filepath.Join(tmpDir, ".clawdbot", "clawdbot.json")
+ configPath := filepath.Join(tmpDir, ".openclaw", "openclaw.json")
if err := c.Edit([]string{"llama3.2"}); err != nil {
t.Fatal(err)
@@ -381,20 +381,20 @@ func TestClawdbotEditSchemaFields(t *testing.T) {
}
}
-func TestClawdbotEditModelNames(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEditModelNames(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configPath := filepath.Join(tmpDir, ".clawdbot", "clawdbot.json")
- cleanup := func() { os.RemoveAll(filepath.Join(tmpDir, ".clawdbot")) }
+ configPath := filepath.Join(tmpDir, ".openclaw", "openclaw.json")
+ cleanup := func() { os.RemoveAll(filepath.Join(tmpDir, ".openclaw")) }
t.Run("model with colon tag", func(t *testing.T) {
cleanup()
if err := c.Edit([]string{"llama3.2:70b"}); err != nil {
t.Fatal(err)
}
- assertClawdbotModelExists(t, configPath, "llama3.2:70b")
- assertClawdbotPrimaryModel(t, configPath, "ollama/llama3.2:70b")
+ assertOpenclawModelExists(t, configPath, "llama3.2:70b")
+ assertOpenclawPrimaryModel(t, configPath, "ollama/llama3.2:70b")
})
t.Run("model with slash", func(t *testing.T) {
@@ -402,8 +402,8 @@ func TestClawdbotEditModelNames(t *testing.T) {
if err := c.Edit([]string{"library/model:tag"}); err != nil {
t.Fatal(err)
}
- assertClawdbotModelExists(t, configPath, "library/model:tag")
- assertClawdbotPrimaryModel(t, configPath, "ollama/library/model:tag")
+ assertOpenclawModelExists(t, configPath, "library/model:tag")
+ assertOpenclawPrimaryModel(t, configPath, "ollama/library/model:tag")
})
t.Run("model with hyphen", func(t *testing.T) {
@@ -411,16 +411,16 @@ func TestClawdbotEditModelNames(t *testing.T) {
if err := c.Edit([]string{"test-model"}); err != nil {
t.Fatal(err)
}
- assertClawdbotModelExists(t, configPath, "test-model")
+ assertOpenclawModelExists(t, configPath, "test-model")
})
}
-func TestClawdbotEditAgentsPreservation(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEditAgentsPreservation(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
cleanup := func() { os.RemoveAll(configDir) }
t.Run("preserve other agent defaults", func(t *testing.T) {
@@ -457,7 +457,7 @@ func TestClawdbotEditAgentsPreservation(t *testing.T) {
})
}
-const testClawdbotFixture = `{
+const testOpenclawFixture = `{
"theme": "dark",
"mcp": {"servers": {"custom": {"enabled": true}}},
"models": {
@@ -475,15 +475,15 @@ const testClawdbotFixture = `{
}
}`
-func TestClawdbotEdit_RoundTrip(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEdit_RoundTrip(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
os.MkdirAll(configDir, 0o755)
- os.WriteFile(configPath, []byte(testClawdbotFixture), 0o644)
+ os.WriteFile(configPath, []byte(testOpenclawFixture), 0o644)
if err := c.Edit([]string{"llama3.2", "mistral"}); err != nil {
t.Fatal(err)
@@ -521,15 +521,15 @@ func TestClawdbotEdit_RoundTrip(t *testing.T) {
}
}
-func TestClawdbotEdit_Idempotent(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEdit_Idempotent(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
os.MkdirAll(configDir, 0o755)
- os.WriteFile(configPath, []byte(testClawdbotFixture), 0o644)
+ os.WriteFile(configPath, []byte(testOpenclawFixture), 0o644)
c.Edit([]string{"llama3.2", "mistral"})
firstData, _ := os.ReadFile(configPath)
@@ -542,15 +542,15 @@ func TestClawdbotEdit_Idempotent(t *testing.T) {
}
}
-func TestClawdbotEdit_MultipleConsecutiveEdits(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEdit_MultipleConsecutiveEdits(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
os.MkdirAll(configDir, 0o755)
- os.WriteFile(configPath, []byte(testClawdbotFixture), 0o644)
+ os.WriteFile(configPath, []byte(testOpenclawFixture), 0o644)
for i := range 10 {
models := []string{"model-a", "model-b"}
@@ -573,12 +573,12 @@ func TestClawdbotEdit_MultipleConsecutiveEdits(t *testing.T) {
}
}
-func TestClawdbotEdit_BackupCreated(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawEdit_BackupCreated(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
- configPath := filepath.Join(configDir, "clawdbot.json")
+ configDir := filepath.Join(tmpDir, ".openclaw")
+ configPath := filepath.Join(configDir, "openclaw.json")
backupDir := filepath.Join(os.TempDir(), "ollama-backups")
os.MkdirAll(configDir, 0o755)
@@ -590,7 +590,7 @@ func TestClawdbotEdit_BackupCreated(t *testing.T) {
t.Fatal(err)
}
- backups, _ := filepath.Glob(filepath.Join(backupDir, "clawdbot.json.*"))
+ backups, _ := filepath.Glob(filepath.Join(backupDir, "openclaw.json.*"))
foundBackup := false
for _, backup := range backups {
data, _ := os.ReadFile(backup)
@@ -605,11 +605,151 @@ func TestClawdbotEdit_BackupCreated(t *testing.T) {
}
}
-func TestClawdbotEdit_CreatesDirectoryIfMissing(t *testing.T) {
- c := &Clawdbot{}
+func TestOpenclawClawdbotAlias(t *testing.T) {
+ for _, alias := range []string{"clawdbot", "moltbot"} {
+ t.Run(alias+" alias resolves to Openclaw runner", func(t *testing.T) {
+ r, ok := integrations[alias]
+ if !ok {
+ t.Fatalf("%s not found in integrations", alias)
+ }
+ if _, ok := r.(*Openclaw); !ok {
+ t.Errorf("%s integration is %T, want *Openclaw", alias, r)
+ }
+ })
+
+ t.Run(alias+" is hidden from selector", func(t *testing.T) {
+ if !integrationAliases[alias] {
+ t.Errorf("%s should be in integrationAliases", alias)
+ }
+ })
+ }
+}
+
+func TestOpenclawLegacyPaths(t *testing.T) {
+ c := &Openclaw{}
+
+ t.Run("falls back to legacy clawdbot path", func(t *testing.T) {
+ tmpDir := t.TempDir()
+ setTestHome(t, tmpDir)
+ legacyDir := filepath.Join(tmpDir, ".clawdbot")
+ os.MkdirAll(legacyDir, 0o755)
+ os.WriteFile(filepath.Join(legacyDir, "clawdbot.json"), []byte(`{}`), 0o644)
+
+ paths := c.Paths()
+ if len(paths) != 1 {
+ t.Fatalf("expected 1 path, got %d", len(paths))
+ }
+ if paths[0] != filepath.Join(legacyDir, "clawdbot.json") {
+ t.Errorf("expected legacy path, got %s", paths[0])
+ }
+ })
+
+ t.Run("prefers new path over legacy", func(t *testing.T) {
+ tmpDir := t.TempDir()
+ setTestHome(t, tmpDir)
+ newDir := filepath.Join(tmpDir, ".openclaw")
+ legacyDir := filepath.Join(tmpDir, ".clawdbot")
+ os.MkdirAll(newDir, 0o755)
+ os.MkdirAll(legacyDir, 0o755)
+ os.WriteFile(filepath.Join(newDir, "openclaw.json"), []byte(`{}`), 0o644)
+ os.WriteFile(filepath.Join(legacyDir, "clawdbot.json"), []byte(`{}`), 0o644)
+
+ paths := c.Paths()
+ if len(paths) != 1 {
+ t.Fatalf("expected 1 path, got %d", len(paths))
+ }
+ if paths[0] != filepath.Join(newDir, "openclaw.json") {
+ t.Errorf("expected new path, got %s", paths[0])
+ }
+ })
+
+ t.Run("Models reads from legacy path", func(t *testing.T) {
+ tmpDir := t.TempDir()
+ setTestHome(t, tmpDir)
+ legacyDir := filepath.Join(tmpDir, ".clawdbot")
+ os.MkdirAll(legacyDir, 0o755)
+ os.WriteFile(filepath.Join(legacyDir, "clawdbot.json"), []byte(`{
+ "models":{"providers":{"ollama":{"models":[{"id":"llama3.2"}]}}}
+ }`), 0o644)
+
+ models := c.Models()
+ if len(models) != 1 || models[0] != "llama3.2" {
+ t.Errorf("expected [llama3.2], got %v", models)
+ }
+ })
+
+ t.Run("Models prefers new path over legacy", func(t *testing.T) {
+ tmpDir := t.TempDir()
+ setTestHome(t, tmpDir)
+ newDir := filepath.Join(tmpDir, ".openclaw")
+ legacyDir := filepath.Join(tmpDir, ".clawdbot")
+ os.MkdirAll(newDir, 0o755)
+ os.MkdirAll(legacyDir, 0o755)
+ os.WriteFile(filepath.Join(newDir, "openclaw.json"), []byte(`{
+ "models":{"providers":{"ollama":{"models":[{"id":"new-model"}]}}}
+ }`), 0o644)
+ os.WriteFile(filepath.Join(legacyDir, "clawdbot.json"), []byte(`{
+ "models":{"providers":{"ollama":{"models":[{"id":"legacy-model"}]}}}
+ }`), 0o644)
+
+ models := c.Models()
+ if len(models) != 1 || models[0] != "new-model" {
+ t.Errorf("expected [new-model], got %v", models)
+ }
+ })
+
+ t.Run("Edit reads new path over legacy when both exist", func(t *testing.T) {
+ tmpDir := t.TempDir()
+ setTestHome(t, tmpDir)
+ newDir := filepath.Join(tmpDir, ".openclaw")
+ legacyDir := filepath.Join(tmpDir, ".clawdbot")
+ os.MkdirAll(newDir, 0o755)
+ os.MkdirAll(legacyDir, 0o755)
+ os.WriteFile(filepath.Join(newDir, "openclaw.json"), []byte(`{"theme":"new"}`), 0o644)
+ os.WriteFile(filepath.Join(legacyDir, "clawdbot.json"), []byte(`{"theme":"legacy"}`), 0o644)
+
+ if err := c.Edit([]string{"llama3.2"}); err != nil {
+ t.Fatal(err)
+ }
+
+ data, _ := os.ReadFile(filepath.Join(newDir, "openclaw.json"))
+ var cfg map[string]any
+ json.Unmarshal(data, &cfg)
+ if cfg["theme"] != "new" {
+ t.Errorf("expected theme from new config, got %v", cfg["theme"])
+ }
+ })
+
+ t.Run("Edit migrates from legacy config", func(t *testing.T) {
+ tmpDir := t.TempDir()
+ setTestHome(t, tmpDir)
+ legacyDir := filepath.Join(tmpDir, ".clawdbot")
+ os.MkdirAll(legacyDir, 0o755)
+ os.WriteFile(filepath.Join(legacyDir, "clawdbot.json"), []byte(`{"theme":"dark"}`), 0o644)
+
+ if err := c.Edit([]string{"llama3.2"}); err != nil {
+ t.Fatal(err)
+ }
+
+ // Should write to new path
+ newPath := filepath.Join(tmpDir, ".openclaw", "openclaw.json")
+ data, err := os.ReadFile(newPath)
+ if err != nil {
+ t.Fatal("expected new config file to be created")
+ }
+ var cfg map[string]any
+ json.Unmarshal(data, &cfg)
+ if cfg["theme"] != "dark" {
+ t.Error("legacy theme setting was not migrated")
+ }
+ })
+}
+
+func TestOpenclawEdit_CreatesDirectoryIfMissing(t *testing.T) {
+ c := &Openclaw{}
tmpDir := t.TempDir()
setTestHome(t, tmpDir)
- configDir := filepath.Join(tmpDir, ".clawdbot")
+ configDir := filepath.Join(tmpDir, ".openclaw")
if _, err := os.Stat(configDir); !os.IsNotExist(err) {
t.Fatal("directory should not exist before test")
diff --git a/docs/docs.json b/docs/docs.json
index db98114c4..4bf21cd4c 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -102,8 +102,8 @@
"group": "Integrations",
"pages": [
"/integrations/claude-code",
- "/integrations/clawdbot",
"/integrations/cline",
+ "/integrations/openclaw",
"/integrations/codex",
"/integrations/droid",
"/integrations/goose",
diff --git a/docs/integrations/clawdbot.mdx b/docs/integrations/openclaw.mdx
similarity index 61%
rename from docs/integrations/clawdbot.mdx
rename to docs/integrations/openclaw.mdx
index eddef5d0f..1a4a79905 100644
--- a/docs/integrations/clawdbot.mdx
+++ b/docs/integrations/openclaw.mdx
@@ -1,41 +1,43 @@
---
-title: Clawdbot
+title: OpenClaw
---
-Clawdbot is a personal AI assistant that runs on your own devices. It bridges messaging services (WhatsApp, Telegram, Slack, Discord, iMessage, and more) to AI coding agents through a centralized gateway.
+OpenClaw is a personal AI assistant that runs on your own devices. It bridges messaging services (WhatsApp, Telegram, Slack, Discord, iMessage, and more) to AI coding agents through a centralized gateway.
## Install
-Install [Clawdbot](https://clawd.bot/)
+Install [OpenClaw](https://openclaw.ai/)
```bash
-npm install -g clawdbot@latest
+npm install -g openclaw@latest
```
Then run the onboarding wizard:
```bash
-clawdbot onboard --install-daemon
+openclaw onboard --install-daemon
```
-Clawdbot requires a larger context window. It is recommended to use a context window of at least 64k tokens. See [Context length](/context-length) for more information.
+OpenClaw requires a larger context window. It is recommended to use a context window of at least 64k tokens. See [Context length](/context-length) for more information.
## Usage with Ollama
### Quick setup
```bash
-ollama launch clawdbot
+ollama launch openclaw
```
-This configures Clawdbot to use Ollama and starts the gateway.
+Previously known as Clawdbot. `ollama launch clawdbot` still works as an alias.
+
+This configures OpenClaw to use Ollama and starts the gateway.
If the gateway is already running, no changes need to be made as the gateway will auto-reload the changes.
To configure without launching:
```shell
-ollama launch clawdbot --config
+ollama launch openclaw --config
```
## Recommended Models