@tanagram/cli 0.1.21 → 0.1.24

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.
package/README.md CHANGED
@@ -97,10 +97,31 @@ tanagram help
97
97
  - **`help`** - Show usage information
98
98
 
99
99
  ### Claude Code Hook
100
- You can install the CLI as a Claude Code [hook](https://code.claude.com/docs/en/hooks) to have Claude automatically iterate on Tanagram's output. Add the following to your `~/.claude/settings.json` (user settings) or `.claude/settings.json` (project settings):
100
+ You can install the CLI as a Claude Code [hook](https://code.claude.com/docs/en/hooks) to have Claude automatically iterate on Tanagram's output.
101
+
102
+ Add this to your `~/.claude/settings.json` (user settings) or `.claude/settings.json` (project settings):
103
+
104
+ ```json
105
+ "hooks": {
106
+ "PostToolUse": [
107
+ {
108
+ "matcher": "Edit|Write",
109
+ "hooks": [
110
+ {
111
+ "type": "command",
112
+ "command": "tanagram"
113
+ }
114
+ ]
115
+ }
116
+ ]
117
+ }
118
+ ```
119
+
120
+ For example, your full `settings.json` file might look like this:
101
121
 
102
122
  ```json
103
123
  {
124
+ "alwaysThinkingEnabled": true,
104
125
  "hooks": {
105
126
  "PostToolUse": [
106
127
  {
@@ -215,47 +236,4 @@ Then run `tanagram` to enforce them locally!
215
236
 
216
237
  ---
217
238
 
218
- ## Contributing
219
-
220
- Contributions are welcome! Please feel free to submit a Pull Request.
221
-
222
- ### Development Setup
223
-
224
- ```bash
225
- # Clone the repository
226
- git clone https://github.com/tanagram/cli.git
227
- cd cli
228
-
229
- # Install dependencies and build
230
- npm install
231
-
232
- # Run tests
233
- npm test
234
-
235
- # Build manually
236
- go build -o bin/tanagram .
237
- ```
238
-
239
- ### Publishing a Release
240
-
241
- To publish a new version:
242
-
243
- ```bash
244
- # 1. Update version in package.json
245
- npm version patch # or minor, or major
246
-
247
- # 2. Commit and tag
248
- git add package.json package-lock.json
249
- git commit -m "Bump version to $(node -p "require('./package.json').version")"
250
- git tag v$(node -p "require('./package.json').version")
251
- git push && git push origin --tags
252
-
253
- # 3. Publish to npm
254
- npm publish
255
- ```
256
-
257
- **Note:** The Go compiler is automatically downloaded and used during `npm install` via the `go-bin` package, so no pre-built binaries needed!
258
-
259
- ## License
260
-
261
- MIT
239
+ Built by [@fluttermatt](https://x.com/fluttermatt) and the Tanagram team. Talk to us [on Twitter](https://x.com/tanagram_) or email: founders AT tanagram.ai
package/commands/run.go CHANGED
@@ -11,6 +11,7 @@ import (
11
11
  "github.com/tanagram/cli/checker"
12
12
  "github.com/tanagram/cli/extractor"
13
13
  "github.com/tanagram/cli/git"
14
+ "github.com/tanagram/cli/metrics"
14
15
  "github.com/tanagram/cli/parser"
15
16
  "github.com/tanagram/cli/storage"
16
17
  )
@@ -72,6 +73,7 @@ func Run() error {
72
73
  if len(filesToSync) > 0 {
73
74
  fmt.Printf("\nSyncing policies with LLM (processing %d changed file(s) in parallel)...\n", len(filesToSync))
74
75
 
76
+ syncStart := time.Now()
75
77
  ctx := context.Background()
76
78
 
77
79
  // Result type for collecting sync results
@@ -164,7 +166,15 @@ func Run() error {
164
166
  return fmt.Errorf("failed to save cache: %w", err)
165
167
  }
166
168
 
169
+ syncDuration := time.Since(syncStart)
167
170
  fmt.Printf("\n✓ Synced %d policies from %d changed file(s)\n", totalPolicies, len(filesToSync))
171
+
172
+ // Track sync metrics
173
+ metrics.Track("cli.sync.complete", map[string]interface{}{
174
+ "files_synced": len(filesToSync),
175
+ "policies_synced": totalPolicies,
176
+ "duration_seconds": syncDuration.Seconds(),
177
+ })
168
178
  }
169
179
 
170
180
  // Load all policies from cache
@@ -196,7 +206,18 @@ func Run() error {
196
206
 
197
207
  // Check changes against policies (both regex and LLM-based)
198
208
  ctx := context.Background()
209
+ checkStart := time.Now()
199
210
  result := checker.CheckChanges(ctx, diffResult.Changes, policies)
211
+ checkDuration := time.Since(checkStart)
212
+
213
+ // Track policy check results (similar to policy.execute.result in github-app)
214
+ metrics.Track("cli.policy.check.result", map[string]interface{}{
215
+ "policies_checked": len(policies),
216
+ "changes_checked": len(diffResult.Changes),
217
+ "violations_found": len(result.Violations),
218
+ "has_violations": len(result.Violations) > 0,
219
+ "duration_seconds": checkDuration.Seconds(),
220
+ })
200
221
 
201
222
  // Handle results based on whether violations were found
202
223
  if len(result.Violations) > 0 {
package/go.mod CHANGED
@@ -5,7 +5,9 @@ go 1.23.0
5
5
  require github.com/anthropics/anthropic-sdk-go v1.17.0
6
6
 
7
7
  require (
8
- github.com/stretchr/testify v1.9.0 // indirect
8
+ github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
9
+ github.com/posthog/posthog-go v1.6.12 // indirect
10
+ github.com/stretchr/testify v1.10.0 // indirect
9
11
  github.com/tidwall/gjson v1.18.0 // indirect
10
12
  github.com/tidwall/match v1.1.1 // indirect
11
13
  github.com/tidwall/pretty v1.2.1 // indirect
package/go.sum CHANGED
@@ -2,10 +2,16 @@ github.com/anthropics/anthropic-sdk-go v1.17.0 h1:BwK8ApcmaAUkvZTiQE0yi3R9XneEFs
2
2
  github.com/anthropics/anthropic-sdk-go v1.17.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
3
3
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4
4
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5
+ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
6
+ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
5
7
  github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6
8
  github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9
+ github.com/posthog/posthog-go v1.6.12 h1:rsOBL/YdMfLJtOVjKJLgdzYmvaL3aIW6IVbAteSe+aI=
10
+ github.com/posthog/posthog-go v1.6.12/go.mod h1:LcC1Nu4AgvV22EndTtrMXTy+7RGVC0MhChSw7Qk5XkY=
7
11
  github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
8
12
  github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
13
+ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
14
+ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
9
15
  github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
10
16
  github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
11
17
  github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
package/install.js CHANGED
@@ -79,8 +79,15 @@ function buildBinary(goCommand) {
79
79
 
80
80
  console.log('🔧 Building Tanagram CLI...');
81
81
 
82
+ // Build ldflags to inject PostHog key at build time
83
+ let ldflags = '';
84
+ if (process.env.POSTHOG_WRITE_KEY) {
85
+ ldflags = `-ldflags="-X 'github.com/tanagram/cli/metrics.posthogWriteKey=${process.env.POSTHOG_WRITE_KEY}'"`;
86
+ console.log(' 📊 Injecting PostHog metrics key...');
87
+ }
88
+
82
89
  try {
83
- execSync(`"${goCommand}" build -o "${binaryPath}" .`, {
90
+ execSync(`"${goCommand}" build ${ldflags} -o "${binaryPath}" .`, {
84
91
  cwd: __dirname,
85
92
  stdio: 'inherit',
86
93
  env: {
package/main.go CHANGED
@@ -5,9 +5,14 @@ import (
5
5
  "os"
6
6
 
7
7
  "github.com/tanagram/cli/commands"
8
+ "github.com/tanagram/cli/metrics"
8
9
  )
9
10
 
10
11
  func main() {
12
+ // Initialize metrics (similar to PosthogService initialization)
13
+ metrics.Init()
14
+ defer metrics.Close()
15
+
11
16
  // Get subcommand (default to "run" if none provided)
12
17
  subcommand := "run"
13
18
  if len(os.Args) > 1 {
@@ -17,10 +22,19 @@ func main() {
17
22
  var err error
18
23
  switch subcommand {
19
24
  case "run":
25
+ metrics.Track("command.execute", map[string]interface{}{
26
+ "command": "run",
27
+ })
20
28
  err = commands.Run()
21
29
  case "sync":
30
+ metrics.Track("command.execute", map[string]interface{}{
31
+ "command": "sync",
32
+ })
22
33
  err = commands.Sync()
23
34
  case "list":
35
+ metrics.Track("command.execute", map[string]interface{}{
36
+ "command": "list",
37
+ })
24
38
  err = commands.List()
25
39
  case "help", "-h", "--help":
26
40
  printHelp()
@@ -32,6 +46,10 @@ func main() {
32
46
  }
33
47
 
34
48
  if err != nil {
49
+ metrics.Track("command.error", map[string]interface{}{
50
+ "command": subcommand,
51
+ "error": err.Error(),
52
+ })
35
53
  fmt.Fprintf(os.Stderr, "Error: %v\n", err)
36
54
  os.Exit(1)
37
55
  }
@@ -0,0 +1,116 @@
1
+ package metrics
2
+
3
+ import (
4
+ "os"
5
+ "runtime"
6
+ "time"
7
+
8
+ "github.com/posthog/posthog-go"
9
+ )
10
+
11
+ var (
12
+ client posthog.Client
13
+
14
+ // Injected at build time via -ldflags
15
+ posthogWriteKey = ""
16
+ posthogHost = "https://us.i.posthog.com"
17
+ )
18
+
19
+ // Init initializes the PostHog client
20
+ // Similar to PosthogService.py and webui/app/lib/posthog.ts
21
+ func Init() {
22
+ // Use embedded key (set at build time)
23
+ writeKey := posthogWriteKey
24
+
25
+ // Allow override via env var for local development
26
+ if envKey := os.Getenv("POSTHOG_WRITE_KEY"); envKey != "" {
27
+ writeKey = envKey
28
+ }
29
+
30
+ if writeKey == "" {
31
+ // Fail silently if not configured - metrics are optional
32
+ return
33
+ }
34
+
35
+ host := posthogHost
36
+ // Allow override via env var for local development
37
+ if envHost := os.Getenv("POSTHOG_HOST"); envHost != "" {
38
+ host = envHost
39
+ }
40
+
41
+ var err error
42
+ client, err = posthog.NewWithConfig(
43
+ writeKey,
44
+ posthog.Config{
45
+ Endpoint: host,
46
+ },
47
+ )
48
+ if err != nil {
49
+ // Log but don't fail - metrics shouldn't break the CLI
50
+ return
51
+ }
52
+ }
53
+
54
+ // Close flushes and closes the PostHog client
55
+ func Close() {
56
+ if client != nil {
57
+ client.Close()
58
+ }
59
+ }
60
+
61
+ // Track sends an event to PostHog
62
+ // Similar to MetricsService.put_metric_single_count()
63
+ func Track(event string, properties map[string]interface{}) {
64
+ if client == nil {
65
+ return // Silently skip if not initialized
66
+ }
67
+
68
+ if properties == nil {
69
+ properties = make(map[string]interface{})
70
+ }
71
+
72
+ // Add context dimensions similar to MetricsService
73
+ properties["$process_person_profile"] = false // Match Python implementation
74
+ properties["_deployment_env"] = getDeploymentEnv()
75
+ properties["os"] = runtime.GOOS
76
+ properties["arch"] = runtime.GOARCH
77
+ properties["cli_version"] = getVersion()
78
+
79
+ err := client.Enqueue(posthog.Capture{
80
+ DistinctId: getDistinctId(),
81
+ Event: event,
82
+ Properties: properties,
83
+ Timestamp: time.Now(),
84
+ })
85
+
86
+ if err != nil {
87
+ // Silently fail - don't break CLI execution for metrics
88
+ return
89
+ }
90
+ }
91
+
92
+ // getDistinctId returns a stable anonymous identifier
93
+ // Similar to user_id_for_github_dot_com() pattern
94
+ func getDistinctId() string {
95
+ // Use hostname as stable identifier
96
+ hostname, err := os.Hostname()
97
+ if err != nil {
98
+ return "unknown"
99
+ }
100
+ return "cli_" + hostname
101
+ }
102
+
103
+ // getDeploymentEnv returns the deployment environment
104
+ // Similar to _env_name_from_github_app_id()
105
+ func getDeploymentEnv() string {
106
+ if env := os.Getenv("TANAGRAM_ENV"); env != "" {
107
+ return env
108
+ }
109
+ return "unknown"
110
+ }
111
+
112
+ // getVersion returns the CLI version
113
+ func getVersion() string {
114
+ // TODO: embed version at build time with -ldflags
115
+ return "0.1.14"
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.1.21",
3
+ "version": "0.1.24",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "scripts": {
10
10
  "postinstall": "node install.js",
11
- "test": "go test ./..."
11
+ "test": "go test ./...",
12
+ "prepublishOnly": "export $(grep -v '^#' .env.publish | xargs) && node install.js"
12
13
  },
13
14
  "keywords": [
14
15
  "tanagram",
@@ -38,6 +39,7 @@
38
39
  "fixtures/",
39
40
  "git/",
40
41
  "llm/",
42
+ "metrics/",
41
43
  "parser/",
42
44
  "storage/",
43
45
  "main.go",