@codemieai/code 0.0.12 → 0.0.13

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.
Files changed (124) hide show
  1. package/README.md +65 -778
  2. package/dist/agents/core/BaseAgentAdapter.d.ts.map +1 -1
  3. package/dist/agents/core/BaseAgentAdapter.js +31 -3
  4. package/dist/agents/core/BaseAgentAdapter.js.map +1 -1
  5. package/dist/agents/core/types.d.ts +18 -0
  6. package/dist/agents/core/types.d.ts.map +1 -1
  7. package/dist/agents/plugins/claude.plugin.d.ts +0 -3
  8. package/dist/agents/plugins/claude.plugin.d.ts.map +1 -1
  9. package/dist/agents/plugins/claude.plugin.js +12 -1
  10. package/dist/agents/plugins/claude.plugin.js.map +1 -1
  11. package/dist/agents/plugins/codex.plugin.d.ts.map +1 -1
  12. package/dist/agents/plugins/codex.plugin.js +40 -4
  13. package/dist/agents/plugins/codex.plugin.js.map +1 -1
  14. package/dist/agents/plugins/gemini.plugin.d.ts.map +1 -1
  15. package/dist/agents/plugins/gemini.plugin.js +50 -5
  16. package/dist/agents/plugins/gemini.plugin.js.map +1 -1
  17. package/dist/agents/registry.d.ts +15 -2
  18. package/dist/agents/registry.d.ts.map +1 -1
  19. package/dist/agents/registry.js +30 -6
  20. package/dist/agents/registry.js.map +1 -1
  21. package/dist/analytics/aggregation/adapters/claude.adapter.d.ts +37 -0
  22. package/dist/analytics/aggregation/adapters/claude.adapter.d.ts.map +1 -0
  23. package/dist/analytics/aggregation/adapters/claude.adapter.js +471 -0
  24. package/dist/analytics/aggregation/adapters/claude.adapter.js.map +1 -0
  25. package/dist/analytics/aggregation/adapters/codex.adapter.d.ts +25 -0
  26. package/dist/analytics/aggregation/adapters/codex.adapter.d.ts.map +1 -0
  27. package/dist/analytics/aggregation/adapters/codex.adapter.js +376 -0
  28. package/dist/analytics/aggregation/adapters/codex.adapter.js.map +1 -0
  29. package/dist/analytics/aggregation/adapters/gemini.adapter.d.ts +28 -0
  30. package/dist/analytics/aggregation/adapters/gemini.adapter.d.ts.map +1 -0
  31. package/dist/analytics/aggregation/adapters/gemini.adapter.js +320 -0
  32. package/dist/analytics/aggregation/adapters/gemini.adapter.js.map +1 -0
  33. package/dist/analytics/aggregation/adapters/index.d.ts +7 -0
  34. package/dist/analytics/aggregation/adapters/index.d.ts.map +1 -0
  35. package/dist/analytics/aggregation/adapters/index.js +7 -0
  36. package/dist/analytics/aggregation/adapters/index.js.map +1 -0
  37. package/dist/analytics/aggregation/aggregator.d.ts +49 -0
  38. package/dist/analytics/aggregation/aggregator.d.ts.map +1 -0
  39. package/dist/analytics/aggregation/aggregator.js +239 -0
  40. package/dist/analytics/aggregation/aggregator.js.map +1 -0
  41. package/dist/analytics/aggregation/core/BaseAnalyticsAdapter.d.ts +63 -0
  42. package/dist/analytics/aggregation/core/BaseAnalyticsAdapter.d.ts.map +1 -0
  43. package/dist/analytics/aggregation/core/BaseAnalyticsAdapter.js +58 -0
  44. package/dist/analytics/aggregation/core/BaseAnalyticsAdapter.js.map +1 -0
  45. package/dist/analytics/aggregation/core/adapter.interface.d.ts +65 -0
  46. package/dist/analytics/aggregation/core/adapter.interface.d.ts.map +1 -0
  47. package/dist/analytics/aggregation/core/adapter.interface.js +9 -0
  48. package/dist/analytics/aggregation/core/adapter.interface.js.map +1 -0
  49. package/dist/analytics/aggregation/core/aggregation-utils.d.ts +66 -0
  50. package/dist/analytics/aggregation/core/aggregation-utils.d.ts.map +1 -0
  51. package/dist/analytics/aggregation/core/aggregation-utils.js +83 -0
  52. package/dist/analytics/aggregation/core/aggregation-utils.js.map +1 -0
  53. package/dist/analytics/aggregation/core/discovery.d.ts +40 -0
  54. package/dist/analytics/aggregation/core/discovery.d.ts.map +1 -0
  55. package/dist/analytics/aggregation/core/discovery.js +132 -0
  56. package/dist/analytics/aggregation/core/discovery.js.map +1 -0
  57. package/dist/analytics/aggregation/core/file-utils.d.ts +23 -0
  58. package/dist/analytics/aggregation/core/file-utils.d.ts.map +1 -0
  59. package/dist/analytics/aggregation/core/file-utils.js +208 -0
  60. package/dist/analytics/aggregation/core/file-utils.js.map +1 -0
  61. package/dist/analytics/aggregation/core/index.d.ts +11 -0
  62. package/dist/analytics/aggregation/core/index.d.ts.map +1 -0
  63. package/dist/analytics/aggregation/core/index.js +11 -0
  64. package/dist/analytics/aggregation/core/index.js.map +1 -0
  65. package/dist/analytics/aggregation/core/project-mapping.d.ts +50 -0
  66. package/dist/analytics/aggregation/core/project-mapping.d.ts.map +1 -0
  67. package/dist/analytics/aggregation/core/project-mapping.js +102 -0
  68. package/dist/analytics/aggregation/core/project-mapping.js.map +1 -0
  69. package/dist/analytics/aggregation/core/streaming.d.ts +26 -0
  70. package/dist/analytics/aggregation/core/streaming.d.ts.map +1 -0
  71. package/dist/analytics/aggregation/core/streaming.js +58 -0
  72. package/dist/analytics/aggregation/core/streaming.js.map +1 -0
  73. package/dist/analytics/aggregation/index.d.ts +8 -0
  74. package/dist/analytics/aggregation/index.d.ts.map +1 -0
  75. package/dist/analytics/aggregation/index.js +8 -0
  76. package/dist/analytics/aggregation/index.js.map +1 -0
  77. package/dist/analytics/aggregation/types.d.ts +258 -0
  78. package/dist/analytics/aggregation/types.d.ts.map +1 -0
  79. package/dist/analytics/aggregation/types.js +8 -0
  80. package/dist/analytics/aggregation/types.js.map +1 -0
  81. package/dist/analytics/config.d.ts +1 -0
  82. package/dist/analytics/config.d.ts.map +1 -1
  83. package/dist/analytics/config.js +20 -1
  84. package/dist/analytics/config.js.map +1 -1
  85. package/dist/analytics/index.d.ts +0 -9
  86. package/dist/analytics/index.d.ts.map +1 -1
  87. package/dist/analytics/index.js +14 -48
  88. package/dist/analytics/index.js.map +1 -1
  89. package/dist/analytics/plugins/api-metrics.plugin.d.ts +4 -1
  90. package/dist/analytics/plugins/api-metrics.plugin.d.ts.map +1 -1
  91. package/dist/analytics/plugins/api-metrics.plugin.js +6 -22
  92. package/dist/analytics/plugins/api-metrics.plugin.js.map +1 -1
  93. package/dist/analytics/plugins/model-metrics.plugin.d.ts +4 -1
  94. package/dist/analytics/plugins/model-metrics.plugin.d.ts.map +1 -1
  95. package/dist/analytics/plugins/model-metrics.plugin.js +7 -17
  96. package/dist/analytics/plugins/model-metrics.plugin.js.map +1 -1
  97. package/dist/analytics/plugins/provider-metrics.plugin.d.ts +4 -1
  98. package/dist/analytics/plugins/provider-metrics.plugin.d.ts.map +1 -1
  99. package/dist/analytics/plugins/provider-metrics.plugin.js +7 -22
  100. package/dist/analytics/plugins/provider-metrics.plugin.js.map +1 -1
  101. package/dist/analytics/plugins/types.d.ts +0 -2
  102. package/dist/analytics/plugins/types.d.ts.map +1 -1
  103. package/dist/analytics/plugins/types.js.map +1 -1
  104. package/dist/analytics/session.d.ts +2 -2
  105. package/dist/analytics/session.d.ts.map +1 -1
  106. package/dist/analytics/session.js +3 -3
  107. package/dist/analytics/session.js.map +1 -1
  108. package/dist/analytics/types.d.ts +1 -1
  109. package/dist/analytics/types.d.ts.map +1 -1
  110. package/dist/cli/commands/analytics.d.ts.map +1 -1
  111. package/dist/cli/commands/analytics.js +512 -252
  112. package/dist/cli/commands/analytics.js.map +1 -1
  113. package/dist/cli/commands/setup.js +70 -1
  114. package/dist/cli/commands/setup.js.map +1 -1
  115. package/dist/utils/codemie-proxy.d.ts.map +1 -1
  116. package/dist/utils/codemie-proxy.js +16 -3
  117. package/dist/utils/codemie-proxy.js.map +1 -1
  118. package/dist/utils/proxy/http-client.d.ts.map +1 -1
  119. package/dist/utils/proxy/http-client.js +8 -2
  120. package/dist/utils/proxy/http-client.js.map +1 -1
  121. package/dist/utils/proxy/interceptors.d.ts.map +1 -1
  122. package/dist/utils/proxy/interceptors.js +4 -15
  123. package/dist/utils/proxy/interceptors.js.map +1 -1
  124. package/package.json +6 -2
@@ -1,43 +1,20 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import inquirer from 'inquirer';
3
+ import Table from 'cli-table3';
4
4
  import { loadAnalyticsConfig } from '../../analytics/config.js';
5
5
  import { logger } from '../../utils/logger.js';
6
- import { readAnalyticsForLocalDate, readAnalyticsForLocalDateRange, filterEvents, calculateStats, exportToCSV, exportToJSON, listAnalyticsFiles, clearOldAnalytics, formatFileSize } from '../../utils/analytics-reader.js';
7
- import { getLocalToday, getDefaultLocalDateRange, getLocalDateString } from '../../utils/date-formatter.js';
8
6
  import { AgentRegistry } from '../../agents/registry.js';
7
+ import { ConfigLoader } from '../../utils/config-loader.js';
8
+ import { CodemieAnalyticsAggregator } from '../../analytics/aggregation/index.js';
9
9
  export function createAnalyticsCommand() {
10
10
  const command = new Command('analytics');
11
11
  command
12
12
  .description('Analytics management and insights')
13
- .addCommand(createStatusCommand())
14
- .addCommand(createStatsCommand())
15
- .addCommand(createExportCommand())
16
- .addCommand(createClearCommand());
17
- return command;
18
- }
19
- function createStatusCommand() {
20
- const command = new Command('status');
21
- command
22
- .description('Show analytics configuration and today\'s statistics')
23
- .option('--json', 'Output as JSON')
24
- .action(async (options) => {
13
+ .action(async () => {
25
14
  try {
26
15
  const config = loadAnalyticsConfig();
27
- // JSON output
28
- if (options.json) {
29
- const today = getLocalToday();
30
- const todayEvents = await readAnalyticsForLocalDate(today);
31
- const stats = todayEvents.length > 0 ? calculateStats(todayEvents) : null;
32
- console.log(JSON.stringify({
33
- config,
34
- todayStats: stats
35
- }, null, 2));
36
- return;
37
- }
38
- // Human-readable output
16
+ // Show configuration
39
17
  console.log(chalk.bold.cyan('\n📊 Analytics Configuration\n'));
40
- // Configuration
41
18
  console.log(chalk.cyan('Status: ') + (config.enabled ? chalk.green('Enabled') : chalk.red('Disabled')));
42
19
  console.log(chalk.cyan('Target: ') + chalk.white(config.target));
43
20
  console.log(chalk.cyan('Local Path: ') + chalk.white(config.localPath));
@@ -46,274 +23,557 @@ function createStatusCommand() {
46
23
  }
47
24
  console.log(chalk.cyan('Flush Interval: ') + chalk.white(`${config.flushInterval}ms`));
48
25
  console.log(chalk.cyan('Buffer Size: ') + chalk.white(`${config.maxBufferSize} events`));
49
- // Today's statistics
50
- console.log(chalk.bold.cyan('\n📈 Today\'s Statistics\n'));
51
- const today = getLocalToday();
52
- const todayEvents = await readAnalyticsForLocalDate(today);
53
- if (todayEvents.length === 0) {
54
- console.log(chalk.white('No analytics data for today yet.\n'));
55
- return;
56
- }
57
- const stats = calculateStats(todayEvents);
58
- console.log(chalk.cyan('Sessions: ') + chalk.white(stats.totalSessions));
59
- console.log(chalk.cyan('API Calls: ') + chalk.white(stats.apiRequests));
60
- console.log(chalk.cyan('Success Rate: ') + chalk.white(`${stats.successRate.toFixed(1)}%`));
61
- console.log(chalk.cyan('Avg Latency: ') + chalk.white(`${Math.round(stats.avgLatency)}ms`));
62
- if (Object.keys(stats.agentUsage).length > 0) {
63
- console.log(chalk.bold.cyan('\nAgent Activity:\n'));
64
- Object.entries(stats.agentUsage)
65
- .sort(([, a], [, b]) => b.sessions - a.sessions)
66
- .slice(0, 5)
67
- .forEach(([, usage]) => {
68
- console.log(` ${chalk.white(usage.displayName.padEnd(20))} ${chalk.white(usage.sessions)} sessions, ${chalk.white(usage.apiCalls)} API calls`);
69
- });
70
- }
26
+ // Show help
27
+ console.log(chalk.bold.cyan('\n📋 Available Commands\n'));
28
+ console.log(chalk.white(' codemie analytics enable ') + chalk.gray('Enable analytics collection'));
29
+ console.log(chalk.white(' codemie analytics disable ') + chalk.gray('Disable analytics collection'));
30
+ console.log(chalk.white(' codemie analytics show ') + chalk.gray('Show analytics from all agents'));
71
31
  console.log();
72
32
  }
73
33
  catch (error) {
74
- logger.error('Failed to show analytics status:', error);
34
+ logger.error('Failed to show analytics configuration:', error);
75
35
  process.exit(1);
76
36
  }
77
- });
37
+ })
38
+ .addCommand(createEnableCommand())
39
+ .addCommand(createDisableCommand())
40
+ .addCommand(createShowCommand());
78
41
  return command;
79
42
  }
80
- function createStatsCommand() {
81
- const command = new Command('stats');
82
- const defaultRange = getDefaultLocalDateRange(7);
43
+ function createEnableCommand() {
44
+ const command = new Command('enable');
83
45
  command
84
- .description('Show analytics statistics')
85
- .option('--from <date>', 'Start date (YYYY-MM-DD, local timezone)', defaultRange.from)
86
- .option('--to <date>', 'End date (YYYY-MM-DD, local timezone)', defaultRange.to)
87
- .option('--agent <name>', 'Filter by agent name')
88
- .action(async (options) => {
46
+ .description('Enable analytics collection')
47
+ .action(async () => {
89
48
  try {
90
- // Validate agent filter against registry
91
- if (options.agent) {
92
- const agentNames = AgentRegistry.getAgentNames();
93
- if (!agentNames.includes(options.agent)) {
94
- const availableAgents = AgentRegistry.getAllAgents()
95
- .map(a => `${a.name} (${a.displayName})`)
96
- .join(', ');
97
- logger.error(`Unknown agent: ${options.agent}`);
98
- console.log(chalk.yellow(`\nAvailable agents: ${availableAgents}\n`));
99
- process.exit(1);
100
- }
101
- }
102
- console.log(chalk.bold.cyan('\n📊 Analytics Statistics\n'));
103
- console.log(chalk.white(`Date Range: ${options.from} to ${options.to} (local timezone)`));
104
- if (options.agent) {
105
- const adapter = AgentRegistry.getAgent(options.agent);
106
- const displayName = adapter?.displayName || options.agent;
107
- console.log(chalk.white(`Agent: ${displayName}`));
108
- }
109
- console.log();
110
- // Read events using local dates
111
- const events = await readAnalyticsForLocalDateRange(options.from, options.to);
112
- if (events.length === 0) {
113
- console.log(chalk.yellow('No analytics data found for the specified period.\n'));
114
- return;
115
- }
116
- // Apply filters
117
- const filters = {};
118
- if (options.agent) {
119
- filters.agent = options.agent;
120
- }
121
- const filteredEvents = filterEvents(events, filters);
122
- if (filteredEvents.length === 0) {
123
- console.log(chalk.yellow('No events match the specified filters.\n'));
124
- return;
125
- }
126
- // Calculate statistics
127
- const stats = calculateStats(filteredEvents);
128
- // Display overview
129
- console.log(chalk.bold.cyan('📋 Overview\n'));
130
- console.log(chalk.cyan('Sessions: ') + chalk.white(stats.totalSessions));
131
- console.log(chalk.cyan('API Calls: ') + chalk.white(stats.apiRequests));
132
- if (stats.apiErrors > 0) {
133
- console.log(chalk.cyan('API Errors: ') + chalk.red(stats.apiErrors));
134
- }
135
- // Display performance
136
- console.log(chalk.bold.cyan('\n⚡ Performance\n'));
137
- console.log(chalk.cyan('Avg Latency: ') + chalk.white(`${Math.round(stats.avgLatency)}ms`));
138
- console.log(chalk.cyan('Success Rate: ') + chalk.white(`${stats.successRate.toFixed(1)}%`));
139
- // Display agent usage
140
- if (Object.keys(stats.agentUsage).length > 0) {
141
- console.log(chalk.bold.cyan('\n🤖 Agent Usage\n'));
142
- const totalApiCalls = Object.values(stats.agentUsage).reduce((sum, usage) => sum + usage.apiCalls, 0);
143
- Object.entries(stats.agentUsage)
144
- .sort(([, a], [, b]) => b.apiCalls - a.apiCalls)
145
- .forEach(([, usage]) => {
146
- const percentage = totalApiCalls > 0 ? ((usage.apiCalls / totalApiCalls) * 100).toFixed(1) : '0.0';
147
- console.log(` ${chalk.white(usage.displayName.padEnd(20))} ${chalk.white(usage.sessions.toString().padStart(3))} sessions ${chalk.white(usage.apiCalls.toString().padStart(4))} API calls (${percentage}%)`);
148
- });
149
- }
150
- // Display model usage
151
- if (Object.keys(stats.modelUsage).length > 0) {
152
- console.log(chalk.bold.cyan('\n🎯 Model Usage\n'));
153
- const totalApiCalls = Object.values(stats.modelUsage).reduce((sum, usage) => sum + usage.apiCalls, 0);
154
- Object.entries(stats.modelUsage)
155
- .sort(([, a], [, b]) => b.apiCalls - a.apiCalls)
156
- .slice(0, 10)
157
- .forEach(([model, usage]) => {
158
- const percentage = ((usage.apiCalls / totalApiCalls) * 100).toFixed(1);
159
- console.log(` ${chalk.white(model.padEnd(30))} ${chalk.white(usage.apiCalls.toString().padStart(4))} calls (${percentage}%)`);
160
- });
161
- }
49
+ await updateAnalyticsEnabled(true);
50
+ logger.success('Analytics enabled');
51
+ console.log(chalk.white('Analytics data will be collected to:'));
52
+ console.log(chalk.cyan(' ~/.codemie/analytics/'));
162
53
  console.log();
163
54
  }
164
55
  catch (error) {
165
- logger.error('Failed to show analytics stats:', error);
56
+ logger.error('Failed to enable analytics:', error);
166
57
  process.exit(1);
167
58
  }
168
59
  });
169
60
  return command;
170
61
  }
171
- function createExportCommand() {
172
- const command = new Command('export');
173
- const defaultRange = getDefaultLocalDateRange(7);
62
+ function createDisableCommand() {
63
+ const command = new Command('disable');
174
64
  command
175
- .description('Export analytics data')
176
- .option('--format <format>', 'Export format (csv, json)', 'csv')
177
- .option('--output <path>', 'Output file path', getDefaultExportPath())
178
- .option('--from <date>', 'Start date (YYYY-MM-DD, local timezone)', defaultRange.from)
179
- .option('--to <date>', 'End date (YYYY-MM-DD, local timezone)', defaultRange.to)
180
- .option('--agent <name>', 'Filter by agent name')
181
- .option('--event-type <type>', 'Filter by event type')
182
- .action(async (options) => {
65
+ .description('Disable analytics collection')
66
+ .action(async () => {
183
67
  try {
184
- // Validate format
185
- if (!['csv', 'json'].includes(options.format)) {
186
- logger.error('Invalid format. Use "csv" or "json".');
187
- process.exit(1);
188
- }
189
- // Validate agent filter against registry
190
- if (options.agent) {
191
- const agentNames = AgentRegistry.getAgentNames();
192
- if (!agentNames.includes(options.agent)) {
193
- const availableAgents = AgentRegistry.getAllAgents()
194
- .map(a => `${a.name} (${a.displayName})`)
195
- .join(', ');
196
- logger.error(`Unknown agent: ${options.agent}`);
197
- console.log(chalk.yellow(`\nAvailable agents: ${availableAgents}\n`));
198
- process.exit(1);
199
- }
200
- }
201
- console.log(chalk.bold.cyan('\n📤 Exporting Analytics Data\n'));
202
- console.log(chalk.white(`Date Range: ${options.from} to ${options.to} (local timezone)`));
203
- console.log(chalk.white(`Format: ${options.format.toUpperCase()}`));
204
- if (options.agent) {
205
- const adapter = AgentRegistry.getAgent(options.agent);
206
- const displayName = adapter?.displayName || options.agent;
207
- console.log(chalk.white(`Agent Filter: ${displayName}`));
208
- }
209
- if (options.eventType) {
210
- console.log(chalk.white(`Event Type Filter: ${options.eventType}`));
211
- }
212
- console.log();
213
- // Read events using local dates
214
- const events = await readAnalyticsForLocalDateRange(options.from, options.to);
215
- if (events.length === 0) {
216
- console.log(chalk.yellow('No analytics data found for the specified period.\n'));
217
- return;
218
- }
219
- // Apply filters
220
- const filters = {};
221
- if (options.agent) {
222
- filters.agent = options.agent;
223
- }
224
- if (options.eventType) {
225
- filters.eventType = options.eventType;
226
- }
227
- const filteredEvents = filterEvents(events, filters);
228
- if (filteredEvents.length === 0) {
229
- console.log(chalk.yellow('No events match the specified filters.\n'));
230
- return;
231
- }
232
- // Export
233
- console.log(chalk.white(`Exporting ${filteredEvents.length} analytics records...`));
234
- if (options.format === 'csv') {
235
- await exportToCSV(filteredEvents, options.output);
236
- }
237
- else {
238
- await exportToJSON(filteredEvents, options.output);
239
- }
240
- logger.success(`Exported to ${options.output}`);
241
- console.log(chalk.white(`Total records: ${filteredEvents.length}`));
68
+ await updateAnalyticsEnabled(false);
69
+ logger.success('Analytics disabled');
70
+ console.log(chalk.white('No analytics data will be collected.'));
242
71
  console.log();
243
72
  }
244
73
  catch (error) {
245
- logger.error('Failed to export analytics:', error);
74
+ logger.error('Failed to disable analytics:', error);
246
75
  process.exit(1);
247
76
  }
248
77
  });
249
78
  return command;
250
79
  }
251
- function createClearCommand() {
252
- const command = new Command('clear');
80
+ function createShowCommand() {
81
+ const command = new Command('show');
253
82
  command
254
- .description('Clear old analytics files')
255
- .option('--older-than <days>', 'Delete files older than N days', '30')
256
- .option('-y, --yes', 'Skip confirmation')
83
+ .description('Show analytics from all agents (Gemini, Claude, Codex, etc.)')
84
+ .option('--from <date>', 'Start date (YYYY-MM-DD)')
85
+ .option('--to <date>', 'End date (YYYY-MM-DD)')
86
+ .option('--agent <name>', 'Filter by agent (gemini|claude|codex|codemie-code)')
87
+ .option('--project <path>', 'Filter by project path')
88
+ .option('--format <format>', 'Output format (json|table)', 'table')
89
+ .option('--output <file>', 'Output file (for JSON format)')
257
90
  .action(async (options) => {
258
91
  try {
259
- const days = parseInt(options.olderThan, 10);
260
- if (isNaN(days) || days < 1) {
261
- logger.error('Invalid number of days. Must be a positive integer.');
92
+ // Check if analytics is disabled
93
+ const config = loadAnalyticsConfig();
94
+ if (!config.enabled) {
95
+ console.log(chalk.yellow('\nAnalytics is disabled. No data is being collected.\n'));
96
+ return;
97
+ }
98
+ console.log(chalk.bold.cyan('\n📊 CodeMie Analytics Aggregation\n'));
99
+ // Parse dates
100
+ const dateFrom = options.from ? new Date(options.from) : undefined;
101
+ const dateTo = options.to ? new Date(options.to) : undefined;
102
+ // Validate dates
103
+ if (dateFrom && isNaN(dateFrom.getTime())) {
104
+ logger.error('Invalid start date format. Use YYYY-MM-DD');
262
105
  process.exit(1);
263
106
  }
264
- console.log(chalk.bold.cyan('\n🗑️ Clear Old Analytics\n'));
265
- // List files that would be deleted
266
- const files = await listAnalyticsFiles();
267
- const threshold = new Date();
268
- threshold.setDate(threshold.getDate() - days);
269
- const filesToDelete = files.filter(file => new Date(file.date) < threshold);
270
- if (filesToDelete.length === 0) {
271
- console.log(chalk.white(`No analytics files older than ${days} days.\n`));
272
- return;
107
+ if (dateTo && isNaN(dateTo.getTime())) {
108
+ logger.error('Invalid end date format. Use YYYY-MM-DD');
109
+ process.exit(1);
273
110
  }
274
- const totalSize = filesToDelete.reduce((sum, file) => sum + file.size, 0);
275
- console.log(chalk.white(`Found ${filesToDelete.length} file(s) older than ${days} days:`));
276
- console.log();
277
- filesToDelete.forEach(file => {
278
- // Display file date (UTC) with local date for reference
279
- const localDate = getLocalDateString(new Date(file.date + 'T12:00:00.000Z'));
280
- const dateDisplay = file.date === localDate ? file.date : `${file.date} (local: ${localDate})`;
281
- console.log(` ${chalk.white(dateDisplay.padEnd(30))} ${chalk.white(formatFileSize(file.size).padStart(10))} ${chalk.white(file.events)} events`);
111
+ // Validate agent if specified
112
+ if (options.agent) {
113
+ const validAgents = AgentRegistry.getAgentNames();
114
+ if (!validAgents.includes(options.agent)) {
115
+ logger.error(`Invalid agent: ${options.agent}`);
116
+ console.log(chalk.white('Available agents:'));
117
+ validAgents.forEach(name => {
118
+ const adapter = AgentRegistry.getAgent(name);
119
+ console.log(` ${chalk.cyan(name.padEnd(15))} ${chalk.white(adapter?.displayName || '')}`);
120
+ });
121
+ process.exit(1);
122
+ }
123
+ }
124
+ // Create aggregator and fetch sessions
125
+ const aggregator = new CodemieAnalyticsAggregator();
126
+ const sessions = await aggregator.aggregateSessions({
127
+ dateFrom,
128
+ dateTo,
129
+ agent: options.agent,
130
+ projectPath: options.project
282
131
  });
283
- console.log();
284
- console.log(chalk.white(`Total space to be freed: ${formatFileSize(totalSize)}`));
285
- console.log();
286
- // Confirm deletion
287
- if (!options.yes) {
288
- const { confirm } = await inquirer.prompt([
289
- {
290
- type: 'confirm',
291
- name: 'confirm',
292
- message: 'Are you sure you want to delete these files?',
293
- default: false
132
+ if (sessions.length === 0) {
133
+ console.log(chalk.yellow('No sessions found matching the criteria.\n'));
134
+ return;
135
+ }
136
+ // Output based on format
137
+ if (options.format === 'json') {
138
+ const output = {
139
+ version: '1.0.0',
140
+ exportedAt: new Date().toISOString(),
141
+ filters: {
142
+ dateFrom: dateFrom?.toISOString(),
143
+ dateTo: dateTo?.toISOString(),
144
+ agent: options.agent,
145
+ projectPath: options.project
146
+ },
147
+ sessions
148
+ };
149
+ if (options.output) {
150
+ const { writeFile } = await import('node:fs/promises');
151
+ await writeFile(options.output, JSON.stringify(output, null, 2));
152
+ logger.success(`Exported to ${options.output}`);
153
+ }
154
+ else {
155
+ console.log(JSON.stringify(output, null, 2));
156
+ }
157
+ }
158
+ else {
159
+ // Table format
160
+ console.log(chalk.white(`Found ${chalk.cyan(sessions.length.toString())} sessions\n`));
161
+ // Show date range of found sessions
162
+ if (sessions.length > 0) {
163
+ const startDates = sessions.map(s => s.startTime.getTime());
164
+ const endDates = sessions.map(s => s.endTime?.getTime() || s.startTime.getTime());
165
+ const earliestDate = new Date(Math.min(...startDates));
166
+ const latestDate = new Date(Math.max(...endDates));
167
+ console.log(chalk.bold.white('📅 Date Range\n'));
168
+ const dateRangeTable = new Table({
169
+ head: [chalk.cyan('Period'), chalk.cyan('Date')],
170
+ style: {
171
+ head: [],
172
+ border: ['grey']
173
+ },
174
+ colWidths: [25, 60]
175
+ });
176
+ dateRangeTable.push([chalk.white('From'), chalk.white(earliestDate.toLocaleString())], [chalk.white('To'), chalk.white(latestDate.toLocaleString())]);
177
+ console.log(dateRangeTable.toString());
178
+ console.log();
179
+ }
180
+ // Calculate summary statistics
181
+ const totalMessages = sessions.reduce((sum, s) => sum + s.userMessageCount + s.assistantMessageCount, 0);
182
+ const totalTokens = sessions.reduce((sum, s) => sum + s.tokens.total, 0);
183
+ const totalInputTokens = sessions.reduce((sum, s) => sum + s.tokens.input, 0);
184
+ const totalOutputTokens = sessions.reduce((sum, s) => sum + s.tokens.output, 0);
185
+ const totalCacheRead = sessions.reduce((sum, s) => sum + s.tokens.cacheRead, 0);
186
+ const totalCacheCreation = sessions.reduce((sum, s) => sum + s.tokens.cacheCreation, 0);
187
+ const totalThoughts = sessions.reduce((sum, s) => sum + (s.tokens.thoughts || 0), 0);
188
+ const totalReasoning = sessions.reduce((sum, s) => sum + (s.tokens.reasoning || 0), 0);
189
+ const totalToolTokens = sessions.reduce((sum, s) => sum + (s.tokens.tool || 0), 0);
190
+ const totalToolCalls = sessions.reduce((sum, s) => sum + s.toolCallCount, 0);
191
+ const totalFileModifications = sessions.reduce((sum, s) => sum + s.fileModifications, 0);
192
+ // 1. SUMMARY - Sessions & Messages
193
+ console.log(chalk.bold.white('📈 Summary Statistics\n'));
194
+ const summaryTable = new Table({
195
+ head: [chalk.cyan('Metric'), chalk.cyan('Value')],
196
+ style: {
197
+ head: [],
198
+ border: ['grey']
199
+ },
200
+ colWidths: [25, 60]
201
+ });
202
+ const totalPrompts = sessions.reduce((sum, s) => sum + s.userMessageCount, 0);
203
+ const totalResponses = sessions.reduce((sum, s) => sum + s.assistantMessageCount, 0);
204
+ summaryTable.push([chalk.white('Sessions'), chalk.white(sessions.length.toString())], [chalk.white('Messages'), chalk.white(`${totalMessages.toLocaleString()} (${totalPrompts.toLocaleString()} prompts + ${totalResponses.toLocaleString()} responses)`)]);
205
+ console.log(summaryTable.toString());
206
+ console.log();
207
+ // 2. Code Generation Statistics
208
+ const sessionsWithFileStats = sessions.filter(s => s.fileStats);
209
+ if (sessionsWithFileStats.length > 0) {
210
+ const totalLinesGenerated = sessionsWithFileStats.reduce((sum, s) => sum + (s.fileStats?.totalLinesAdded || 0), 0);
211
+ const totalLinesRemoved = sessionsWithFileStats.reduce((sum, s) => sum + (s.fileStats?.totalLinesRemoved || 0), 0);
212
+ const totalFilesCreated = sessionsWithFileStats.reduce((sum, s) => sum + (s.fileStats?.filesCreated || 0), 0);
213
+ const totalFilesModified = sessionsWithFileStats.reduce((sum, s) => sum + (s.fileStats?.filesModified || 0), 0);
214
+ const netLines = totalLinesGenerated - totalLinesRemoved;
215
+ console.log(chalk.bold.white('📝 Code Generation\n'));
216
+ const codeGenTable = new Table({
217
+ head: [chalk.cyan('Metric'), chalk.cyan('Value')],
218
+ style: {
219
+ head: [],
220
+ border: ['grey']
221
+ },
222
+ colWidths: [25, 60]
223
+ });
224
+ codeGenTable.push([chalk.white('Lines Generated'), chalk.white(totalLinesGenerated.toLocaleString())]);
225
+ if (totalLinesRemoved > 0) {
226
+ codeGenTable.push([chalk.white('Lines Removed'), chalk.white(totalLinesRemoved.toLocaleString())], [chalk.white('Net Lines'), chalk.white(netLines.toLocaleString())]);
227
+ }
228
+ codeGenTable.push([chalk.white('Files Created'), chalk.white(totalFilesCreated.toString())], [chalk.white('Files Modified'), chalk.white(totalFilesModified.toString())], [chalk.white('File Modifications'), chalk.white(totalFileModifications.toString())]);
229
+ console.log(codeGenTable.toString());
230
+ console.log();
231
+ }
232
+ // 3. Token Breakdown
233
+ console.log(chalk.bold.white('💰 Token Usage\n'));
234
+ const tokenTable = new Table({
235
+ head: [chalk.cyan('Type'), chalk.cyan('Tokens')],
236
+ style: {
237
+ head: [],
238
+ border: ['grey']
239
+ },
240
+ colWidths: [25, 60]
241
+ });
242
+ tokenTable.push([chalk.white('Total'), chalk.white(totalTokens.toLocaleString())], [chalk.white('Input'), chalk.white(totalInputTokens.toLocaleString())], [chalk.white('Output'), chalk.white(totalOutputTokens.toLocaleString())], [chalk.white('Cache Read'), chalk.white(totalCacheRead.toLocaleString())]);
243
+ if (totalCacheCreation > 0) {
244
+ tokenTable.push([chalk.white('Cache Creation'), chalk.white(totalCacheCreation.toLocaleString())]);
245
+ }
246
+ if (totalThoughts > 0) {
247
+ tokenTable.push([chalk.white('Thoughts'), chalk.white(totalThoughts.toLocaleString())]);
248
+ }
249
+ if (totalReasoning > 0) {
250
+ tokenTable.push([chalk.white('Reasoning'), chalk.white(totalReasoning.toLocaleString())]);
251
+ }
252
+ if (totalToolTokens > 0) {
253
+ tokenTable.push([chalk.white('Tool'), chalk.white(totalToolTokens.toLocaleString())]);
254
+ }
255
+ console.log(tokenTable.toString());
256
+ console.log();
257
+ // 4. BY AGENT - Agent breakdown
258
+ const byAgent = {};
259
+ for (const session of sessions) {
260
+ if (!byAgent[session.agent]) {
261
+ const adapter = AgentRegistry.getAgent(session.agent);
262
+ byAgent[session.agent] = {
263
+ sessions: 0,
264
+ tokens: 0,
265
+ prompts: 0,
266
+ apiCalls: 0,
267
+ displayName: adapter?.displayName || session.agent
268
+ };
269
+ }
270
+ byAgent[session.agent].sessions++;
271
+ byAgent[session.agent].tokens += session.tokens.total;
272
+ byAgent[session.agent].prompts += session.userMessageCount;
273
+ // Count API calls from model usage
274
+ byAgent[session.agent].apiCalls += Object.values(session.modelUsage).reduce((sum, count) => sum + count, 0);
275
+ }
276
+ console.log(chalk.bold.white('🤖 Breakdown by Agent\n'));
277
+ const sortedAgents = Object.entries(byAgent)
278
+ .sort((a, b) => b[1].sessions - a[1].sessions);
279
+ // Create table for agent breakdown
280
+ const agentTable = new Table({
281
+ head: [chalk.cyan('Agent'), chalk.cyan('Prompts'), chalk.cyan('Sessions'), chalk.cyan('API Calls'), chalk.cyan('Share')],
282
+ style: {
283
+ head: [],
284
+ border: ['grey']
285
+ }
286
+ });
287
+ for (const [, stats] of sortedAgents) {
288
+ const percentage = ((stats.sessions / sessions.length) * 100).toFixed(1);
289
+ agentTable.push([
290
+ chalk.white(stats.displayName),
291
+ chalk.white(stats.prompts.toString()),
292
+ chalk.white(stats.sessions.toString()),
293
+ chalk.white(stats.apiCalls.toString()),
294
+ chalk.white(`${percentage}%`)
295
+ ]);
296
+ }
297
+ console.log(agentTable.toString());
298
+ console.log();
299
+ // Show model usage breakdown
300
+ const modelBreakdown = {};
301
+ for (const session of sessions) {
302
+ for (const [modelName, count] of Object.entries(session.modelUsage)) {
303
+ modelBreakdown[modelName] = (modelBreakdown[modelName] || 0) + count;
304
+ }
305
+ }
306
+ if (Object.keys(modelBreakdown).length > 0) {
307
+ const totalModelCalls = Object.values(modelBreakdown).reduce((sum, count) => sum + count, 0);
308
+ console.log(chalk.bold.white(' Models:\n'));
309
+ const sortedModels = Object.entries(modelBreakdown)
310
+ .sort((a, b) => b[1] - a[1]);
311
+ // Create table for models
312
+ const modelTable = new Table({
313
+ head: [chalk.cyan('Model'), chalk.cyan('Calls'), chalk.cyan('Share')],
314
+ style: {
315
+ head: [],
316
+ border: ['grey']
317
+ },
318
+ colWidths: [55, 12, 12]
319
+ });
320
+ for (const [modelName, count] of sortedModels) {
321
+ const percentage = ((count / totalModelCalls) * 100).toFixed(1);
322
+ modelTable.push([
323
+ chalk.white(modelName),
324
+ chalk.white(count.toString()),
325
+ chalk.white(`${percentage}%`)
326
+ ]);
327
+ }
328
+ console.log(' ' + modelTable.toString().split('\n').join('\n '));
329
+ console.log();
330
+ }
331
+ // 5. TOOL USAGE
332
+ if (totalToolCalls > 0) {
333
+ const toolBreakdown = {};
334
+ const toolStatusBreakdown = {};
335
+ for (const session of sessions) {
336
+ for (const [toolName, count] of Object.entries(session.toolUsage)) {
337
+ toolBreakdown[toolName] = (toolBreakdown[toolName] || 0) + count;
338
+ }
339
+ for (const [toolName, status] of Object.entries(session.toolStatus)) {
340
+ if (!toolStatusBreakdown[toolName]) {
341
+ toolStatusBreakdown[toolName] = { success: 0, failure: 0 };
342
+ }
343
+ toolStatusBreakdown[toolName].success += status.success;
344
+ toolStatusBreakdown[toolName].failure += status.failure;
345
+ }
346
+ }
347
+ if (Object.keys(toolBreakdown).length > 0) {
348
+ console.log(chalk.bold.white('🔧 Tool Usage\n'));
349
+ const sortedTools = Object.entries(toolBreakdown)
350
+ .sort((a, b) => b[1] - a[1]);
351
+ // Create table for tools
352
+ const toolTable = new Table({
353
+ head: [chalk.cyan('Tool'), chalk.cyan('Calls'), chalk.cyan('Share'), chalk.cyan('Status'), chalk.cyan('Success Rate')],
354
+ style: {
355
+ head: [],
356
+ border: ['grey']
357
+ },
358
+ colWidths: [20, 10, 8, 20, 15]
359
+ });
360
+ for (const [toolName, count] of sortedTools) {
361
+ const percentage = ((count / totalToolCalls) * 100).toFixed(1);
362
+ const status = toolStatusBreakdown[toolName];
363
+ const successRate = status ? ((status.success / count) * 100).toFixed(1) : '0.0';
364
+ // Build status text with color coding
365
+ let statusText;
366
+ let successRateText;
367
+ if (status && status.failure > 0 && status.success === 0) {
368
+ // Critical: 0% success rate with failures - RED
369
+ statusText = chalk.red(`(${status.success} ✓, ${status.failure} ✗)`);
370
+ successRateText = chalk.red(`${successRate}%`);
371
+ }
372
+ else if (status && status.failure > 0) {
373
+ // Warning: some failures but has successes - YELLOW
374
+ statusText = chalk.yellow(`(${status.success} ✓, ${status.failure} ✗)`);
375
+ successRateText = chalk.yellow(`${successRate}%`);
376
+ }
377
+ else {
378
+ // Success: no failures - GREEN
379
+ statusText = chalk.green(`(${status?.success || 0} ✓)`);
380
+ successRateText = chalk.green(`${successRate}%`);
381
+ }
382
+ toolTable.push([
383
+ chalk.white(toolName),
384
+ chalk.white(count.toString()),
385
+ chalk.white(`${percentage}%`),
386
+ statusText,
387
+ successRateText
388
+ ]);
389
+ }
390
+ console.log(toolTable.toString());
391
+ console.log();
392
+ }
393
+ }
394
+ // 6. BREAKDOWN BY PROJECT
395
+ const byProject = {};
396
+ for (const session of sessions) {
397
+ const projectPath = session.projectPath || 'other';
398
+ if (!byProject[projectPath]) {
399
+ byProject[projectPath] = {
400
+ sessions: 0,
401
+ tokens: 0,
402
+ linesGenerated: 0,
403
+ filesCreated: 0,
404
+ filesModified: 0,
405
+ languageStats: {},
406
+ formatStats: {}
407
+ };
408
+ }
409
+ const project = byProject[projectPath];
410
+ project.sessions++;
411
+ project.tokens += session.tokens.total;
412
+ project.linesGenerated += session.fileStats?.totalLinesAdded || 0;
413
+ project.filesCreated += session.fileStats?.filesCreated || 0;
414
+ project.filesModified += session.fileStats?.filesModified || 0;
415
+ // Aggregate language stats for this project
416
+ // We track unique sessions per language using Set
417
+ if (session.fileStats?.byLanguage) {
418
+ for (const [lang, stats] of Object.entries(session.fileStats.byLanguage)) {
419
+ if (!project.languageStats[lang]) {
420
+ project.languageStats[lang] = {
421
+ sessionIds: new Set(),
422
+ tokens: 0,
423
+ lines: 0,
424
+ filesCreated: 0,
425
+ filesModified: 0
426
+ };
427
+ }
428
+ // Track unique session ID
429
+ project.languageStats[lang].sessionIds.add(session.sessionId);
430
+ // Attribute tokens proportionally based on lines generated
431
+ const totalLinesInSession = session.fileStats.totalLinesAdded || 1;
432
+ const tokenShare = (stats.linesAdded / totalLinesInSession) * session.tokens.total;
433
+ project.languageStats[lang].tokens += tokenShare;
434
+ project.languageStats[lang].lines += stats.linesAdded;
435
+ project.languageStats[lang].filesCreated += stats.filesCreated;
436
+ project.languageStats[lang].filesModified += stats.filesModified;
437
+ }
438
+ }
439
+ // Aggregate format stats for this project
440
+ if (session.fileStats?.byFormat) {
441
+ for (const [format, stats] of Object.entries(session.fileStats.byFormat)) {
442
+ if (!project.formatStats[format]) {
443
+ project.formatStats[format] = {
444
+ sessionIds: new Set(),
445
+ tokens: 0,
446
+ lines: 0,
447
+ filesCreated: 0,
448
+ filesModified: 0
449
+ };
450
+ }
451
+ // Track unique session ID
452
+ project.formatStats[format].sessionIds.add(session.sessionId);
453
+ // Attribute tokens proportionally based on lines generated
454
+ const totalLinesInSession = session.fileStats.totalLinesAdded || 1;
455
+ const tokenShare = (stats.linesAdded / totalLinesInSession) * session.tokens.total;
456
+ project.formatStats[format].tokens += tokenShare;
457
+ project.formatStats[format].lines += stats.linesAdded;
458
+ project.formatStats[format].filesCreated += stats.filesCreated;
459
+ project.formatStats[format].filesModified += stats.filesModified;
460
+ }
461
+ }
462
+ }
463
+ if (Object.keys(byProject).length > 0) {
464
+ console.log(chalk.bold.white('📁 Breakdown by Project\n'));
465
+ const sortedProjects = Object.entries(byProject)
466
+ .sort((a, b) => b[1].linesGenerated - a[1].linesGenerated);
467
+ for (const [projectPath, stats] of sortedProjects) {
468
+ const totalLinesInProjects = Object.values(byProject).reduce((sum, p) => sum + p.linesGenerated, 0);
469
+ const percentage = totalLinesInProjects > 0 ? ((stats.linesGenerated / totalLinesInProjects) * 100).toFixed(1) : '0.0';
470
+ console.log(chalk.cyan(`\n ${projectPath}\n`));
471
+ // Project summary table
472
+ const projectSummaryTable = new Table({
473
+ head: [chalk.cyan('Sessions'), chalk.cyan('Tokens'), chalk.cyan('Lines'), chalk.cyan('Created'), chalk.cyan('Modified'), chalk.cyan('Share')],
474
+ style: {
475
+ head: [],
476
+ border: ['grey']
477
+ },
478
+ colWidths: [12, 16, 10, 11, 12, 10]
479
+ });
480
+ projectSummaryTable.push([
481
+ chalk.white(stats.sessions.toString()),
482
+ chalk.white(stats.tokens.toLocaleString()),
483
+ chalk.white(stats.linesGenerated.toLocaleString()),
484
+ chalk.white(stats.filesCreated.toString()),
485
+ chalk.white(stats.filesModified.toString()),
486
+ chalk.white(`${percentage}%`)
487
+ ]);
488
+ console.log(' ' + projectSummaryTable.toString().split('\n').join('\n '));
489
+ // Show language breakdown for this project
490
+ if (Object.keys(stats.languageStats).length > 0) {
491
+ console.log(chalk.white('\n By Language:\n'));
492
+ const sortedLangs = Object.entries(stats.languageStats)
493
+ .sort((a, b) => b[1].lines - a[1].lines)
494
+ .slice(0, 10);
495
+ const langTable = new Table({
496
+ head: [chalk.cyan('Language'), chalk.cyan('Sessions'), chalk.cyan('Tokens'), chalk.cyan('Lines'), chalk.cyan('Created'), chalk.cyan('Modified'), chalk.cyan('Share')],
497
+ style: {
498
+ head: [],
499
+ border: ['grey']
500
+ },
501
+ colWidths: [18, 12, 16, 10, 11, 12, 9]
502
+ });
503
+ for (const [lang, langStats] of sortedLangs) {
504
+ const langPercentage = stats.linesGenerated > 0 ? ((langStats.lines / stats.linesGenerated) * 100).toFixed(1) : '0.0';
505
+ const sessionCount = langStats.sessionIds.size;
506
+ langTable.push([
507
+ chalk.white(lang),
508
+ chalk.white(sessionCount.toString()),
509
+ chalk.white(Math.round(langStats.tokens).toLocaleString()),
510
+ chalk.white(langStats.lines.toLocaleString()),
511
+ chalk.white(langStats.filesCreated.toString()),
512
+ chalk.white(langStats.filesModified.toString()),
513
+ chalk.white(`${langPercentage}%`)
514
+ ]);
515
+ }
516
+ console.log(' ' + langTable.toString().split('\n').join('\n '));
517
+ }
518
+ // Show format breakdown for this project
519
+ if (Object.keys(stats.formatStats).length > 0) {
520
+ console.log(chalk.white('\n By Format:\n'));
521
+ const sortedFormats = Object.entries(stats.formatStats)
522
+ .sort((a, b) => b[1].lines - a[1].lines)
523
+ .slice(0, 10);
524
+ const formatTable = new Table({
525
+ head: [chalk.cyan('Format'), chalk.cyan('Sessions'), chalk.cyan('Tokens'), chalk.cyan('Lines'), chalk.cyan('Created'), chalk.cyan('Modified'), chalk.cyan('Share')],
526
+ style: {
527
+ head: [],
528
+ border: ['grey']
529
+ },
530
+ colWidths: [18, 12, 16, 10, 11, 12, 9]
531
+ });
532
+ for (const [format, formatStats] of sortedFormats) {
533
+ const formatPercentage = stats.linesGenerated > 0 ? ((formatStats.lines / stats.linesGenerated) * 100).toFixed(1) : '0.0';
534
+ const sessionCount = formatStats.sessionIds.size;
535
+ formatTable.push([
536
+ chalk.white(format),
537
+ chalk.white(sessionCount.toString()),
538
+ chalk.white(Math.round(formatStats.tokens).toLocaleString()),
539
+ chalk.white(formatStats.lines.toLocaleString()),
540
+ chalk.white(formatStats.filesCreated.toString()),
541
+ chalk.white(formatStats.filesModified.toString()),
542
+ chalk.white(`${formatPercentage}%`)
543
+ ]);
544
+ }
545
+ console.log(' ' + formatTable.toString().split('\n').join('\n '));
546
+ }
547
+ console.log();
294
548
  }
295
- ]);
296
- if (!confirm) {
297
- console.log(chalk.yellow('\nDeletion cancelled.\n'));
298
- return;
299
549
  }
300
550
  }
301
- // Delete files
302
- const deletedFiles = await clearOldAnalytics(days);
303
- logger.success(`Deleted ${deletedFiles.length} file(s)`);
304
- console.log(chalk.white(`Freed ${formatFileSize(totalSize)}`));
305
- console.log();
306
551
  }
307
552
  catch (error) {
308
- logger.error('Failed to clear analytics:', error);
553
+ logger.error('Failed to aggregate analytics:', error);
309
554
  process.exit(1);
310
555
  }
311
556
  });
312
557
  return command;
313
558
  }
314
559
  // Helper functions
315
- function getDefaultExportPath() {
316
- const date = getLocalToday();
317
- return `analytics-${date}.csv`;
560
+ async function updateAnalyticsEnabled(enabled) {
561
+ // Load current multi-provider config
562
+ const config = await ConfigLoader.loadMultiProviderConfig();
563
+ // Update or create analytics config
564
+ if (!config.analytics) {
565
+ config.analytics = {
566
+ enabled,
567
+ target: 'local',
568
+ localPath: '~/.codemie/analytics',
569
+ flushInterval: 5000,
570
+ maxBufferSize: 100
571
+ };
572
+ }
573
+ else {
574
+ config.analytics.enabled = enabled;
575
+ }
576
+ // Save updated config
577
+ await ConfigLoader.saveMultiProviderConfig(config);
318
578
  }
319
579
  //# sourceMappingURL=analytics.js.map