@wundr.io/cli 1.0.10 → 1.0.12

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 (269) hide show
  1. package/bin/wundr.js +8 -4
  2. package/package.json +23 -23
  3. package/src/ai/ai-service.ts +16 -17
  4. package/src/ai/claude-client.ts +16 -16
  5. package/src/ai/conversation-manager.ts +29 -29
  6. package/src/cli.ts +4 -4
  7. package/src/commands/ai.ts +246 -78
  8. package/src/commands/alignment.ts +74 -74
  9. package/src/commands/analyze-optimized.ts +111 -78
  10. package/src/commands/analyze.ts +14 -14
  11. package/src/commands/batch.ts +179 -42
  12. package/src/commands/chat.ts +37 -30
  13. package/src/commands/claude-init.ts +41 -45
  14. package/src/commands/claude-setup.ts +204 -119
  15. package/src/commands/computer-setup.ts +85 -43
  16. package/src/commands/create-command.ts +4 -4
  17. package/src/commands/create.ts +27 -27
  18. package/src/commands/dashboard.ts +24 -24
  19. package/src/commands/govern.ts +25 -25
  20. package/src/commands/governance.ts +34 -34
  21. package/src/commands/guardian.ts +56 -56
  22. package/src/commands/init.ts +25 -22
  23. package/src/commands/orchestrator.ts +68 -41
  24. package/src/commands/performance-optimizer.ts +34 -35
  25. package/src/commands/plugins.ts +27 -27
  26. package/src/commands/project-update.ts +175 -72
  27. package/src/commands/rag.ts +185 -78
  28. package/src/commands/session.ts +35 -35
  29. package/src/commands/setup.ts +40 -344
  30. package/src/commands/test-init.ts +3 -3
  31. package/src/commands/test.ts +4 -4
  32. package/src/commands/watch.ts +28 -29
  33. package/src/commands/worktree.ts +49 -49
  34. package/src/context/context-manager.ts +10 -10
  35. package/src/context/session-manager.ts +41 -41
  36. package/src/framework/command-interface.ts +520 -0
  37. package/src/framework/command-registry.ts +942 -0
  38. package/src/framework/completion-exporter.ts +383 -0
  39. package/src/framework/debug-logger.ts +519 -0
  40. package/src/framework/error-handler.ts +867 -0
  41. package/src/framework/help-generator.ts +540 -0
  42. package/src/framework/index.ts +169 -0
  43. package/src/framework/interactive-repl.ts +703 -0
  44. package/src/framework/output-formatter.ts +834 -0
  45. package/src/framework/progress-manager.ts +539 -0
  46. package/src/index.ts +4 -4
  47. package/src/interactive/interactive-mode.ts +16 -16
  48. package/src/lib/conflict-resolution.ts +799 -9
  49. package/src/lib/merge-strategy.ts +529 -7
  50. package/src/lib/safety-mechanisms.ts +422 -18
  51. package/src/lib/state-detection.ts +1015 -13
  52. package/src/nlp/command-mapper.ts +29 -29
  53. package/src/nlp/command-parser.ts +17 -17
  54. package/src/nlp/intent-classifier.ts +7 -7
  55. package/src/nlp/intent-parser.ts +54 -52
  56. package/src/plugins/plugin-manager.ts +61 -39
  57. package/src/tests/computer-setup-integration.test.ts +46 -15
  58. package/src/types/modules.d.ts +424 -1
  59. package/src/utils/backup-rollback-manager.ts +11 -8
  60. package/src/utils/config-manager.ts +3 -3
  61. package/src/utils/error-handler.ts +2 -2
  62. package/src/utils/logger.ts +22 -22
  63. package/templates/batch/ci-cd.yaml +7 -7
  64. package/test-suites/api/health.spec.ts +20 -23
  65. package/test-suites/helpers/test-config.ts +14 -13
  66. package/test-suites/ui/accessibility.spec.ts +27 -22
  67. package/test-suites/ui/smoke.spec.ts +26 -21
  68. package/LICENSE +0 -21
  69. package/dist/ai/ai-service.d.ts +0 -152
  70. package/dist/ai/ai-service.d.ts.map +0 -1
  71. package/dist/ai/ai-service.js +0 -430
  72. package/dist/ai/ai-service.js.map +0 -1
  73. package/dist/ai/claude-client.d.ts +0 -130
  74. package/dist/ai/claude-client.d.ts.map +0 -1
  75. package/dist/ai/claude-client.js +0 -340
  76. package/dist/ai/claude-client.js.map +0 -1
  77. package/dist/ai/conversation-manager.d.ts +0 -164
  78. package/dist/ai/conversation-manager.d.ts.map +0 -1
  79. package/dist/ai/conversation-manager.js +0 -614
  80. package/dist/ai/conversation-manager.js.map +0 -1
  81. package/dist/ai/index.d.ts +0 -5
  82. package/dist/ai/index.d.ts.map +0 -1
  83. package/dist/ai/index.js +0 -8
  84. package/dist/ai/index.js.map +0 -1
  85. package/dist/cli.d.ts +0 -36
  86. package/dist/cli.d.ts.map +0 -1
  87. package/dist/cli.js +0 -192
  88. package/dist/cli.js.map +0 -1
  89. package/dist/commands/ai.d.ts +0 -89
  90. package/dist/commands/ai.d.ts.map +0 -1
  91. package/dist/commands/ai.js +0 -799
  92. package/dist/commands/ai.js.map +0 -1
  93. package/dist/commands/alignment.d.ts +0 -78
  94. package/dist/commands/alignment.d.ts.map +0 -1
  95. package/dist/commands/alignment.js +0 -817
  96. package/dist/commands/alignment.js.map +0 -1
  97. package/dist/commands/analyze-optimized.d.ts +0 -14
  98. package/dist/commands/analyze-optimized.d.ts.map +0 -1
  99. package/dist/commands/analyze-optimized.js +0 -600
  100. package/dist/commands/analyze-optimized.js.map +0 -1
  101. package/dist/commands/analyze.d.ts +0 -65
  102. package/dist/commands/analyze.d.ts.map +0 -1
  103. package/dist/commands/analyze.js +0 -435
  104. package/dist/commands/analyze.js.map +0 -1
  105. package/dist/commands/batch.d.ts +0 -71
  106. package/dist/commands/batch.d.ts.map +0 -1
  107. package/dist/commands/batch.js +0 -738
  108. package/dist/commands/batch.js.map +0 -1
  109. package/dist/commands/chat.d.ts +0 -71
  110. package/dist/commands/chat.d.ts.map +0 -1
  111. package/dist/commands/chat.js +0 -674
  112. package/dist/commands/chat.js.map +0 -1
  113. package/dist/commands/claude-init.d.ts +0 -28
  114. package/dist/commands/claude-init.d.ts.map +0 -1
  115. package/dist/commands/claude-init.js +0 -591
  116. package/dist/commands/claude-init.js.map +0 -1
  117. package/dist/commands/claude-setup.d.ts +0 -119
  118. package/dist/commands/claude-setup.d.ts.map +0 -1
  119. package/dist/commands/claude-setup.js +0 -1073
  120. package/dist/commands/claude-setup.js.map +0 -1
  121. package/dist/commands/computer-setup-commands.d.ts +0 -53
  122. package/dist/commands/computer-setup-commands.d.ts.map +0 -1
  123. package/dist/commands/computer-setup-commands.js +0 -705
  124. package/dist/commands/computer-setup-commands.js.map +0 -1
  125. package/dist/commands/computer-setup.d.ts +0 -7
  126. package/dist/commands/computer-setup.d.ts.map +0 -1
  127. package/dist/commands/computer-setup.js +0 -849
  128. package/dist/commands/computer-setup.js.map +0 -1
  129. package/dist/commands/create-command.d.ts +0 -7
  130. package/dist/commands/create-command.d.ts.map +0 -1
  131. package/dist/commands/create-command.js +0 -158
  132. package/dist/commands/create-command.js.map +0 -1
  133. package/dist/commands/create.d.ts +0 -74
  134. package/dist/commands/create.d.ts.map +0 -1
  135. package/dist/commands/create.js +0 -556
  136. package/dist/commands/create.js.map +0 -1
  137. package/dist/commands/dashboard.d.ts +0 -91
  138. package/dist/commands/dashboard.d.ts.map +0 -1
  139. package/dist/commands/dashboard.js +0 -538
  140. package/dist/commands/dashboard.js.map +0 -1
  141. package/dist/commands/govern.d.ts +0 -70
  142. package/dist/commands/govern.d.ts.map +0 -1
  143. package/dist/commands/govern.js +0 -481
  144. package/dist/commands/govern.js.map +0 -1
  145. package/dist/commands/governance.d.ts +0 -17
  146. package/dist/commands/governance.d.ts.map +0 -1
  147. package/dist/commands/governance.js +0 -703
  148. package/dist/commands/governance.js.map +0 -1
  149. package/dist/commands/guardian.d.ts +0 -20
  150. package/dist/commands/guardian.d.ts.map +0 -1
  151. package/dist/commands/guardian.js +0 -597
  152. package/dist/commands/guardian.js.map +0 -1
  153. package/dist/commands/init.d.ts +0 -59
  154. package/dist/commands/init.d.ts.map +0 -1
  155. package/dist/commands/init.js +0 -650
  156. package/dist/commands/init.js.map +0 -1
  157. package/dist/commands/orchestrator.d.ts +0 -7
  158. package/dist/commands/orchestrator.d.ts.map +0 -1
  159. package/dist/commands/orchestrator.js +0 -571
  160. package/dist/commands/orchestrator.js.map +0 -1
  161. package/dist/commands/performance-optimizer.d.ts +0 -30
  162. package/dist/commands/performance-optimizer.d.ts.map +0 -1
  163. package/dist/commands/performance-optimizer.js +0 -650
  164. package/dist/commands/performance-optimizer.js.map +0 -1
  165. package/dist/commands/plugins.d.ts +0 -87
  166. package/dist/commands/plugins.d.ts.map +0 -1
  167. package/dist/commands/plugins.js +0 -685
  168. package/dist/commands/plugins.js.map +0 -1
  169. package/dist/commands/rag.d.ts +0 -7
  170. package/dist/commands/rag.d.ts.map +0 -1
  171. package/dist/commands/rag.js +0 -748
  172. package/dist/commands/rag.js.map +0 -1
  173. package/dist/commands/session.d.ts +0 -41
  174. package/dist/commands/session.d.ts.map +0 -1
  175. package/dist/commands/session.js +0 -441
  176. package/dist/commands/session.js.map +0 -1
  177. package/dist/commands/setup.d.ts +0 -29
  178. package/dist/commands/setup.d.ts.map +0 -1
  179. package/dist/commands/setup.js +0 -397
  180. package/dist/commands/setup.js.map +0 -1
  181. package/dist/commands/test-init.d.ts +0 -9
  182. package/dist/commands/test-init.d.ts.map +0 -1
  183. package/dist/commands/test-init.js +0 -222
  184. package/dist/commands/test-init.js.map +0 -1
  185. package/dist/commands/test.d.ts +0 -25
  186. package/dist/commands/test.d.ts.map +0 -1
  187. package/dist/commands/test.js +0 -217
  188. package/dist/commands/test.js.map +0 -1
  189. package/dist/commands/vp.d.ts +0 -7
  190. package/dist/commands/vp.d.ts.map +0 -1
  191. package/dist/commands/vp.js +0 -571
  192. package/dist/commands/vp.js.map +0 -1
  193. package/dist/commands/watch.d.ts +0 -76
  194. package/dist/commands/watch.d.ts.map +0 -1
  195. package/dist/commands/watch.js +0 -613
  196. package/dist/commands/watch.js.map +0 -1
  197. package/dist/commands/worktree.d.ts +0 -63
  198. package/dist/commands/worktree.d.ts.map +0 -1
  199. package/dist/commands/worktree.js +0 -774
  200. package/dist/commands/worktree.js.map +0 -1
  201. package/dist/context/context-manager.d.ts +0 -155
  202. package/dist/context/context-manager.d.ts.map +0 -1
  203. package/dist/context/context-manager.js +0 -383
  204. package/dist/context/context-manager.js.map +0 -1
  205. package/dist/context/index.d.ts +0 -3
  206. package/dist/context/index.d.ts.map +0 -1
  207. package/dist/context/index.js +0 -6
  208. package/dist/context/index.js.map +0 -1
  209. package/dist/context/session-manager.d.ts +0 -207
  210. package/dist/context/session-manager.d.ts.map +0 -1
  211. package/dist/context/session-manager.js +0 -686
  212. package/dist/context/session-manager.js.map +0 -1
  213. package/dist/index.d.ts +0 -8
  214. package/dist/index.d.ts.map +0 -1
  215. package/dist/index.js +0 -51
  216. package/dist/index.js.map +0 -1
  217. package/dist/interactive/interactive-mode.d.ts +0 -76
  218. package/dist/interactive/interactive-mode.d.ts.map +0 -1
  219. package/dist/interactive/interactive-mode.js +0 -732
  220. package/dist/interactive/interactive-mode.js.map +0 -1
  221. package/dist/nlp/command-mapper.d.ts +0 -174
  222. package/dist/nlp/command-mapper.d.ts.map +0 -1
  223. package/dist/nlp/command-mapper.js +0 -624
  224. package/dist/nlp/command-mapper.js.map +0 -1
  225. package/dist/nlp/command-parser.d.ts +0 -106
  226. package/dist/nlp/command-parser.d.ts.map +0 -1
  227. package/dist/nlp/command-parser.js +0 -417
  228. package/dist/nlp/command-parser.js.map +0 -1
  229. package/dist/nlp/index.d.ts +0 -5
  230. package/dist/nlp/index.d.ts.map +0 -1
  231. package/dist/nlp/index.js +0 -8
  232. package/dist/nlp/index.js.map +0 -1
  233. package/dist/nlp/intent-classifier.d.ts +0 -59
  234. package/dist/nlp/intent-classifier.d.ts.map +0 -1
  235. package/dist/nlp/intent-classifier.js +0 -384
  236. package/dist/nlp/intent-classifier.js.map +0 -1
  237. package/dist/nlp/intent-parser.d.ts +0 -152
  238. package/dist/nlp/intent-parser.d.ts.map +0 -1
  239. package/dist/nlp/intent-parser.js +0 -744
  240. package/dist/nlp/intent-parser.js.map +0 -1
  241. package/dist/plugins/plugin-manager.d.ts +0 -120
  242. package/dist/plugins/plugin-manager.d.ts.map +0 -1
  243. package/dist/plugins/plugin-manager.js +0 -595
  244. package/dist/plugins/plugin-manager.js.map +0 -1
  245. package/dist/types/index.d.ts +0 -224
  246. package/dist/types/index.d.ts.map +0 -1
  247. package/dist/types/index.js +0 -3
  248. package/dist/types/index.js.map +0 -1
  249. package/dist/utils/backup-rollback-manager.d.ts +0 -72
  250. package/dist/utils/backup-rollback-manager.d.ts.map +0 -1
  251. package/dist/utils/backup-rollback-manager.js +0 -289
  252. package/dist/utils/backup-rollback-manager.js.map +0 -1
  253. package/dist/utils/claude-config-installer.d.ts +0 -98
  254. package/dist/utils/claude-config-installer.d.ts.map +0 -1
  255. package/dist/utils/claude-config-installer.js +0 -678
  256. package/dist/utils/claude-config-installer.js.map +0 -1
  257. package/dist/utils/config-manager.d.ts +0 -73
  258. package/dist/utils/config-manager.d.ts.map +0 -1
  259. package/dist/utils/config-manager.js +0 -339
  260. package/dist/utils/config-manager.js.map +0 -1
  261. package/dist/utils/error-handler.d.ts +0 -46
  262. package/dist/utils/error-handler.d.ts.map +0 -1
  263. package/dist/utils/error-handler.js +0 -169
  264. package/dist/utils/error-handler.js.map +0 -1
  265. package/dist/utils/logger.d.ts +0 -25
  266. package/dist/utils/logger.d.ts.map +0 -1
  267. package/dist/utils/logger.js +0 -105
  268. package/dist/utils/logger.js.map +0 -1
  269. package/src/commands/computer-setup-commands.ts +0 -872
