@tanagram/cli 0.4.0 → 0.4.2

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/api/client.go ADDED
@@ -0,0 +1,106 @@
1
+ package api
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io"
7
+ "net/http"
8
+ "os"
9
+ "time"
10
+
11
+ "github.com/tanagram/cli/auth"
12
+ )
13
+
14
+ // TanagramAPIClient handles communication with the Tanagram API
15
+ type TanagramAPIClient struct {
16
+ baseURL string
17
+ jwt string
18
+ client *http.Client
19
+ }
20
+
21
+ // Repository represents a git repository
22
+ type Repository struct {
23
+ ID string `json:"id"`
24
+ Name string `json:"name"`
25
+ Owner string `json:"owner"`
26
+ IsPrivate bool `json:"is_private"`
27
+ }
28
+
29
+ // Policy represents a Tanagram policy
30
+ type Policy struct {
31
+ ID string `json:"id"`
32
+ Name string `json:"name"`
33
+ OrganizationID string `json:"organization_id"`
34
+ Substrate string `json:"substrate"` // "llm" or "tql"
35
+ DescriptionFromUser string `json:"description_from_user"`
36
+ DescriptionRewrittenByLLM string `json:"description_rewritten_by_llm"`
37
+ CustomMessage string `json:"custom_message"`
38
+ EnabledStatus string `json:"enabled_status"`
39
+ CreatedAt time.Time `json:"created_at"`
40
+ UpdatedAt time.Time `json:"updated_at"`
41
+ PolicyRepositories []Repository `json:"policy_repositories"`
42
+ ViolationsCount int `json:"violations_count"`
43
+ }
44
+
45
+ // PolicyListResponse is the response from GET /api/policies/
46
+ type PolicyListResponse struct {
47
+ Policies []Policy `json:"policies"`
48
+ Total int `json:"total"`
49
+ }
50
+
51
+ // getAPIBaseURL returns the API base URL
52
+ func getAPIBaseURL() string {
53
+ if url := os.Getenv("TANAGRAM_API_URL"); url != "" {
54
+ return url
55
+ }
56
+ return "https://api.tanagram.ai"
57
+ }
58
+
59
+ // NewAPIClient creates a new Tanagram API client
60
+ func NewAPIClient() (*TanagramAPIClient, error) {
61
+ jwt, err := auth.GetAccessToken()
62
+ if err != nil {
63
+ return nil, err
64
+ }
65
+
66
+ return &TanagramAPIClient{
67
+ baseURL: getAPIBaseURL(),
68
+ jwt: jwt,
69
+ client: &http.Client{
70
+ Timeout: 30 * time.Second,
71
+ },
72
+ }, nil
73
+ }
74
+
75
+ // GetPolicies fetches all policies from the API
76
+ func (c *TanagramAPIClient) GetPolicies() (*PolicyListResponse, error) {
77
+ req, err := http.NewRequest("GET", c.baseURL+"/api/policies/", nil)
78
+ if err != nil {
79
+ return nil, fmt.Errorf("failed to create request: %w", err)
80
+ }
81
+
82
+ req.Header.Set("Authorization", "Bearer "+c.jwt)
83
+ req.Header.Set("Content-Type", "application/json")
84
+
85
+ resp, err := c.client.Do(req)
86
+ if err != nil {
87
+ return nil, fmt.Errorf("failed to fetch policies: %w", err)
88
+ }
89
+ defer resp.Body.Close()
90
+
91
+ if resp.StatusCode == 401 {
92
+ return nil, fmt.Errorf("authentication failed - your session may have expired. Please run 'tanagram login' again")
93
+ }
94
+
95
+ if resp.StatusCode != 200 {
96
+ body, _ := io.ReadAll(resp.Body)
97
+ return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
98
+ }
99
+
100
+ var result PolicyListResponse
101
+ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
102
+ return nil, fmt.Errorf("failed to parse response: %w", err)
103
+ }
104
+
105
+ return &result, nil
106
+ }
package/auth/auth.go ADDED
@@ -0,0 +1,26 @@
1
+ package auth
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "github.com/zalando/go-keyring"
7
+ )
8
+
9
+ const (
10
+ serviceName = "tanagram-cli"
11
+ accessTokenKey = "access-token"
12
+ )
13
+
14
+ // GetAccessToken retrieves the stored access token from the keyring
15
+ func GetAccessToken() (string, error) {
16
+ token, err := keyring.Get(serviceName, accessTokenKey)
17
+ if err != nil {
18
+ return "", fmt.Errorf("not logged in - run 'tanagram login' first: %w", err)
19
+ }
20
+ return token, nil
21
+ }
22
+
23
+ // SetAccessToken stores the access token in the keyring
24
+ func SetAccessToken(token string) error {
25
+ return keyring.Set(serviceName, accessTokenKey, token)
26
+ }
package/main.go CHANGED
@@ -179,10 +179,7 @@ USAGE:
179
179
  COMMANDS:
180
180
  run Check git changes against policies (default)
181
181
  login Authenticate with Tanagram using Stytch B2B
182
- <<<<<<< HEAD
183
182
  sync-policies Sync cloud policies from Tanagram
184
- =======
185
- >>>>>>> origin/main
186
183
  snapshot Create a snapshot of current file state (used by PreToolUse hook)
187
184
  sync Manually sync instruction files to cache
188
185
  list Show all cached policies
@@ -195,10 +192,7 @@ EXAMPLES:
195
192
  tanagram # Check changes (auto-syncs if files changed)
196
193
  tanagram run # Same as above
197
194
  tanagram login # Authenticate with Tanagram
198
- <<<<<<< HEAD
199
195
  tanagram sync-policies # Sync cloud policies from Tanagram
200
- =======
201
- >>>>>>> origin/main
202
196
  tanagram snapshot # Create snapshot before making changes
203
197
  tanagram sync # Manually sync local instruction files
204
198
  tanagram list # View all cached policies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,6 +33,8 @@
33
33
  },
34
34
  "files": [
35
35
  "bin/tanagram.js",
36
+ "api/",
37
+ "auth/",
36
38
  "checker/",
37
39
  "commands/",
38
40
  "config/",
@@ -45,6 +47,7 @@
45
47
  "snapshot/",
46
48
  "storage/",
47
49
  "tui/",
50
+ "utils/",
48
51
  "main.go",
49
52
  "go.mod",
50
53
  "go.sum",
@@ -0,0 +1,36 @@
1
+ package utils
2
+
3
+ import (
4
+ "os"
5
+ "sync"
6
+
7
+ "github.com/shirou/gopsutil/v3/process"
8
+ )
9
+
10
+ var (
11
+ parentProcessName string
12
+ parentProcessOnce sync.Once
13
+ )
14
+
15
+ // GetParentProcess returns the name of the parent process.
16
+ // The result is memoized after the first call to avoid repeated system calls.
17
+ func GetParentProcess() string {
18
+ parentProcessOnce.Do(func() {
19
+ parentProcessName = "unknown"
20
+
21
+ parentPID := int32(os.Getppid())
22
+ parent, err := process.NewProcess(parentPID)
23
+ if err != nil {
24
+ return
25
+ }
26
+
27
+ name, err := parent.Name()
28
+ if err != nil {
29
+ return
30
+ }
31
+
32
+ parentProcessName = name
33
+ })
34
+
35
+ return parentProcessName
36
+ }