claude-code-templates 1.26.2 → 1.26.4

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.26.2",
3
+ "version": "1.26.4",
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": {
@@ -1187,6 +1187,12 @@ class ChatsMobile {
1187
1187
  * Stop the server
1188
1188
  */
1189
1189
  async stop() {
1190
+ // Prevent multiple stop calls
1191
+ if (this.isStopped) {
1192
+ return;
1193
+ }
1194
+ this.isStopped = true;
1195
+
1190
1196
  if (this.cloudflaredProcess) {
1191
1197
  try {
1192
1198
  this.cloudflaredProcess.kill('SIGTERM');
@@ -1195,26 +1201,28 @@ class ChatsMobile {
1195
1201
  this.log('warn', chalk.yellow('⚠️ Error stopping Cloudflare Tunnel:', error.message));
1196
1202
  }
1197
1203
  }
1198
-
1204
+
1199
1205
  if (this.webSocketServer) {
1200
1206
  try {
1207
+ console.log(chalk.gray('🔌 Closing WebSocket server...'));
1201
1208
  await this.webSocketServer.close();
1202
- this.log('info', chalk.gray('🌐 WebSocket server stopped'));
1209
+ console.log(chalk.green(' WebSocket server closed'));
1203
1210
  } catch (error) {
1204
1211
  this.log('warn', chalk.yellow('⚠️ Error stopping WebSocket server:', error.message));
1205
1212
  }
1206
1213
  }
1207
-
1214
+
1208
1215
  if (this.httpServer) {
1209
1216
  await new Promise((resolve) => {
1210
1217
  this.httpServer.close(resolve);
1211
1218
  });
1212
1219
  }
1213
-
1220
+
1214
1221
  if (this.fileWatcher) {
1222
+ console.log(chalk.gray('🛑 Stopping file watchers...'));
1215
1223
  await this.fileWatcher.stop();
1216
1224
  }
1217
-
1225
+
1218
1226
  console.log(chalk.gray('🛑 Chats Mobile server stopped'));
1219
1227
  }
1220
1228
  }
@@ -1245,14 +1253,35 @@ async function startChatsMobile(options = {}) {
1245
1253
  }
1246
1254
 
1247
1255
  console.log(chalk.gray('Press Ctrl+C to stop'));
1248
-
1249
- // Handle graceful shutdown
1250
- process.on('SIGINT', async () => {
1256
+
1257
+ // Handle graceful shutdown - remove existing listeners first to prevent duplicates
1258
+ const shutdownHandler = async () => {
1259
+ if (chatsMobile.isShuttingDown) return; // Prevent multiple shutdown attempts
1260
+ chatsMobile.isShuttingDown = true;
1261
+
1251
1262
  console.log(chalk.yellow('\n🛑 Shutting down...'));
1252
- await chatsMobile.stop();
1253
- process.exit(0);
1254
- });
1255
-
1263
+
1264
+ // Remove this specific handler to prevent it from being called again
1265
+ process.removeListener('SIGINT', shutdownHandler);
1266
+ process.removeListener('SIGTERM', shutdownHandler);
1267
+
1268
+ try {
1269
+ await chatsMobile.stop();
1270
+ process.exit(0);
1271
+ } catch (error) {
1272
+ console.error(chalk.red('❌ Error during shutdown:'), error);
1273
+ process.exit(1);
1274
+ }
1275
+ };
1276
+
1277
+ // Remove any existing SIGINT/SIGTERM listeners to prevent duplicates
1278
+ process.removeAllListeners('SIGINT');
1279
+ process.removeAllListeners('SIGTERM');
1280
+
1281
+ // Add the new handler
1282
+ process.on('SIGINT', shutdownHandler);
1283
+ process.on('SIGTERM', shutdownHandler);
1284
+
1256
1285
  } catch (error) {
1257
1286
  console.error(chalk.red('❌ Failed to start Chats Mobile:'), error);
1258
1287
  process.exit(1);
package/src/index.js CHANGED
@@ -126,6 +126,10 @@ async function createClaudeConfig(options = {}) {
126
126
 
127
127
  // Handle sandbox execution FIRST (before individual components)
128
128
  if (options.sandbox) {
129
+ trackingService.trackCommandExecution('sandbox', {
130
+ provider: options.sandbox,
131
+ hasPrompt: !!options.prompt
132
+ });
129
133
  await executeSandbox(options, targetDir);
130
134
  return;
131
135
  }
@@ -174,24 +178,28 @@ async function createClaudeConfig(options = {}) {
174
178
 
175
179
  // Handle command stats analysis (both singular and plural)
176
180
  if (options.commandStats || options.commandsStats) {
181
+ trackingService.trackCommandExecution('command-stats');
177
182
  await runCommandStats(options);
178
183
  return;
179
184
  }
180
-
185
+
181
186
  // Handle hook stats analysis (both singular and plural)
182
187
  if (options.hookStats || options.hooksStats) {
188
+ trackingService.trackCommandExecution('hook-stats');
183
189
  await runHookStats(options);
184
190
  return;
185
191
  }
186
-
192
+
187
193
  // Handle MCP stats analysis (both singular and plural)
188
194
  if (options.mcpStats || options.mcpsStats) {
195
+ trackingService.trackCommandExecution('mcp-stats');
189
196
  await runMCPStats(options);
190
197
  return;
191
198
  }
192
199
 
193
200
  // Handle analytics dashboard
194
201
  if (options.analytics) {
202
+ trackingService.trackCommandExecution('analytics', { tunnel: options.tunnel || false });
195
203
  trackingService.trackAnalyticsDashboard({ page: 'dashboard', source: 'command_line' });
196
204
  await runAnalytics(options);
197
205
  return;
@@ -199,6 +207,7 @@ async function createClaudeConfig(options = {}) {
199
207
 
200
208
  // Handle plugin dashboard
201
209
  if (options.plugins) {
210
+ trackingService.trackCommandExecution('plugins');
202
211
  trackingService.trackAnalyticsDashboard({ page: 'plugins', source: 'command_line' });
203
212
  await runPluginDashboard(options);
204
213
  return;
@@ -206,20 +215,23 @@ async function createClaudeConfig(options = {}) {
206
215
 
207
216
  // Handle chats dashboard (now points to mobile chats interface)
208
217
  if (options.chats) {
218
+ trackingService.trackCommandExecution('chats', { tunnel: options.tunnel || false });
209
219
  trackingService.trackAnalyticsDashboard({ page: 'chats-mobile', source: 'command_line' });
210
220
  await startChatsMobile(options);
211
221
  return;
212
222
  }
213
-
223
+
214
224
  // Handle agents dashboard (separate from chats)
215
225
  if (options.agents) {
226
+ trackingService.trackCommandExecution('agents', { tunnel: options.tunnel || false });
216
227
  trackingService.trackAnalyticsDashboard({ page: 'agents', source: 'command_line' });
217
228
  await runAnalytics({ ...options, openTo: 'agents' });
218
229
  return;
219
230
  }
220
-
231
+
221
232
  // Handle mobile chats interface
222
233
  if (options.chatsMobile) {
234
+ trackingService.trackCommandExecution('chats-mobile', { tunnel: options.tunnel || false });
223
235
  trackingService.trackAnalyticsDashboard({ page: 'chats-mobile', source: 'command_line' });
224
236
  await startChatsMobile(options);
225
237
  return;
@@ -269,8 +281,9 @@ async function createClaudeConfig(options = {}) {
269
281
  // Handle health check
270
282
  let shouldRunSetup = false;
271
283
  if (options.healthCheck || options.health || options.check || options.verify) {
284
+ trackingService.trackCommandExecution('health-check');
272
285
  const healthResult = await runHealthCheck();
273
-
286
+
274
287
  // Track health check usage
275
288
  trackingService.trackHealthCheck({
276
289
  setup_recommended: healthResult.runSetup,
@@ -214,6 +214,78 @@ class TrackingService {
214
214
  ...metadata
215
215
  });
216
216
  }
217
+
218
+ /**
219
+ * Track CLI command execution
220
+ * @param {string} commandName - Command name (chats, analytics, health-check, plugins, sandbox, etc.)
221
+ * @param {object} metadata - Additional context (optional)
222
+ */
223
+ async trackCommandExecution(commandName, metadata = {}) {
224
+ if (!this.trackingEnabled) {
225
+ return;
226
+ }
227
+
228
+ try {
229
+ const payload = {
230
+ command: commandName,
231
+ cliVersion: this.getCliVersion(),
232
+ nodeVersion: process.version,
233
+ platform: process.platform,
234
+ arch: process.arch,
235
+ sessionId: this.generateSessionId(),
236
+ metadata: metadata
237
+ };
238
+
239
+ // Fire-and-forget to Neon Database
240
+ this.sendCommandTracking(payload)
241
+ .catch(error => {
242
+ if (process.env.CCT_DEBUG === 'true') {
243
+ console.debug('📊 Command tracking info (non-critical):', error.message);
244
+ }
245
+ });
246
+
247
+ } catch (error) {
248
+ if (process.env.CCT_DEBUG === 'true') {
249
+ console.debug('📊 Command tracking error (non-critical):', error.message);
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Send command tracking to Neon Database
256
+ */
257
+ async sendCommandTracking(payload) {
258
+ const controller = new AbortController();
259
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
260
+
261
+ try {
262
+ const response = await fetch('https://www.aitmpl.com/api/track-command-usage', {
263
+ method: 'POST',
264
+ headers: {
265
+ 'Content-Type': 'application/json',
266
+ 'User-Agent': `claude-code-templates/${payload.cliVersion}`
267
+ },
268
+ body: JSON.stringify(payload),
269
+ signal: controller.signal
270
+ });
271
+
272
+ clearTimeout(timeoutId);
273
+
274
+ if (process.env.CCT_DEBUG === 'true') {
275
+ if (response.ok) {
276
+ console.debug('📊 Command execution tracked successfully');
277
+ } else {
278
+ console.debug(`📊 Command tracking failed with status: ${response.status}`);
279
+ }
280
+ }
281
+
282
+ } catch (error) {
283
+ clearTimeout(timeoutId);
284
+ if (process.env.CCT_DEBUG === 'true') {
285
+ console.debug('📊 Command tracking failed (non-critical):', error.message);
286
+ }
287
+ }
288
+ }
217
289
  }
218
290
 
219
291
  // Export singleton instance