@tanagram/cli 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -180,7 +180,7 @@ func ConfigList() error {
180
180
  projectStatus := checkHookStatus(projectSettingsPath)
181
181
 
182
182
  // Print results
183
- fmt.Println("Claude Code Hook Status:\n")
183
+ fmt.Println("Claude Code Hook Status:")
184
184
 
185
185
  fmt.Printf("User Settings (~/.claude/settings.json):\n")
186
186
  printHookStatus(userStatus, userSettingsPath)
@@ -350,3 +350,35 @@ func printHookStatus(status HookStatus, path string) {
350
350
  }
351
351
  }
352
352
 
353
+ // EnsureClaudeConfigured checks if Claude Code hooks are configured,
354
+ // and automatically sets them up if not. This is called on first run
355
+ // of commands that need Claude Code integration.
356
+ func EnsureClaudeConfigured() error {
357
+ home, err := os.UserHomeDir()
358
+ if err != nil {
359
+ // Silently skip if we can't get home dir - not critical
360
+ return nil
361
+ }
362
+
363
+ settingsPath := filepath.Join(home, ".claude", "settings.json")
364
+ status := checkHookStatus(settingsPath)
365
+
366
+ // If hooks are already configured, we're done
367
+ if status.IsUpToDate {
368
+ return nil
369
+ }
370
+
371
+ // If file doesn't exist or hooks aren't configured, set them up
372
+ if !status.FileExists || !status.PreHookExists || !status.PostHookExists {
373
+ fmt.Println("Setting up Claude Code integration...")
374
+ if err := ConfigClaude(); err != nil {
375
+ // Don't fail the command if hook setup fails - just warn
376
+ fmt.Fprintf(os.Stderr, "Warning: Failed to setup Claude Code hooks: %v\n", err)
377
+ fmt.Fprintf(os.Stderr, "You can manually setup hooks later with: tanagram config claude\n\n")
378
+ return nil
379
+ }
380
+ fmt.Println() // Add blank line after setup message
381
+ }
382
+
383
+ return nil
384
+ }
package/go.mod CHANGED
@@ -2,33 +2,42 @@ module github.com/tanagram/cli
2
2
 
3
3
  go 1.24.0
4
4
 
5
- require github.com/anthropics/anthropic-sdk-go v1.17.0
5
+ require (
6
+ github.com/anthropics/anthropic-sdk-go v1.17.0
7
+ github.com/charmbracelet/bubbletea v1.3.10
8
+ github.com/charmbracelet/lipgloss v1.1.0
9
+ github.com/posthog/posthog-go v1.6.12
10
+ github.com/shirou/gopsutil/v3 v3.24.5
11
+ )
6
12
 
7
13
  require (
8
14
  github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
9
- github.com/charmbracelet/bubbletea v1.3.10 // indirect
10
15
  github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
11
- github.com/charmbracelet/lipgloss v1.1.0 // indirect
12
16
  github.com/charmbracelet/x/ansi v0.10.1 // indirect
13
17
  github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
14
18
  github.com/charmbracelet/x/term v0.2.1 // indirect
15
19
  github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
20
+ github.com/go-ole/go-ole v1.2.6 // indirect
16
21
  github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
17
22
  github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
23
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
18
24
  github.com/mattn/go-isatty v0.0.20 // indirect
19
25
  github.com/mattn/go-localereader v0.0.1 // indirect
20
26
  github.com/mattn/go-runewidth v0.0.16 // indirect
21
27
  github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
22
28
  github.com/muesli/cancelreader v0.2.2 // indirect
23
29
  github.com/muesli/termenv v0.16.0 // indirect
24
- github.com/posthog/posthog-go v1.6.12 // indirect
30
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
25
31
  github.com/rivo/uniseg v0.4.7 // indirect
26
- github.com/stretchr/testify v1.10.0 // indirect
32
+ github.com/shoenig/go-m1cpu v0.1.6 // indirect
27
33
  github.com/tidwall/gjson v1.18.0 // indirect
28
34
  github.com/tidwall/match v1.1.1 // indirect
29
35
  github.com/tidwall/pretty v1.2.1 // indirect
30
36
  github.com/tidwall/sjson v1.2.5 // indirect
37
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
38
+ github.com/tklauser/numcpus v0.6.1 // indirect
31
39
  github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
40
+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
32
41
  golang.org/x/sys v0.36.0 // indirect
33
42
  golang.org/x/text v0.27.0 // indirect
34
43
  )
package/go.sum CHANGED
@@ -18,10 +18,17 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
18
18
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
19
19
  github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
20
20
  github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
21
+ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
22
+ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
23
+ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
24
+ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
25
+ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
21
26
  github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
22
27
  github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
23
28
  github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
24
29
  github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
30
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
31
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
25
32
  github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
26
33
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
27
34
  github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
@@ -38,11 +45,17 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
38
45
  github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
39
46
  github.com/posthog/posthog-go v1.6.12 h1:rsOBL/YdMfLJtOVjKJLgdzYmvaL3aIW6IVbAteSe+aI=
40
47
  github.com/posthog/posthog-go v1.6.12/go.mod h1:LcC1Nu4AgvV22EndTtrMXTy+7RGVC0MhChSw7Qk5XkY=
