• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

smallnest / goclaw / 21939180632

12 Feb 2026 08:32AM UTC coverage: 6.444% (+3.3%) from 3.106%
21939180632

push

github

chaoyuepan
refactor skill package

0 of 263 new or added lines in 2 files covered. (0.0%)

337 existing lines in 3 files now uncovered.

1447 of 22455 relevant lines covered (6.44%)

0.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/cli/root.go
1
package cli
2

3
import (
4
        "context"
5
        "fmt"
6
        "os"
7
        "os/signal"
8
        "syscall"
9

10
        "github.com/smallnest/goclaw/agent"
11
        "github.com/smallnest/goclaw/agent/tools"
12
        "github.com/smallnest/goclaw/bus"
13
        "github.com/smallnest/goclaw/channels"
14
        "github.com/smallnest/goclaw/cli/commands"
15
        "github.com/smallnest/goclaw/config"
16
        "github.com/smallnest/goclaw/cron"
17
        "github.com/smallnest/goclaw/gateway"
18
        "github.com/smallnest/goclaw/internal"
19
        "github.com/smallnest/goclaw/internal/logger"
20
        "github.com/smallnest/goclaw/internal/workspace"
21
        "github.com/smallnest/goclaw/providers"
22
        "github.com/smallnest/goclaw/session"
23
        "github.com/spf13/cobra"
24
        "go.uber.org/zap"
25
)
26

27
var rootCmd = &cobra.Command{
28
        Use:   "goclaw",
29
        Short: "Go-based AI Agent framework",
30
        Long:  `goclaw is a Go language implementation of an AI Agent framework, inspired by nanobot.`,
31
}
32

33
var startCmd = &cobra.Command{
34
        Use:   "start",
35
        Short: "Start the goclaw agent",
36
        Run:   runStart,
37
}
38

39
var configCmd = &cobra.Command{
40
        Use:   "config",
41
        Short: "Configuration management",
42
}
43

44
var configShowCmd = &cobra.Command{
45
        Use:   "show",
46
        Short: "Show current configuration",
47
        Run:   runConfigShow,
48
}
49

50
var installCmd = &cobra.Command{
51
        Use:   "install",
52
        Short: "Install goclaw workspace templates",
53
        Run:   runInstall,
54
}
55

56
// Flags for install command
57
var (
58
        installConfigPath    string
59
        installWorkspacePath string
60
)
61

62
func init() {
×
63
        // Add install command flags
×
64
        installCmd.Flags().StringVar(&installConfigPath, "config", "", "Path to config file")
×
65
        installCmd.Flags().StringVar(&installWorkspacePath, "workspace", "", "Path to workspace directory (overrides config)")
×
66

×
67
        rootCmd.AddCommand(startCmd)
×
68
        rootCmd.AddCommand(installCmd)
×
69
        rootCmd.AddCommand(configCmd)
×
70
        configCmd.AddCommand(configShowCmd)
×
71
        rootCmd.AddCommand(agentsCmd)
×
72
        rootCmd.AddCommand(agentCmd)
×
73
        rootCmd.AddCommand(sessionsCmd)
×
NEW
74
        rootCmd.AddCommand(onboardCmd)
×
75

×
76
        // Register memory and logs commands from commands package
×
77
        // Note: skills command is already registered in cli/skills.go
×
78
        rootCmd.AddCommand(commands.MemoryCmd)
×
79
        rootCmd.AddCommand(commands.LogsCmd)
×
80

×
81
        // Register browser, tui, gateway, health, status commands
×
82
        rootCmd.AddCommand(commands.BrowserCommand())
×
83
        rootCmd.AddCommand(commands.TUICommand())
×
84
        rootCmd.AddCommand(commands.GatewayCommand())
×
85
        rootCmd.AddCommand(commands.HealthCommand())
×
86
        rootCmd.AddCommand(commands.StatusCommand())
×
87

×
88
        // Register approvals, cron, system commands (registered via init)
×
89
        // These commands auto-register themselves
×
UNCOV
90
}
×
91

92
// Execute 执行 CLI
93
func Execute() error {
×
94
        return rootCmd.Execute()
×
UNCOV
95
}
×
96

