claude-code-templates 1.19.0 → 1.19.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.19.0",
3
+ "version": "1.19.1",
4
4
  "description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -90,6 +90,6 @@
90
90
  ],
91
91
  "devDependencies": {
92
92
  "jest": "^30.0.4",
93
- "jest-watch-typeahead": "^2.2.2"
93
+ "jest-watch-typeahead": "^3.0.1"
94
94
  }
95
95
  }
package/src/index.js CHANGED
@@ -401,8 +401,11 @@ async function installIndividualAgent(agentName, targetDir, options) {
401
401
  source: 'github_main'
402
402
  });
403
403
 
404
+ return true;
405
+
404
406
  } catch (error) {
405
407
  console.log(chalk.red(`❌ Error installing agent: ${error.message}`));
408
+ return false;
406
409
  }
407
410
  }
408
411
 
@@ -464,8 +467,11 @@ async function installIndividualCommand(commandName, targetDir, options) {
464
467
  source: 'github_main'
465
468
  });
466
469
 
470
+ return true;
471
+
467
472
  } catch (error) {
468
473
  console.log(chalk.red(`❌ Error installing command: ${error.message}`));
474
+ return false;
469
475
  }
470
476
  }
471
477
 
@@ -547,8 +553,11 @@ async function installIndividualMCP(mcpName, targetDir, options) {
547
553
  source: 'github_main'
548
554
  });
549
555
 
556
+ return true;
557
+
550
558
  } catch (error) {
551
559
  console.log(chalk.red(`❌ Error installing MCP: ${error.message}`));
560
+ return false;
552
561
  }
553
562
  }
554
563
 
@@ -712,13 +721,20 @@ async function installIndividualSetting(settingName, targetDir, options) {
712
721
  // Check for conflicting top-level settings
713
722
  Object.keys(settingConfig).forEach(key => {
714
723
  if (key !== 'permissions' && key !== 'env' && key !== 'hooks' &&
715
- existingConfig[key] !== undefined && existingConfig[key] !== settingConfig[key]) {
716
- conflicts.push(`Setting "${key}" (current: "${existingConfig[key]}", new: "${settingConfig[key]}")`);
724
+ existingConfig[key] !== undefined && JSON.stringify(existingConfig[key]) !== JSON.stringify(settingConfig[key])) {
725
+
726
+ // For objects, just indicate the setting name without showing the complex values
727
+ if (typeof existingConfig[key] === 'object' && existingConfig[key] !== null &&
728
+ typeof settingConfig[key] === 'object' && settingConfig[key] !== null) {
729
+ conflicts.push(`Setting "${key}" (will be overwritten with new configuration)`);
730
+ } else {
731
+ conflicts.push(`Setting "${key}" (current: "${existingConfig[key]}", new: "${settingConfig[key]}")`);
732
+ }
717
733
  }
718
734
  });
719
735
 
720
- // Ask user about conflicts if any exist and not in silent mode
721
- if (conflicts.length > 0 && !options.silent) {
736
+ // Ask user about conflicts if any exist
737
+ if (conflicts.length > 0) {
722
738
  console.log(chalk.yellow(`\n⚠️ Conflicts detected while installing setting "${settingName}" in ${installLocation}:`));
723
739
  conflicts.forEach(conflict => console.log(chalk.gray(` • ${conflict}`)));
724
740
 
@@ -734,10 +750,6 @@ async function installIndividualSetting(settingName, targetDir, options) {
734
750
  console.log(chalk.yellow(`⏹️ Installation of setting "${settingName}" in ${installLocation} cancelled by user.`));
735
751
  continue; // Skip this location and continue with others
736
752
  }
737
- } else if (conflicts.length > 0 && options.silent) {
738
- // In silent mode (batch installation), skip conflicting settings and warn
739
- console.log(chalk.yellow(`⚠️ Skipping setting "${settingName}" in ${installLocation} due to conflicts (use individual installation to resolve)`));
740
- continue; // Skip this location and continue with others
741
753
  }
742
754
 
743
755
  // Deep merge configurations
@@ -809,8 +821,11 @@ async function installIndividualSetting(settingName, targetDir, options) {
809
821
  }
810
822
  }
811
823
 
824
+ return successfulInstallations;
825
+
812
826
  } catch (error) {
813
827
  console.log(chalk.red(`❌ Error installing setting: ${error.message}`));
828
+ return 0;
814
829
  }
