Skip to content

Commit 61d2799

Browse files
authored
Add error tracking and reporting system (#39)
* Add error tracking and reporting system * need an ability to cancel a spinner action if an error occurs while running the action
1 parent e11c7a6 commit 61d2799

27 files changed

+823
-123
lines changed

.github/workflows/go.yml

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717
with:
1818
go-version: '1.24'
1919

20+
- name: Generate Error Codes
21+
run: go generate ./...
22+
2023
- name: Build
2124
run: go build -v .
2225

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ cli
44
dist/
55
**/.DS_Store
66
/my*test*
7-
/test*
7+
/test*
8+
.agentuity-crash-*

Makefile

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
.PHONY: build fmt
1+
.PHONY: build fmt generate
22

3-
build: fmt
3+
build: fmt generate
44
@go build -o agentuity
55

66
fmt:
7-
@go fmt ./...
7+
@go fmt ./...
8+
9+
generate:
10+
@echo "Running go generate..."
11+
@go generate ./...

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,20 @@ For more detailed information about any command, you can use:
104104
agentuity [command] --help
105105
```
106106

107+
## Development
108+
109+
### Error Code System
110+
111+
The CLI uses a centralized error code system to provide consistent error messages and codes. Error codes are defined in `error_codes.yaml` at the root of the project and are automatically generated into Go code.
112+
113+
To add a new error code:
114+
115+
1. Edit `error_codes.yaml` and add a new entry with a unique code and descriptive message
116+
2. Run `go generate ./...` to update the Go code
117+
3. Use the generated error type in your code with `errsystem.New(errsystem.ErrYourError, err)`
118+
119+
For more details, see the [Error Code System documentation](tools/README.md).
120+
107121
## License
108122

109123
See the [LICENSE](LICENSE.md) file for details.

cmd/agent.go

+20-17
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99

1010
"github.com/agentuity/cli/internal/agent"
11+
"github.com/agentuity/cli/internal/errsystem"
1112
"github.com/agentuity/cli/internal/project"
1213
"github.com/agentuity/cli/internal/templates"
1314
"github.com/agentuity/cli/internal/tui"
@@ -37,7 +38,7 @@ var agentDeleteCmd = &cobra.Command{
3738
logger := env.NewLogger(cmd)
3839
apikey := viper.GetString("auth.api_key")
3940
if apikey == "" {
40-
logger.Fatal("you are not logged in")
41+
logger.Fatal("You are not logged in. Please run `agentuity login` to login.")
4142
}
4243
theproject := ensureProject(cmd)
4344
apiUrl, _ := getURLs(logger)
@@ -68,7 +69,7 @@ var agentDeleteCmd = &cobra.Command{
6869
var err error
6970
deleted, err = agent.DeleteAgents(logger, apiUrl, apikey, theproject.Project.ProjectId, selected)
7071
if err != nil {
71-
logger.Fatal("failed to delete Agents: %s", err)
72+
errsystem.New(errsystem.ErrApiRequest, err, errsystem.WithContextMessage("Failed to delete agents")).ShowErrorAndExit()
7273
}
7374
for _, key := range keys {
7475
agent := state[key]
@@ -83,7 +84,7 @@ var agentDeleteCmd = &cobra.Command{
8384
return
8485
}
8586

86-
tui.ShowSpinner(logger, "Deleting Agents ...", action)
87+
tui.ShowSpinner("Deleting Agents ...", action)
8788
tui.ShowSuccess("%s deleted successfully", util.Pluralize(len(deleted), "Agent", "Agents"))
8889
},
8990
}
@@ -96,14 +97,14 @@ var agentCreateCmd = &cobra.Command{
9697
logger := env.NewLogger(cmd)
9798
apikey := viper.GetString("auth.api_key")
9899
if apikey == "" {
99-
logger.Fatal("you are not logged in")
100+
logger.Fatal("You are not logged in. Please run `agentuity login` to login.")
100101
}
101102
theproject := ensureProject(cmd)
102103
apiUrl, _ := getURLs(logger)
103104

104105
remoteAgents, err := getAgentList(logger, apiUrl, apikey, theproject)
105106
if err != nil {
106-
logger.Fatal("failed to list Agents: %s", err)
107+
errsystem.New(errsystem.ErrApiRequest, err, errsystem.WithContextMessage("Failed to get agent list")).ShowErrorAndExit()
107108
}
108109

109110
initScreenWithLogo()
@@ -122,12 +123,12 @@ var agentCreateCmd = &cobra.Command{
122123
action := func() {
123124
agentID, err := agent.CreateAgent(logger, apiUrl, apikey, theproject.Project.ProjectId, name, description)
124125
if err != nil {
125-
logger.Fatal("failed to create agent: %s", err)
126+
errsystem.New(errsystem.ErrApiRequest, err, errsystem.WithContextMessage("Failed to create agent")).ShowErrorAndExit()
126127
}
127128

128129
rules, err := templates.LoadTemplateRuleForIdentifier(theproject.Project.Bundler.Identifier)
129130
if err != nil {
130-
logger.Fatal("failed to load template rules for %s: %s", theproject.Project.Bundler.Identifier, err)
131+
errsystem.New(errsystem.ErrInvalidConfiguration, err, errsystem.WithAttributes(map[string]any{"identifier": theproject.Project.Bundler.Identifier})).ShowErrorAndExit()
131132
}
132133

133134
if err := rules.NewAgent(templates.TemplateContext{
@@ -136,7 +137,7 @@ var agentCreateCmd = &cobra.Command{
136137
Description: description,
137138
ProjectDir: theproject.Dir,
138139
}); err != nil {
139-
logger.Fatal("failed to create agent: %s", err)
140+
errsystem.New(errsystem.ErrApiRequest, err, errsystem.WithAttributes(map[string]any{"name": name})).ShowErrorAndExit()
140141
}
141142

142143
theproject.Project.Agents = append(theproject.Project.Agents, project.AgentConfig{
@@ -146,10 +147,10 @@ var agentCreateCmd = &cobra.Command{
146147
})
147148

148149
if err := theproject.Project.Save(theproject.Dir); err != nil {
149-
logger.Fatal("failed to save project: %s", err)
150+
errsystem.New(errsystem.ErrSaveProject, err, errsystem.WithContextMessage("Failed to save project to disk")).ShowErrorAndExit()
150151
}
151152
}
152-
tui.ShowSpinner(logger, "Creating agent ...", action)
153+
tui.ShowSpinner("Creating agent ...", action)
153154
tui.ShowSuccess("Agent created successfully")
154155
},
155156
}
@@ -167,7 +168,7 @@ func getAgentList(logger logger.Logger, apiUrl string, apikey string, project pr
167168
action := func() {
168169
remoteAgents, err = agent.ListAgents(logger, apiUrl, apikey, project.Project.ProjectId)
169170
}
170-
tui.ShowSpinner(logger, "Fetching Agents ...", action)
171+
tui.ShowSpinner("Fetching Agents ...", action)
171172
return remoteAgents, err
172173
}
173174

@@ -178,12 +179,14 @@ func normalAgentName(name string) string {
178179
func reconcileAgentList(logger logger.Logger, apiUrl string, apikey string, theproject projectContext) ([]string, map[string]agentListState) {
179180
remoteAgents, err := getAgentList(logger, apiUrl, apikey, theproject)
180181
if err != nil {
181-
logger.Fatal("failed to fetch Agents for project: %s", err)
182+
errsystem.New(errsystem.ErrApiRequest, err, errsystem.WithContextMessage("Failed to get agent list")).ShowErrorAndExit()
182183
}
183184

184185
rules, err := templates.LoadTemplateRuleForIdentifier(theproject.Project.Bundler.Identifier)
185186
if err != nil {
186-
logger.Fatal("failed to load the agent template for %s. %s", theproject.Project.Bundler.Identifier, err)
187+
errsystem.New(errsystem.ErrInvalidConfiguration, err,
188+
errsystem.WithContextMessage("Failed loading template rule"),
189+
errsystem.WithAttributes(map[string]any{"identifier": theproject.Project.Bundler.Identifier})).ShowErrorAndExit()
187190
}
188191

189192
// make a map of the agents in the agentuity config file
@@ -207,7 +210,7 @@ func reconcileAgentList(logger logger.Logger, apiUrl string, apikey string, thep
207210
}
208211
localAgents, err := util.ListDir(agentSrcDir)
209212
if err != nil {
210-
logger.Fatal("failed to list local Agents: %s", err)
213+
errsystem.New(errsystem.ErrListFilesAndDirectories, err, errsystem.WithContextMessage("Failed to list agent source directory")).ShowErrorAndExit()
211214
}
212215
for _, filename := range localAgents {
213216
agentName := filepath.Base(filepath.Dir(filename))
@@ -337,13 +340,13 @@ func showAgentWarnings(remoteIssues int, localIssues int, deploying bool) bool {
337340

338341
var agentListCmd = &cobra.Command{
339342
Use: "list",
340-
Short: "List all the Agents in the project which are deployed",
343+
Short: "List all Agents in the project",
341344
Aliases: []string{"ls"},
342345
Run: func(cmd *cobra.Command, args []string) {
343346
logger := env.NewLogger(cmd)
344347
apikey := viper.GetString("auth.api_key")
345348
if apikey == "" {
346-
logger.Fatal("you are not logged in")
349+
logger.Fatal("You are not logged in. Please run `agentuity login` to login.")
347350
}
348351
project := ensureProject(cmd)
349352
apiUrl, _ := getURLs(logger)
@@ -359,7 +362,7 @@ var agentListCmd = &cobra.Command{
359362

360363
root, localIssues, remoteIssues, err := buildAgentTree(keys, state, project)
361364
if err != nil {
362-
logger.Fatal("%s", err)
365+
errsystem.New(errsystem.ErrInvalidConfiguration, err, errsystem.WithContextMessage("Failed to build agent tree")).ShowErrorAndExit()
363366
}
364367

365368
fmt.Println(root)

cmd/auth.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"github.com/agentuity/cli/internal/auth"
5+
"github.com/agentuity/cli/internal/errsystem"
56
"github.com/agentuity/cli/internal/tui"
67
"github.com/agentuity/go-common/env"
78
"github.com/spf13/cobra"
@@ -25,12 +26,14 @@ var authLoginCmd = &cobra.Command{
2526
initScreenWithLogo()
2627
authResult, err := auth.Login(logger, appUrl)
2728
if err != nil {
28-
logger.Fatal("failed to login: %s", err)
29+
errsystem.New(errsystem.ErrAuthenticateUser, err,
30+
errsystem.WithContextMessage("Failed to login")).ShowErrorAndExit()
2931
}
3032
viper.Set("auth.api_key", authResult.APIKey)
3133
viper.Set("auth.user_id", authResult.UserId)
3234
if err := viper.WriteConfig(); err != nil {
33-
logger.Fatal("failed to write config: %s", err)
35+
errsystem.New(errsystem.ErrWriteConfigurationFile, err,
36+
errsystem.WithContextMessage("Failed to write viper config")).ShowErrorAndExit()
3437
}
3538
tui.ShowSuccess("You are now logged in")
3639
},
@@ -44,16 +47,18 @@ var authLogoutCmd = &cobra.Command{
4447
_, appUrl := getURLs(logger)
4548
token := viper.GetString("auth.api_key")
4649
if token == "" {
47-
logger.Fatal("you are not logged in")
50+
logger.Fatal("You are not logged in. Please run `agentuity login` to login.")
4851
}
4952
viper.Set("auth.api_key", "")
5053
viper.Set("auth.user_id", "")
5154
if err := viper.WriteConfig(); err != nil {
52-
logger.Fatal("failed to write config: %s", err)
55+
errsystem.New(errsystem.ErrWriteConfigurationFile, err,
56+
errsystem.WithContextMessage("Failed to write viper config")).ShowErrorAndExit()
5357
}
5458
initScreenWithLogo()
5559
if err := auth.Logout(logger, appUrl, token); err != nil {
56-
logger.Fatal("failed to logout: %s", err)
60+
errsystem.New(errsystem.ErrApiRequest, err,
61+
errsystem.WithContextMessage("Failed to logout")).ShowErrorAndExit()
5762
}
5863
tui.ShowSuccess("You have been logged out")
5964
},
@@ -66,11 +71,11 @@ var authWhoamiCmd = &cobra.Command{
6671
logger := env.NewLogger(cmd)
6772
apikey := viper.GetString("auth.api_key")
6873
if apikey == "" {
69-
logger.Fatal("you are not logged in")
74+
logger.Fatal("You are not logged in. Please run `agentuity login` to login.")
7075
}
7176
userId := viper.GetString("auth.user_id")
7277
if userId == "" {
73-
logger.Fatal("you are not logged in")
78+
logger.Fatal("You are not logged in. Please run `agentuity login` to login.")
7479
}
7580
logger.Info("You are logged in with user id: %s", userId)
7681
},

cmd/bundle.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/agentuity/cli/internal/bundler"
8+
"github.com/agentuity/cli/internal/errsystem"
89
"github.com/spf13/cobra"
910
)
1011

@@ -22,7 +23,7 @@ var bundleCmd = &cobra.Command{
2223
ProjectDir: projectContext.Dir,
2324
Production: production,
2425
}); err != nil {
25-
projectContext.Logger.Fatal("%s", err)
26+
errsystem.New(errsystem.ErrInvalidConfiguration, err, errsystem.WithContextMessage("Failed to bundle project")).ShowErrorAndExit()
2627
}
2728
projectContext.Logger.Debug("bundled in %s", time.Since(started))
2829
},

0 commit comments

Comments
 (0)