97
// runStart 启动 Agent
98
func runStart(cmd *cobra.Command, args []string) {
×
99
        // 确保内置技能被复制到用户目录
×
100
        if err := internal.EnsureBuiltinSkills(); err != nil {
×
101
                fmt.Fprintf(os.Stderr, "Warning: Failed to ensure builtin skills: %v\n", err)
×
102
        }
×
103

104
        // 确保配置文件存在
UNCOV
105
        configCreated, err := internal.EnsureConfig()
×
106
        if err != nil {
×
107
                fmt.Fprintf(os.Stderr, "Warning: Failed to ensure config: %v\n", err)
×
108
        }
×
109
        if configCreated {
×
110
                fmt.Println("Config file created at: " + internal.GetConfigPath())
×
UNCOV
111
                fmt.Println("Please edit the config file to set your API keys and other settings.")
×
112
                fmt.Println()
×
113
        }
×
114

115
        // 加载配置
116
        cfg, err := config.Load("")
×
117
        if err != nil {
×
UNCOV
118
                fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
×
UNCOV
119
                os.Exit(1)
×
120
        }
×
121

122
        // 初始化日志
123
        if err := logger.Init("info", false); err != nil {
×
UNCOV
124
                fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err)
×
UNCOV
125
                os.Exit(1)
×
126
        }
×
127
        defer func() { _ = logger.Sync() }()
×
128

129
        logger.Info("Starting goclaw agent")
×
130

×
131
        // 验证配置
×
UNCOV
132
        if err := config.Validate(cfg); err != nil {
×
UNCOV
133
                logger.Fatal("Invalid configuration", zap.Error(err))
×
134
        }
×
135

136
        // 获取 workspace 目录
137
        workspaceDir, err := config.GetWorkspacePath(cfg)
×
138
        if err != nil {
×
139
                logger.Fatal("Failed to get workspace path", zap.Error(err))
×
140
        }
×
141

142
        // 创建 workspace 管理器并确保文件存在
UNCOV
143
        workspaceMgr := workspace.NewManager(workspaceDir)
×
UNCOV
144
        if err := workspaceMgr.Ensure(); err != nil {
×
145
                logger.Warn("Failed to ensure workspace files", zap.Error(err))
×
146
        } else {
×
147
                logger.Info("Workspace ready", zap.String("path", workspaceDir))
×
148
        }
×
149

150
        // 创建消息总线
151
        messageBus := bus.NewMessageBus(100)
×
152
        defer messageBus.Close()
×
153

×
154
        // 创建会话管理器
×
155
        sessionDir := os.Getenv("HOME") + "/.goclaw/sessions"
×
156
        sessionMgr, err := session.NewManager(sessionDir)
×
157
        if err != nil {
×
158
                logger.Fatal("Failed to create session manager", zap.Error(err))
×
159
        }
×
160

161
        // 创建记忆存储
UNCOV
162
        memoryStore := agent.NewMemoryStore(workspaceDir)
×
UNCOV
163

×
UNCOV
164
        // 创建上下文构建器
×
165
        contextBuilder := agent.NewContextBuilder(memoryStore, workspaceDir)
×
166

×
167
        // 创建工具注册表
×
168
        toolRegistry := agent.NewToolRegistry()
×
169

×
UNCOV
170
        // 创建技能加载器
×
UNCOV
171
        skillsLoader := agent.NewSkillsLoader(workspaceDir, []string{})
×
UNCOV
172
        if err := skillsLoader.Discover(); err != nil {
×
173
                logger.Warn("Failed to discover skills", zap.Error(err))
×
174
        } else {
×
175
                skills := skillsLoader.List()
×
UNCOV
176
                if len(skills) > 0 {
×
UNCOV
177
                        logger.Info("Skills loaded", zap.Int("count", len(skills)))
×
178
                }
×
179
        }
180

181
        // 注册文件系统工具
182
        fsTool := tools.NewFileSystemTool(cfg.Tools.FileSystem.AllowedPaths, cfg.Tools.FileSystem.DeniedPaths, workspaceDir)
×
183
        for _, tool := range fsTool.GetTools() {
×
184
                if err := toolRegistry.RegisterExisting(tool); err != nil {
×
185
                        logger.Warn("Failed to register tool", zap.String("tool", tool.Name()))
×
186
                }
×
187
        }
188

189
        // 注册 use_skill 工具(用于两阶段技能加载)
UNCOV
190
        if err := toolRegistry.RegisterExisting(tools.NewUseSkillTool()); err != nil {
×
UNCOV
191
                logger.Warn("Failed to register use_skill tool", zap.Error(err))
×
UNCOV
192
        }
×
193

194
        // 注册 Shell 工具
195
        shellTool := tools.NewShellTool(
×
196
                cfg.Tools.Shell.Enabled,
×
197
                cfg.Tools.Shell.AllowedCmds,
×
198
                cfg.Tools.Shell.DeniedCmds,
×
199
                cfg.Tools.Shell.Timeout,
×
200
                cfg.Tools.Shell.WorkingDir,
×
201
                cfg.Tools.Shell.Sandbox,
×
UNCOV
202
        )
