@vreko/cli 3.0.1

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 (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +45 -0
  3. package/dist/CeremonyView-LQS7FTMK.js +134 -0
  4. package/dist/CeremonyView-LQS7FTMK.js.map +1 -0
  5. package/dist/InitApp-7K5DTYSW.js +1479 -0
  6. package/dist/InitApp-7K5DTYSW.js.map +1 -0
  7. package/dist/SkippedTestDetector-PJSKSOZR.js +7 -0
  8. package/dist/SkippedTestDetector-PJSKSOZR.js.map +1 -0
  9. package/dist/TuiApp-FX23XQBK.js +8 -0
  10. package/dist/TuiApp-FX23XQBK.js.map +1 -0
  11. package/dist/analysis-ABEO6RTN.js +8 -0
  12. package/dist/analysis-ABEO6RTN.js.map +1 -0
  13. package/dist/auth-XNBEBNPY.js +7669 -0
  14. package/dist/auth-XNBEBNPY.js.map +1 -0
  15. package/dist/ceremony-M7CXVBVA.js +45 -0
  16. package/dist/ceremony-M7CXVBVA.js.map +1 -0
  17. package/dist/chunk-A3QSZJPD.js +3147 -0
  18. package/dist/chunk-A3QSZJPD.js.map +1 -0
  19. package/dist/chunk-ASGZ5B6C.js +3969 -0
  20. package/dist/chunk-ASGZ5B6C.js.map +1 -0
  21. package/dist/chunk-DMXC2JTC.js +58 -0
  22. package/dist/chunk-DMXC2JTC.js.map +1 -0
  23. package/dist/chunk-EEBSK2IH.js +161 -0
  24. package/dist/chunk-EEBSK2IH.js.map +1 -0
  25. package/dist/chunk-EWOJGXRX.js +22 -0
  26. package/dist/chunk-EWOJGXRX.js.map +1 -0
  27. package/dist/chunk-F7GEJLP7.js +2389 -0
  28. package/dist/chunk-F7GEJLP7.js.map +1 -0
  29. package/dist/chunk-GOYL3F4T.js +605 -0
  30. package/dist/chunk-GOYL3F4T.js.map +1 -0
  31. package/dist/chunk-GRMRYWYS.js +17 -0
  32. package/dist/chunk-GRMRYWYS.js.map +1 -0
  33. package/dist/chunk-GSUGROXB.js +1951 -0
  34. package/dist/chunk-GSUGROXB.js.map +1 -0
  35. package/dist/chunk-H7773ONB.js +50 -0
  36. package/dist/chunk-H7773ONB.js.map +1 -0
  37. package/dist/chunk-HFQHU5LC.js +445 -0
  38. package/dist/chunk-HFQHU5LC.js.map +1 -0
  39. package/dist/chunk-IVHUBLJD.js +318 -0
  40. package/dist/chunk-IVHUBLJD.js.map +1 -0
  41. package/dist/chunk-KJWKY4L4.js +14 -0
  42. package/dist/chunk-KJWKY4L4.js.map +1 -0
  43. package/dist/chunk-MJVY2XUN.js +1793 -0
  44. package/dist/chunk-MJVY2XUN.js.map +1 -0
  45. package/dist/chunk-QWZVCJII.js +1797 -0
  46. package/dist/chunk-QWZVCJII.js.map +1 -0
  47. package/dist/chunk-VTSNRV3V.js +3237 -0
  48. package/dist/chunk-VTSNRV3V.js.map +1 -0
  49. package/dist/chunk-W5B4GTXR.js +1466 -0
  50. package/dist/chunk-W5B4GTXR.js.map +1 -0
  51. package/dist/chunk-WZEZLVOW.js +4995 -0
  52. package/dist/chunk-WZEZLVOW.js.map +1 -0
  53. package/dist/chunk-YPTTIXKC.js +199 -0
  54. package/dist/chunk-YPTTIXKC.js.map +1 -0
  55. package/dist/chunk-Z55UGM6X.js +6360 -0
  56. package/dist/chunk-Z55UGM6X.js.map +1 -0
  57. package/dist/chunk-ZIIRQODJ.js +110 -0
  58. package/dist/chunk-ZIIRQODJ.js.map +1 -0
  59. package/dist/chunk-ZSUQ4FMB.js +77 -0
  60. package/dist/chunk-ZSUQ4FMB.js.map +1 -0
  61. package/dist/client-JMTSZS3V.js +10 -0
  62. package/dist/client-JMTSZS3V.js.map +1 -0
  63. package/dist/deprecated-snap.js +19 -0
  64. package/dist/deprecated-snap.js.map +1 -0
  65. package/dist/dist-2KWBZFLA.js +14 -0
  66. package/dist/dist-2KWBZFLA.js.map +1 -0
  67. package/dist/dist-5ZYKNNU3.js +7 -0
  68. package/dist/dist-5ZYKNNU3.js.map +1 -0
  69. package/dist/dist-CP3RFHPI.js +11 -0
  70. package/dist/dist-CP3RFHPI.js.map +1 -0
  71. package/dist/gecko-53ITAGG6.js +56 -0
  72. package/dist/gecko-53ITAGG6.js.map +1 -0
  73. package/dist/guards-QAFC64NO.js +7 -0
  74. package/dist/guards-QAFC64NO.js.map +1 -0
  75. package/dist/index.js +57785 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/init-command-246JIVXM.js +7 -0
  78. package/dist/init-command-246JIVXM.js.map +1 -0
  79. package/dist/init-core-KAI7LCXZ.js +12 -0
  80. package/dist/init-core-KAI7LCXZ.js.map +1 -0
  81. package/dist/init-scan-RZNYDTUV.js +1919 -0
  82. package/dist/init-scan-RZNYDTUV.js.map +1 -0
  83. package/dist/local-service-adapter-6KNN6WQL.js +8 -0
  84. package/dist/local-service-adapter-6KNN6WQL.js.map +1 -0
  85. package/dist/secure-credentials-JXWAQLS2.js +306 -0
  86. package/dist/secure-credentials-JXWAQLS2.js.map +1 -0
  87. package/dist/tui-TPJPUS2R.js +111 -0
  88. package/dist/tui-TPJPUS2R.js.map +1 -0
  89. package/dist/vreko-dir-O3RLG7PI.js +8 -0
  90. package/dist/vreko-dir-O3RLG7PI.js.map +1 -0
  91. package/package.json +132 -0
  92. package/scripts/check-banned-words.ts +152 -0
  93. package/scripts/hooks/posttooluse-file-notify.sh +108 -0
  94. package/scripts/hooks/pretooluse-fragile-guard.sh +82 -0
  95. package/scripts/post-install-notice.js +24 -0
  96. package/scripts/postinstall.mjs +84 -0
  97. package/scripts/preuninstall.mjs +34 -0
  98. package/scripts/verify-jsx-transform.mjs +55 -0
@@ -0,0 +1,1466 @@
1
+ #!/usr/bin/env node
2
+ import { BRAND_COLORS } from './chunk-DMXC2JTC.js';
3
+ import { __name } from './chunk-EWOJGXRX.js';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { Box, Text, useInput, useApp } from 'ink';
6
+ import React, { useState, useEffect, useRef } from 'react';
7
+ import { extendTheme, defaultTheme, ThemeProvider, Badge, Alert, Spinner, ProgressBar, Select, UnorderedList } from '@inkjs/ui';
8
+ import * as Sentry from '@sentry/node';
9
+
10
+ process.env.VREKO_CLI='true';process.env.NODE_NO_WARNINGS='1';
11
+ var vrekoTheme = extendTheme(defaultTheme, {
12
+ components: {
13
+ Spinner: {
14
+ styles: {
15
+ frame: /* @__PURE__ */ __name(() => ({
16
+ color: BRAND_COLORS.primary
17
+ }), "frame")
18
+ }
19
+ },
20
+ Badge: {
21
+ styles: {
22
+ container: /* @__PURE__ */ __name(({ color }) => ({
23
+ borderColor: color === "green" ? BRAND_COLORS.primary : void 0
24
+ }), "container")
25
+ }
26
+ },
27
+ ProgressBar: {
28
+ styles: {
29
+ filled: /* @__PURE__ */ __name(() => ({
30
+ color: BRAND_COLORS.primary
31
+ }), "filled")
32
+ }
33
+ }
34
+ }
35
+ });
36
+ function VrekoTheme({ children }) {
37
+ return /* @__PURE__ */ jsx(ThemeProvider, {
38
+ theme: vrekoTheme,
39
+ children
40
+ });
41
+ }
42
+ __name(VrekoTheme, "VrekoTheme");
43
+ var InkErrorBoundary = class extends React.Component {
44
+ static {
45
+ __name(this, "InkErrorBoundary");
46
+ }
47
+ constructor(props) {
48
+ super(props);
49
+ this.state = {
50
+ hasError: false,
51
+ error: null
52
+ };
53
+ }
54
+ static getDerivedStateFromError(error) {
55
+ return {
56
+ hasError: true,
57
+ error
58
+ };
59
+ }
60
+ componentDidCatch(error, info) {
61
+ Sentry.captureException(error, {
62
+ extra: {
63
+ componentStack: info.componentStack,
64
+ panel: this.props.panel
65
+ }
66
+ });
67
+ }
68
+ render() {
69
+ if (this.state.hasError) {
70
+ return /* @__PURE__ */ jsx(Box, {
71
+ children: /* @__PURE__ */ jsxs(Text, {
72
+ color: "red",
73
+ children: [
74
+ "\u26A0",
75
+ " ",
76
+ this.props.panel,
77
+ " panel error - data unavailable"
78
+ ]
79
+ })
80
+ });
81
+ }
82
+ return this.props.children;
83
+ }
84
+ };
85
+ var BRAND_GREEN = "#4ADE80";
86
+ var DARK = "#0F172A";
87
+ var WHITE = "#F8FAFC";
88
+ var SUBTLE = "#64748B";
89
+ function GeckoGlyph() {
90
+ return /* @__PURE__ */ jsxs(Text, {
91
+ children: [
92
+ /* @__PURE__ */ jsx(Text, {
93
+ color: BRAND_GREEN,
94
+ children: "\u25B0"
95
+ }),
96
+ /* @__PURE__ */ jsx(Text, {
97
+ color: DARK,
98
+ backgroundColor: BRAND_GREEN,
99
+ children: "\u25CF"
100
+ }),
101
+ /* @__PURE__ */ jsx(Text, {
102
+ color: BRAND_GREEN,
103
+ children: "\u25B8"
104
+ })
105
+ ]
106
+ });
107
+ }
108
+ __name(GeckoGlyph, "GeckoGlyph");
109
+ function VrekoHeader({ version, variant = "default", subtitle = "developer intelligence" }) {
110
+ if (variant === "ceremony") {
111
+ return /* @__PURE__ */ jsxs(Box, {
112
+ flexDirection: "column",
113
+ marginBottom: 1,
114
+ children: [
115
+ /* @__PURE__ */ jsxs(Text, {
116
+ children: [
117
+ /* @__PURE__ */ jsx(GeckoGlyph, {}),
118
+ /* @__PURE__ */ jsx(Text, {
119
+ color: WHITE,
120
+ children: " vreko"
121
+ }),
122
+ /* @__PURE__ */ jsxs(Text, {
123
+ color: SUBTLE,
124
+ children: [
125
+ " v",
126
+ version
127
+ ]
128
+ })
129
+ ]
130
+ }),
131
+ /* @__PURE__ */ jsx(Text, {
132
+ children: /* @__PURE__ */ jsxs(Text, {
133
+ color: SUBTLE,
134
+ children: [
135
+ " ",
136
+ subtitle
137
+ ]
138
+ })
139
+ })
140
+ ]
141
+ });
142
+ }
143
+ return /* @__PURE__ */ jsxs(Box, {
144
+ marginBottom: 1,
145
+ children: [
146
+ /* @__PURE__ */ jsx(GeckoGlyph, {}),
147
+ /* @__PURE__ */ jsx(Text, {
148
+ color: WHITE,
149
+ children: " vreko"
150
+ }),
151
+ /* @__PURE__ */ jsxs(Text, {
152
+ color: SUBTLE,
153
+ children: [
154
+ " v",
155
+ version
156
+ ]
157
+ })
158
+ ]
159
+ });
160
+ }
161
+ __name(VrekoHeader, "VrekoHeader");
162
+ function useDaemonPolling(client, intervalMs = 5e3) {
163
+ const [daemon, setDaemon] = useState(null);
164
+ const [error, setError] = useState(null);
165
+ useEffect(() => {
166
+ let cancelled = false;
167
+ function formatUptime(ms) {
168
+ const s = Math.floor(ms / 1e3);
169
+ const m = Math.floor(s / 60);
170
+ const h = Math.floor(m / 60);
171
+ const d = Math.floor(h / 24);
172
+ if (d > 0) {
173
+ return `${d}d ${h % 24}h`;
174
+ }
175
+ if (h > 0) {
176
+ return `${h}h ${m % 60}m`;
177
+ }
178
+ if (m > 0) {
179
+ return `${m}m ${s % 60}s`;
180
+ }
181
+ return `${s}s`;
182
+ }
183
+ __name(formatUptime, "formatUptime");
184
+ const poll = /* @__PURE__ */ __name(async () => {
185
+ try {
186
+ const status = await client.daemon.status();
187
+ if (cancelled) {
188
+ return;
189
+ }
190
+ setDaemon({
191
+ pid: status.pid,
192
+ version: status.version,
193
+ uptime: formatUptime(status.uptime),
194
+ connections: status.connections,
195
+ memoryMB: Math.round(status.memoryUsage.heapUsed / (1024 * 1024))
196
+ });
197
+ setError(null);
198
+ } catch (err) {
199
+ if (cancelled) {
200
+ return;
201
+ }
202
+ setDaemon(null);
203
+ setError(err instanceof Error ? err.message : String(err));
204
+ }
205
+ }, "poll");
206
+ poll();
207
+ const id = setInterval(poll, intervalMs);
208
+ return () => {
209
+ cancelled = true;
210
+ clearInterval(id);
211
+ };
212
+ }, [
213
+ client,
214
+ intervalMs
215
+ ]);
216
+ return {
217
+ daemon,
218
+ error
219
+ };
220
+ }
221
+ __name(useDaemonPolling, "useDaemonPolling");
222
+ var INITIAL = {
223
+ snapshots: {
224
+ count: 0,
225
+ latestAt: null
226
+ },
227
+ learnings: {
228
+ count: 0
229
+ },
230
+ momentum: null,
231
+ error: null
232
+ };
233
+ function useSlowPolling(client, intervalMs = 6e4) {
234
+ const [state, setState] = useState(INITIAL);
235
+ const lastGoodRef = useRef({
236
+ snapshots: INITIAL.snapshots,
237
+ learnings: INITIAL.learnings,
238
+ momentum: INITIAL.momentum
239
+ });
240
+ useEffect(() => {
241
+ let cancelled = false;
242
+ const cwd = process.cwd();
243
+ const poll = /* @__PURE__ */ __name(async () => {
244
+ const lg = lastGoodRef.current;
245
+ let snapshotCount = lg.snapshots.count;
246
+ let latestAt = lg.snapshots.latestAt;
247
+ let learningCount = lg.learnings.count;
248
+ let momentumData = lg.momentum;
249
+ const errors = [];
250
+ try {
251
+ const snap = await client.snapshot.list({
252
+ limit: 1,
253
+ orderBy: "createdAt",
254
+ orderDir: "desc"
255
+ });
256
+ snapshotCount = snap.totalCount ?? 0;
257
+ latestAt = snap.snapshots[0]?.createdAt ?? null;
258
+ } catch (err) {
259
+ errors.push(`snapshots: ${err instanceof Error ? err.message : String(err)}`);
260
+ }
261
+ try {
262
+ const learn = await client.learning.list({
263
+ workspace: cwd,
264
+ limit: 1
265
+ });
266
+ learningCount = learn.total;
267
+ } catch (err) {
268
+ errors.push(`learnings: ${err instanceof Error ? err.message : String(err)}`);
269
+ }
270
+ try {
271
+ const mom = await client.momentum.status({
272
+ workspace: cwd
273
+ });
274
+ momentumData = {
275
+ fileCount: mom.fileCount,
276
+ averageScore: mom.averageScore
277
+ };
278
+ } catch (err) {
279
+ errors.push(`momentum: ${err instanceof Error ? err.message : String(err)}`);
280
+ }
281
+ if (cancelled) {
282
+ return;
283
+ }
284
+ lastGoodRef.current = {
285
+ snapshots: {
286
+ count: snapshotCount,
287
+ latestAt
288
+ },
289
+ learnings: {
290
+ count: learningCount
291
+ },
292
+ momentum: momentumData
293
+ };
294
+ setState({
295
+ snapshots: {
296
+ count: snapshotCount,
297
+ latestAt
298
+ },
299
+ learnings: {
300
+ count: learningCount
301
+ },
302
+ momentum: momentumData,
303
+ error: errors.length > 0 ? errors.join("; ") : null
304
+ });
305
+ }, "poll");
306
+ poll();
307
+ const id = setInterval(poll, intervalMs);
308
+ return () => {
309
+ cancelled = true;
310
+ clearInterval(id);
311
+ };
312
+ }, [
313
+ client,
314
+ intervalMs
315
+ ]);
316
+ return state;
317
+ }
318
+ __name(useSlowPolling, "useSlowPolling");
319
+
320
+ // src/ui/tui/panels/DashboardPanel.tsx
321
+ function formatTimeAgo(ts) {
322
+ if (!ts) {
323
+ return "never";
324
+ }
325
+ const diffMs = Date.now() - new Date(ts).getTime();
326
+ const mins = Math.floor(diffMs / 6e4);
327
+ if (mins < 1) {
328
+ return "just now";
329
+ }
330
+ if (mins < 60) {
331
+ return `${mins}m ago`;
332
+ }
333
+ const hours = Math.floor(mins / 60);
334
+ if (hours < 24) {
335
+ return `${hours}h ago`;
336
+ }
337
+ return `${Math.floor(hours / 24)}d ago`;
338
+ }
339
+ __name(formatTimeAgo, "formatTimeAgo");
340
+ function DashboardPanel({ client }) {
341
+ const { daemon, error: daemonError } = useDaemonPolling(client, 5e3);
342
+ const { snapshots, learnings, momentum, error: slowError } = useSlowPolling(client, 6e4);
343
+ const columns = process.stdout.columns ?? 80;
344
+ const daemonVersion = daemon?.version ?? " - ";
345
+ const isConnected = daemon !== null;
346
+ const error = daemonError ?? slowError;
347
+ return /* @__PURE__ */ jsxs(Box, {
348
+ flexDirection: "column",
349
+ paddingX: 1,
350
+ children: [
351
+ /* @__PURE__ */ jsx(VrekoHeader, {
352
+ version: daemonVersion
353
+ }),
354
+ /* @__PURE__ */ jsx(Box, {
355
+ marginBottom: 1,
356
+ children: /* @__PURE__ */ jsx(Badge, {
357
+ color: isConnected ? "green" : "red",
358
+ children: isConnected ? "daemon connected" : "daemon offline"
359
+ })
360
+ }),
361
+ error && /* @__PURE__ */ jsx(Box, {
362
+ marginBottom: 1,
363
+ children: /* @__PURE__ */ jsx(Alert, {
364
+ variant: "error",
365
+ children: error
366
+ })
367
+ }),
368
+ /* @__PURE__ */ jsxs(Box, {
369
+ flexDirection: columns >= 100 ? "row" : "column",
370
+ gap: 3,
371
+ children: [
372
+ /* @__PURE__ */ jsxs(Box, {
373
+ flexDirection: "column",
374
+ minWidth: 24,
375
+ children: [
376
+ /* @__PURE__ */ jsx(Text, {
377
+ bold: true,
378
+ underline: true,
379
+ children: "Daemon"
380
+ }),
381
+ daemon ? /* @__PURE__ */ jsxs(Fragment, {
382
+ children: [
383
+ /* @__PURE__ */ jsxs(Text, {
384
+ children: [
385
+ "PID ",
386
+ /* @__PURE__ */ jsx(Text, {
387
+ color: BRAND_COLORS.primary,
388
+ children: daemon.pid
389
+ })
390
+ ]
391
+ }),
392
+ /* @__PURE__ */ jsxs(Text, {
393
+ children: [
394
+ "Uptime ",
395
+ /* @__PURE__ */ jsx(Text, {
396
+ color: BRAND_COLORS.primary,
397
+ children: daemon.uptime
398
+ })
399
+ ]
400
+ }),
401
+ /* @__PURE__ */ jsxs(Text, {
402
+ children: [
403
+ "Memory ",
404
+ /* @__PURE__ */ jsxs(Text, {
405
+ dimColor: true,
406
+ children: [
407
+ daemon.memoryMB,
408
+ " MB"
409
+ ]
410
+ })
411
+ ]
412
+ }),
413
+ /* @__PURE__ */ jsxs(Text, {
414
+ children: [
415
+ "Conns ",
416
+ /* @__PURE__ */ jsx(Text, {
417
+ dimColor: true,
418
+ children: daemon.connections
419
+ })
420
+ ]
421
+ })
422
+ ]
423
+ }) : /* @__PURE__ */ jsx(Spinner, {
424
+ label: "connecting..."
425
+ })
426
+ ]
427
+ }),
428
+ /* @__PURE__ */ jsxs(Box, {
429
+ flexDirection: "column",
430
+ minWidth: 24,
431
+ children: [
432
+ /* @__PURE__ */ jsx(Text, {
433
+ bold: true,
434
+ underline: true,
435
+ children: "Protection"
436
+ }),
437
+ /* @__PURE__ */ jsxs(Text, {
438
+ children: [
439
+ "Snapshots ",
440
+ /* @__PURE__ */ jsx(Text, {
441
+ color: BRAND_COLORS.primary,
442
+ children: snapshots.count
443
+ })
444
+ ]
445
+ }),
446
+ /* @__PURE__ */ jsxs(Text, {
447
+ children: [
448
+ "Last snap ",
449
+ /* @__PURE__ */ jsx(Text, {
450
+ dimColor: true,
451
+ children: formatTimeAgo(snapshots.latestAt)
452
+ })
453
+ ]
454
+ }),
455
+ /* @__PURE__ */ jsxs(Text, {
456
+ children: [
457
+ "Learnings ",
458
+ /* @__PURE__ */ jsx(Text, {
459
+ color: BRAND_COLORS.primary,
460
+ children: learnings.count
461
+ })
462
+ ]
463
+ })
464
+ ]
465
+ }),
466
+ /* @__PURE__ */ jsxs(Box, {
467
+ flexDirection: "column",
468
+ minWidth: 30,
469
+ children: [
470
+ /* @__PURE__ */ jsx(Text, {
471
+ bold: true,
472
+ underline: true,
473
+ children: "Momentum"
474
+ }),
475
+ momentum ? /* @__PURE__ */ jsxs(Fragment, {
476
+ children: [
477
+ /* @__PURE__ */ jsxs(Text, {
478
+ children: [
479
+ "Files tracked ",
480
+ /* @__PURE__ */ jsx(Text, {
481
+ dimColor: true,
482
+ children: momentum.fileCount
483
+ })
484
+ ]
485
+ }),
486
+ /* @__PURE__ */ jsxs(Box, {
487
+ children: [
488
+ /* @__PURE__ */ jsx(Text, {
489
+ children: "Score "
490
+ }),
491
+ /* @__PURE__ */ jsx(ProgressBar, {
492
+ value: Math.round(momentum.averageScore * 100)
493
+ }),
494
+ /* @__PURE__ */ jsxs(Text, {
495
+ children: [
496
+ " ",
497
+ /* @__PURE__ */ jsxs(Text, {
498
+ dimColor: true,
499
+ children: [
500
+ Math.round(momentum.averageScore * 100),
501
+ "%"
502
+ ]
503
+ })
504
+ ]
505
+ })
506
+ ]
507
+ })
508
+ ]
509
+ }) : /* @__PURE__ */ jsx(Text, {
510
+ dimColor: true,
511
+ children: "No momentum data yet"
512
+ })
513
+ ]
514
+ })
515
+ ]
516
+ }),
517
+ /* @__PURE__ */ jsx(Box, {
518
+ marginTop: 1,
519
+ children: /* @__PURE__ */ jsx(Text, {
520
+ dimColor: true,
521
+ children: "r:refresh 1-4:panels q:quit"
522
+ })
523
+ })
524
+ ]
525
+ });
526
+ }
527
+ __name(DashboardPanel, "DashboardPanel");
528
+ function LearningsPanel({ client }) {
529
+ const [learnings, setLearnings] = useState([]);
530
+ const [total, setTotal] = useState(0);
531
+ const [isLoading, setIsLoading] = useState(true);
532
+ const [error, setError] = useState(null);
533
+ const [filter, setFilter] = useState("all");
534
+ const [showFilterMenu, setShowFilterMenu] = useState(false);
535
+ const hasStarted = useRef(false);
536
+ useEffect(() => {
537
+ if (hasStarted.current) {
538
+ return;
539
+ }
540
+ hasStarted.current = true;
541
+ let cancelled = false;
542
+ const cwd = process.cwd();
543
+ const load = /* @__PURE__ */ __name(async () => {
544
+ setIsLoading(true);
545
+ try {
546
+ const result = await client.learning.list({
547
+ workspace: cwd,
548
+ limit: 50
549
+ });
550
+ if (cancelled) {
551
+ return;
552
+ }
553
+ const items = result.learnings.map((l) => ({
554
+ type: l.type,
555
+ trigger: l.trigger,
556
+ action: l.action
557
+ }));
558
+ setLearnings(items);
559
+ setTotal(result.total);
560
+ setError(null);
561
+ } catch (err) {
562
+ if (cancelled) {
563
+ return;
564
+ }
565
+ setError(err instanceof Error ? err.message : String(err));
566
+ } finally {
567
+ if (!cancelled) {
568
+ setIsLoading(false);
569
+ }
570
+ }
571
+ }, "load");
572
+ load();
573
+ return () => {
574
+ cancelled = true;
575
+ };
576
+ }, [
577
+ client
578
+ ]);
579
+ useInput((input) => {
580
+ if (input === "f") {
581
+ setShowFilterMenu((v) => !v);
582
+ }
583
+ });
584
+ const filtered = learnings.filter((l) => {
585
+ if (filter === "all") {
586
+ return true;
587
+ }
588
+ return l.action.length > 0;
589
+ });
590
+ const filterOptions = [
591
+ {
592
+ label: "All learnings",
593
+ value: "all"
594
+ },
595
+ {
596
+ label: "With action only",
597
+ value: "active"
598
+ }
599
+ ];
600
+ return /* @__PURE__ */ jsxs(Box, {
601
+ flexDirection: "column",
602
+ paddingX: 1,
603
+ children: [
604
+ /* @__PURE__ */ jsxs(Box, {
605
+ gap: 2,
606
+ marginBottom: 1,
607
+ children: [
608
+ /* @__PURE__ */ jsxs(Text, {
609
+ bold: true,
610
+ children: [
611
+ "Learnings (",
612
+ total,
613
+ " total)"
614
+ ]
615
+ }),
616
+ /* @__PURE__ */ jsxs(Text, {
617
+ dimColor: true,
618
+ children: [
619
+ "[f:filter = ",
620
+ filter,
621
+ "]"
622
+ ]
623
+ })
624
+ ]
625
+ }),
626
+ error && /* @__PURE__ */ jsx(Box, {
627
+ marginBottom: 1,
628
+ children: /* @__PURE__ */ jsx(Alert, {
629
+ variant: "error",
630
+ children: error
631
+ })
632
+ }),
633
+ isLoading && /* @__PURE__ */ jsx(Spinner, {
634
+ label: "Loading patterns..."
635
+ }),
636
+ !isLoading && learnings.length === 0 && !error && /* @__PURE__ */ jsx(Box, {
637
+ marginTop: 1,
638
+ children: /* @__PURE__ */ jsx(Text, {
639
+ dimColor: true,
640
+ children: "No patterns learned yet. Run an AI session and use vreko_learn to record patterns."
641
+ })
642
+ }),
643
+ showFilterMenu ? /* @__PURE__ */ jsxs(Box, {
644
+ flexDirection: "column",
645
+ marginBottom: 1,
646
+ children: [
647
+ /* @__PURE__ */ jsx(Text, {
648
+ children: "Filter by:"
649
+ }),
650
+ /* @__PURE__ */ jsx(Select, {
651
+ options: filterOptions,
652
+ onChange: /* @__PURE__ */ __name((v) => {
653
+ setFilter(v);
654
+ setShowFilterMenu(false);
655
+ }, "onChange")
656
+ })
657
+ ]
658
+ }) : !isLoading && filtered.length > 0 && /* @__PURE__ */ jsxs(UnorderedList, {
659
+ children: [
660
+ filtered.slice(0, 30).map((l, i) => (
661
+ // biome-ignore lint/suspicious/noArrayIndexKey: stable list rendered once at mount
662
+ /* @__PURE__ */ jsx(UnorderedList.Item, {
663
+ children: /* @__PURE__ */ jsxs(Text, {
664
+ children: [
665
+ /* @__PURE__ */ jsx(Text, {
666
+ bold: true,
667
+ children: l.type
668
+ }),
669
+ /* @__PURE__ */ jsxs(Text, {
670
+ dimColor: true,
671
+ children: [
672
+ " ",
673
+ "- ",
674
+ l.trigger.length > 40 ? `${l.trigger.slice(0, 40)}\u2026` : l.trigger
675
+ ]
676
+ })
677
+ ]
678
+ })
679
+ }, `${l.type}-${i}`)
680
+ )),
681
+ filtered.length > 30 && /* @__PURE__ */ jsx(UnorderedList.Item, {
682
+ children: /* @__PURE__ */ jsxs(Text, {
683
+ dimColor: true,
684
+ children: [
685
+ "...and ",
686
+ filtered.length - 30,
687
+ " more. Use vr patterns for full list."
688
+ ]
689
+ })
690
+ })
691
+ ]
692
+ }),
693
+ /* @__PURE__ */ jsx(Box, {
694
+ marginTop: 1,
695
+ children: /* @__PURE__ */ jsx(Text, {
696
+ dimColor: true,
697
+ children: "f:filter 1-4:panels q:quit"
698
+ })
699
+ })
700
+ ]
701
+ });
702
+ }
703
+ __name(LearningsPanel, "LearningsPanel");
704
+ function formatSessionDuration(startedAt) {
705
+ const diffMs = Date.now() - new Date(startedAt).getTime();
706
+ const mins = Math.floor(diffMs / 6e4);
707
+ const hours = Math.floor(mins / 60);
708
+ if (hours > 0) {
709
+ return `${hours}h ${mins % 60}m`;
710
+ }
711
+ return `${mins}m`;
712
+ }
713
+ __name(formatSessionDuration, "formatSessionDuration");
714
+ function SessionCard({ session }) {
715
+ return /* @__PURE__ */ jsxs(Box, {
716
+ flexDirection: "column",
717
+ borderStyle: "round",
718
+ paddingX: 1,
719
+ marginBottom: 1,
720
+ children: [
721
+ /* @__PURE__ */ jsxs(Text, {
722
+ children: [
723
+ "ID ",
724
+ /* @__PURE__ */ jsxs(Text, {
725
+ bold: true,
726
+ children: [
727
+ session.id.slice(0, 20),
728
+ "..."
729
+ ]
730
+ })
731
+ ]
732
+ }),
733
+ /* @__PURE__ */ jsxs(Text, {
734
+ children: [
735
+ "Duration ",
736
+ /* @__PURE__ */ jsx(Text, {
737
+ color: BRAND_COLORS.primary,
738
+ children: formatSessionDuration(session.startedAt)
739
+ })
740
+ ]
741
+ }),
742
+ /* @__PURE__ */ jsxs(Text, {
743
+ children: [
744
+ "Started ",
745
+ /* @__PURE__ */ jsx(Text, {
746
+ dimColor: true,
747
+ children: new Date(session.startedAt).toLocaleTimeString()
748
+ })
749
+ ]
750
+ })
751
+ ]
752
+ });
753
+ }
754
+ __name(SessionCard, "SessionCard");
755
+ function SessionUnavailableNotice({ show }) {
756
+ if (!show) {
757
+ return null;
758
+ }
759
+ return /* @__PURE__ */ jsxs(Box, {
760
+ marginTop: 1,
761
+ flexDirection: "column",
762
+ children: [
763
+ /* @__PURE__ */ jsx(Text, {
764
+ dimColor: true,
765
+ children: "Session management is available via MCP tools:"
766
+ }),
767
+ /* @__PURE__ */ jsx(Text, {
768
+ dimColor: true,
769
+ children: " vreko_begin vreko_end vreko_pulse"
770
+ }),
771
+ /* @__PURE__ */ jsx(Text, {
772
+ dimColor: true,
773
+ children: "In-TUI session control arriving in a future release."
774
+ })
775
+ ]
776
+ });
777
+ }
778
+ __name(SessionUnavailableNotice, "SessionUnavailableNotice");
779
+ function useSessionLoader(client, cwd) {
780
+ const [session, setSession] = useState(null);
781
+ const [isLoading, setIsLoading] = useState(true);
782
+ const [error, setError] = useState(null);
783
+ const [sessionUnavailable, setSessionUnavailable] = useState(false);
784
+ useEffect(() => {
785
+ let cancelled = false;
786
+ const load = /* @__PURE__ */ __name(async () => {
787
+ setIsLoading(true);
788
+ try {
789
+ const response = await client.session.current({
790
+ workspacePath: cwd
791
+ });
792
+ if (cancelled) {
793
+ return;
794
+ }
795
+ const currentSession = response && typeof response === "object" && "session" in response ? response.session : response;
796
+ if (currentSession) {
797
+ setSession({
798
+ id: currentSession.id,
799
+ startedAt: currentSession.startedAt
800
+ });
801
+ } else {
802
+ setSession(null);
803
+ }
804
+ } catch (err) {
805
+ if (cancelled) {
806
+ return;
807
+ }
808
+ const msg = err instanceof Error ? err.message : String(err);
809
+ if (msg.includes("not found") || msg.includes("not implemented") || msg.includes("method")) {
810
+ setSessionUnavailable(true);
811
+ } else {
812
+ setError(msg);
813
+ }
814
+ } finally {
815
+ if (!cancelled) {
816
+ setIsLoading(false);
817
+ }
818
+ }
819
+ }, "load");
820
+ load();
821
+ return () => {
822
+ cancelled = true;
823
+ };
824
+ }, [
825
+ client,
826
+ cwd
827
+ ]);
828
+ return {
829
+ session,
830
+ setSession,
831
+ isLoading,
832
+ error,
833
+ setError,
834
+ sessionUnavailable
835
+ };
836
+ }
837
+ __name(useSessionLoader, "useSessionLoader");
838
+ function useSessionActions(client, cwd, session, setSession, setError) {
839
+ const [viewMode, setViewMode] = useState("overview");
840
+ const [statusMessage, setStatusMessage] = useState(null);
841
+ const isMountedRef = useRef(true);
842
+ useEffect(() => {
843
+ isMountedRef.current = true;
844
+ return () => {
845
+ isMountedRef.current = false;
846
+ };
847
+ }, []);
848
+ const handleAction = /* @__PURE__ */ __name(async (action) => {
849
+ if (action === "back") {
850
+ setViewMode("overview");
851
+ return;
852
+ }
853
+ setViewMode("working");
854
+ if (action === "start") {
855
+ try {
856
+ const result = await client.session.start({
857
+ workspacePath: cwd
858
+ });
859
+ if (!isMountedRef.current) {
860
+ return;
861
+ }
862
+ if (result?.id) {
863
+ setSession({
864
+ id: result.id,
865
+ startedAt: result.startedAt
866
+ });
867
+ setStatusMessage("Session started.");
868
+ }
869
+ } catch (err) {
870
+ if (!isMountedRef.current) {
871
+ return;
872
+ }
873
+ setError(err instanceof Error ? err.message : String(err));
874
+ } finally {
875
+ if (isMountedRef.current) {
876
+ setViewMode("overview");
877
+ }
878
+ }
879
+ }
880
+ if (action === "end" && session) {
881
+ try {
882
+ await client.session.end({
883
+ sessionId: session.id
884
+ });
885
+ if (!isMountedRef.current) {
886
+ return;
887
+ }
888
+ setSession(null);
889
+ setStatusMessage("Session ended. Ceremony written to .vreko/docs/last-ceremony.md.");
890
+ } catch (err) {
891
+ if (!isMountedRef.current) {
892
+ return;
893
+ }
894
+ setError(err instanceof Error ? err.message : String(err));
895
+ } finally {
896
+ if (isMountedRef.current) {
897
+ setViewMode("overview");
898
+ }
899
+ }
900
+ }
901
+ }, "handleAction");
902
+ return {
903
+ viewMode,
904
+ statusMessage,
905
+ handleAction
906
+ };
907
+ }
908
+ __name(useSessionActions, "useSessionActions");
909
+ function SessionPanel({ client }) {
910
+ const cwd = process.cwd();
911
+ const { session, setSession, isLoading, error, setError, sessionUnavailable } = useSessionLoader(client, cwd);
912
+ const { viewMode, statusMessage, handleAction } = useSessionActions(client, cwd, session, setSession, setError);
913
+ const sessionActions = session ? [
914
+ {
915
+ label: "End session (save & ceremony)",
916
+ value: "end"
917
+ },
918
+ {
919
+ label: "<- Cancel",
920
+ value: "back"
921
+ }
922
+ ] : [
923
+ {
924
+ label: "Start new session",
925
+ value: "start"
926
+ },
927
+ {
928
+ label: "<- Cancel",
929
+ value: "back"
930
+ }
931
+ ];
932
+ return /* @__PURE__ */ jsxs(Box, {
933
+ flexDirection: "column",
934
+ paddingX: 1,
935
+ children: [
936
+ /* @__PURE__ */ jsx(Text, {
937
+ bold: true,
938
+ children: "Session"
939
+ }),
940
+ isLoading && /* @__PURE__ */ jsx(Spinner, {
941
+ label: "Loading session..."
942
+ }),
943
+ /* @__PURE__ */ jsx(SessionUnavailableNotice, {
944
+ show: sessionUnavailable
945
+ }),
946
+ error && /* @__PURE__ */ jsx(Box, {
947
+ marginTop: 1,
948
+ children: /* @__PURE__ */ jsx(Alert, {
949
+ variant: "error",
950
+ children: error
951
+ })
952
+ }),
953
+ statusMessage && !error && /* @__PURE__ */ jsx(Box, {
954
+ marginTop: 1,
955
+ children: /* @__PURE__ */ jsx(Alert, {
956
+ variant: "success",
957
+ children: statusMessage
958
+ })
959
+ }),
960
+ !isLoading && !sessionUnavailable && /* @__PURE__ */ jsxs(Box, {
961
+ flexDirection: "column",
962
+ marginTop: 1,
963
+ children: [
964
+ session ? /* @__PURE__ */ jsx(SessionCard, {
965
+ session
966
+ }) : /* @__PURE__ */ jsx(Box, {
967
+ marginBottom: 1,
968
+ children: /* @__PURE__ */ jsx(Text, {
969
+ dimColor: true,
970
+ children: "No active session."
971
+ })
972
+ }),
973
+ viewMode === "working" ? /* @__PURE__ */ jsx(Spinner, {
974
+ label: "Working..."
975
+ }) : /* @__PURE__ */ jsx(Select, {
976
+ options: sessionActions,
977
+ onChange: handleAction
978
+ })
979
+ ]
980
+ }),
981
+ /* @__PURE__ */ jsx(Box, {
982
+ marginTop: 1,
983
+ children: /* @__PURE__ */ jsx(Text, {
984
+ dimColor: true,
985
+ children: "1-4:panels q:quit"
986
+ })
987
+ })
988
+ ]
989
+ });
990
+ }
991
+ __name(SessionPanel, "SessionPanel");
992
+ function formatSnapshotDate(ts) {
993
+ const d = new Date(ts);
994
+ return d.toLocaleString("en-US", {
995
+ month: "short",
996
+ day: "numeric",
997
+ hour: "2-digit",
998
+ minute: "2-digit"
999
+ });
1000
+ }
1001
+ __name(formatSnapshotDate, "formatSnapshotDate");
1002
+ function SnapshotPanel({ client }) {
1003
+ const [snapshots, setSnapshots] = useState([]);
1004
+ const [totalCount, setTotalCount] = useState(0);
1005
+ const [isLoading, setIsLoading] = useState(true);
1006
+ const [error, setError] = useState(null);
1007
+ const [viewMode, setViewMode] = useState("list");
1008
+ const [selectedId, setSelectedId] = useState(null);
1009
+ const [actionResult, setActionResult] = useState(null);
1010
+ const [page, setPage] = useState(0);
1011
+ const hasStarted = useRef(false);
1012
+ const PAGE_SIZE = 20;
1013
+ useEffect(() => {
1014
+ if (hasStarted.current && page === 0) {
1015
+ return;
1016
+ }
1017
+ hasStarted.current = true;
1018
+ let cancelled = false;
1019
+ const load = /* @__PURE__ */ __name(async () => {
1020
+ setIsLoading(true);
1021
+ try {
1022
+ const result = await client.snapshot.list({
1023
+ limit: PAGE_SIZE,
1024
+ cursor: page > 0 ? String(page * PAGE_SIZE) : void 0,
1025
+ orderBy: "createdAt",
1026
+ orderDir: "desc"
1027
+ });
1028
+ if (cancelled) {
1029
+ return;
1030
+ }
1031
+ const items = result.snapshots.map((s) => ({
1032
+ id: s.id,
1033
+ relativePath: s.relativePath,
1034
+ filePath: s.filePath,
1035
+ trigger: s.trigger,
1036
+ createdAt: s.createdAt
1037
+ }));
1038
+ setSnapshots((prev) => page === 0 ? items : [
1039
+ ...prev,
1040
+ ...items
1041
+ ]);
1042
+ setTotalCount(result.totalCount ?? result.snapshots.length);
1043
+ setError(null);
1044
+ } catch (err) {
1045
+ if (cancelled) {
1046
+ return;
1047
+ }
1048
+ setError(err instanceof Error ? err.message : String(err));
1049
+ } finally {
1050
+ if (!cancelled) {
1051
+ setIsLoading(false);
1052
+ }
1053
+ }
1054
+ }, "load");
1055
+ load();
1056
+ return () => {
1057
+ cancelled = true;
1058
+ };
1059
+ }, [
1060
+ client,
1061
+ page
1062
+ ]);
1063
+ const snapshotOptions = [
1064
+ ...snapshots.map((s) => ({
1065
+ label: `${formatSnapshotDate(s.createdAt)} [${s.trigger}] ${s.relativePath}`,
1066
+ value: s.id
1067
+ })),
1068
+ ...snapshots.length < totalCount ? [
1069
+ {
1070
+ label: `Load more (${totalCount - snapshots.length} remaining)`,
1071
+ value: "__load_more__"
1072
+ }
1073
+ ] : []
1074
+ ];
1075
+ const actionOptions = [
1076
+ {
1077
+ label: "Restore this snapshot",
1078
+ value: "restore"
1079
+ },
1080
+ {
1081
+ label: "Diff against current file",
1082
+ value: "diff"
1083
+ },
1084
+ {
1085
+ label: "Delete snapshot",
1086
+ value: "delete"
1087
+ },
1088
+ {
1089
+ label: "Back to list",
1090
+ value: "back"
1091
+ }
1092
+ ];
1093
+ const handleSnapshotSelect = /* @__PURE__ */ __name((value) => {
1094
+ if (value === "__load_more__") {
1095
+ setPage((p) => p + 1);
1096
+ return;
1097
+ }
1098
+ setSelectedId(value);
1099
+ setViewMode("action");
1100
+ setActionResult(null);
1101
+ }, "handleSnapshotSelect");
1102
+ const handleAction = /* @__PURE__ */ __name(async (action) => {
1103
+ if (action === "back") {
1104
+ setViewMode("list");
1105
+ setSelectedId(null);
1106
+ return;
1107
+ }
1108
+ if (!selectedId) {
1109
+ return;
1110
+ }
1111
+ setActionResult(null);
1112
+ try {
1113
+ switch (action) {
1114
+ case "restore": {
1115
+ await client.snapshot.restore({
1116
+ snapshotId: selectedId,
1117
+ createBackup: true,
1118
+ dryRun: false
1119
+ });
1120
+ setActionResult("Snapshot restored successfully.");
1121
+ break;
1122
+ }
1123
+ case "diff": {
1124
+ const result = await client.snapshot.diff({
1125
+ baseSnapshotId: selectedId,
1126
+ contextLines: 3,
1127
+ format: "unified"
1128
+ });
1129
+ const stats = result.stats;
1130
+ setActionResult(`Diff: +${stats.additions} -${stats.deletions} across ${stats.filesChanged} file(s)`);
1131
+ break;
1132
+ }
1133
+ case "delete": {
1134
+ await client.snapshot.delete({
1135
+ snapshotIds: [
1136
+ selectedId
1137
+ ],
1138
+ dryRun: false
1139
+ });
1140
+ setActionResult("Snapshot deleted.");
1141
+ hasStarted.current = false;
1142
+ setPage(0);
1143
+ setSnapshots([]);
1144
+ setViewMode("list");
1145
+ break;
1146
+ }
1147
+ }
1148
+ } catch (err) {
1149
+ setActionResult(`Error: ${err instanceof Error ? err.message : String(err)}`);
1150
+ }
1151
+ }, "handleAction");
1152
+ return /* @__PURE__ */ jsxs(Box, {
1153
+ flexDirection: "column",
1154
+ paddingX: 1,
1155
+ children: [
1156
+ /* @__PURE__ */ jsxs(Text, {
1157
+ bold: true,
1158
+ children: [
1159
+ "Snapshots (",
1160
+ totalCount,
1161
+ " total)"
1162
+ ]
1163
+ }),
1164
+ error && /* @__PURE__ */ jsx(Box, {
1165
+ marginTop: 1,
1166
+ children: /* @__PURE__ */ jsx(Alert, {
1167
+ variant: "error",
1168
+ children: error
1169
+ })
1170
+ }),
1171
+ isLoading && snapshots.length === 0 && /* @__PURE__ */ jsx(Spinner, {
1172
+ label: "Loading snapshots..."
1173
+ }),
1174
+ !isLoading && snapshots.length === 0 && !error && /* @__PURE__ */ jsx(Box, {
1175
+ marginTop: 1,
1176
+ children: /* @__PURE__ */ jsx(Text, {
1177
+ dimColor: true,
1178
+ children: "No snapshots yet. Vreko creates snapshots automatically as you work with AI."
1179
+ })
1180
+ }),
1181
+ viewMode === "list" && snapshotOptions.length > 0 && /* @__PURE__ */ jsxs(Box, {
1182
+ marginTop: 1,
1183
+ flexDirection: "column",
1184
+ children: [
1185
+ /* @__PURE__ */ jsx(Text, {
1186
+ dimColor: true,
1187
+ children: "\u2191\u2193 navigate ENTER select 1-4:panels"
1188
+ }),
1189
+ /* @__PURE__ */ jsx(Select, {
1190
+ options: snapshotOptions,
1191
+ onChange: handleSnapshotSelect,
1192
+ visibleOptionCount: 12
1193
+ })
1194
+ ]
1195
+ }),
1196
+ viewMode === "action" && selectedId && /* @__PURE__ */ jsxs(Box, {
1197
+ flexDirection: "column",
1198
+ marginTop: 1,
1199
+ children: [
1200
+ /* @__PURE__ */ jsxs(Text, {
1201
+ children: [
1202
+ "Selected: ",
1203
+ /* @__PURE__ */ jsx(Text, {
1204
+ bold: true,
1205
+ children: selectedId.slice(0, 8)
1206
+ })
1207
+ ]
1208
+ }),
1209
+ actionResult && /* @__PURE__ */ jsx(Box, {
1210
+ marginY: 1,
1211
+ children: /* @__PURE__ */ jsx(Alert, {
1212
+ variant: actionResult.startsWith("Error") ? "error" : "success",
1213
+ children: actionResult
1214
+ })
1215
+ }),
1216
+ /* @__PURE__ */ jsx(Select, {
1217
+ options: actionOptions,
1218
+ onChange: handleAction
1219
+ })
1220
+ ]
1221
+ }),
1222
+ /* @__PURE__ */ jsx(Box, {
1223
+ marginTop: 1,
1224
+ children: /* @__PURE__ */ jsx(Text, {
1225
+ dimColor: true,
1226
+ children: "1-4:panels q:quit"
1227
+ })
1228
+ })
1229
+ ]
1230
+ });
1231
+ }
1232
+ __name(SnapshotPanel, "SnapshotPanel");
1233
+ function StatusPanel({ client }) {
1234
+ const { daemon, error: daemonError } = useDaemonPolling(client, 5e3);
1235
+ const { snapshots, learnings, momentum, error: slowError } = useSlowPolling(client, 6e4);
1236
+ const isConnected = daemon !== null;
1237
+ const error = daemonError ?? slowError;
1238
+ const daemonVersion = daemon?.version ?? " - ";
1239
+ return /* @__PURE__ */ jsxs(Box, {
1240
+ flexDirection: "column",
1241
+ paddingX: 1,
1242
+ children: [
1243
+ /* @__PURE__ */ jsx(VrekoHeader, {
1244
+ version: daemonVersion,
1245
+ subtitle: "workspace status"
1246
+ }),
1247
+ /* @__PURE__ */ jsx(Box, {
1248
+ marginBottom: 1,
1249
+ children: /* @__PURE__ */ jsx(Badge, {
1250
+ color: isConnected ? "green" : "red",
1251
+ children: isConnected ? `daemon v${daemonVersion} pid:${daemon?.pid} uptime:${daemon?.uptime}` : "daemon offline - run: vr service start"
1252
+ })
1253
+ }),
1254
+ error && /* @__PURE__ */ jsx(Box, {
1255
+ marginBottom: 1,
1256
+ children: /* @__PURE__ */ jsx(Alert, {
1257
+ variant: "warning",
1258
+ children: error
1259
+ })
1260
+ }),
1261
+ /* @__PURE__ */ jsxs(Box, {
1262
+ flexDirection: "column",
1263
+ borderStyle: "round",
1264
+ paddingX: 1,
1265
+ marginBottom: 1,
1266
+ children: [
1267
+ /* @__PURE__ */ jsx(Text, {
1268
+ bold: true,
1269
+ children: "Workspace Health"
1270
+ }),
1271
+ /* @__PURE__ */ jsxs(Text, {
1272
+ children: [
1273
+ "Snapshots ",
1274
+ /* @__PURE__ */ jsx(Text, {
1275
+ color: BRAND_COLORS.primary,
1276
+ children: snapshots.count
1277
+ })
1278
+ ]
1279
+ }),
1280
+ /* @__PURE__ */ jsxs(Text, {
1281
+ children: [
1282
+ "Learnings ",
1283
+ /* @__PURE__ */ jsx(Text, {
1284
+ color: BRAND_COLORS.primary,
1285
+ children: learnings.count
1286
+ })
1287
+ ]
1288
+ }),
1289
+ /* @__PURE__ */ jsxs(Text, {
1290
+ children: [
1291
+ "Momentum",
1292
+ " ",
1293
+ /* @__PURE__ */ jsx(Text, {
1294
+ dimColor: true,
1295
+ children: momentum ? `${Math.round(momentum.averageScore * 100)}%` : "uncalibrated"
1296
+ })
1297
+ ]
1298
+ }),
1299
+ /* @__PURE__ */ jsxs(Text, {
1300
+ children: [
1301
+ "Connection",
1302
+ " ",
1303
+ isConnected ? /* @__PURE__ */ jsx(Text, {
1304
+ color: BRAND_COLORS.primary,
1305
+ children: "healthy"
1306
+ }) : /* @__PURE__ */ jsx(Text, {
1307
+ color: BRAND_COLORS.error,
1308
+ children: "offline"
1309
+ })
1310
+ ]
1311
+ })
1312
+ ]
1313
+ }),
1314
+ !daemon && /* @__PURE__ */ jsx(Spinner, {
1315
+ label: "Connecting to daemon..."
1316
+ }),
1317
+ /* @__PURE__ */ jsx(Box, {
1318
+ marginTop: 1,
1319
+ children: /* @__PURE__ */ jsx(Text, {
1320
+ dimColor: true,
1321
+ children: "r:refresh 1-4:panels q:quit"
1322
+ })
1323
+ })
1324
+ ]
1325
+ });
1326
+ }
1327
+ __name(StatusPanel, "StatusPanel");
1328
+ var LABELS = {
1329
+ dashboard: [
1330
+ "[1] Dashboard",
1331
+ "[1]"
1332
+ ],
1333
+ session: [
1334
+ "[2] Session",
1335
+ "[2]"
1336
+ ],
1337
+ snapshots: [
1338
+ "[3] Snapshots",
1339
+ "[3]"
1340
+ ],
1341
+ learnings: [
1342
+ "[4] Learnings",
1343
+ "[4]"
1344
+ ]
1345
+ };
1346
+ var PANELS = [
1347
+ "dashboard",
1348
+ "session",
1349
+ "snapshots",
1350
+ "learnings"
1351
+ ];
1352
+ function TabBar({ active }) {
1353
+ const columns = process.stdout.columns ?? 80;
1354
+ const narrow = columns < 80;
1355
+ return /* @__PURE__ */ jsxs(Box, {
1356
+ gap: 2,
1357
+ marginBottom: 1,
1358
+ borderStyle: "single",
1359
+ borderColor: "#4ADE80",
1360
+ paddingX: 1,
1361
+ children: [
1362
+ PANELS.map((p) => /* @__PURE__ */ jsx(Text, {
1363
+ color: p === active ? "#4ADE80" : void 0,
1364
+ bold: p === active,
1365
+ dimColor: p !== active,
1366
+ children: narrow ? LABELS[p][1] : LABELS[p][0]
1367
+ }, p)),
1368
+ /* @__PURE__ */ jsx(Text, {
1369
+ dimColor: true,
1370
+ children: " q:quit r:refresh"
1371
+ })
1372
+ ]
1373
+ });
1374
+ }
1375
+ __name(TabBar, "TabBar");
1376
+
1377
+ // src/ui/tui/TuiApp.tsx
1378
+ var PANELS2 = [
1379
+ "dashboard",
1380
+ "session",
1381
+ "snapshots",
1382
+ "learnings"
1383
+ ];
1384
+ function TuiApp({ client, initialPanel = "dashboard", statusFocus = false }) {
1385
+ const { exit } = useApp();
1386
+ const [activePanel, setActivePanel] = useState(initialPanel);
1387
+ useEffect(() => {
1388
+ return () => {
1389
+ client.close();
1390
+ };
1391
+ }, [
1392
+ client
1393
+ ]);
1394
+ useInput((input, key) => {
1395
+ if (input === "q" || key.ctrl && input === "c") {
1396
+ exit();
1397
+ return;
1398
+ }
1399
+ if (input === "1") {
1400
+ setActivePanel("dashboard");
1401
+ return;
1402
+ }
1403
+ if (input === "2") {
1404
+ setActivePanel("session");
1405
+ return;
1406
+ }
1407
+ if (input === "3") {
1408
+ setActivePanel("snapshots");
1409
+ return;
1410
+ }
1411
+ if (input === "4") {
1412
+ setActivePanel("learnings");
1413
+ return;
1414
+ }
1415
+ if (key.rightArrow) {
1416
+ const idx = PANELS2.indexOf(activePanel);
1417
+ setActivePanel(PANELS2[(idx + 1) % PANELS2.length]);
1418
+ return;
1419
+ }
1420
+ if (key.leftArrow) {
1421
+ const idx = PANELS2.indexOf(activePanel);
1422
+ setActivePanel(PANELS2[(idx - 1 + PANELS2.length) % PANELS2.length]);
1423
+ }
1424
+ });
1425
+ return /* @__PURE__ */ jsx(VrekoTheme, {
1426
+ children: /* @__PURE__ */ jsxs(Box, {
1427
+ flexDirection: "column",
1428
+ children: [
1429
+ /* @__PURE__ */ jsx(TabBar, {
1430
+ active: activePanel
1431
+ }),
1432
+ activePanel === "dashboard" && /* @__PURE__ */ jsx(InkErrorBoundary, {
1433
+ panel: "dashboard",
1434
+ children: statusFocus ? /* @__PURE__ */ jsx(StatusPanel, {
1435
+ client
1436
+ }) : /* @__PURE__ */ jsx(DashboardPanel, {
1437
+ client
1438
+ })
1439
+ }),
1440
+ activePanel === "session" && /* @__PURE__ */ jsx(InkErrorBoundary, {
1441
+ panel: "session",
1442
+ children: /* @__PURE__ */ jsx(SessionPanel, {
1443
+ client
1444
+ })
1445
+ }),
1446
+ activePanel === "snapshots" && /* @__PURE__ */ jsx(InkErrorBoundary, {
1447
+ panel: "snapshots",
1448
+ children: /* @__PURE__ */ jsx(SnapshotPanel, {
1449
+ client
1450
+ })
1451
+ }),
1452
+ activePanel === "learnings" && /* @__PURE__ */ jsx(InkErrorBoundary, {
1453
+ panel: "learnings",
1454
+ children: /* @__PURE__ */ jsx(LearningsPanel, {
1455
+ client
1456
+ })
1457
+ })
1458
+ ]
1459
+ })
1460
+ });
1461
+ }
1462
+ __name(TuiApp, "TuiApp");
1463
+
1464
+ export { TuiApp };
1465
+ //# sourceMappingURL=chunk-W5B4GTXR.js.map
1466
+ //# sourceMappingURL=chunk-W5B4GTXR.js.map