@@ -0,0 +1,519 @@
1
+ /**
2
+ * Debug Logger - Verbose/debug mode with detailed logging and TTY detection.
3
+ *
4
+ * Provides:
5
+ * - Verbosity levels (silent, error, warn, info, debug, trace)
6
+ * - Pipe-friendly output (auto-detects TTY vs piped output)
7
+ * - Structured log entries with timestamps and context
8
+ * - Color-coded output with chalk (disabled for non-TTY)
9
+ * - Performance timing helpers
10
+ * - Log filtering by tag/component
11
+ *
12
+ * @module framework/debug-logger
13
+ */
14
+
15
+ import chalk from 'chalk';
16
+
17
+ import type { ContextLogger, GlobalOptions } from './command-interface';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Types
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Log verbosity levels ordered by severity.
25
+ */
26
+ export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
27
+
28
+ /**
29
+ * Structured log entry for programmatic consumption.
30
+ */
31
+ export interface LogEntry {
32
+ timestamp: Date;
33
+ level: LogLevel;
34
+ message: string;
35
+ tag?: string;
36
+ data?: unknown;
37
+ durationMs?: number;
38
+ }
39
+
40
+ /**
41
+ * Configuration for the debug logger.
42
+ */
43
+ export interface DebugLoggerOptions {
44
+ /** Minimum log level to display. Defaults to 'info'. */
45
+ level?: LogLevel;
46
+
47
+ /** Whether stdout is a TTY. Auto-detected if not set. */
48
+ isTTY?: boolean;
49
+
50
+ /** Whether to disable color output. */
51
+ noColor?: boolean;
52
+
53
+ /** Whether to include timestamps in output. Defaults to true in debug/trace. */
54
+ timestamps?: boolean;
55
+
56
+ /** Tags to include. If set, only matching tags are displayed. */
57
+ includeTags?: string[];
58
+
59
+ /** Tags to exclude from display. */
60
+ excludeTags?: string[];
61
+
62
+ /** Custom write function. Defaults to process.stderr.write. */
63
+ write?: (message: string) => void;
64
+
65
+ /** Custom error write function. Defaults to process.stderr.write. */
66
+ writeError?: (message: string) => void;
67
+
68
+ /** Collect log entries for later inspection (testing). */
69
+ collectEntries?: boolean;
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Constants
74
+ // ---------------------------------------------------------------------------
75
+
76
+ const LEVEL_ORDER: Record<LogLevel, number> = {
77
+ silent: 0,
78
+ error: 1,
79
+ warn: 2,
80
+ info: 3,
81
+ debug: 4,
82
+ trace: 5,
83
+ };
84
+
85
+ const LEVEL_LABELS: Record<LogLevel, string> = {
86
+ silent: '',
87
+ error: 'ERR',
88
+ warn: 'WRN',
89
+ info: 'INF',
90
+ debug: 'DBG',
91
+ trace: 'TRC',
92
+ };
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Debug Logger
96
+ // ---------------------------------------------------------------------------
97
+
98
+ export class DebugLogger implements ContextLogger {
99
+ private level: LogLevel;
100
+ private isTTY: boolean;
101
+ private noColor: boolean;
102
+ private showTimestamps: boolean;
103
+ private includeTags: Set<string> | null;
104
+ private excludeTags: Set<string>;
105
+ private write: (message: string) => void;
106
+ private writeError: (message: string) => void;
107
+ private timers: Map<string, number> = new Map();
108
+ private entries: LogEntry[] = [];
109
+ private collectEntries: boolean;
110
+
111
+ constructor(options: DebugLoggerOptions = {}) {
112
+ this.level = options.level ?? 'info';
113
+ this.isTTY = options.isTTY ?? process.stdout.isTTY === true;
114
+ this.noColor =
115
+ options.noColor ?? (process.env['NO_COLOR'] === '1' || !this.isTTY);
116
+ this.showTimestamps =
117
+ options.timestamps ?? (this.level === 'debug' || this.level === 'trace');
118
+ this.includeTags = options.includeTags
119
+ ? new Set(options.includeTags)
120
+ : null;
121
+ this.excludeTags = new Set(options.excludeTags ?? []);
122
+ this.write = options.write ?? ((msg: string) => process.stderr.write(msg));
123
+ this.writeError =
124
+ options.writeError ?? ((msg: string) => process.stderr.write(msg));
125
+ this.collectEntries = options.collectEntries ?? false;
126
+
127
+ // Disable chalk color if not TTY or noColor
128
+ if (this.noColor) {
129
+ chalk.level = 0;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Create a logger from global CLI options.
135
+ */
136
+ static fromGlobalOptions(options: GlobalOptions): DebugLogger {
137
+ let level: LogLevel = 'info';
138
+ if (options.quiet) level = 'error';
139
+ if (options.verbose) level = 'debug';
140
+
141
+ return new DebugLogger({
142
+ level,
143
+ noColor: options.noColor,
144
+ });
145
+ }
146
+
147
+ // -------------------------------------------------------------------------
148
+ // Core log methods implementing ContextLogger
149
+ // -------------------------------------------------------------------------
150
+
151
+ debug(message: string, ...args: unknown[]): void {
152
+ this.log('debug', message, undefined, args.length > 0 ? args : undefined);
153
+ }
154
+
155
+ info(message: string, ...args: unknown[]): void {
156
+ this.log('info', message, undefined, args.length > 0 ? args : undefined);
157
+ }
158
+
159
+ warn(message: string, ...args: unknown[]): void {
160
+ this.log('warn', message, undefined, args.length > 0 ? args : undefined);
161
+ }
162
+
163
+ error(message: string, ...args: unknown[]): void {
164
+ this.log('error', message, undefined, args.length > 0 ? args : undefined);
165
+ }
166
+
167
+ success(message: string, ...args: unknown[]): void {
168
+ this.log(
169
+ 'info',
170
+ message,
171
+ undefined,
172
+ args.length > 0 ? args : undefined,
173
+ true
174
+ );
175
+ }
176
+
177
+ trace(message: string, ...args: unknown[]): void {
178
+ this.log('trace', message, undefined, args.length > 0 ? args : undefined);
179
+ }
180
+
181
+ // -------------------------------------------------------------------------
182
+ // Tagged logging
183
+ // -------------------------------------------------------------------------
184
+
185
+ /**
186
+ * Create a tagged sub-logger that prefixes all messages with a component tag.
187
+ */
188
+ tagged(tag: string): TaggedLogger {
189
+ return new TaggedLogger(this, tag);
190
+ }
191
+
192
+ /**
193
+ * Log a message with a specific tag.
194
+ */
195
+ logTagged(
196
+ level: LogLevel,
197
+ tag: string,
198
+ message: string,
199
+ data?: unknown
200
+ ): void {
201
+ this.log(level, message, tag, data);
202
+ }
203
+
204
+ // -------------------------------------------------------------------------
205
+ // Performance timing
206
+ // -------------------------------------------------------------------------
207
+
208
+ /**
209
+ * Start a performance timer.
210
+ *
211
+ * @param label - Timer label
212
+ */
213
+ time(label: string): void {
214
+ this.timers.set(label, performance.now());
215
+ this.log('debug', `Timer started: ${label}`);
216
+ }
217
+
218
+ /**
219
+ * End a performance timer and log the duration.
220
+ *
221
+ * @param label - Timer label
222
+ * @returns Duration in milliseconds, or -1 if timer not found
223
+ */
224
+ timeEnd(label: string): number {
225
+ const start = this.timers.get(label);
226
+ if (start === undefined) {
227
+ this.log('warn', `Timer not found: ${label}`);
228
+ return -1;
229
+ }
230
+
231
+ const duration = performance.now() - start;
232
+ this.timers.delete(label);
233
+
234
+ const entry: LogEntry = {
235
+ timestamp: new Date(),
236
+ level: 'debug',
237
+ message: `Timer ${label}: ${duration.toFixed(2)}ms`,
238
+ tag: 'perf',
239
+ durationMs: duration,
240
+ };
241
+
242
+ if (this.collectEntries) {
243
+ this.entries.push(entry);
244
+ }
245
+
246
+ this.log('debug', `${label}: ${this.formatDuration(duration)}`);
247
+ return duration;
248
+ }
249
+
250
+ // -------------------------------------------------------------------------
251
+ // Pipe-friendly output
252
+ // -------------------------------------------------------------------------
253
+
254
+ /**
255
+ * Write data to stdout suitable for piping.
256
+ * Uses plain text without colors when output is piped.
257
+ *
258
+ * @param data - Data to output
259
+ */
260
+ stdout(data: string): void {
261
+ process.stdout.write(data);
262
+ if (!data.endsWith('\n')) {
263
+ process.stdout.write('\n');
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Check if output is being piped (non-TTY).
269
+ */
270
+ isPiped(): boolean {
271
+ return !this.isTTY;
272
+ }
273
+
274
+ // -------------------------------------------------------------------------
275
+ // Configuration
276
+ // -------------------------------------------------------------------------
277
+
278
+ /**
279
+ * Update the log level at runtime.
280
+ */
281
+ setLevel(level: LogLevel): void {
282
+ this.level = level;
283
+ this.showTimestamps = level === 'debug' || level === 'trace';
284
+ }
285
+
286
+ /**
287
+ * Get the current log level.
288
+ */
289
+ getLevel(): LogLevel {
290
+ return this.level;
291
+ }
292
+
293
+ /**
294
+ * Get all collected log entries (when collectEntries is enabled).
295
+ */
296
+ getEntries(): readonly LogEntry[] {
297
+ return this.entries;
298
+ }
299
+
300
+ /**
301
+ * Clear collected entries.
302
+ */
303
+ clearEntries(): void {
304
+ this.entries = [];
305
+ }
306
+
307
+ // -------------------------------------------------------------------------
308
+ // Private
309
+ // -------------------------------------------------------------------------
310
+
311
+ /**
312
+ * Core log implementation.
313
+ */
314
+ private log(
315
+ level: LogLevel,
316
+ message: string,
317
+ tag?: string,
318
+ data?: unknown,
319
+ isSuccess?: boolean
320
+ ): void {
321
+ // Check level threshold
322
+ if (LEVEL_ORDER[level] > LEVEL_ORDER[this.level]) {
323
+ return;
324
+ }
325
+
326
+ // Check tag filters
327
+ if (tag) {
328
+ if (this.excludeTags.has(tag)) return;
329
+ if (this.includeTags && !this.includeTags.has(tag)) return;
330
+ }
331
+
332
+ // Collect entry
333
+ if (this.collectEntries) {
334
+ this.entries.push({
335
+ timestamp: new Date(),
336
+ level,
337
+ message,
338
+ tag,
339
+ data,
340
+ });
341
+ }
342
+
343
+ // Format and write
344
+ const formatted = this.formatMessage(level, message, tag, isSuccess);
345
+ const writeFn = level === 'error' ? this.writeError : this.write;
346
+ writeFn(formatted + '\n');
347
+
348
+ // Write data details in debug/trace mode
349
+ if (data !== undefined && LEVEL_ORDER[this.level] >= LEVEL_ORDER['debug']) {
350
+ const dataStr =
351
+ typeof data === 'string' ? data : JSON.stringify(data, null, 2);
352
+ const indented = dataStr
353
+ .split('\n')
354
+ .map(line => ' ' + line)
355
+ .join('\n');
356
+ writeFn(this.colorize('gray', indented) + '\n');
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Format a log message for display.
362
+ */
363
+ private formatMessage(
364
+ level: LogLevel,
365
+ message: string,
366
+ tag?: string,
367
+ isSuccess?: boolean
368
+ ): string {
369
+ const parts: string[] = [];
370
+
371
+ // Timestamp
372
+ if (this.showTimestamps) {
373
+ const ts = new Date().toISOString().substring(11, 23); // HH:mm:ss.SSS
374
+ parts.push(this.colorize('gray', ts));
375
+ }
376
+
377
+ // Level badge
378
+ const label = LEVEL_LABELS[level];
379
+ if (label) {
380
+ parts.push(this.colorizeLevel(level, label));
381
+ }
382
+
383
+ // Tag
384
+ if (tag) {
385
+ parts.push(this.colorize('cyan', `[${tag}]`));
386
+ }
387
+
388
+ // Message
389
+ if (isSuccess) {
390
+ parts.push(this.colorize('green', message));
391
+ } else {
392
+ parts.push(this.colorizeLevelMessage(level, message));
393
+ }
394
+
395
+ return parts.join(' ');
396
+ }
397
+
398
+ /**
399
+ * Colorize a level badge.
400
+ */
401
+ private colorizeLevel(level: LogLevel, text: string): string {
402
+ if (this.noColor) return `[${text}]`;
403
+
404
+ switch (level) {
405
+ case 'error':
406
+ return chalk.red.bold(`[${text}]`);
407
+ case 'warn':
408
+ return chalk.yellow(`[${text}]`);
409
+ case 'info':
410
+ return chalk.blue(`[${text}]`);
411
+ case 'debug':
412
+ return chalk.gray(`[${text}]`);
413
+ case 'trace':
414
+ return chalk.dim(`[${text}]`);
415
+ default:
416
+ return `[${text}]`;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Colorize a message based on its level.
422
+ */
423
+ private colorizeLevelMessage(level: LogLevel, text: string): string {
424
+ if (this.noColor) return text;
425
+
426
+ switch (level) {
427
+ case 'error':
428
+ return chalk.red(text);
429
+ case 'warn':
430
+ return chalk.yellow(text);
431
+ case 'trace':
432
+ return chalk.dim(text);
433
+ default:
434
+ return text;
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Apply a color name to text.
440
+ */
441
+ private colorize(color: string, text: string): string {
442
+ if (this.noColor) return text;
443
+
444
+ const colorFn = (chalk as unknown as Record<string, (s: string) => string>)[
445
+ color
446
+ ];
447
+ return colorFn ? colorFn(text) : text;
448
+ }
449
+
450
+ /**
451
+ * Format a duration in milliseconds to a human-readable string.
452
+ */
453
+ private formatDuration(ms: number): string {
454
+ if (ms < 1) return `${(ms * 1000).toFixed(0)}us`;
455
+ if (ms < 1000) return `${ms.toFixed(1)}ms`;
456
+ if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
457
+ return `${(ms / 60000).toFixed(1)}min`;
458
+ }
459
+ }
460
+
461
+ // ---------------------------------------------------------------------------
462
+ // Tagged Logger
463
+ // ---------------------------------------------------------------------------
464
+
465
+ /**
466
+ * A logger that prepends a tag to all messages.
467
+ * Created via `DebugLogger.tagged('component')`.
468
+ */
469
+ export class TaggedLogger implements ContextLogger {
470
+ constructor(
471
+ private parent: DebugLogger,
472
+ private tag: string
473
+ ) {}
474
+
475
+ debug(message: string, ...args: unknown[]): void {
476
+ this.parent.logTagged(
477
+ 'debug',
478
+ this.tag,
479
+ message,
480
+ args.length > 0 ? args : undefined
481
+ );
482
+ }
483
+
484
+ info(message: string, ...args: unknown[]): void {
485
+ this.parent.logTagged(
486
+ 'info',
487
+ this.tag,
488
+ message,
489
+ args.length > 0 ? args : undefined
490
+ );
491
+ }
492
+
493
+ warn(message: string, ...args: unknown[]): void {
494
+ this.parent.logTagged(
495
+ 'warn',
496
+ this.tag,
497
+ message,
498
+ args.length > 0 ? args : undefined
499
+ );
500
+ }
501
+
502
+ error(message: string, ...args: unknown[]): void {
503
+ this.parent.logTagged(
504
+ 'error',
505
+ this.tag,
506
+ message,
507
+ args.length > 0 ? args : undefined
508
+ );
509
+ }
510
+
511
+ success(message: string, ...args: unknown[]): void {
512
+ this.parent.logTagged(
513
+ 'info',
514
+ this.tag,
515
+ message,
516
+ args.length > 0 ? args : undefined
517
+ );
518
+ }
519
+ }