×
UNCOV
203
        for _, tool := range shellTool.GetTools() {
×
UNCOV
204
                if err := toolRegistry.RegisterExisting(tool); err != nil {
×
205
                        logger.Warn("Failed to register tool", zap.String("tool", tool.Name()))
×
206
                }
×
207
        }
208

209
        // 注册 Web 工具
210
        webTool := tools.NewWebTool(
×
211
                cfg.Tools.Web.SearchAPIKey,
×
UNCOV
212
                cfg.Tools.Web.SearchEngine,
×
UNCOV
213
                cfg.Tools.Web.Timeout,
×
214
        )
×
215
        for _, tool := range webTool.GetTools() {
×
216
                if err := toolRegistry.RegisterExisting(tool); err != nil {
×
217
                        logger.Warn("Failed to register tool", zap.String("tool", tool.Name()))
×
218
                }
×
219
        }
220

221
        // 注册智能搜索工具(支持 web search 失败时自动回退到 Google browser 搜索)
222
        browserTimeout := 30
×
UNCOV
223
        if cfg.Tools.Browser.Timeout > 0 {
×
224
                browserTimeout = cfg.Tools.Browser.Timeout
×
UNCOV
225
        }
×
UNCOV
226
        if err := toolRegistry.RegisterExisting(tools.NewSmartSearch(webTool, true, browserTimeout).GetTool()); err != nil {
×
UNCOV
227
                logger.Warn("Failed to register smart_search tool", zap.Error(err))
×
228
        }
×
229

230
        // 注册浏览器工具(如果启用)
231
        if cfg.Tools.Browser.Enabled {
×
232
                browserTool := tools.NewBrowserTool(
×
233
                        cfg.Tools.Browser.Headless,
×
234
                        cfg.Tools.Browser.Timeout,
×
235
                )
×
236
                for _, tool := range browserTool.GetTools() {
×
237
                        if err := toolRegistry.RegisterExisting(tool); err != nil {
×
238
                                logger.Warn("Failed to register tool", zap.String("tool", tool.Name()))
×
239
                        }
×
240
                }
241
                logger.Info("Browser tools registered")
×
242
        }
243

244
        // 创建 LLM 提供商
245
        provider, err := providers.NewProvider(cfg)
×
246
        if err != nil {
×
247
                logger.Fatal("Failed to create LLM provider", zap.Error(err))
×
248
        }
×
249
        defer provider.Close()
×
UNCOV
250

×
UNCOV
251
        // 创建上下文
×
252
        ctx, cancel := context.WithCancel(context.Background())
×
253
        defer cancel()
×
254

×
255
        // 创建通道管理器
×
256
        channelMgr := channels.NewManager(messageBus)
×
257
        if err := channelMgr.SetupFromConfig(cfg); err != nil {
×
258
                logger.Warn("Failed to setup channels from config", zap.Error(err))
×
259
        }
×
260

261
        // 创建网关服务器
262
        gatewayServer := gateway.NewServer(&cfg.Gateway, messageBus, channelMgr, sessionMgr)
×
263
        if err := gatewayServer.Start(ctx); err != nil {
×
264
                logger.Warn("Failed to start gateway server", zap.Error(err))
×
265
        }
×
266
        defer func() { _ = gatewayServer.Stop() }()
×
267

268
        // 创建调度器
269
        scheduler := cron.NewScheduler(messageBus, provider, sessionMgr)
×
270

×
271
        // 创建 Agent
×
272
        agentInstance, err := agent.NewAgent(&agent.NewAgentConfig{
×
273
                Bus:          messageBus,
×
274
                Provider:     provider,
×
275
                SessionMgr:   sessionMgr,
×
276
                Tools:        toolRegistry,
×
UNCOV
277
                Context:      contextBuilder,
×
UNCOV
278
                Workspace:    workspaceDir,
×
279
                MaxIteration: cfg.Agents.Defaults.MaxIterations,
×
280
        })
×
281
        if err != nil {
×
282
                logger.Fatal("Failed to create agent", zap.Error(err))
×
283
        }
×
284

285
        // 处理信号
286
        sigChan := make(chan os.Signal, 1)
×
287
        signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
×
288

×
289
        // 启动通道
×
290
        if err := channelMgr.Start(ctx); err != nil {
×
291
                logger.Error("Failed to start channels", zap.Error(err))
×
UNCOV
292
        }
×
293
        defer func() { _ = channelMgr.Stop() }()
×
294

295
        // 启动调度器
