@wundr.io/cli 1.0.1 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/ai/ai-service.d.ts +152 -0
  2. package/dist/ai/ai-service.d.ts.map +1 -0
  3. package/dist/ai/ai-service.js +430 -0
  4. package/dist/ai/ai-service.js.map +1 -0
  5. package/dist/ai/claude-client.d.ts +130 -0
  6. package/dist/ai/claude-client.d.ts.map +1 -0
  7. package/dist/ai/claude-client.js +340 -0
  8. package/dist/ai/claude-client.js.map +1 -0
  9. package/dist/ai/conversation-manager.d.ts +164 -0
  10. package/dist/ai/conversation-manager.d.ts.map +1 -0
  11. package/dist/ai/conversation-manager.js +614 -0
  12. package/dist/ai/conversation-manager.js.map +1 -0
  13. package/dist/ai/index.d.ts +5 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +8 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/cli.d.ts +36 -0
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/cli.js +192 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/commands/ai.d.ts +89 -0
  22. package/dist/commands/ai.d.ts.map +1 -0
  23. package/dist/commands/ai.js +799 -0
  24. package/dist/commands/ai.js.map +1 -0
  25. package/dist/commands/alignment.d.ts +78 -0
  26. package/dist/commands/alignment.d.ts.map +1 -0
  27. package/dist/commands/alignment.js +817 -0
  28. package/dist/commands/alignment.js.map +1 -0
  29. package/dist/commands/analyze-optimized.d.ts +14 -0
  30. package/dist/commands/analyze-optimized.d.ts.map +1 -0
  31. package/dist/commands/analyze-optimized.js +600 -0
  32. package/dist/commands/analyze-optimized.js.map +1 -0
  33. package/dist/commands/analyze.d.ts +65 -0
  34. package/dist/commands/analyze.d.ts.map +1 -0
  35. package/dist/commands/analyze.js +435 -0
  36. package/dist/commands/analyze.js.map +1 -0
  37. package/dist/commands/batch.d.ts +71 -0
  38. package/dist/commands/batch.d.ts.map +1 -0
  39. package/dist/commands/batch.js +738 -0
  40. package/dist/commands/batch.js.map +1 -0
  41. package/dist/commands/chat.d.ts +71 -0
  42. package/dist/commands/chat.d.ts.map +1 -0
  43. package/dist/commands/chat.js +674 -0
  44. package/dist/commands/chat.js.map +1 -0
  45. package/dist/commands/claude-init.d.ts +28 -0
  46. package/dist/commands/claude-init.d.ts.map +1 -0
  47. package/dist/commands/claude-init.js +591 -0
  48. package/dist/commands/claude-init.js.map +1 -0
  49. package/dist/commands/claude-setup.d.ts +119 -0
  50. package/dist/commands/claude-setup.d.ts.map +1 -0
  51. package/dist/commands/claude-setup.js +1073 -0
  52. package/dist/commands/claude-setup.js.map +1 -0
  53. package/dist/commands/computer-setup-commands.d.ts +53 -0
  54. package/dist/commands/computer-setup-commands.d.ts.map +1 -0
  55. package/dist/commands/computer-setup-commands.js +705 -0
  56. package/dist/commands/computer-setup-commands.js.map +1 -0
  57. package/dist/commands/computer-setup.d.ts +7 -0
  58. package/dist/commands/computer-setup.d.ts.map +1 -0
  59. package/dist/commands/computer-setup.js +849 -0
  60. package/dist/commands/computer-setup.js.map +1 -0
  61. package/dist/commands/create-command.d.ts +7 -0
  62. package/dist/commands/create-command.d.ts.map +1 -0
  63. package/dist/commands/create-command.js +158 -0
  64. package/dist/commands/create-command.js.map +1 -0
  65. package/dist/commands/create.d.ts +74 -0
  66. package/dist/commands/create.d.ts.map +1 -0
  67. package/dist/commands/create.js +556 -0
  68. package/dist/commands/create.js.map +1 -0
  69. package/dist/commands/dashboard.d.ts +91 -0
  70. package/dist/commands/dashboard.d.ts.map +1 -0
  71. package/dist/commands/dashboard.js +538 -0
  72. package/dist/commands/dashboard.js.map +1 -0
  73. package/dist/commands/govern.d.ts +70 -0
  74. package/dist/commands/govern.d.ts.map +1 -0
  75. package/dist/commands/govern.js +481 -0
  76. package/dist/commands/govern.js.map +1 -0
  77. package/dist/commands/governance.d.ts +17 -0
  78. package/dist/commands/governance.d.ts.map +1 -0
  79. package/dist/commands/governance.js +703 -0
  80. package/dist/commands/governance.js.map +1 -0
  81. package/dist/commands/guardian.d.ts +20 -0
  82. package/dist/commands/guardian.d.ts.map +1 -0
  83. package/dist/commands/guardian.js +597 -0
  84. package/dist/commands/guardian.js.map +1 -0
  85. package/dist/commands/init.d.ts +59 -0
  86. package/dist/commands/init.d.ts.map +1 -0
  87. package/dist/commands/init.js +650 -0
  88. package/dist/commands/init.js.map +1 -0
  89. package/dist/commands/performance-optimizer.d.ts +30 -0
  90. package/dist/commands/performance-optimizer.d.ts.map +1 -0
  91. package/dist/commands/performance-optimizer.js +650 -0
  92. package/dist/commands/performance-optimizer.js.map +1 -0
  93. package/dist/commands/plugins.d.ts +87 -0
  94. package/dist/commands/plugins.d.ts.map +1 -0
  95. package/dist/commands/plugins.js +685 -0
  96. package/dist/commands/plugins.js.map +1 -0
  97. package/dist/commands/rag.d.ts +7 -0
  98. package/dist/commands/rag.d.ts.map +1 -0
  99. package/dist/commands/rag.js +748 -0
  100. package/dist/commands/rag.js.map +1 -0
  101. package/dist/commands/session.d.ts +41 -0
  102. package/dist/commands/session.d.ts.map +1 -0
  103. package/dist/commands/session.js +441 -0
  104. package/dist/commands/session.js.map +1 -0
  105. package/dist/commands/setup.d.ts +29 -0
  106. package/dist/commands/setup.d.ts.map +1 -0
  107. package/dist/commands/setup.js +397 -0
  108. package/dist/commands/setup.js.map +1 -0
  109. package/dist/commands/test-init.d.ts +9 -0
  110. package/dist/commands/test-init.d.ts.map +1 -0
  111. package/dist/commands/test-init.js +222 -0
  112. package/dist/commands/test-init.js.map +1 -0
  113. package/dist/commands/test.d.ts +25 -0
  114. package/dist/commands/test.d.ts.map +1 -0
  115. package/dist/commands/test.js +217 -0
  116. package/dist/commands/test.js.map +1 -0
  117. package/dist/commands/vp.d.ts +7 -0
  118. package/dist/commands/vp.d.ts.map +1 -0
  119. package/dist/commands/vp.js +571 -0
  120. package/dist/commands/vp.js.map +1 -0
  121. package/dist/commands/watch.d.ts +76 -0
  122. package/dist/commands/watch.d.ts.map +1 -0
  123. package/dist/commands/watch.js +613 -0
  124. package/dist/commands/watch.js.map +1 -0
  125. package/dist/commands/worktree.d.ts +63 -0
  126. package/dist/commands/worktree.d.ts.map +1 -0
  127. package/dist/commands/worktree.js +774 -0
  128. package/dist/commands/worktree.js.map +1 -0
  129. package/dist/context/context-manager.d.ts +155 -0
  130. package/dist/context/context-manager.d.ts.map +1 -0
  131. package/dist/context/context-manager.js +383 -0
  132. package/dist/context/context-manager.js.map +1 -0
  133. package/dist/context/index.d.ts +3 -0
  134. package/dist/context/index.d.ts.map +1 -0
  135. package/dist/context/index.js +6 -0
  136. package/dist/context/index.js.map +1 -0
  137. package/dist/context/session-manager.d.ts +207 -0
  138. package/dist/context/session-manager.d.ts.map +1 -0
  139. package/dist/context/session-manager.js +686 -0
  140. package/dist/context/session-manager.js.map +1 -0
  141. package/dist/index.d.ts +8 -0
  142. package/dist/index.d.ts.map +1 -0
  143. package/dist/index.js +51 -0
  144. package/dist/index.js.map +1 -0
  145. package/dist/interactive/interactive-mode.d.ts +76 -0
  146. package/dist/interactive/interactive-mode.d.ts.map +1 -0
  147. package/dist/interactive/interactive-mode.js +732 -0
  148. package/dist/interactive/interactive-mode.js.map +1 -0
  149. package/dist/nlp/command-mapper.d.ts +174 -0
  150. package/dist/nlp/command-mapper.d.ts.map +1 -0
  151. package/dist/nlp/command-mapper.js +624 -0
  152. package/dist/nlp/command-mapper.js.map +1 -0
  153. package/dist/nlp/command-parser.d.ts +106 -0
  154. package/dist/nlp/command-parser.d.ts.map +1 -0
  155. package/dist/nlp/command-parser.js +417 -0
  156. package/dist/nlp/command-parser.js.map +1 -0
  157. package/dist/nlp/index.d.ts +5 -0
  158. package/dist/nlp/index.d.ts.map +1 -0
  159. package/dist/nlp/index.js +8 -0
  160. package/dist/nlp/index.js.map +1 -0
  161. package/dist/nlp/intent-classifier.d.ts +59 -0
  162. package/dist/nlp/intent-classifier.d.ts.map +1 -0
  163. package/dist/nlp/intent-classifier.js +384 -0
  164. package/dist/nlp/intent-classifier.js.map +1 -0
  165. package/dist/nlp/intent-parser.d.ts +152 -0
  166. package/dist/nlp/intent-parser.d.ts.map +1 -0
  167. package/dist/nlp/intent-parser.js +744 -0
  168. package/dist/nlp/intent-parser.js.map +1 -0
  169. package/dist/plugins/plugin-manager.d.ts +120 -0
  170. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  171. package/dist/plugins/plugin-manager.js +595 -0
  172. package/dist/plugins/plugin-manager.js.map +1 -0
  173. package/dist/types/index.d.ts +224 -0
  174. package/dist/types/index.d.ts.map +1 -0
  175. package/dist/types/index.js +3 -0
  176. package/dist/types/index.js.map +1 -0
  177. package/dist/utils/backup-rollback-manager.d.ts +72 -0
  178. package/dist/utils/backup-rollback-manager.d.ts.map +1 -0
  179. package/dist/utils/backup-rollback-manager.js +289 -0
  180. package/dist/utils/backup-rollback-manager.js.map +1 -0
  181. package/dist/utils/claude-config-installer.d.ts +94 -0
  182. package/dist/utils/claude-config-installer.d.ts.map +1 -0
  183. package/dist/utils/claude-config-installer.js +628 -0
  184. package/dist/utils/claude-config-installer.js.map +1 -0
  185. package/dist/utils/config-manager.d.ts +73 -0
  186. package/dist/utils/config-manager.d.ts.map +1 -0
  187. package/dist/utils/config-manager.js +339 -0
  188. package/dist/utils/config-manager.js.map +1 -0
  189. package/dist/utils/error-handler.d.ts +46 -0
  190. package/dist/utils/error-handler.d.ts.map +1 -0
  191. package/dist/utils/error-handler.js +169 -0
  192. package/dist/utils/error-handler.js.map +1 -0
  193. package/dist/utils/logger.d.ts +25 -0
  194. package/dist/utils/logger.d.ts.map +1 -0
  195. package/dist/utils/logger.js +105 -0
  196. package/dist/utils/logger.js.map +1 -0
  197. package/package.json +6 -4
  198. package/src/ai/ai-service.ts +22 -19
  199. package/src/ai/claude-client.ts +20 -16
  200. package/src/ai/conversation-manager.ts +37 -30
  201. package/src/cli.ts +46 -17
  202. package/src/commands/ai.ts +196 -88
  203. package/src/commands/alignment.ts +1212 -0
  204. package/src/commands/analyze-optimized.ts +394 -89
  205. package/src/commands/analyze.ts +22 -20
  206. package/src/commands/batch.ts +41 -38
  207. package/src/commands/chat.ts +37 -34
  208. package/src/commands/claude-init.ts +38 -30
  209. package/src/commands/claude-setup.ts +692 -97
  210. package/src/commands/computer-setup-commands.ts +45 -39
  211. package/src/commands/computer-setup.ts +490 -20
  212. package/src/commands/create-command.ts +7 -7
  213. package/src/commands/create.ts +36 -33
  214. package/src/commands/dashboard.ts +33 -28
  215. package/src/commands/govern.ts +34 -29
  216. package/src/commands/governance.ts +1005 -0
  217. package/src/commands/guardian.ts +887 -0
  218. package/src/commands/init.ts +112 -22
  219. package/src/commands/performance-optimizer.ts +48 -42
  220. package/src/commands/plugins.ts +35 -32
  221. package/src/commands/project-update.ts +1053 -0
  222. package/src/commands/rag.ts +904 -0
  223. package/src/commands/session.ts +631 -0
  224. package/src/commands/setup.ts +35 -31
  225. package/src/commands/test-init.ts +6 -5
  226. package/src/commands/test.ts +7 -6
  227. package/src/commands/vp.ts +762 -0
  228. package/src/commands/watch.ts +44 -33
  229. package/src/commands/worktree.ts +1057 -0
  230. package/src/context/context-manager.ts +15 -12
  231. package/src/context/session-manager.ts +51 -40
  232. package/src/index.ts +7 -6
  233. package/src/interactive/interactive-mode.ts +25 -18
  234. package/src/lib/conflict-resolution.ts +28 -0
  235. package/src/lib/merge-strategy.ts +28 -0
  236. package/src/lib/safety-mechanisms.ts +47 -0
  237. package/src/lib/state-detection.ts +28 -0
  238. package/src/nlp/command-mapper.ts +35 -30
  239. package/src/nlp/command-parser.ts +20 -17
  240. package/src/nlp/intent-classifier.ts +7 -7
  241. package/src/nlp/intent-parser.ts +61 -49
  242. package/src/plugins/plugin-manager.ts +27 -23
  243. package/src/types/index.ts +1 -1
  244. package/src/types/modules.d.ts +1 -0
  245. package/src/utils/backup-rollback-manager.ts +13 -11
  246. package/src/utils/claude-config-installer.ts +18 -16
  247. package/src/utils/config-manager.ts +12 -9
  248. package/src/utils/error-handler.ts +5 -3
  249. package/src/utils/logger.ts +35 -12