815
830
  }
816
831
 
@@ -966,8 +981,8 @@ async function installIndividualHook(hookName, targetDir, options) {
966
981
  // This is because Claude Code's array format naturally supports multiple hooks
967
982
  // Conflicts are less likely and generally hooks can coexist
968
983
 
969
- // Ask user about conflicts if any exist and not in silent mode
970
- if (conflicts.length > 0 && !options.silent) {
984
+ // Ask user about conflicts if any exist
985
+ if (conflicts.length > 0) {
971
986
  console.log(chalk.yellow(`\n⚠️ Conflicts detected while installing hook "${hookName}" in ${installLocation}:`));
972
987
  conflicts.forEach(conflict => console.log(chalk.gray(` • ${conflict}`)));
973
988
 
@@ -983,10 +998,6 @@ async function installIndividualHook(hookName, targetDir, options) {
983
998
  console.log(chalk.yellow(`⏹️ Installation of hook "${hookName}" in ${installLocation} cancelled by user.`));
984
999
  continue; // Skip this location and continue with others
985
1000
  }
986
- } else if (conflicts.length > 0 && options.silent) {
987
- // In silent mode (batch installation), skip conflicting hooks and warn
988
- console.log(chalk.yellow(`⚠️ Skipping hook "${hookName}" in ${installLocation} due to conflicts (use individual installation to resolve)`));
989
- continue; // Skip this location and continue with others
990
1001
  }
991
1002
 
992
1003
  // Deep merge configurations with proper hook array structure
@@ -1066,8 +1077,11 @@ async function installIndividualHook(hookName, targetDir, options) {
1066
1077
  }
1067
1078
  }
1068
1079
 
1080
+ return successfulInstallations;
1081
+
1069
1082
  } catch (error) {
1070
1083
  console.log(chalk.red(`❌ Error installing hook: ${error.message}`));
1084
+ return 0;
1071
1085
  }
1072
1086
  }
1073
1087
 