48
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
49
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
41
50
  github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
42
51
  github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
43
52
  github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
44
- github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
45
- github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
53
+ github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
54
+ github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
55
+ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
56
+ github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
57
+ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
58
+ github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
46
59
  github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
47
60
  github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
48
61
  github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -55,13 +68,26 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
55
68
  github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
56
69
  github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
57
70
  github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
71
+ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
72
+ github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
73
+ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
74
+ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
58
75
  github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
59
76
  github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
77
+ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
78
+ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
79
+ golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
80
+ golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
81
+ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
82
+ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
60
83
  golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
61
84
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
85
+ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
86
+ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
62
87
  golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
63
88
  golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
64
89
  golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
65
90
  golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
91
+ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
66
92
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
67
93
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package/main.go CHANGED
@@ -43,6 +43,12 @@ func main() {
43
43
  metrics.Track("cli.command.execute", map[string]interface{}{
44
44
  "command": "run",
45
45
  })
46
+ // Auto-setup Claude Code hooks on first run
47
+ if err := commands.EnsureClaudeConfigured(); err != nil {
48
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
49
+ exitCode = 1
50
+ return
51
+ }
46
52
  err = commands.Run()
47
53
  case "snapshot":
48
54
  metrics.Track("cli.command.execute", map[string]interface{}{
@@ -53,11 +59,23 @@ func main() {
53
59
  metrics.Track("cli.command.execute", map[string]interface{}{
54
60
  "command": "sync",
55
61
  })
62
+ // Auto-setup Claude Code hooks on first run
63
+ if err := commands.EnsureClaudeConfigured(); err != nil {
64
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
65
+ exitCode = 1
66
+ return
67
+ }
56
68
  err = commands.Sync()
57
69
  case "list":
58
70
  metrics.Track("cli.command.execute", map[string]interface{}{
59
71
  "command": "list",
60
72
  })
73
+ // Auto-setup Claude Code hooks on first run
74
+ if err := commands.EnsureClaudeConfigured(); err != nil {
75
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
76
+ exitCode = 1
77
+ return
78
+ }
61
79
  err = commands.List()
62
80
  case "config":
63
81
  // Handle config subcommands
@@ -2,10 +2,12 @@ package metrics
2
2
 
3
3
  import (
4
4
  "os"
5
+ "os/user"
5
6
  "runtime"
6
7
  "time"
7
8
 
8
9
  "github.com/posthog/posthog-go"
10
+ "github.com/shirou/gopsutil/v3/process"
9
11
  )
10
12
 
11
13
  var (
@@ -77,10 +79,13 @@ func Track(event string, properties map[string]interface{}) {
77
79
 
78
80
  // Add context dimensions similar to MetricsService
79
81
  properties["$process_person_profile"] = false // Match Python implementation
80
- properties["_deployment_env"] = getDeploymentEnv()
81
82
  properties["os"] = runtime.GOOS
82
83
  properties["arch"] = runtime.GOARCH
83
84
  properties["cli_version"] = getVersion()
85
+ properties["os_user"] = getUser()
86
+ properties["hostname"] = getHostname()
87
+ properties["cwd"] = getCwd()
88
+ properties["parent_process"] = getParentProcess()
84
89
 
85
90
  // Build properties - include all custom properties from the caller
86
91
  props := posthog.NewProperties()
@@ -104,24 +109,51 @@ func Track(event string, properties map[string]interface{}) {
104
109
  // getDistinctId returns a stable anonymous identifier
105
110
  // Similar to user_id_for_github_dot_com() pattern
106
111
  func getDistinctId() string {
107
- // Use hostname as stable identifier
112
+ return "cli_" + getHostname()
113
+ }
114
+
115
+ // getHostname returns the computer's hostname
116
+ func getHostname() string {
108
117
  hostname, err := os.Hostname()
109
118
  if err != nil {
110
119
  return "unknown"
111
120
  }
112
- return "cli_" + hostname
121
+ return hostname
113
122
  }
114
123
 
115
- // getDeploymentEnv returns the deployment environment
116
- // Similar to _env_name_from_github_app_id()
117
- func getDeploymentEnv() string {
118
- if env := os.Getenv("TANAGRAM_ENV"); env != "" {
119
- return env
124
+ // getUser returns the current OS user's login name
125
+ func getUser() string {
126
+ u, err := user.Current()
127
+ if err != nil {
128
+ return "unknown"
129
+ }
130
+ return u.Username
131
+ }
132
+
133
+ // getCwd returns the current working directory
134
+ func getCwd() string {
135
+ cwd, err := os.Getwd()
136
+ if err != nil {
137
+ return "unknown"
120
138
  }
121
- return "unknown"
139
+ return cwd
122
140
  }
123
141
 
124
142
  // getVersion returns the CLI version
125
143
  func getVersion() string {
126
144
  return version
127
145
  }
146
+
147
+ // getParentProcess returns the name of the parent process
148
+ func getParentProcess() string {
149
+ parentPID := int32(os.Getppid())
150
+ parent, err := process.NewProcess(parentPID)
151
+ if err != nil {
152
+ return "unknown"
153
+ }
154
+ name, err := parent.Name()
155
+ if err != nil {
156
+ return "unknown"
157
+ }
158
+ return name
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {