@wundr.io/cli 1.0.11 → 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.
- package/bin/wundr.js +8 -4
- package/package.json +23 -23
- package/src/ai/ai-service.ts +16 -17
- package/src/ai/claude-client.ts +16 -16
- package/src/ai/conversation-manager.ts +29 -29
- package/src/cli.ts +4 -4
- package/src/commands/ai.ts +246 -78
- package/src/commands/alignment.ts +74 -74
- package/src/commands/analyze-optimized.ts +111 -78
- package/src/commands/analyze.ts +14 -14
- package/src/commands/batch.ts +179 -42
- package/src/commands/chat.ts +37 -30
- package/src/commands/claude-init.ts +41 -45
- package/src/commands/claude-setup.ts +204 -119
- package/src/commands/computer-setup.ts +85 -43
- package/src/commands/create-command.ts +4 -4
- package/src/commands/create.ts +27 -27
- package/src/commands/dashboard.ts +24 -24
- package/src/commands/govern.ts +25 -25
- package/src/commands/governance.ts +34 -34
- package/src/commands/guardian.ts +56 -56
- package/src/commands/init.ts +25 -22
- package/src/commands/orchestrator.ts +68 -41
- package/src/commands/performance-optimizer.ts +34 -35
- package/src/commands/plugins.ts +27 -27
- package/src/commands/project-update.ts +175 -72
- package/src/commands/rag.ts +185 -78
- package/src/commands/session.ts +35 -35
- package/src/commands/setup.ts +40 -344
- package/src/commands/test-init.ts +3 -3
- package/src/commands/test.ts +4 -4
- package/src/commands/watch.ts +28 -29
- package/src/commands/worktree.ts +49 -49
- package/src/context/context-manager.ts +10 -10
- package/src/context/session-manager.ts +41 -41
- package/src/framework/command-interface.ts +520 -0
- package/src/framework/command-registry.ts +942 -0
- package/src/framework/completion-exporter.ts +383 -0
- package/src/framework/debug-logger.ts +519 -0
- package/src/framework/error-handler.ts +867 -0
- package/src/framework/help-generator.ts +540 -0
- package/src/framework/index.ts +169 -0
- package/src/framework/interactive-repl.ts +703 -0
- package/src/framework/output-formatter.ts +834 -0
- package/src/framework/progress-manager.ts +539 -0
- package/src/index.ts +4 -4
- package/src/interactive/interactive-mode.ts +16 -16
- package/src/lib/conflict-resolution.ts +799 -9
- package/src/lib/merge-strategy.ts +529 -7
- package/src/lib/safety-mechanisms.ts +422 -18
- package/src/lib/state-detection.ts +1015 -13
- package/src/nlp/command-mapper.ts +29 -29
- package/src/nlp/command-parser.ts +17 -17
- package/src/nlp/intent-classifier.ts +7 -7
- package/src/nlp/intent-parser.ts +54 -52
- package/src/plugins/plugin-manager.ts +61 -39
- package/src/tests/computer-setup-integration.test.ts +46 -15
- package/src/types/modules.d.ts +424 -1
- package/src/utils/backup-rollback-manager.ts +11 -8
- package/src/utils/config-manager.ts +3 -3
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/logger.ts +22 -22
- package/templates/batch/ci-cd.yaml +7 -7
- package/test-suites/api/health.spec.ts +20 -23
- package/test-suites/helpers/test-config.ts +14 -13
- package/test-suites/ui/accessibility.spec.ts +27 -22
- package/test-suites/ui/smoke.spec.ts +26 -21
- package/LICENSE +0 -21
- package/dist/ai/ai-service.d.ts +0 -152
- package/dist/ai/ai-service.d.ts.map +0 -1
- package/dist/ai/ai-service.js +0 -430
- package/dist/ai/ai-service.js.map +0 -1
- package/dist/ai/claude-client.d.ts +0 -130
- package/dist/ai/claude-client.d.ts.map +0 -1
- package/dist/ai/claude-client.js +0 -340
- package/dist/ai/claude-client.js.map +0 -1
- package/dist/ai/conversation-manager.d.ts +0 -164
- package/dist/ai/conversation-manager.d.ts.map +0 -1
- package/dist/ai/conversation-manager.js +0 -614
- package/dist/ai/conversation-manager.js.map +0 -1
- package/dist/ai/index.d.ts +0 -5
- package/dist/ai/index.d.ts.map +0 -1
- package/dist/ai/index.js +0 -8
- package/dist/ai/index.js.map +0 -1
- package/dist/cli.d.ts +0 -36
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -192
- package/dist/cli.js.map +0 -1
- package/dist/commands/ai.d.ts +0 -89
- package/dist/commands/ai.d.ts.map +0 -1
- package/dist/commands/ai.js +0 -799
- package/dist/commands/ai.js.map +0 -1
- package/dist/commands/alignment.d.ts +0 -78
- package/dist/commands/alignment.d.ts.map +0 -1
- package/dist/commands/alignment.js +0 -817
- package/dist/commands/alignment.js.map +0 -1
- package/dist/commands/analyze-optimized.d.ts +0 -14
- package/dist/commands/analyze-optimized.d.ts.map +0 -1
- package/dist/commands/analyze-optimized.js +0 -600
- package/dist/commands/analyze-optimized.js.map +0 -1
- package/dist/commands/analyze.d.ts +0 -65
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js +0 -435
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/batch.d.ts +0 -71
- package/dist/commands/batch.d.ts.map +0 -1
- package/dist/commands/batch.js +0 -738
- package/dist/commands/batch.js.map +0 -1
- package/dist/commands/chat.d.ts +0 -71
- package/dist/commands/chat.d.ts.map +0 -1
- package/dist/commands/chat.js +0 -674
- package/dist/commands/chat.js.map +0 -1
- package/dist/commands/claude-init.d.ts +0 -28
- package/dist/commands/claude-init.d.ts.map +0 -1
- package/dist/commands/claude-init.js +0 -591
- package/dist/commands/claude-init.js.map +0 -1
- package/dist/commands/claude-setup.d.ts +0 -119
- package/dist/commands/claude-setup.d.ts.map +0 -1
- package/dist/commands/claude-setup.js +0 -1073
- package/dist/commands/claude-setup.js.map +0 -1
- package/dist/commands/computer-setup-commands.d.ts +0 -53
- package/dist/commands/computer-setup-commands.d.ts.map +0 -1
- package/dist/commands/computer-setup-commands.js +0 -705
- package/dist/commands/computer-setup-commands.js.map +0 -1
- package/dist/commands/computer-setup.d.ts +0 -7
- package/dist/commands/computer-setup.d.ts.map +0 -1
- package/dist/commands/computer-setup.js +0 -849
- package/dist/commands/computer-setup.js.map +0 -1
- package/dist/commands/create-command.d.ts +0 -7
- package/dist/commands/create-command.d.ts.map +0 -1
- package/dist/commands/create-command.js +0 -158
- package/dist/commands/create-command.js.map +0 -1
- package/dist/commands/create.d.ts +0 -74
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -556
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/dashboard.d.ts +0 -91
- package/dist/commands/dashboard.d.ts.map +0 -1
- package/dist/commands/dashboard.js +0 -538
- package/dist/commands/dashboard.js.map +0 -1
- package/dist/commands/govern.d.ts +0 -70
- package/dist/commands/govern.d.ts.map +0 -1
- package/dist/commands/govern.js +0 -481
- package/dist/commands/govern.js.map +0 -1
- package/dist/commands/governance.d.ts +0 -17
- package/dist/commands/governance.d.ts.map +0 -1
- package/dist/commands/governance.js +0 -703
- package/dist/commands/governance.js.map +0 -1
- package/dist/commands/guardian.d.ts +0 -20
- package/dist/commands/guardian.d.ts.map +0 -1
- package/dist/commands/guardian.js +0 -597
- package/dist/commands/guardian.js.map +0 -1
- package/dist/commands/init.d.ts +0 -59
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -650
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/orchestrator.d.ts +0 -7
- package/dist/commands/orchestrator.d.ts.map +0 -1
- package/dist/commands/orchestrator.js +0 -571
- package/dist/commands/orchestrator.js.map +0 -1
- package/dist/commands/performance-optimizer.d.ts +0 -30
- package/dist/commands/performance-optimizer.d.ts.map +0 -1
- package/dist/commands/performance-optimizer.js +0 -650
- package/dist/commands/performance-optimizer.js.map +0 -1
- package/dist/commands/plugins.d.ts +0 -87
- package/dist/commands/plugins.d.ts.map +0 -1
- package/dist/commands/plugins.js +0 -685
- package/dist/commands/plugins.js.map +0 -1
- package/dist/commands/rag.d.ts +0 -7
- package/dist/commands/rag.d.ts.map +0 -1
- package/dist/commands/rag.js +0 -748
- package/dist/commands/rag.js.map +0 -1
- package/dist/commands/session.d.ts +0 -41
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js +0 -441
- package/dist/commands/session.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -29
- package/dist/commands/setup.d.ts.map +0 -1
- package/dist/commands/setup.js +0 -397
- package/dist/commands/setup.js.map +0 -1
- package/dist/commands/test-init.d.ts +0 -9
- package/dist/commands/test-init.d.ts.map +0 -1
- package/dist/commands/test-init.js +0 -222
- package/dist/commands/test-init.js.map +0 -1
- package/dist/commands/test.d.ts +0 -25
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js +0 -217
- package/dist/commands/test.js.map +0 -1
- package/dist/commands/vp.d.ts +0 -7
- package/dist/commands/vp.d.ts.map +0 -1
- package/dist/commands/vp.js +0 -571
- package/dist/commands/vp.js.map +0 -1
- package/dist/commands/watch.d.ts +0 -76
- package/dist/commands/watch.d.ts.map +0 -1
- package/dist/commands/watch.js +0 -613
- package/dist/commands/watch.js.map +0 -1
- package/dist/commands/worktree.d.ts +0 -63
- package/dist/commands/worktree.d.ts.map +0 -1
- package/dist/commands/worktree.js +0 -774
- package/dist/commands/worktree.js.map +0 -1
- package/dist/context/context-manager.d.ts +0 -155
- package/dist/context/context-manager.d.ts.map +0 -1
- package/dist/context/context-manager.js +0 -383
- package/dist/context/context-manager.js.map +0 -1
- package/dist/context/index.d.ts +0 -3
- package/dist/context/index.d.ts.map +0 -1
- package/dist/context/index.js +0 -6
- package/dist/context/index.js.map +0 -1
- package/dist/context/session-manager.d.ts +0 -207
- package/dist/context/session-manager.d.ts.map +0 -1
- package/dist/context/session-manager.js +0 -686
- package/dist/context/session-manager.js.map +0 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -51
- package/dist/index.js.map +0 -1
- package/dist/interactive/interactive-mode.d.ts +0 -76
- package/dist/interactive/interactive-mode.d.ts.map +0 -1
- package/dist/interactive/interactive-mode.js +0 -732
- package/dist/interactive/interactive-mode.js.map +0 -1
- package/dist/nlp/command-mapper.d.ts +0 -174
- package/dist/nlp/command-mapper.d.ts.map +0 -1
- package/dist/nlp/command-mapper.js +0 -624
- package/dist/nlp/command-mapper.js.map +0 -1
- package/dist/nlp/command-parser.d.ts +0 -106
- package/dist/nlp/command-parser.d.ts.map +0 -1
- package/dist/nlp/command-parser.js +0 -417
- package/dist/nlp/command-parser.js.map +0 -1
- package/dist/nlp/index.d.ts +0 -5
- package/dist/nlp/index.d.ts.map +0 -1
- package/dist/nlp/index.js +0 -8
- package/dist/nlp/index.js.map +0 -1
- package/dist/nlp/intent-classifier.d.ts +0 -59
- package/dist/nlp/intent-classifier.d.ts.map +0 -1
- package/dist/nlp/intent-classifier.js +0 -384
- package/dist/nlp/intent-classifier.js.map +0 -1
- package/dist/nlp/intent-parser.d.ts +0 -152
- package/dist/nlp/intent-parser.d.ts.map +0 -1
- package/dist/nlp/intent-parser.js +0 -744
- package/dist/nlp/intent-parser.js.map +0 -1
- package/dist/plugins/plugin-manager.d.ts +0 -120
- package/dist/plugins/plugin-manager.d.ts.map +0 -1
- package/dist/plugins/plugin-manager.js +0 -595
- package/dist/plugins/plugin-manager.js.map +0 -1
- package/dist/types/index.d.ts +0 -224
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/utils/backup-rollback-manager.d.ts +0 -72
- package/dist/utils/backup-rollback-manager.d.ts.map +0 -1
- package/dist/utils/backup-rollback-manager.js +0 -289
- package/dist/utils/backup-rollback-manager.js.map +0 -1
- package/dist/utils/claude-config-installer.d.ts +0 -98
- package/dist/utils/claude-config-installer.d.ts.map +0 -1
- package/dist/utils/claude-config-installer.js +0 -678
- package/dist/utils/claude-config-installer.js.map +0 -1
- package/dist/utils/config-manager.d.ts +0 -73
- package/dist/utils/config-manager.d.ts.map +0 -1
- package/dist/utils/config-manager.js +0 -339
- package/dist/utils/config-manager.js.map +0 -1
- package/dist/utils/error-handler.d.ts +0 -46
- package/dist/utils/error-handler.d.ts.map +0 -1
- package/dist/utils/error-handler.js +0 -169
- package/dist/utils/error-handler.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -25
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -105
- package/dist/utils/logger.js.map +0 -1
- 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
|
+
}
|