@@ -0,0 +1,887 @@
1
+ /**
2
+ * Guardian Dashboard CLI Commands
3
+ *
4
+ * Provides CLI access to the Guardian Dashboard for alignment monitoring,
5
+ * drift detection, and intervention management.
6
+ *
7
+ * Commands:
8
+ * - wundr guardian report - Generate daily alignment report
9
+ * - wundr guardian review - Show sessions requiring review
10
+ * - wundr guardian interventions - List recent interventions
11
+ * - wundr guardian dashboard - Open/display dashboard
12
+ */
13
+
14
+ import * as fs from 'fs/promises';
15
+ import * as os from 'os';
16
+ import * as path from 'path';
17
+
18
+ import chalk from 'chalk';
19
+ import { Command } from 'commander';
20
+ import ora from 'ora';
21
+
22
+ import {
23
+ AlignmentDebtCalculator,
24
+ DriftScoreAggregator,
25
+ InterventionRecommender,
26
+ type InterventionRecommendation,
27
+ type InterventionSeverity,
28
+ type AggregatedDriftReport,
29
+ type SessionDriftData,
30
+ type HealthStatus,
31
+ } from '@wundr/guardian-dashboard';
32
+
33
+ // ============================================================================
34
+ // Constants
35
+ // ============================================================================
36
+
37
+ const GUARDIAN_BASE_DIR = path.join(os.homedir(), '.wundr', 'guardian');
38
+ const INTERVENTIONS_FILE = path.join(GUARDIAN_BASE_DIR, 'interventions.json');
39
+ const REVIEW_QUEUE_FILE = path.join(GUARDIAN_BASE_DIR, 'review-queue.json');
40
+ const SESSIONS_STATE_FILE = path.join(
41
+ os.homedir(),
42
+ '.wundr',
43
+ 'sessions',
44
+ 'state.json',
45
+ );
46
+ const REPORTS_DIR = path.join(GUARDIAN_BASE_DIR, 'reports');
47
+
48
+ // ============================================================================
49
+ // Types
50
+ // ============================================================================
51
+
52
+ interface ReportOptions {
53
+ date?: string;
54
+ output?: string;
55
+ format?: 'md' | 'json' | 'html';
56
+ }
57
+
58
+ interface InterventionsOptions {
59
+ days?: string;
60
+ session?: string;
61
+ }
62
+
63
+ interface StoredIntervention {
64
+ id: string;
65
+ sessionId: string;
66
+ timestamp: string;
67
+ severity: InterventionSeverity;
68
+ dimension: string;
69
+ action: string;
70
+ rationale: string;
71
+ status: 'pending' | 'applied' | 'dismissed';
72
+ resolvedAt?: string;
73
+ }
74
+
75
+ interface ReviewQueueItem {
76
+ sessionId: string;
77
+ flaggedAt: string;
78
+ reason: string;
79
+ severity: InterventionSeverity;
80
+ driftScore: number;
81
+ metrics?: {
82
+ policyViolationRate?: number;
83
+ intentOutcomeGap?: number;
84
+ evaluatorDisagreement?: number;
85
+ };
86
+ reviewed?: boolean;
87
+ reviewedAt?: string;
88
+ }
89
+
90
+ interface GuardianState {
91
+ interventions: StoredIntervention[];
92
+ reviewQueue: ReviewQueueItem[];
93
+ }
94
+
95
+ // ============================================================================
96
+ // Utility Functions
97
+ // ============================================================================
98
+
99
+ function getTimestamp(): string {
100
+ return new Date().toISOString();
101
+ }
102
+
103
+ function formatDate(date: Date): string {
104
+ return date.toISOString().split('T')[0] ?? '';
105
+ }
106
+
107
+ function padRight(str: string, length: number): string {
108
+ return str.length >= length
109
+ ? str.substring(0, length)
110
+ : str + ' '.repeat(length - str.length);
111
+ }
112
+
113
+ function truncate(str: string, length: number): string {
114
+ if (str.length <= length) {
115
+ return str;
116
+ }
117
+ return str.substring(0, length - 3) + '...';
118
+ }
119
+
120
+ async function ensureGuardianDir(): Promise<void> {
121
+ await fs.mkdir(GUARDIAN_BASE_DIR, { recursive: true });
122
+ await fs.mkdir(REPORTS_DIR, { recursive: true });
123
+ }
124
+
125
+ /**
126
+ * Get color function based on severity
127
+ */
128
+ function getSeverityColor(
129
+ severity: InterventionSeverity,
130
+ ): (str: string) => string {
131
+ switch (severity) {
132
+ case 'critical':
133
+ return chalk.red;
134
+ case 'high':
135
+ return chalk.yellow;
136
+ case 'medium':
137
+ return chalk.blue;
138
+ case 'low':
139
+ return chalk.gray;
140
+ default:
141
+ return chalk.white;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Get color function based on health status
147
+ */
148
+ function getStatusColor(status: HealthStatus): (str: string) => string {
149
+ switch (status) {
150
+ case 'HEALTHY':
151
+ return chalk.green;
152
+ case 'CONCERNING':
153
+ return chalk.yellow;
154
+ case 'CRITICAL':
155
+ return chalk.red;
156
+ default:
157
+ return chalk.white;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Format severity badge
163
+ */
164
+ function getSeverityBadge(severity: InterventionSeverity): string {
165
+ const badges: Record<InterventionSeverity, string> = {
166
+ critical: '[CRITICAL]',
167
+ high: '[HIGH]',
168
+ medium: '[MEDIUM]',
169
+ low: '[LOW]',
170
+ };
171
+ return badges[severity] ?? '[UNKNOWN]';
172
+ }
173
+
174
+ /**
175
+ * Load guardian state from disk
176
+ */
177
+ async function loadGuardianState(): Promise<GuardianState> {
178
+ try {
179
+ await ensureGuardianDir();
180
+
181
+ let interventions: StoredIntervention[] = [];
182
+ let reviewQueue: ReviewQueueItem[] = [];
183
+
184
+ try {
185
+ const interventionsContent = await fs.readFile(
186
+ INTERVENTIONS_FILE,
187
+ 'utf-8',
188
+ );
189
+ interventions = JSON.parse(interventionsContent) as StoredIntervention[];
190
+ } catch {
191
+ // File doesn't exist yet
192
+ }
193
+
194
+ try {
195
+ const reviewQueueContent = await fs.readFile(REVIEW_QUEUE_FILE, 'utf-8');
196
+ reviewQueue = JSON.parse(reviewQueueContent) as ReviewQueueItem[];
197
+ } catch {
198
+ // File doesn't exist yet
199
+ }
200
+
201
+ return { interventions, reviewQueue };
202
+ } catch {
203
+ return { interventions: [], reviewQueue: [] };
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Save guardian state to disk
209
+ */
210
+ async function saveGuardianState(state: GuardianState): Promise<void> {
211
+ await ensureGuardianDir();
212
+ await fs.writeFile(
213
+ INTERVENTIONS_FILE,
214
+ JSON.stringify(state.interventions, null, 2),
215
+ );
216
+ await fs.writeFile(
217
+ REVIEW_QUEUE_FILE,
218
+ JSON.stringify(state.reviewQueue, null, 2),
219
+ );
220
+ }
221
+
222
+ /**
223
+ * Load session drift data from sessions state
224
+ */
225
+ async function loadSessionDriftData(): Promise<SessionDriftData[]> {
226
+ try {
227
+ const content = await fs.readFile(SESSIONS_STATE_FILE, 'utf-8');
228
+ const state = JSON.parse(content) as {
229
+ sessions?: Array<{
230
+ sessionId: string;
231
+ startedAt: string;
232
+ metrics?: { errors?: number };
233
+ }>;
234
+ };
235
+
236
+ // Convert session data to drift data format
237
+ return (state.sessions ?? []).map(session => ({
238
+ sessionId: session.sessionId,
239
+ timestamp: new Date(session.startedAt),
240
+ driftScore: calculateDriftScoreFromSession(session),
241
+ metrics: {
242
+ testCoverage: 80, // Default values - would come from actual session data
243
+ codePatternAdherence: 85,
244
+ documentationCoverage: 70,
245
+ securityCompliance: 90,
246
+ performanceBenchmark: 75,
247
+ },
248
+ }));
249
+ } catch {
250
+ return [];
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Calculate drift score from session data
256
+ */
257
+ function calculateDriftScoreFromSession(session: {
258
+ metrics?: { errors?: number };
259
+ }): number {
260
+ // Simplified drift calculation - in production would use actual metrics
261
+ const baseScore = 85;
262
+ const errorPenalty = (session.metrics?.errors ?? 0) * 5;
263
+ return Math.max(0, Math.min(100, baseScore - errorPenalty));
264
+ }
265
+
266
+ // ============================================================================
267
+ // Command Implementations
268
+ // ============================================================================
269
+
270
+ /**
271
+ * Generate daily alignment report
272
+ */
273
+ async function generateReport(options: ReportOptions): Promise<void> {
274
+ const spinner = ora('Generating alignment report...').start();
275
+
276
+ try {
277
+ await ensureGuardianDir();
278
+
279
+ // Parse date option
280
+ const reportDate = options.date ? new Date(options.date) : new Date();
281
+ const dateStr = formatDate(reportDate);
282
+
283
+ // Load session data
284
+ const sessionData = await loadSessionDriftData();
285
+ const guardianState = await loadGuardianState();
286
+
287
+ // Create aggregator and calculate report
288
+ const aggregator = new DriftScoreAggregator();
289
+ aggregator.addSessions(sessionData);
290
+
291
+ const report = aggregator.aggregateSessionScores(sessionData);
292
+
293
+ // Get intervention recommendations
294
+ const recommender = new InterventionRecommender();
295
+ const recommendations =
296
+ sessionData.length > 0
297
+ ? recommender.recommendInterventions({
298
+ policyViolationRate: 0.02,
299
+ intentOutcomeGap: 0.1,
300
+ evaluatorDisagreementRate: 0.15,
301
+ })
302
+ : [];
303
+
304
+ spinner.stop();
305
+
306
+ // Format output
307
+ const format = options.format ?? 'md';
308
+
309
+ if (format === 'json') {
310
+ const jsonReport = {
311
+ date: dateStr,
312
+ generatedAt: getTimestamp(),
313
+ summary: {
314
+ totalSessions: report.totalSessions,
315
+ averageDriftScore: report.averageScore,
316
+ minScore: report.minScore,
317
+ maxScore: report.maxScore,
318
+ overallStatus: report.overallStatus,
319
+ trend: report.trend,
320
+ },
321
+ criticalSessions: report.criticalSessions.length,
322
+ concerningSessions: report.concerningSessions.length,
323
+ interventions: recommendations,
324
+ reviewQueue: guardianState.reviewQueue.filter(r => !r.reviewed).length,
325
+ };
326
+
327
+ if (options.output) {
328
+ await fs.writeFile(options.output, JSON.stringify(jsonReport, null, 2));
329
+ console.log(chalk.green(`Report saved to: ${options.output}`));
330
+ } else {
331
+ console.log(JSON.stringify(jsonReport, null, 2));
332
+ }
333
+ return;
334
+ }
335
+
336
+ // Markdown format
337
+ const mdReport = generateMarkdownReport(
338
+ dateStr,
339
+ report,
340
+ recommendations,
341
+ guardianState,
342
+ );
343
+
344
+ if (options.output) {
345
+ await fs.writeFile(options.output, mdReport);
346
+ console.log(chalk.green(`Report saved to: ${options.output}`));
347
+ } else {
348
+ // Display in terminal
349
+ displayTerminalReport(dateStr, report, recommendations, guardianState);
350
+ }
351
+ } catch (error) {
352
+ spinner.fail('Failed to generate report');
353
+ console.error(
354
+ chalk.red(error instanceof Error ? error.message : String(error)),
355
+ );
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Generate markdown report
361
+ */
362
+ function generateMarkdownReport(
363
+ dateStr: string,
364
+ report: AggregatedDriftReport,
365
+ recommendations: InterventionRecommendation[],
366
+ guardianState: GuardianState,
367
+ ): string {
368
+ let md = '# Guardian Daily Alignment Report\n\n';
369
+ md += `**Date:** ${dateStr}\n`;
370
+ md += `**Generated:** ${getTimestamp()}\n\n`;
371
+
372
+ md += '## Summary\n\n';
373
+ md += '| Metric | Value |\n';
374
+ md += '|--------|-------|\n';
375
+ md += `| Total Sessions | ${report.totalSessions} |\n`;
376
+ md += `| Average Drift Score | ${report.averageScore.toFixed(1)} |\n`;
377
+ md += `| Overall Status | ${report.overallStatus} |\n`;
378
+ md += `| Trend | ${report.trend} |\n`;
379
+ md += `| Critical Sessions | ${report.criticalSessions.length} |\n`;
380
+ md += `| Concerning Sessions | ${report.concerningSessions.length} |\n`;
381
+ md += '\n';
382
+
383
+ if (recommendations.length > 0) {
384
+ md += '## Intervention Recommendations\n\n';
385
+ for (const rec of recommendations) {
386
+ md += `### ${rec.dimension} (${rec.severity.toUpperCase()})\n\n`;
387
+ md += `- **Action:** ${rec.action}\n`;
388
+ md += `- **Rationale:** ${rec.rationale}\n`;
389
+ md += `- **Urgency:** ${rec.urgency} hours\n\n`;
390
+ }
391
+ }
392
+
393
+ const pendingReviews = guardianState.reviewQueue.filter(r => !r.reviewed);
394
+ if (pendingReviews.length > 0) {
395
+ md += '## Sessions Requiring Review\n\n';
396
+ md += '| Session ID | Severity | Drift Score | Reason |\n';
397
+ md += '|------------|----------|-------------|--------|\n';
398
+ for (const item of pendingReviews) {
399
+ md += `| ${item.sessionId} | ${item.severity.toUpperCase()} | ${item.driftScore.toFixed(1)} | ${item.reason} |\n`;
400
+ }
401
+ md += '\n';
402
+ }
403
+
404
+ return md;
405
+ }
406
+
407
+ /**
408
+ * Display report in terminal with colors
409
+ */
410
+ function displayTerminalReport(
411
+ dateStr: string,
412
+ report: AggregatedDriftReport,
413
+ recommendations: InterventionRecommendation[],
414
+ guardianState: GuardianState,
415
+ ): void {
416
+ console.log(chalk.cyan('\n' + '='.repeat(70)));
417
+ console.log(chalk.cyan.bold(' GUARDIAN DAILY ALIGNMENT REPORT'));
418
+ console.log(chalk.cyan('='.repeat(70)));
419
+
420
+ console.log(chalk.gray(`Date: ${dateStr}`));
421
+ console.log(chalk.gray(`Generated: ${getTimestamp()}\n`));
422
+
423
+ // Summary section
424
+ console.log(chalk.bold('Summary'));
425
+ console.log(chalk.gray('-'.repeat(40)));
426
+
427
+ const statusColor = getStatusColor(report.overallStatus);
428
+ console.log(`Total Sessions: ${chalk.white(report.totalSessions)}`);
429
+ console.log(
430
+ `Average Drift Score: ${chalk.white(report.averageScore.toFixed(1))}`,
431
+ );
432
+ console.log(`Overall Status: ${statusColor(report.overallStatus)}`);
433
+ console.log(`Trend: ${chalk.white(report.trend)}`);
434
+ console.log(
435
+ `Critical Sessions: ${report.criticalSessions.length > 0 ? chalk.red(report.criticalSessions.length) : chalk.green('0')}`,
436
+ );
437
+ console.log(
438
+ `Concerning Sessions: ${report.concerningSessions.length > 0 ? chalk.yellow(report.concerningSessions.length) : chalk.green('0')}`,
439
+ );
440
+ console.log('');
441
+
442
+ // Interventions section
443
+ if (recommendations.length > 0) {
444
+ console.log(chalk.bold('Intervention Recommendations'));
445
+ console.log(chalk.gray('-'.repeat(40)));
446
+
447
+ for (const rec of recommendations) {
448
+ const severityColor = getSeverityColor(rec.severity);
449
+ console.log(
450
+ `${severityColor(getSeverityBadge(rec.severity))} ${chalk.white(rec.dimension)}`,
451
+ );
452
+ console.log(` Action: ${rec.action}`);
453
+ console.log(` Urgency: ${rec.urgency}h`);
454
+ console.log('');
455
+ }
456
+ } else {
457
+ console.log(chalk.green('No interventions required.'));
458
+ console.log('');
459
+ }
460
+
461
+ // Review queue
462
+ const pendingReviews = guardianState.reviewQueue.filter(r => !r.reviewed);
463
+ if (pendingReviews.length > 0) {
464
+ console.log(chalk.bold('Sessions Requiring Review'));
465
+ console.log(chalk.gray('-'.repeat(40)));
466
+ console.log(
467
+ chalk.yellow(
468
+ `${pendingReviews.length} session(s) flagged for Guardian attention.`,
469
+ ),
470
+ );
471
+ console.log(chalk.gray('Run "wundr guardian review" for details.\n'));
472
+ }
473
+
474
+ console.log(chalk.cyan('='.repeat(70)));
475
+ }
476
+
477
+ /**
478
+ * Show sessions requiring Guardian review
479
+ */
480
+ async function showReviewQueue(): Promise<void> {
481
+ const spinner = ora('Loading review queue...').start();
482
+
483
+ try {
484
+ const state = await loadGuardianState();
485
+ const pendingReviews = state.reviewQueue.filter(r => !r.reviewed);
486
+
487
+ spinner.stop();
488
+
489
+ console.log(chalk.cyan('\nGuardian Review Queue'));
490
+ console.log(chalk.gray('='.repeat(90)));
491
+
492
+ if (pendingReviews.length === 0) {
493
+ console.log(chalk.green('\nNo sessions require Guardian review.'));
494
+ console.log(
495
+ chalk.gray('All systems operating within acceptable parameters.\n'),
496
+ );
497
+ return;
498
+ }
499
+
500
+ // Table header
501
+ console.log(
502
+ chalk.cyan(
503
+ padRight('Session ID', 20) +
504
+ padRight('Severity', 12) +
505
+ padRight('Drift Score', 14) +
506
+ padRight('Flagged At', 22) +
507
+ padRight('Reason', 22),
508
+ ),
509
+ );
510
+ console.log(chalk.gray('-'.repeat(90)));
511
+
512
+ // Table rows
513
+ for (const item of pendingReviews) {
514
+ const severityColor = getSeverityColor(item.severity);
515
+ const flaggedAt = new Date(item.flaggedAt).toLocaleString();
516
+
517
+ console.log(
518
+ padRight(item.sessionId, 20) +
519
+ severityColor(padRight(getSeverityBadge(item.severity), 12)) +
520
+ padRight(item.driftScore.toFixed(1), 14) +
521
+ padRight(flaggedAt, 22) +
522
+ chalk.gray(truncate(item.reason, 22)),
523
+ );
524
+ }
525
+
526
+ console.log(chalk.gray('-'.repeat(90)));
527
+ console.log(
528
+ chalk.gray(`Total: ${pendingReviews.length} session(s) pending review\n`),
529
+ );
530
+
531
+ // Show summary by severity
532
+ const criticalCount = pendingReviews.filter(
533
+ r => r.severity === 'critical',
534
+ ).length;
535
+ const highCount = pendingReviews.filter(r => r.severity === 'high').length;
536
+ const mediumCount = pendingReviews.filter(
537
+ r => r.severity === 'medium',
538
+ ).length;
539
+
540
+ if (criticalCount > 0) {
541
+ console.log(chalk.red(` CRITICAL: ${criticalCount}`));
542
+ }
543
+ if (highCount > 0) {
544
+ console.log(chalk.yellow(` HIGH: ${highCount}`));
545
+ }
546
+ if (mediumCount > 0) {
547
+ console.log(chalk.blue(` MEDIUM: ${mediumCount}`));
548
+ }
549
+ console.log('');
550
+ } catch (error) {
551
+ spinner.fail('Failed to load review queue');
552
+ console.error(
553
+ chalk.red(error instanceof Error ? error.message : String(error)),
554
+ );
555
+ }
556
+ }
557
+
558
+ /**
559
+ * List recent interventions
560
+ */
561
+ async function listInterventions(options: InterventionsOptions): Promise<void> {
562
+ const spinner = ora('Loading interventions...').start();
563
+
564
+ try {
565
+ const state = await loadGuardianState();
566
+ let interventions = state.interventions;
567
+
568
+ // Filter by days
569
+ const days = parseInt(options.days ?? '7', 10);
570
+ const cutoffDate = new Date();
571
+ cutoffDate.setDate(cutoffDate.getDate() - days);
572
+
573
+ interventions = interventions.filter(
574
+ i => new Date(i.timestamp) >= cutoffDate,
575
+ );
576
+
577
+ // Filter by session
578
+ if (options.session) {
579
+ interventions = interventions.filter(
580
+ i => i.sessionId === options.session,
581
+ );
582
+ }
583
+
584
+ // Sort by timestamp descending (most recent first)
585
+ interventions.sort(
586
+ (a, b) =>
587
+ new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
588
+ );
589
+
590
+ spinner.stop();
591
+
592
+ console.log(chalk.cyan('\nGuardian Interventions'));
593
+ console.log(
594
+ chalk.gray(
595
+ `Last ${days} days${options.session ? ` - Session: ${options.session}` : ''}`,
596
+ ),
597
+ );
598
+ console.log(chalk.gray('='.repeat(100)));
599
+
600
+ if (interventions.length === 0) {
601
+ console.log(
602
+ chalk.green('\nNo interventions recorded in the specified time period.'),
603
+ );
604
+ console.log('');
605
+ return;
606
+ }
607
+
608
+ // Table header
609
+ console.log(
610
+ chalk.cyan(
611
+ padRight('ID', 12) +
612
+ padRight('Session', 16) +
613
+ padRight('Severity', 12) +
614
+ padRight('Dimension', 20) +
615
+ padRight('Status', 12) +
616
+ padRight('Time', 18),
617
+ ),
618
+ );
619
+ console.log(chalk.gray('-'.repeat(100)));
620
+
621
+ // Table rows
622
+ for (const intervention of interventions) {
623
+ const severityColor = getSeverityColor(intervention.severity);
624
+ const statusColor =
625
+ intervention.status === 'applied'
626
+ ? chalk.green
627
+ : intervention.status === 'dismissed'
628
+ ? chalk.gray
629
+ : chalk.yellow;
630
+ const timestamp = new Date(intervention.timestamp).toLocaleString();
631
+
632
+ console.log(
633
+ padRight(intervention.id, 12) +
634
+ padRight(truncate(intervention.sessionId, 14), 16) +
635
+ severityColor(padRight(getSeverityBadge(intervention.severity), 12)) +
636
+ padRight(truncate(intervention.dimension, 18), 20) +
637
+ statusColor(padRight(`[${intervention.status.toUpperCase()}]`, 12)) +
638
+ chalk.gray(padRight(timestamp, 18)),
639
+ );
640
+ }
641
+
642
+ console.log(chalk.gray('-'.repeat(100)));
643
+ console.log(chalk.gray(`Total: ${interventions.length} intervention(s)\n`));
644
+
645
+ // Show summary
646
+ const applied = interventions.filter(i => i.status === 'applied').length;
647
+ const pending = interventions.filter(i => i.status === 'pending').length;
648
+ const dismissed = interventions.filter(
649
+ i => i.status === 'dismissed',
650
+ ).length;
651
+
652
+ console.log(chalk.gray('Summary:'));
653
+ console.log(
654
+ ` Applied: ${chalk.green(applied)} Pending: ${chalk.yellow(pending)} Dismissed: ${chalk.gray(dismissed)}`,
655
+ );
656
+ console.log('');
657
+ } catch (error) {
658
+ spinner.fail('Failed to load interventions');
659
+ console.error(
660
+ chalk.red(error instanceof Error ? error.message : String(error)),
661
+ );
662
+ }
663
+ }
664
+
665
+ /**
666
+ * Display terminal-based dashboard
667
+ */
668
+ async function displayDashboard(): Promise<void> {
669
+ const spinner = ora('Loading Guardian Dashboard...').start();
670
+
671
+ try {
672
+ const guardianState = await loadGuardianState();
673
+ const sessionData = await loadSessionDriftData();
674
+
675
+ // Calculate aggregated stats
676
+ const aggregator = new DriftScoreAggregator();
677
+ aggregator.addSessions(sessionData);
678
+ const report = aggregator.aggregateSessionScores(sessionData);
679
+
680
+ // Get recommendations
681
+ const recommender = new InterventionRecommender();
682
+ const recommendations = recommender.recommendInterventions({
683
+ policyViolationRate: 0.02,
684
+ intentOutcomeGap: 0.1,
685
+ evaluatorDisagreementRate: 0.15,
686
+ });
687
+
688
+ spinner.stop();
689
+
690
+ // Clear screen and display dashboard
691
+ console.clear();
692
+
693
+ console.log(chalk.cyan.bold('\n' + '='.repeat(80)));
694
+ console.log(
695
+ chalk.cyan.bold(' GUARDIAN ALIGNMENT DASHBOARD'),
696
+ );
697
+ console.log(chalk.cyan.bold('='.repeat(80)));
698
+ console.log(
699
+ chalk.gray(
700
+ ` Last Updated: ${new Date().toLocaleString()}`,
701
+ ),
702
+ );
703
+ console.log('');
704
+
705
+ // Status Overview Panel
706
+ console.log(chalk.cyan.bold(' STATUS OVERVIEW'));
707
+ console.log(chalk.gray(' ' + '-'.repeat(78)));
708
+
709
+ const statusColor = getStatusColor(report.overallStatus);
710
+ console.log(
711
+ ` Overall Status: ${statusColor(report.overallStatus.padEnd(12))} Avg Drift Score: ${chalk.white(report.averageScore.toFixed(1).padEnd(8))} Trend: ${chalk.white(report.trend)}`,
712
+ );
713
+ console.log('');
714
+
715
+ // Sessions Panel
716
+ console.log(chalk.cyan.bold(' SESSIONS'));
717
+ console.log(chalk.gray(' ' + '-'.repeat(78)));
718
+ console.log(
719
+ ` Total: ${chalk.white(report.totalSessions.toString().padEnd(6))} Critical: ${report.criticalSessions.length > 0 ? chalk.red(report.criticalSessions.length.toString().padEnd(4)) : chalk.green('0'.padEnd(4))} Concerning: ${report.concerningSessions.length > 0 ? chalk.yellow(report.concerningSessions.length.toString().padEnd(4)) : chalk.green('0'.padEnd(4))} Healthy: ${chalk.green((report.totalSessions - report.criticalSessions.length - report.concerningSessions.length).toString())}`,
720
+ );
721
+ console.log('');
722
+
723
+ // Interventions Panel
724
+ console.log(chalk.cyan.bold(' INTERVENTIONS'));
725
+ console.log(chalk.gray(' ' + '-'.repeat(78)));
726
+
727
+ const pendingInterventions = guardianState.interventions.filter(
728
+ i => i.status === 'pending',
729
+ );
730
+ const recentApplied = guardianState.interventions.filter(
731
+ i =>
732
+ i.status === 'applied' &&
733
+ new Date(i.timestamp) > new Date(Date.now() - 24 * 60 * 60 * 1000),
734
+ );
735
+
736
+ console.log(
737
+ ` Pending: ${pendingInterventions.length > 0 ? chalk.yellow(pendingInterventions.length.toString().padEnd(6)) : chalk.green('0'.padEnd(6))} Applied (24h): ${chalk.white(recentApplied.length.toString().padEnd(6))} Recommended: ${recommendations.length > 0 ? chalk.yellow(recommendations.length.toString()) : chalk.green('0')}`,
738
+ );
739
+ console.log('');
740
+
741
+ // Review Queue Panel
742
+ console.log(chalk.cyan.bold(' REVIEW QUEUE'));
743
+ console.log(chalk.gray(' ' + '-'.repeat(78)));
744
+
745
+ const pendingReviews = guardianState.reviewQueue.filter(r => !r.reviewed);
746
+
747
+ if (pendingReviews.length === 0) {
748
+ console.log(chalk.green(' No sessions require Guardian review.'));
749
+ } else {
750
+ const criticalReviews = pendingReviews.filter(
751
+ r => r.severity === 'critical',
752
+ ).length;
753
+ const highReviews = pendingReviews.filter(
754
+ r => r.severity === 'high',
755
+ ).length;
756
+
757
+ console.log(
758
+ ` Pending: ${chalk.yellow(pendingReviews.length.toString().padEnd(6))} Critical: ${criticalReviews > 0 ? chalk.red(criticalReviews.toString().padEnd(6)) : chalk.green('0'.padEnd(6))} High: ${highReviews > 0 ? chalk.yellow(highReviews.toString()) : chalk.green('0')}`,
759
+ );
760
+ }
761
+ console.log('');
762
+
763
+ // Recommendations Panel
764
+ if (recommendations.length > 0) {
765
+ console.log(chalk.cyan.bold(' ACTIVE RECOMMENDATIONS'));
766
+ console.log(chalk.gray(' ' + '-'.repeat(78)));
767
+
768
+ for (const rec of recommendations.slice(0, 3)) {
769
+ const severityColor = getSeverityColor(rec.severity);
770
+ console.log(
771
+ ` ${severityColor(getSeverityBadge(rec.severity))} ${rec.dimension}: ${truncate(rec.action, 50)}`,
772
+ );
773
+ }
774
+
775
+ if (recommendations.length > 3) {
776
+ console.log(
777
+ chalk.gray(` ... and ${recommendations.length - 3} more`),
778
+ );
779
+ }
780
+ console.log('');
781
+ }
782
+
783
+ // Quick Actions
784
+ console.log(chalk.cyan.bold(' QUICK ACTIONS'));
785
+ console.log(chalk.gray(' ' + '-'.repeat(78)));
786
+ console.log(
787
+ chalk.gray(
788
+ ' wundr guardian report - Generate full alignment report',
789
+ ),
790
+ );
791
+ console.log(
792
+ chalk.gray(
793
+ ' wundr guardian review - View sessions requiring attention',
794
+ ),
795
+ );
796
+ console.log(
797
+ chalk.gray(' wundr guardian interventions - List recent interventions'),
798
+ );
799
+ console.log('');
800
+
801
+ console.log(chalk.cyan.bold('='.repeat(80)));
802
+ console.log('');
803
+ } catch (error) {
804
+ spinner.fail('Failed to load dashboard');
805
+ console.error(
806
+ chalk.red(error instanceof Error ? error.message : String(error)),
807
+ );
808
+ }
809
+ }
810
+
811
+ // ============================================================================
812
+ // Command Factory
813
+ // ============================================================================
814
+
815
+ /**
816
+ * Creates the guardian command with all subcommands
817
+ */
818
+ export function createGuardianCommand(): Command {
819
+ const guardian = new Command('guardian')
820
+ .description(
821
+ 'Guardian Dashboard - AI alignment monitoring and intervention management',
822
+ )
823
+ .addHelpText(
824
+ 'after',
825
+ chalk.gray(`
826
+ Examples:
827
+ ${chalk.green('wundr guardian')} Display Guardian dashboard
828
+ ${chalk.green('wundr guardian report')} Generate daily alignment report
829
+ ${chalk.green('wundr guardian report --date 2025-01-15')} Generate report for specific date
830
+ ${chalk.green('wundr guardian report -o report.md')} Save report to file
831
+ ${chalk.green('wundr guardian review')} Show sessions requiring Guardian review
832
+ ${chalk.green('wundr guardian interventions')} List recent interventions
833
+ ${chalk.green('wundr guardian interventions --days 14')} Show interventions from last 14 days
834
+ `),
835
+ );
836
+
837
+ // Default action - show dashboard
838
+ guardian.action(async () => {
839
+ await displayDashboard();
840
+ });
841
+
842
+ // Report subcommand
843
+ guardian
844
+ .command('report')
845
+ .description('Generate daily alignment report')
846
+ .option('-d, --date <date>', 'Report date (YYYY-MM-DD format)')
847
+ .option('-o, --output <file>', 'Output file path')
848
+ .option('-f, --format <format>', 'Output format (md|json|html)', 'md')
849
+ .action(async (options: ReportOptions) => {
850
+ await generateReport(options);
851
+ });
852
+
853
+ // Review subcommand
854
+ guardian
855
+ .command('review')
856
+ .description('Show sessions requiring Guardian review')
857
+ .action(async () => {
858
+ await showReviewQueue();
859
+ });
860
+
861
+ // Interventions subcommand
862
+ guardian
863
+ .command('interventions')
864
+ .description('List recent interventions')
865
+ .option('-d, --days <n>', 'Number of days to look back', '7')
866
+ .option('-s, --session <id>', 'Filter by session ID')
867
+ .action(async (options: InterventionsOptions) => {
868
+ await listInterventions(options);
869
+ });
870
+
871
+ // Dashboard subcommand (explicit)
872
+ guardian
873
+ .command('dashboard')
874
+ .description('Display Guardian dashboard in terminal')
875
+ .action(async () => {
876
+ await displayDashboard();
877
+ });
878
+
879
+ return guardian;
880
+ }
881
+
882
+ // ============================================================================
883
+ // Exports
884
+ // ============================================================================
885
+
886
+ export { createGuardianCommand as guardianCommand };
887
+ export default createGuardianCommand;