@tanagram/cli 0.4.6 → 0.4.8

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 CHANGED
@@ -28,18 +28,18 @@ type Repository struct {
28
28
 
29
29
  // Policy represents a Tanagram policy
30
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"`
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
43
  }
44
44
 
45
45
  // PolicyListResponse is the response from GET /api/policies/
@@ -84,7 +84,7 @@ func ConfigClaude(settingsPath string) error {
84
84
  }
85
85
 
86
86
  if preHookExists && postHookExists {
87
- fmt.Println("✓ Tanagram hooks are already configured in ~/.claude/settings.json")
87
+ fmt.Printf("✓ Tanagram hooks are already configured in %s\n", settingsPath)
88
88
  return nil
89
89
  }
90
90
 
@@ -120,7 +120,7 @@ func ConfigClaude(settingsPath string) error {
120
120
  return fmt.Errorf("failed to save settings: %w", err)
121
121
  }
122
122
 
123
- fmt.Println("✓ Tanagram hooks added to ~/.claude/settings.json")
123
+ fmt.Printf("✓ Tanagram hooks added to %s\n", settingsPath)
124
124
  fmt.Println("\nClaude Code will now:")
125
125
  fmt.Println(" - Snapshot file state before each Edit/Write (PreToolUse)")
126
126
  fmt.Println(" - Check only Claude's changes after Edit/Write (PostToolUse)")
@@ -191,7 +191,7 @@ func ConfigCursor(hooksPath string) error {
191
191
  }
192
192
 
193
193
  if beforeSubmitHookExists && stopHookExists {
194
- fmt.Println("✓ Tanagram hooks are already configured in ~/.cursor/hooks.json")
194
+ fmt.Printf("✓ Tanagram hooks are already configured in %s\n", hooksPath)
195
195
  return nil
196
196
  }
197
197
 
@@ -215,7 +215,7 @@ func ConfigCursor(hooksPath string) error {
215
215
  return fmt.Errorf("failed to save hooks: %w", err)
216
216
  }
217
217
 
218
- fmt.Println("✓ Tanagram hooks added to ~/.cursor/hooks.json")
218
+ fmt.Printf("✓ Tanagram hooks added to %s\n", hooksPath)
219
219
  fmt.Println("\nCursor will now:")
220
220
  fmt.Println(" - Snapshot file state before each prompt (beforeSubmitPrompt)")
221
221
  fmt.Println(" - Check only Cursor's changes after agent completes (stop)")
package/commands/login.go CHANGED
@@ -78,11 +78,14 @@ func Login() error {
78
78
  }
79
79
 
80
80
  // Send success message to browser
81
- w.Header().Set("Content-Type", "text/html")
81
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
82
82
  fmt.Fprintf(w, `
83
83
  <!DOCTYPE html>
84
84
  <html>
85
- <head><title>Tanagram Login</title></head>
85
+ <head>
86
+ <meta charset="utf-8">
87
+ <title>Tanagram Login</title>
88
+ </head>
86
89
  <body style="font-family: system-ui; text-align: center; padding: 50px;">
87
90
  <h1>✓ Login Successful</h1>
88
91
  <p>You can close this window and return to your terminal.</p>
package/commands/sync.go CHANGED
@@ -191,20 +191,20 @@ func FindInstructionFiles(gitRoot string) ([]string, error) {
191
191
 
192
192
  // Directories to skip
193
193
  skipDirs := map[string]bool{
194
- ".git": true,
195
- "node_modules": true,
196
- "vendor": true,
197
- ".venv": true,
198
- "venv": true,
199
- "__pycache__": true,
200
- ".pytest_cache": true,
201
- ".mypy_cache": true,
202
- "dist": true,
203
- "build": true,
204
- ".tanagram": true,
205
- ".conductor": true, // Skip conductor directories
206
- "repos": true, // Skip cloned repos
207
- "monorepo_clones": true, // Skip cloned repos
194
+ ".git": true,
195
+ "node_modules": true,
196
+ "vendor": true,
197
+ ".venv": true,
198
+ "venv": true,
199
+ "__pycache__": true,
200
+ ".pytest_cache": true,
201
+ ".mypy_cache": true,
202
+ "dist": true,
203
+ "build": true,
204
+ ".tanagram": true,
205
+ ".conductor": true, // Skip conductor directories
206
+ "repos": true, // Skip cloned repos
207
+ "monorepo_clones": true, // Skip cloned repos
208
208
  }
209
209
 
210
210
  // Search from git root down
@@ -107,5 +107,3 @@ Instruction File Content:
107
107
 
108
108
  Remember: Return ONLY the JSON object, no other text, no markdown formatting.`, content)
109
109
  }
110
-
111
-
package/install.js CHANGED
@@ -97,12 +97,53 @@ function buildBinary(goCommand) {
97
97
  }
98
98
 
99
99
  console.log('✓ Tanagram CLI installed successfully');
100
- return true;
100
+ return binaryPath;
101
101
  } catch (error) {
102
102
  throw new Error(`Build failed: ${error.message}`);
103
103
  }
104
104
  }
105
105
 
106
+ function isCIEnvironment() {
107
+ return (
108
+ process.env.CI === 'true' ||
109
+ process.env.GITHUB_ACTIONS === 'true' ||
110
+ process.env.BUILDKITE === 'true' ||
111
+ process.env.GITLAB_CI === 'true' ||
112
+ process.env.TF_BUILD === 'True'
113
+ );
114
+ }
115
+
116
+ function shouldConfigureHooks() {
117
+ if (process.env.TANAGRAM_SKIP_HOOKS === '1' || process.env.TANAGRAM_SKIP_HOOKS === 'true') {
118
+ return false;
119
+ }
120
+ if (isCIEnvironment()) {
121
+ return false;
122
+ }
123
+ return true;
124
+ }
125
+
126
+ function configureEditorHooks(binaryPath) {
127
+ if (!shouldConfigureHooks()) {
128
+ console.log('Skipping Tanagram editor hook setup (CI or TANAGRAM_SKIP_HOOKS set).');
129
+ return;
130
+ }
131
+
132
+ console.log('Configuring Tanagram editor hooks (Claude Code, Cursor)...');
133
+
134
+ try {
135
+ execSync(`"${binaryPath}" config claude`, { stdio: 'inherit' });
136
+ } catch (err) {
137
+ console.warn('Warning: Failed to configure Claude hooks:', err.message);
138
+ }
139
+
140
+ try {
141
+ execSync(`"${binaryPath}" config cursor`, { stdio: 'inherit' });
142
+ } catch (err) {
143
+ console.warn('Warning: Failed to configure Cursor hooks:', err.message);
144
+ }
145
+ }
146
+
106
147
  // Main installation flow
107
148
  (async () => {
108
149
  try {
@@ -120,7 +161,10 @@ function buildBinary(goCommand) {
120
161
  }
121
162
 
122
163
  // 4. Build the binary
123
- buildBinary(goCommand);
164
+ const binaryPath = buildBinary(goCommand);
165
+
166
+ // 5. Configure hooks
167
+ configureEditorHooks(binaryPath);
124
168
 
125
169
  process.exit(0);
126
170
  } catch (error) {
@@ -143,5 +143,3 @@ func getCwd() string {
143
143
  func getVersion() string {
144
144
  return version
145
145
  }
146
-
147
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {
package/parser/agents.go CHANGED
@@ -63,4 +63,3 @@ func parsePolicy(text string) *Policy {
63
63
  OriginalText: text,
64
64
  }
65
65
  }
66
-
@@ -23,8 +23,8 @@ type FileState struct {
23
23
 
24
24
  // Snapshot represents a snapshot of the working directory
25
25
  type Snapshot struct {
26
- Timestamp time.Time `json:"timestamp"`
27
- Files map[string]*FileState `json:"files"`
26
+ Timestamp time.Time `json:"timestamp"`
27
+ Files map[string]*FileState `json:"files"`
28
28
  }
29
29
 
30
30
  // SnapshotPath returns the path to the snapshot file
package/storage/cache.go CHANGED
@@ -28,7 +28,7 @@ type SerializablePolicy struct {
28
28
  type Cache struct {
29
29
  FileMD5s map[string]string // filepath -> MD5 hash
30
30
  Policies map[string][]SerializablePolicy // filepath -> policies
31
- CachePath string // where the cache file is stored
31
+ CachePath string // where the cache file is stored
32
32
  }
33
33
 
34
34
  // NewCache creates a new empty cache
package/tui/puzzle.go CHANGED
@@ -34,7 +34,7 @@ type TangramPiece struct {
34
34
  Type PieceType
35
35
  Color lipgloss.Color
36
36
  Position Position
37
- Rotation int // 0, 90, 180, 270
37
+ Rotation int // 0, 90, 180, 270
38
38
  Flipped bool
39
39
  }
40
40