@tanagram/cli 0.5.54 → 0.5.56

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.
Binary file
Binary file
Binary file
Binary file
Binary file
package/install.js CHANGED
@@ -10,10 +10,9 @@ const pkg = require('./package.json');
10
10
  const POSTHOG_KEY = 'phc_sMsUvf0nK50rZdztSlX9rDJqIreLcXj4dyGS0tORQpQ';
11
11
  const POSTHOG_HOST = 'phe.tanagram.ai';
12
12
 
13
- // Generate anonymous distinct ID based on machine
13
+ // Generate anonymous distinct ID based on machine (must match Go CLI convention in metrics/metrics.go)
14
14
  function getDistinctId() {
15
- const machineId = os.hostname() + os.userInfo().username;
16
- return crypto.createHash('sha256').update(machineId).digest('hex').slice(0, 16);
15
+ return 'cli_' + os.hostname();
17
16
  }
18
17
 
19
18
  // Track event to PostHog
@@ -263,7 +262,7 @@ function ensureOpenCode() {
263
262
  const isFirstTimeUser = !fs.existsSync(tanagramDir);
264
263
 
265
264
  // Track install start
266
- track('cli.install.start', { first_time: isFirstTimeUser });
265
+ track('cli.install.start', { first_time: isFirstTimeUser, install_method: 'npm' });
267
266
 
268
267
  try {
269
268
  // Find and install prebuilt binary
@@ -280,12 +279,12 @@ function ensureOpenCode() {
280
279
  ensureOpenCode();
281
280
 
282
281
  // Track install success
283
- track('cli.install.success');
282
+ track('cli.install.success', { install_method: 'npm' });
284
283
 
285
284
  process.exit(0);
286
285
  } catch (error) {
287
286
  console.error('\n❌ Installation failed:', error.message);
288
- track('cli.install.failure', { error: error.message });
287
+ track('cli.install.failure', { error: error.message, install_method: 'npm' });
289
288
  process.exit(1);
290
289
  }
291
290
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.5.54",
3
+ "version": "0.5.56",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "postinstall": "node install.js",
11
+ "preuninstall": "node uninstall.js",
11
12
  "test": "go test ./..."
12
13
  },
13
14
  "keywords": [
@@ -33,6 +34,7 @@
33
34
  "bin/tanagram.js",
34
35
  "dist/npm/",
35
36
  "install.js",
37
+ "uninstall.js",
36
38
  "skills/",
37
39
  "README.md",
38
40
  "LICENSE"
package/uninstall.js ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const https = require('https');
7
+ const crypto = require('crypto');
8
+ const pkg = require('./package.json');
9
+
10
+ const POSTHOG_KEY = 'phc_sMsUvf0nK50rZdztSlX9rDJqIreLcXj4dyGS0tORQpQ';
11
+ const POSTHOG_HOST = 'phe.tanagram.ai';
12
+
13
+ function getDistinctId() {
14
+ const machineId = os.hostname() + os.userInfo().username;
15
+ return crypto.createHash('sha256').update(machineId).digest('hex').slice(0, 16);
16
+ }
17
+
18
+ function track(event, properties = {}) {
19
+ if (isCIEnvironment()) return;
20
+
21
+ const data = JSON.stringify({
22
+ api_key: POSTHOG_KEY,
23
+ event: event,
24
+ properties: {
25
+ distinct_id: getDistinctId(),
26
+ version: pkg.version,
27
+ platform: os.platform(),
28
+ arch: os.arch(),
29
+ node_version: process.version,
30
+ ...properties
31
+ },
32
+ timestamp: new Date().toISOString()
33
+ });
34
+
35
+ const req = https.request({
36
+ hostname: POSTHOG_HOST,
37
+ port: 443,
38
+ path: '/capture/',
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ 'Content-Length': data.length
43
+ }
44
+ });
45
+
46
+ req.on('error', () => {});
47
+ req.write(data);
48
+ req.end();
49
+ }
50
+
51
+ function isCIEnvironment() {
52
+ return (
53
+ process.env.CI === 'true' ||
54
+ process.env.GITHUB_ACTIONS === 'true' ||
55
+ process.env.BUILDKITE === 'true' ||
56
+ process.env.GITLAB_CI === 'true' ||
57
+ process.env.TF_BUILD === 'True'
58
+ );
59
+ }
60
+
61
+ function removeClaudeSkill() {
62
+ const skillsDir = path.join(os.homedir(), '.claude', 'skills', 'tanagram');
63
+
64
+ try {
65
+ if (fs.existsSync(skillsDir)) {
66
+ fs.rmSync(skillsDir, { recursive: true });
67
+ console.error('✓ Tanagram skill removed');
68
+ track('cli.skill.uninstall.success');
69
+ }
70
+ } catch (err) {
71
+ console.error('Warning: Failed to remove Tanagram skill:', err.message);
72
+ track('cli.skill.uninstall.failure', { error: err.message });
73
+ }
74
+ }
75
+
76
+ function removeStopHook() {
77
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
78
+
79
+ try {
80
+ if (!fs.existsSync(settingsPath)) return;
81
+
82
+ const raw = fs.readFileSync(settingsPath, 'utf8');
83
+ const settings = JSON.parse(raw);
84
+
85
+ if (!settings.hooks || !Array.isArray(settings.hooks.Stop)) return;
86
+
87
+ const before = settings.hooks.Stop.length;
88
+ settings.hooks.Stop = settings.hooks.Stop.filter(entry =>
89
+ !(Array.isArray(entry.hooks) && entry.hooks.some(h => h.command === 'tanagram stop-hook'))
90
+ );
91
+ const after = settings.hooks.Stop.length;
92
+
93
+ if (before === after) return;
94
+
95
+ // Clean up empty arrays/objects
96
+ if (settings.hooks.Stop.length === 0) {
97
+ delete settings.hooks.Stop;
98
+ }
99
+ if (Object.keys(settings.hooks).length === 0) {
100
+ delete settings.hooks;
101
+ }
102
+
103
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
104
+ console.error('✓ Tanagram Stop hook removed');
105
+ track('cli.stop_hook.uninstall.success');
106
+ } catch (err) {
107
+ console.error('Warning: Failed to remove Stop hook:', err.message);
108
+ track('cli.stop_hook.uninstall.failure', { error: err.message });
109
+ }
110
+ }
111
+
112
+ function removeOpenCode() {
113
+ if (isCIEnvironment()) return;
114
+
115
+ const { execSync } = require('child_process');
116
+
117
+ try {
118
+ execSync('opencode --version', { stdio: 'ignore' });
119
+ } catch {
120
+ // Not installed, nothing to do
121
+ return;
122
+ }
123
+
124
+ try {
125
+ execSync('npm uninstall -g opencode-ai', { stdio: 'pipe' });
126
+ console.error('✓ OpenCode uninstalled');
127
+ track('cli.opencode.uninstall.success');
128
+ } catch (err) {
129
+ console.error('Warning: Failed to uninstall OpenCode:', err.message);
130
+ track('cli.opencode.uninstall.failure', { error: err.message });
131
+ }
132
+ }
133
+
134
+ // Main uninstall flow
135
+ track('cli.uninstall.start');
136
+
137
+ removeClaudeSkill();
138
+ removeStopHook();
139
+ removeOpenCode();
140
+
141
+ track('cli.uninstall.success');
142
+ console.error('✓ Tanagram CLI uninstalled successfully');