@@ -1208,6 +1222,9 @@ async function installMultipleComponents(options, targetDir) {
1208
1222
  console.log(chalk.gray(` Settings: ${components.settings.length}`));
1209
1223
  console.log(chalk.gray(` Hooks: ${components.hooks.length}`));
1210
1224
 
1225
+ // Counter for successfully installed components
1226
+ let successfullyInstalled = 0;
1227
+
1211
1228
  // Ask for installation locations once for configuration components (if any exist and not in silent mode)
1212
1229
  let sharedInstallLocations = ['local']; // default
1213
1230
  const hasSettingsOrHooks = components.settings.length > 0 || components.hooks.length > 0;
@@ -1253,39 +1270,44 @@ async function installMultipleComponents(options, targetDir) {
1253
1270
  // Install agents
1254
1271
  for (const agent of components.agents) {
1255
1272
  console.log(chalk.gray(` Installing agent: ${agent}`));
1256
- await installIndividualAgent(agent, targetDir, { ...options, silent: true });
1273
+ const agentSuccess = await installIndividualAgent(agent, targetDir, { ...options, silent: true });
1274
+ if (agentSuccess) successfullyInstalled++;
1257
1275
  }
1258
1276
 
1259
1277
  // Install commands
1260
1278
  for (const command of components.commands) {
1261
1279
  console.log(chalk.gray(` Installing command: ${command}`));
1262
- await installIndividualCommand(command, targetDir, { ...options, silent: true });
1280
+ const commandSuccess = await installIndividualCommand(command, targetDir, { ...options, silent: true });
1281
+ if (commandSuccess) successfullyInstalled++;
1263
1282
  }
1264
1283
 
1265
1284
  // Install MCPs
1266
1285
  for (const mcp of components.mcps) {
1267
1286
  console.log(chalk.gray(` Installing MCP: ${mcp}`));
1268
- await installIndividualMCP(mcp, targetDir, { ...options, silent: true });
1287
+ const mcpSuccess = await installIndividualMCP(mcp, targetDir, { ...options, silent: true });
1288
+ if (mcpSuccess) successfullyInstalled++;
1269
1289
  }
1270
1290
 
1271
1291
  // Install settings (using shared installation locations)
1272
1292
  for (const setting of components.settings) {
1273
1293
  console.log(chalk.gray(` Installing setting: ${setting}`));
1274
- await installIndividualSetting(setting, targetDir, {
1294
+ const settingSuccess = await installIndividualSetting(setting, targetDir, {
1275
1295
  ...options,
1276
1296
  silent: true,
1277
1297
  sharedInstallLocations: sharedInstallLocations
1278
1298
  });
1299
+ if (settingSuccess > 0) successfullyInstalled++;
1279
1300
  }
1280
1301
 
1281
1302
  // Install hooks (using shared installation locations)
1282
1303
  for (const hook of components.hooks) {
1283
1304
  console.log(chalk.gray(` Installing hook: ${hook}`));
1284
- await installIndividualHook(hook, targetDir, {
1305
+ const hookSuccess = await installIndividualHook(hook, targetDir, {
1285
1306
  ...options,
1286
1307
  silent: true,
1287
1308
  sharedInstallLocations: sharedInstallLocations
1288
1309
  });
1310
+ if (hookSuccess > 0) successfullyInstalled++;
1289
1311
  }
1290
1312
 
1291
1313
  // Handle YAML workflow if provided
@@ -1317,7 +1339,15 @@ async function installMultipleComponents(options, targetDir) {
1317
1339
  }
1318
1340
  }
1319
1341
 
1320
- console.log(chalk.green(`\n✅ Successfully installed ${totalComponents} components!`));
1342
+ if (successfullyInstalled === totalComponents) {
1343
+ console.log(chalk.green(`\n✅ Successfully installed ${successfullyInstalled} components!`));
1344
+ } else if (successfullyInstalled > 0) {
1345
+ console.log(chalk.yellow(`\n⚠️ Successfully installed ${successfullyInstalled} of ${totalComponents} components.`));
1346
+ console.log(chalk.red(`❌ ${totalComponents - successfullyInstalled} component(s) failed to install.`));
1347
+ } else {
1348
+ console.log(chalk.red(`\n❌ No components were installed successfully.`));
1349
+ return; // Exit early if nothing was installed
1350
+ }
1321
1351
  console.log(chalk.cyan(`📁 Components installed to: .claude/`));
1322
1352
 
1323
1353
  if (options.yaml) {
@@ -1325,17 +1355,7 @@ async function installMultipleComponents(options, targetDir) {
1325
1355
  console.log(chalk.cyan(`🚀 Use the workflow file with Claude Code to execute the complete setup`));
1326
1356
  }
1327
1357
 
1328
- // Track installation
1329
- trackingService.trackDownload('multi-component', 'batch', {
1330
- installation_type: 'multi-component',
1331
- agents_count: components.agents.length,
1332
- commands_count: components.commands.length,
1333
- mcps_count: components.mcps.length,
1334
- settings_count: components.settings.length,
1335
- hooks_count: components.hooks.length,
1336
- has_yaml: !!options.yaml,
1337
- target_directory: path.relative(process.cwd(), targetDir)
1338
- });
1358
+ // Note: Individual components are already tracked separately in their installation functions
1339
1359
 
1340
1360
  // Handle prompt execution if provided
1341
1361
  if (options.prompt) {
@@ -1,12 +1,10 @@
1
1
  /**
2
- * TrackingService - Download analytics using GitHub Issues as backend
2
+ * TrackingService - Anonymous download analytics using Supabase database
3
3
  * Records component installations for analytics without impacting user experience
4
4
  */
5
5
 
6
6
  class TrackingService {
7
7
  constructor() {
8
- this.repoOwner = 'davila7';
9
- this.repoName = 'claude-code-templates';
10
8
  this.trackingEnabled = this.shouldEnableTracking();
11
9
  this.timeout = 5000; // 5s timeout for tracking requests
12
10
  }
@@ -83,35 +81,20 @@ class TrackingService {
83
81
  }
84
82
 
85
83
  /**
86
- * Send tracking data via public telemetry endpoint (like Google Analytics)
84
+ * Send tracking data to database endpoint
87
85
  */
88
86
  async sendTrackingData(trackingData) {
89
87
  const controller = new AbortController();
90
88
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
91
89
 
92
90
  try {
93
- // Build query parameters for GET request (like image tracking)
94
- const params = new URLSearchParams({
95
- type: trackingData.component_type,
96
- name: trackingData.component_name,
97
- platform: trackingData.environment.platform || 'unknown',
98
- cli: trackingData.environment.cli_version || 'unknown',
99
- session: trackingData.session_id.substring(0, 8) // Only first 8 chars for privacy
100
- });
101
-
102
- // Use GitHub Pages tracking endpoint via custom domain (no auth needed)
103
- await fetch(`https://aitmpl.com/api/track.html?${params}`, {
104
- method: 'GET',
105
- mode: 'no-cors', // Prevents CORS errors
106
- signal: controller.signal
107
- });
91
+ // Send to Vercel database endpoint
92
+ await this.sendToDatabase(trackingData, controller.signal);
108
93
 
109
94
  clearTimeout(timeoutId);
110
95
 
111
- // No need to check response with no-cors mode
112
- // Only show success message when debugging
113
96
  if (process.env.CCT_DEBUG === 'true') {
114
- console.debug('📊 Download tracked successfully via telemetry');
97
+ console.debug('📊 Download tracked successfully');
115
98
  }
116
99
 
117
100
  } catch (error) {
@@ -123,6 +106,63 @@ class TrackingService {
123
106
  }
124
107
  }
125
108
 
109
+ /**
110
+ * Send tracking data to Vercel database
111
+ */
112
+ async sendToDatabase(trackingData, signal) {
113
+ try {
114
+ // Extract component path from metadata
115
+ const componentPath = trackingData.metadata?.target_directory ||
116
+ trackingData.metadata?.path ||
117
+ trackingData.component_name;
118
+
119
+ // Extract category from metadata or component name
120
+ const category = trackingData.metadata?.category ||
121
+ (trackingData.component_name.includes('/') ?
122
+ trackingData.component_name.split('/')[0] : 'general');
123
+
124
+ const payload = {
125
+ type: trackingData.component_type,
126
+ name: trackingData.component_name,
127
+ path: componentPath,
128
+ category: category,
129
+ cliVersion: trackingData.environment?.cli_version || 'unknown'
130
+ };
131
+
132
+ const response = await fetch('https://www.aitmpl.com/api/track-download-supabase', {
133
+ method: 'POST',
134
+ headers: {
135
+ 'Content-Type': 'application/json',
136
+ 'User-Agent': `claude-code-templates/${trackingData.environment?.cli_version || 'unknown'}`
137
+ },
138
+ body: JSON.stringify(payload),
139
+ signal: signal
140
+ });
141
+
142
+ if (process.env.CCT_DEBUG === 'true') {
143
+ console.debug('📊 Payload sent:', JSON.stringify(payload, null, 2));
144
+ if (response.ok) {
145
+ console.debug('📊 Successfully saved to database');
146
+ } else {
147
+ console.debug(`📊 Database save failed with status: ${response.status}`);
148
+ try {
149
+ const errorText = await response.text();
150
+ console.debug('📊 Error response:', errorText);
151
+ } catch (e) {
152
+ console.debug('📊 Could not read error response');
153
+ }
154
+ }
155
+ }
156
+
157
+ } catch (error) {
158
+ if (process.env.CCT_DEBUG === 'true') {
159
+ console.debug('📊 Database tracking failed:', error.message);
160
+ }
161
+ // Don't throw - tracking should be non-blocking
162
+ }
163
+ }
164
+
165
+
126
166
  /**
127
167
  * Generate a session ID for grouping related downloads
128
168
  */