296
        if err := scheduler.Start(ctx); err != nil {
×
297
                logger.Error("Failed to start scheduler", zap.Error(err))
×
UNCOV
298
        }
×
UNCOV
299
        defer scheduler.Stop()
×
UNCOV
300

×
301
        // 启动出站消息分发
×
302
        logger.Info("About to start outbound message dispatcher")
×
303
        go func() {
×
304
                defer func() {
×
UNCOV
305
                        if r := recover(); r != nil {
×
UNCOV
306
                                logger.Error("Outbound message dispatcher panicked",
×
UNCOV
307
                                        zap.Any("panic", r))
×
308
                        }
×
309
                }()
310
                if err := channelMgr.DispatchOutbound(ctx); err != nil {
×
311
                        logger.Error("Outbound message dispatcher exited with error", zap.Error(err))
×
312
                } else {
×
313
                        logger.Info("Outbound message dispatcher exited normally")
×
314
                }
×
315
        }()
316

317
        // 启动 Agent
UNCOV
318
        go func() {
×
UNCOV
319
                if err := agentInstance.Start(ctx); err != nil {
×
320
                        logger.Error("Agent error", zap.Error(err))
×
321
                }
×
322
        }()
323

324
        // 等待信号
325
        <-sigChan
×
UNCOV
326
        logger.Info("Received shutdown signal")
×
327

×
328
        // 停止 Agent
×
329
        if err := agentInstance.Stop(); err != nil {
×
330
                logger.Error("Failed to stop agent", zap.Error(err))
×
UNCOV
331
        }
×
332

UNCOV
333
        logger.Info("goclaw agent stopped")
×
334
}
335

336
// runConfigShow 显示配置
337
func runConfigShow(cmd *cobra.Command, args []string) {
×
338
        cfg, err := config.Load("")
×
339
        if err != nil {
×
340
                fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
×
UNCOV
341
                os.Exit(1)
×
UNCOV
342
        }
×
343

344
        fmt.Println("Current Configuration:")
×
345
        fmt.Printf("  Model: %s\n", cfg.Agents.Defaults.Model)
×
346
        fmt.Printf("  Max Iterations: %d\n", cfg.Agents.Defaults.MaxIterations)
×
347
        fmt.Printf("  Temperature: %.1f\n", cfg.Agents.Defaults.Temperature)
×
348
}
349

350
// runInstall 安装 goclaw workspace 模板
UNCOV
351
func runInstall(cmd *cobra.Command, args []string) {
×
UNCOV
352
        // 加载配置
×
353
        cfg, err := config.Load(installConfigPath)
×
354
        if err != nil {
×
355
                fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
×
356
                os.Exit(1)
×
357
        }
×
358

359
        // 获取 workspace 目录
360
        workspaceDir := installWorkspacePath
×
361
        if workspaceDir == "" {
×
362
                workspaceDir, err = config.GetWorkspacePath(cfg)
×
363
                if err != nil {
×
364
                        fmt.Fprintf(os.Stderr, "Failed to get workspace path: %v\n", err)
×
365
                        os.Exit(1)
×
366
                }
×
367
        }
368

369
        // 创建 workspace 管理器并确保文件存在
370
        workspaceMgr := workspace.NewManager(workspaceDir)
×
371
        if err := workspaceMgr.Ensure(); err != nil {
×
372
                fmt.Fprintf(os.Stderr, "Failed to ensure workspace: %v\n", err)
×
373
                os.Exit(1)
×
374
        }
×
375

UNCOV
376
        fmt.Printf("Workspace installed successfully at: %s\n", workspaceDir)
×
UNCOV
377
        fmt.Println("\nWorkspace files:")
×
378
        files, err := workspaceMgr.ListFiles()
×
UNCOV
379
        if err != nil {
×
UNCOV
380
                fmt.Fprintf(os.Stderr, "Failed to list files: %v\n", err)
×
UNCOV
381
                return
×
UNCOV
382
        }
×
UNCOV
383
        for _, f := range files {
×
UNCOV
384
                fmt.Printf("  - %s\n", f)
×
UNCOV
385
        }
×
386

UNCOV
387
        memoryFiles, err := workspaceMgr.ListMemoryFiles()
×
UNCOV
388
        if err == nil && len(memoryFiles) > 0 {
×
UNCOV
389
                fmt.Println("\nMemory files:")
×
UNCOV
390
                for _, f := range memoryFiles {
×
UNCOV
391
                        fmt.Printf("  - memory/%s\n", f)
×
UNCOV
392
                }
×
393
        }
394

UNCOV
395
        fmt.Println("\nYou can now customize these files to define your agent's personality and behavior.")
×
396
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc