neuro-cli 4.1.0

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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. package/package.json +83 -0
@@ -0,0 +1,157 @@
1
+ export interface TelemetryConfig {
2
+ /** Whether telemetry is enabled (opt-in, default: false) */
3
+ enabled: boolean;
4
+ /** Storage directory for telemetry data */
5
+ dataDir: string;
6
+ /** Whether to include model performance metrics */
7
+ trackModelPerformance: boolean;
8
+ /** Whether to include tool usage stats */
9
+ trackToolUsage: boolean;
10
+ /** Whether to include session metrics */
11
+ trackSessionMetrics: boolean;
12
+ /** Retention period in days (default: 90) */
13
+ retentionDays: number;
14
+ /** Anonymous session ID hash (no PII) */
15
+ anonymousId: string;
16
+ }
17
+ export interface SessionMetric {
18
+ sessionId: string;
19
+ duration: number;
20
+ messageCount: number;
21
+ modelUsed: string;
22
+ inputTokens: number;
23
+ outputTokens: number;
24
+ cost: number;
25
+ toolsCalled: number;
26
+ timestamp: number;
27
+ }
28
+ export interface ToolUsageMetric {
29
+ toolName: string;
30
+ callCount: number;
31
+ successCount: number;
32
+ errorCount: number;
33
+ avgDurationMs: number;
34
+ lastUsed: number;
35
+ }
36
+ export interface ModelPerformanceMetric {
37
+ model: string;
38
+ requestCount: number;
39
+ totalInputTokens: number;
40
+ totalOutputTokens: number;
41
+ totalCost: number;
42
+ avgLatencyMs: number;
43
+ errorCount: number;
44
+ p50LatencyMs: number;
45
+ p95LatencyMs: number;
46
+ p99LatencyMs: number;
47
+ }
48
+ export interface TelemetryReport {
49
+ generatedAt: number;
50
+ period: {
51
+ start: number;
52
+ end: number;
53
+ };
54
+ totalSessions: number;
55
+ totalMessages: number;
56
+ totalTokens: {
57
+ input: number;
58
+ output: number;
59
+ };
60
+ totalCost: number;
61
+ toolUsage: ToolUsageMetric[];
62
+ modelPerformance: ModelPerformanceMetric[];
63
+ sessionMetrics: SessionMetric[];
64
+ }
65
+ export declare class TelemetrySystem {
66
+ private config;
67
+ private sessionMetrics;
68
+ private toolUsageMap;
69
+ private modelPerformanceMap;
70
+ private currentSessionStart;
71
+ private currentSessionMessages;
72
+ private currentSessionTools;
73
+ private currentSessionModel;
74
+ private currentSessionInputTokens;
75
+ private currentSessionOutputTokens;
76
+ private currentSessionCost;
77
+ constructor(config?: Partial<TelemetryConfig>);
78
+ /**
79
+ * Check if telemetry is enabled
80
+ */
81
+ isEnabled(): boolean;
82
+ /**
83
+ * Enable telemetry (opt-in)
84
+ */
85
+ enable(): void;
86
+ /**
87
+ * Disable telemetry
88
+ */
89
+ disable(): void;
90
+ /**
91
+ * Toggle telemetry on/off
92
+ */
93
+ toggle(): boolean;
94
+ /**
95
+ * Record a session start
96
+ */
97
+ startSession(sessionId: string, model: string): void;
98
+ /**
99
+ * Record a message in the current session
100
+ */
101
+ recordMessage(): void;
102
+ /**
103
+ * Record a tool call
104
+ */
105
+ recordToolCall(toolName: string, durationMs: number, success: boolean): void;
106
+ /**
107
+ * Record model performance data
108
+ */
109
+ recordModelPerformance(model: string, inputTokens: number, outputTokens: number, cost: number, latencyMs: number, error: boolean): void;
110
+ /**
111
+ * End the current session and record its metrics
112
+ */
113
+ endSession(sessionId: string): void;
114
+ /**
115
+ * Get all tool usage metrics
116
+ */
117
+ getToolUsageMetrics(): ToolUsageMetric[];
118
+ /**
119
+ * Get model performance metrics
120
+ */
121
+ getModelPerformanceMetrics(): ModelPerformanceMetric[];
122
+ /**
123
+ * Get session metrics
124
+ */
125
+ getSessionMetrics(): SessionMetric[];
126
+ /**
127
+ * Generate a full telemetry report
128
+ */
129
+ generateReport(startTimestamp?: number, endTimestamp?: number): TelemetryReport;
130
+ /**
131
+ * Export telemetry data as JSON string
132
+ */
133
+ exportJSON(pretty?: boolean): string;
134
+ /**
135
+ * Export telemetry data to a file
136
+ */
137
+ exportToFile(filePath: string): void;
138
+ /**
139
+ * Print a summary of telemetry data
140
+ */
141
+ printSummary(): void;
142
+ /**
143
+ * Clear all telemetry data
144
+ */
145
+ clearData(): void;
146
+ /**
147
+ * Get current config
148
+ */
149
+ getConfig(): TelemetryConfig;
150
+ private percentile;
151
+ private ensureDataDir;
152
+ private persistConfig;
153
+ private persistData;
154
+ private loadPersistedData;
155
+ private cleanupOldData;
156
+ }
157
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1,412 @@
1
+ // ============================================================
2
+ // NeuroCLI - Telemetry System
3
+ // Anonymous usage tracking (opt-in), session metrics,
4
+ // tool usage, model performance. Privacy-first: no PII.
5
+ // ============================================================
6
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { homedir } from 'os';
9
+ import chalk from 'chalk';
10
+ // -----------------------------------------------------------
11
+ // Internal helpers
12
+ // -----------------------------------------------------------
13
+ const DEFAULT_DATA_DIR = join(homedir(), '.neuro', 'telemetry');
14
+ function generateAnonymousId() {
15
+ const chars = 'abcdef0123456789';
16
+ let id = '';
17
+ for (let i = 0; i < 16; i++) {
18
+ id += chars[Math.floor(Math.random() * chars.length)];
19
+ }
20
+ return id;
21
+ }
22
+ function defaultConfig() {
23
+ return {
24
+ enabled: false,
25
+ dataDir: DEFAULT_DATA_DIR,
26
+ trackModelPerformance: true,
27
+ trackToolUsage: true,
28
+ trackSessionMetrics: true,
29
+ retentionDays: 90,
30
+ anonymousId: generateAnonymousId(),
31
+ };
32
+ }
33
+ // -----------------------------------------------------------
34
+ // TelemetrySystem
35
+ // -----------------------------------------------------------
36
+ export class TelemetrySystem {
37
+ config;
38
+ sessionMetrics = [];
39
+ toolUsageMap = new Map();
40
+ modelPerformanceMap = new Map();
41
+ currentSessionStart = Date.now();
42
+ currentSessionMessages = 0;
43
+ currentSessionTools = 0;
44
+ currentSessionModel = '';
45
+ currentSessionInputTokens = 0;
46
+ currentSessionOutputTokens = 0;
47
+ currentSessionCost = 0;
48
+ constructor(config) {
49
+ this.config = { ...defaultConfig(), ...config };
50
+ this.ensureDataDir();
51
+ this.loadPersistedData();
52
+ }
53
+ // ----------------------------------------------------------
54
+ // Public API
55
+ // ----------------------------------------------------------
56
+ /**
57
+ * Check if telemetry is enabled
58
+ */
59
+ isEnabled() {
60
+ return this.config.enabled;
61
+ }
62
+ /**
63
+ * Enable telemetry (opt-in)
64
+ */
65
+ enable() {
66
+ this.config.enabled = true;
67
+ this.persistConfig();
68
+ console.log(chalk.green('Telemetry enabled. No PII is collected.'));
69
+ }
70
+ /**
71
+ * Disable telemetry
72
+ */
73
+ disable() {
74
+ this.config.enabled = false;
75
+ this.persistConfig();
76
+ console.log(chalk.gray('Telemetry disabled.'));
77
+ }
78
+ /**
79
+ * Toggle telemetry on/off
80
+ */
81
+ toggle() {
82
+ this.config.enabled = !this.config.enabled;
83
+ this.persistConfig();
84
+ return this.config.enabled;
85
+ }
86
+ /**
87
+ * Record a session start
88
+ */
89
+ startSession(sessionId, model) {
90
+ if (!this.config.enabled || !this.config.trackSessionMetrics)
91
+ return;
92
+ this.currentSessionStart = Date.now();
93
+ this.currentSessionMessages = 0;
94
+ this.currentSessionTools = 0;
95
+ this.currentSessionModel = model;
96
+ this.currentSessionInputTokens = 0;
97
+ this.currentSessionOutputTokens = 0;
98
+ this.currentSessionCost = 0;
99
+ }
100
+ /**
101
+ * Record a message in the current session
102
+ */
103
+ recordMessage() {
104
+ if (!this.config.enabled || !this.config.trackSessionMetrics)
105
+ return;
106
+ this.currentSessionMessages++;
107
+ }
108
+ /**
109
+ * Record a tool call
110
+ */
111
+ recordToolCall(toolName, durationMs, success) {
112
+ if (!this.config.enabled || !this.config.trackToolUsage)
113
+ return;
114
+ this.currentSessionTools++;
115
+ let metric = this.toolUsageMap.get(toolName);
116
+ if (!metric) {
117
+ metric = {
118
+ toolName,
119
+ callCount: 0,
120
+ successCount: 0,
121
+ errorCount: 0,
122
+ avgDurationMs: 0,
123
+ lastUsed: Date.now(),
124
+ };
125
+ }
126
+ const totalDuration = metric.avgDurationMs * metric.callCount + durationMs;
127
+ metric.callCount++;
128
+ if (success)
129
+ metric.successCount++;
130
+ else
131
+ metric.errorCount++;
132
+ metric.avgDurationMs = totalDuration / metric.callCount;
133
+ metric.lastUsed = Date.now();
134
+ this.toolUsageMap.set(toolName, metric);
135
+ }
136
+ /**
137
+ * Record model performance data
138
+ */
139
+ recordModelPerformance(model, inputTokens, outputTokens, cost, latencyMs, error) {
140
+ if (!this.config.enabled || !this.config.trackModelPerformance)
141
+ return;
142
+ this.currentSessionInputTokens += inputTokens;
143
+ this.currentSessionOutputTokens += outputTokens;
144
+ this.currentSessionCost += cost;
145
+ let perf = this.modelPerformanceMap.get(model);
146
+ if (!perf) {
147
+ perf = { latencies: [], errors: 0, requests: 0, inputTokens: 0, outputTokens: 0, cost: 0 };
148
+ }
149
+ perf.requests++;
150
+ perf.inputTokens += inputTokens;
151
+ perf.outputTokens += outputTokens;
152
+ perf.cost += cost;
153
+ perf.latencies.push(latencyMs);
154
+ if (error)
155
+ perf.errors++;
156
+ // Keep only last 1000 latency samples
157
+ if (perf.latencies.length > 1000) {
158
+ perf.latencies = perf.latencies.slice(-1000);
159
+ }
160
+ this.modelPerformanceMap.set(model, perf);
161
+ }
162
+ /**
163
+ * End the current session and record its metrics
164
+ */
165
+ endSession(sessionId) {
166
+ if (!this.config.enabled || !this.config.trackSessionMetrics)
167
+ return;
168
+ const metric = {
169
+ sessionId,
170
+ duration: Date.now() - this.currentSessionStart,
171
+ messageCount: this.currentSessionMessages,
172
+ modelUsed: this.currentSessionModel,
173
+ inputTokens: this.currentSessionInputTokens,
174
+ outputTokens: this.currentSessionOutputTokens,
175
+ cost: this.currentSessionCost,
176
+ toolsCalled: this.currentSessionTools,
177
+ timestamp: Date.now(),
178
+ };
179
+ this.sessionMetrics.push(metric);
180
+ this.persistData();
181
+ }
182
+ /**
183
+ * Get all tool usage metrics
184
+ */
185
+ getToolUsageMetrics() {
186
+ return Array.from(this.toolUsageMap.values());
187
+ }
188
+ /**
189
+ * Get model performance metrics
190
+ */
191
+ getModelPerformanceMetrics() {
192
+ const metrics = [];
193
+ for (const [model, perf] of this.modelPerformanceMap) {
194
+ const sorted = [...perf.latencies].sort((a, b) => a - b);
195
+ const p50 = this.percentile(sorted, 50);
196
+ const p95 = this.percentile(sorted, 95);
197
+ const p99 = this.percentile(sorted, 99);
198
+ const avg = sorted.length > 0 ? sorted.reduce((a, b) => a + b, 0) / sorted.length : 0;
199
+ metrics.push({
200
+ model,
201
+ requestCount: perf.requests,
202
+ totalInputTokens: perf.inputTokens,
203
+ totalOutputTokens: perf.outputTokens,
204
+ totalCost: perf.cost,
205
+ avgLatencyMs: Math.round(avg),
206
+ errorCount: perf.errors,
207
+ p50LatencyMs: Math.round(p50),
208
+ p95LatencyMs: Math.round(p95),
209
+ p99LatencyMs: Math.round(p99),
210
+ });
211
+ }
212
+ return metrics;
213
+ }
214
+ /**
215
+ * Get session metrics
216
+ */
217
+ getSessionMetrics() {
218
+ return [...this.sessionMetrics];
219
+ }
220
+ /**
221
+ * Generate a full telemetry report
222
+ */
223
+ generateReport(startTimestamp, endTimestamp) {
224
+ const end = endTimestamp || Date.now();
225
+ const start = startTimestamp || end - 30 * 24 * 60 * 60 * 1000; // default: last 30 days
226
+ const filteredSessions = this.sessionMetrics.filter(s => s.timestamp >= start && s.timestamp <= end);
227
+ return {
228
+ generatedAt: Date.now(),
229
+ period: { start, end },
230
+ totalSessions: filteredSessions.length,
231
+ totalMessages: filteredSessions.reduce((sum, s) => sum + s.messageCount, 0),
232
+ totalTokens: {
233
+ input: filteredSessions.reduce((sum, s) => sum + s.inputTokens, 0),
234
+ output: filteredSessions.reduce((sum, s) => sum + s.outputTokens, 0),
235
+ },
236
+ totalCost: filteredSessions.reduce((sum, s) => sum + s.cost, 0),
237
+ toolUsage: this.getToolUsageMetrics(),
238
+ modelPerformance: this.getModelPerformanceMetrics(),
239
+ sessionMetrics: filteredSessions,
240
+ };
241
+ }
242
+ /**
243
+ * Export telemetry data as JSON string
244
+ */
245
+ exportJSON(pretty) {
246
+ const report = this.generateReport();
247
+ return pretty ? JSON.stringify(report, null, 2) : JSON.stringify(report);
248
+ }
249
+ /**
250
+ * Export telemetry data to a file
251
+ */
252
+ exportToFile(filePath) {
253
+ try {
254
+ const data = this.exportJSON(true);
255
+ writeFileSync(filePath, data, 'utf-8');
256
+ console.log(chalk.green(`Telemetry data exported to ${filePath}`));
257
+ }
258
+ catch (error) {
259
+ console.log(chalk.red(`Failed to export telemetry: ${error instanceof Error ? error.message : String(error)}`));
260
+ }
261
+ }
262
+ /**
263
+ * Print a summary of telemetry data
264
+ */
265
+ printSummary() {
266
+ if (!this.config.enabled) {
267
+ console.log(chalk.gray('Telemetry is disabled. Use /telemetry on to enable.'));
268
+ return;
269
+ }
270
+ const report = this.generateReport();
271
+ console.log('');
272
+ console.log(chalk.bold('--- NeuroCLI Telemetry Report ---'));
273
+ console.log('');
274
+ console.log(` Anonymous ID: ${chalk.cyan(this.config.anonymousId)}`);
275
+ console.log(` Period: ${new Date(report.period.start).toLocaleDateString()} - ${new Date(report.period.end).toLocaleDateString()}`);
276
+ console.log('');
277
+ console.log(` Total Sessions: ${chalk.yellow(String(report.totalSessions))}`);
278
+ console.log(` Total Messages: ${chalk.yellow(String(report.totalMessages))}`);
279
+ console.log(` Total Tokens In: ${chalk.yellow(report.totalTokens.input.toLocaleString())}`);
280
+ console.log(` Total Tokens Out:${chalk.yellow(report.totalTokens.output.toLocaleString())}`);
281
+ console.log(` Total Cost: ${chalk.green('$' + report.totalCost.toFixed(4))}`);
282
+ console.log('');
283
+ // Tool usage
284
+ if (report.toolUsage.length > 0) {
285
+ console.log(chalk.bold(' Tool Usage:'));
286
+ for (const tool of report.toolUsage.sort((a, b) => b.callCount - a.callCount).slice(0, 10)) {
287
+ const successRate = tool.callCount > 0 ? ((tool.successCount / tool.callCount) * 100).toFixed(1) : '0';
288
+ console.log(` ${chalk.cyan(tool.toolName)}: ${tool.callCount} calls (${successRate}% success, avg ${Math.round(tool.avgDurationMs)}ms)`);
289
+ }
290
+ console.log('');
291
+ }
292
+ // Model performance
293
+ if (report.modelPerformance.length > 0) {
294
+ console.log(chalk.bold(' Model Performance:'));
295
+ for (const model of report.modelPerformance) {
296
+ console.log(` ${chalk.cyan(model.model)}:`);
297
+ console.log(` Requests: ${model.requestCount} | Errors: ${model.errorCount}`);
298
+ console.log(` Latency: p50=${model.p50LatencyMs}ms p95=${model.p95LatencyMs}ms p99=${model.p99LatencyMs}ms`);
299
+ console.log(` Cost: $${model.totalCost.toFixed(4)}`);
300
+ }
301
+ console.log('');
302
+ }
303
+ console.log(chalk.bold('---------------------------------'));
304
+ console.log('');
305
+ }
306
+ /**
307
+ * Clear all telemetry data
308
+ */
309
+ clearData() {
310
+ this.sessionMetrics = [];
311
+ this.toolUsageMap.clear();
312
+ this.modelPerformanceMap.clear();
313
+ this.persistData();
314
+ console.log(chalk.green('Telemetry data cleared.'));
315
+ }
316
+ /**
317
+ * Get current config
318
+ */
319
+ getConfig() {
320
+ return { ...this.config };
321
+ }
322
+ // ----------------------------------------------------------
323
+ // Private helpers
324
+ // ----------------------------------------------------------
325
+ percentile(sorted, p) {
326
+ if (sorted.length === 0)
327
+ return 0;
328
+ const index = Math.ceil((p / 100) * sorted.length) - 1;
329
+ return sorted[Math.max(0, index)];
330
+ }
331
+ ensureDataDir() {
332
+ if (!existsSync(this.config.dataDir)) {
333
+ mkdirSync(this.config.dataDir, { recursive: true });
334
+ }
335
+ }
336
+ persistConfig() {
337
+ try {
338
+ const configPath = join(this.config.dataDir, 'config.json');
339
+ writeFileSync(configPath, JSON.stringify(this.config, null, 2), 'utf-8');
340
+ }
341
+ catch { /* Silently fail */ }
342
+ }
343
+ persistData() {
344
+ if (!this.config.enabled)
345
+ return;
346
+ try {
347
+ this.ensureDataDir();
348
+ // Persist session metrics
349
+ const sessionPath = join(this.config.dataDir, 'sessions.json');
350
+ writeFileSync(sessionPath, JSON.stringify(this.sessionMetrics, null, 2), 'utf-8');
351
+ // Persist tool usage
352
+ const toolPath = join(this.config.dataDir, 'tools.json');
353
+ writeFileSync(toolPath, JSON.stringify(Array.from(this.toolUsageMap.entries()), null, 2), 'utf-8');
354
+ // Persist model performance
355
+ const modelPath = join(this.config.dataDir, 'models.json');
356
+ const serializableModels = Array.from(this.modelPerformanceMap.entries()).map(([key, val]) => ({
357
+ key,
358
+ ...val,
359
+ }));
360
+ writeFileSync(modelPath, JSON.stringify(serializableModels, null, 2), 'utf-8');
361
+ // Cleanup old data
362
+ this.cleanupOldData();
363
+ }
364
+ catch { /* Silently fail */ }
365
+ }
366
+ loadPersistedData() {
367
+ try {
368
+ // Load config
369
+ const configPath = join(this.config.dataDir, 'config.json');
370
+ if (existsSync(configPath)) {
371
+ const raw = readFileSync(configPath, 'utf-8');
372
+ const saved = JSON.parse(raw);
373
+ this.config = { ...this.config, ...saved };
374
+ }
375
+ if (!this.config.enabled)
376
+ return;
377
+ // Load session metrics
378
+ const sessionPath = join(this.config.dataDir, 'sessions.json');
379
+ if (existsSync(sessionPath)) {
380
+ const raw = readFileSync(sessionPath, 'utf-8');
381
+ this.sessionMetrics = JSON.parse(raw);
382
+ }
383
+ // Load tool usage
384
+ const toolPath = join(this.config.dataDir, 'tools.json');
385
+ if (existsSync(toolPath)) {
386
+ const raw = readFileSync(toolPath, 'utf-8');
387
+ const entries = JSON.parse(raw);
388
+ this.toolUsageMap = new Map(entries);
389
+ }
390
+ // Load model performance
391
+ const modelPath = join(this.config.dataDir, 'models.json');
392
+ if (existsSync(modelPath)) {
393
+ const raw = readFileSync(modelPath, 'utf-8');
394
+ const items = JSON.parse(raw);
395
+ this.modelPerformanceMap = new Map(items.map(item => [item.key, {
396
+ latencies: item.latencies,
397
+ errors: item.errors,
398
+ requests: item.requests,
399
+ inputTokens: item.inputTokens,
400
+ outputTokens: item.outputTokens,
401
+ cost: item.cost,
402
+ }]));
403
+ }
404
+ }
405
+ catch { /* Silently fail - telemetry should never block the CLI */ }
406
+ }
407
+ cleanupOldData() {
408
+ const cutoff = Date.now() - this.config.retentionDays * 24 * 60 * 60 * 1000;
409
+ this.sessionMetrics = this.sessionMetrics.filter(s => s.timestamp >= cutoff);
410
+ }
411
+ }
412
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1,134 @@
1
+ export interface TerminalCapabilities {
2
+ osc52: boolean;
3
+ trueColor: boolean;
4
+ unicode: boolean;
5
+ iterm2: boolean;
6
+ kitty: boolean;
7
+ sixel: boolean;
8
+ title: boolean;
9
+ hyperlinks: boolean;
10
+ }
11
+ export interface TerminalSize {
12
+ rows: number;
13
+ cols: number;
14
+ }
15
+ export interface SplitPane {
16
+ id: string;
17
+ label: string;
18
+ content: string[];
19
+ width: number;
20
+ height: number;
21
+ scrollOffset: number;
22
+ }
23
+ export interface SplitPaneLayout {
24
+ panes: SplitPane[];
25
+ gap: number;
26
+ borderStyle: 'single' | 'double' | 'round' | 'none';
27
+ }
28
+ export interface ProgressBarOptions {
29
+ total: number;
30
+ current: number;
31
+ width?: number;
32
+ label?: string;
33
+ completeChar?: string;
34
+ incompleteChar?: string;
35
+ showPercent?: boolean;
36
+ showETA?: boolean;
37
+ startTime?: number;
38
+ }
39
+ export interface SpinnerOptions {
40
+ frames?: string[];
41
+ interval?: number;
42
+ label?: string;
43
+ color?: 'cyan' | 'green' | 'yellow' | 'red' | 'magenta' | 'blue';
44
+ }
45
+ export declare class TerminalUX {
46
+ private capabilities;
47
+ private spinnerInterval;
48
+ private spinnerActive;
49
+ constructor();
50
+ /**
51
+ * Copy text to the terminal clipboard using the OSC 52 escape sequence.
52
+ * Works in remote SSH sessions when the terminal emulator supports it.
53
+ * Returns true if the sequence was written, false if OSC 52 is unsupported.
54
+ */
55
+ copyToClipboard(text: string): boolean;
56
+ /**
57
+ * Check whether the current terminal supports OSC 52 clipboard operations.
58
+ */
59
+ isOSC52Supported(): boolean;
60
+ /**
61
+ * Launch an interactive TUI command (e.g. vim, top, htop) and capture its
62
+ * exit code and any output. The child process inherits stdio so the user
63
+ * can interact with the TUI directly.
64
+ */
65
+ launchInteractive(command: string, options?: {
66
+ rows?: number;
67
+ cols?: number;
68
+ }): Promise<{
69
+ exitCode: number;
70
+ output: string;
71
+ }>;
72
+ /**
73
+ * Detect whether a command is a known TUI / interactive application.
74
+ */
75
+ isTUIApp(command: string): boolean;
76
+ /**
77
+ * Render a split-pane layout to a string. Each pane has a label and content
78
+ * lines, rendered side-by-side with borders.
79
+ */
80
+ renderSplitPane(layout: SplitPaneLayout): string;
81
+ /**
82
+ * Apply chalk-based syntax highlighting to a code string for the given language.
83
+ * Returns a chalk-colored string suitable for terminal output.
84
+ */
85
+ highlight(code: string, language: string): string;
86
+ /**
87
+ * Highlight a unified diff string with color coding.
88
+ * - Added lines: green
89
+ * - Removed lines: red
90
+ * - Header lines (---, +++, @@): cyan
91
+ * - Context lines: dim gray
92
+ */
93
+ highlightDiff(diff: string): string;
94
+ /**
95
+ * Return detected terminal capabilities.
96
+ */
97
+ getTerminalCapabilities(): TerminalCapabilities;
98
+ /**
99
+ * Return the current terminal dimensions.
100
+ */
101
+ getTerminalSize(): TerminalSize;
102
+ /**
103
+ * Check whether the terminal supports 24-bit true color.
104
+ */
105
+ supportsTrueColor(): boolean;
106
+ /**
107
+ * Check whether the terminal supports Unicode characters (beyond ASCII).
108
+ */
109
+ supportsUnicode(): boolean;
110
+ /**
111
+ * Render a progress bar as a string.
112
+ */
113
+ renderProgressBar(options: ProgressBarOptions): string;
114
+ /**
115
+ * Start a spinner animation on the terminal.
116
+ * Returns a stop function.
117
+ */
118
+ startSpinner(options?: SpinnerOptions): {
119
+ stop: (finalMessage?: string) => void;
120
+ };
121
+ /**
122
+ * Render a multi-step progress indicator (checklist).
123
+ */
124
+ renderStepProgress(steps: Array<{
125
+ label: string;
126
+ status: 'pending' | 'running' | 'done' | 'failed';
127
+ }>): string;
128
+ private detectCapabilities;
129
+ private highlightLine;
130
+ private parseCommand;
131
+ private truncateToWidth;
132
+ private formatDuration;
133
+ }
134
+ //# sourceMappingURL=terminal-ux.d.ts.map