squads-cli 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +196 -1152
  2. package/dist/auth-YW3UPFSB.js +23 -0
  3. package/dist/autonomy-BWTVDEAT.js +102 -0
  4. package/dist/autonomy-BWTVDEAT.js.map +1 -0
  5. package/dist/chunk-3KCWNZWW.js +401 -0
  6. package/dist/chunk-3KCWNZWW.js.map +1 -0
  7. package/dist/chunk-67RO2HKR.js +174 -0
  8. package/dist/chunk-67RO2HKR.js.map +1 -0
  9. package/dist/chunk-7JVD7RD4.js +275 -0
  10. package/dist/chunk-7JVD7RD4.js.map +1 -0
  11. package/dist/chunk-BODLDQY7.js +452 -0
  12. package/dist/chunk-BODLDQY7.js.map +1 -0
  13. package/dist/chunk-FFFCFZ6A.js +121 -0
  14. package/dist/chunk-FFFCFZ6A.js.map +1 -0
  15. package/dist/chunk-FIWT2NMM.js +165 -0
  16. package/dist/chunk-FIWT2NMM.js.map +1 -0
  17. package/dist/chunk-L6GQCHDF.js +222 -0
  18. package/dist/chunk-L6GQCHDF.js.map +1 -0
  19. package/dist/{chunk-O7UV3FWI.js → chunk-LDM62TIX.js} +2 -2
  20. package/dist/chunk-LDM62TIX.js.map +1 -0
  21. package/dist/chunk-LOA3KWYJ.js +294 -0
  22. package/dist/chunk-LOA3KWYJ.js.map +1 -0
  23. package/dist/chunk-NA45DFXY.js +616 -0
  24. package/dist/chunk-NA45DFXY.js.map +1 -0
  25. package/dist/{chunk-4CMAEQQY.js → chunk-NQN6JPI7.js} +4 -3
  26. package/dist/chunk-NQN6JPI7.js.map +1 -0
  27. package/dist/chunk-OQJHPULO.js +103 -0
  28. package/dist/chunk-OQJHPULO.js.map +1 -0
  29. package/dist/chunk-QHNUMM4V.js +87 -0
  30. package/dist/chunk-QHNUMM4V.js.map +1 -0
  31. package/dist/chunk-RM6BWILN.js +74 -0
  32. package/dist/chunk-RM6BWILN.js.map +1 -0
  33. package/dist/chunk-WBR5J7EX.js +90 -0
  34. package/dist/chunk-WBR5J7EX.js.map +1 -0
  35. package/dist/chunk-Z2UKDBNL.js +162 -0
  36. package/dist/chunk-Z2UKDBNL.js.map +1 -0
  37. package/dist/cli.js +2136 -12600
  38. package/dist/cli.js.map +1 -1
  39. package/dist/context-M2A2DOFV.js +291 -0
  40. package/dist/context-M2A2DOFV.js.map +1 -0
  41. package/dist/context-feed-JMNW4GAM.js +391 -0
  42. package/dist/context-feed-JMNW4GAM.js.map +1 -0
  43. package/dist/cost-N37I4UTA.js +274 -0
  44. package/dist/cost-N37I4UTA.js.map +1 -0
  45. package/dist/create-554W5HNU.js +286 -0
  46. package/dist/create-554W5HNU.js.map +1 -0
  47. package/dist/daemon-XWPQPPPN.js +546 -0
  48. package/dist/daemon-XWPQPPPN.js.map +1 -0
  49. package/dist/dashboard-L7YKVQEB.js +945 -0
  50. package/dist/dashboard-L7YKVQEB.js.map +1 -0
  51. package/dist/dashboard-MFNRLCEE.js +794 -0
  52. package/dist/dashboard-MFNRLCEE.js.map +1 -0
  53. package/dist/doctor-RG75M5RO.js +346 -0
  54. package/dist/doctor-RG75M5RO.js.map +1 -0
  55. package/dist/env-config-KCLDBKYX.js +21 -0
  56. package/dist/exec-JQKBF7BL.js +197 -0
  57. package/dist/exec-JQKBF7BL.js.map +1 -0
  58. package/dist/feedback-KA2UYBZG.js +229 -0
  59. package/dist/feedback-KA2UYBZG.js.map +1 -0
  60. package/dist/github-UQTM5KMS.js +23 -0
  61. package/dist/goal-EOPC5ZCD.js +168 -0
  62. package/dist/goal-EOPC5ZCD.js.map +1 -0
  63. package/dist/health-3FZDOSR5.js +209 -0
  64. package/dist/health-3FZDOSR5.js.map +1 -0
  65. package/dist/history-TFVXJEDH.js +229 -0
  66. package/dist/history-TFVXJEDH.js.map +1 -0
  67. package/dist/index.js +1 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/init-UOWTNMIE.js +747 -0
  70. package/dist/init-UOWTNMIE.js.map +1 -0
  71. package/dist/kpi-2SQ2WCVT.js +413 -0
  72. package/dist/kpi-2SQ2WCVT.js.map +1 -0
  73. package/dist/learn-6ERTERAO.js +269 -0
  74. package/dist/learn-6ERTERAO.js.map +1 -0
  75. package/dist/list-KSOMUBMB.js +92 -0
  76. package/dist/list-KSOMUBMB.js.map +1 -0
  77. package/dist/login-ST6PAXYE.js +155 -0
  78. package/dist/login-ST6PAXYE.js.map +1 -0
  79. package/dist/memory-3CSNKXIL.js +562 -0
  80. package/dist/memory-3CSNKXIL.js.map +1 -0
  81. package/dist/progress-FKG4V2VH.js +202 -0
  82. package/dist/progress-FKG4V2VH.js.map +1 -0
  83. package/dist/providers-66PDCORB.js +65 -0
  84. package/dist/providers-66PDCORB.js.map +1 -0
  85. package/dist/results-2MJFLWEO.js +224 -0
  86. package/dist/results-2MJFLWEO.js.map +1 -0
  87. package/dist/run-72OQLH5A.js +2685 -0
  88. package/dist/run-72OQLH5A.js.map +1 -0
  89. package/dist/session-6H67XPAQ.js +64 -0
  90. package/dist/session-6H67XPAQ.js.map +1 -0
  91. package/dist/{chunk-NHGLXN2F.js → sessions-GVQIMN4W.js} +23 -459
  92. package/dist/sessions-GVQIMN4W.js.map +1 -0
  93. package/dist/{squad-parser-4BI3G4RS.js → squad-parser-CM3HOIWM.js} +2 -2
  94. package/dist/squad-parser-CM3HOIWM.js.map +1 -0
  95. package/dist/stats-ONZI557Q.js +335 -0
  96. package/dist/stats-ONZI557Q.js.map +1 -0
  97. package/dist/status-FYH42FTB.js +346 -0
  98. package/dist/status-FYH42FTB.js.map +1 -0
  99. package/dist/sync-HJZJNXHW.js +800 -0
  100. package/dist/sync-HJZJNXHW.js.map +1 -0
  101. package/dist/update-B4WMUOPO.js +83 -0
  102. package/dist/update-B4WMUOPO.js.map +1 -0
  103. package/dist/{update-ALJKFFM7.js → update-L7FGHN6W.js} +2 -2
  104. package/dist/update-L7FGHN6W.js.map +1 -0
  105. package/package.json +18 -10
  106. package/dist/chunk-4CMAEQQY.js.map +0 -1
  107. package/dist/chunk-NHGLXN2F.js.map +0 -1
  108. package/dist/chunk-O7UV3FWI.js.map +0 -1
  109. package/dist/sessions-6PB7ALCE.js +0 -16
  110. /package/dist/{sessions-6PB7ALCE.js.map → auth-YW3UPFSB.js.map} +0 -0
  111. /package/dist/{squad-parser-4BI3G4RS.js.map → env-config-KCLDBKYX.js.map} +0 -0
  112. /package/dist/{update-ALJKFFM7.js.map → github-UQTM5KMS.js.map} +0 -0
@@ -0,0 +1,794 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ RESET,
4
+ barChart,
5
+ bold,
6
+ box,
7
+ colors,
8
+ gradient,
9
+ padEnd,
10
+ progressBar,
11
+ sparkline,
12
+ truncate,
13
+ writeLine
14
+ } from "./chunk-N7KDWU4W.js";
15
+ import "./chunk-7OCVIDC7.js";
16
+
17
+ // src/lib/dashboard/loader.ts
18
+ import { readFileSync, readdirSync, existsSync } from "fs";
19
+ import { join, basename } from "path";
20
+ import { createRequire } from "module";
21
+ var require2 = createRequire(import.meta.url);
22
+ var yaml = require2("js-yaml");
23
+ var dashboardCache = /* @__PURE__ */ new Map();
24
+ function findDashboardsDir() {
25
+ let dir = process.cwd();
26
+ for (let i = 0; i < 10; i++) {
27
+ const dashDir = join(dir, ".agents", "dashboards");
28
+ if (existsSync(dashDir)) {
29
+ return dashDir;
30
+ }
31
+ const parent = join(dir, "..");
32
+ if (parent === dir) break;
33
+ dir = parent;
34
+ }
35
+ return null;
36
+ }
37
+ function listDashboards() {
38
+ const dir = findDashboardsDir();
39
+ if (!dir) return [];
40
+ try {
41
+ return readdirSync(dir).filter((f) => f.endsWith(".yaml") && !f.startsWith("_")).map((f) => basename(f, ".yaml"));
42
+ } catch {
43
+ return [];
44
+ }
45
+ }
46
+ function loadDashboard(name) {
47
+ if (dashboardCache.has(name)) {
48
+ return dashboardCache.get(name);
49
+ }
50
+ const dir = findDashboardsDir();
51
+ if (!dir) return null;
52
+ const filePath = join(dir, `${name}.yaml`);
53
+ if (!existsSync(filePath)) return null;
54
+ try {
55
+ const content = readFileSync(filePath, "utf-8");
56
+ const def = yaml.load(content);
57
+ if (!def.name || !def.title || !def.source || !def.metrics || !def.views) {
58
+ console.error(`Invalid dashboard definition: ${name}`);
59
+ return null;
60
+ }
61
+ dashboardCache.set(name, def);
62
+ return def;
63
+ } catch (err) {
64
+ console.error(`Error loading dashboard ${name}:`, err);
65
+ return null;
66
+ }
67
+ }
68
+ function loadAllDashboards() {
69
+ const names = listDashboards();
70
+ return names.map((name) => loadDashboard(name)).filter((d) => d !== null);
71
+ }
72
+ function clearDashboardCache() {
73
+ dashboardCache.clear();
74
+ }
75
+ function findDashboard(query) {
76
+ const exact = loadDashboard(query);
77
+ if (exact) return exact;
78
+ const all = listDashboards();
79
+ const match = all.find(
80
+ (name) => name.toLowerCase().includes(query.toLowerCase())
81
+ );
82
+ if (match) return loadDashboard(match);
83
+ return null;
84
+ }
85
+
86
+ // src/lib/dashboard/sources/postgres.ts
87
+ var postgresSource = {
88
+ name: "postgres",
89
+ async query(_sql, _params = []) {
90
+ return { rows: [], columns: [] };
91
+ },
92
+ async isAvailable() {
93
+ return false;
94
+ },
95
+ async close() {
96
+ }
97
+ };
98
+ function buildQuery(table, metrics, groupBy, where, orderBy, limit) {
99
+ const selectParts = [];
100
+ if (groupBy && groupBy.length > 0) {
101
+ for (const col of groupBy) {
102
+ selectParts.push(col);
103
+ }
104
+ }
105
+ for (const metric of metrics) {
106
+ selectParts.push(`${metric.sql} AS ${metric.name}`);
107
+ }
108
+ let sql = `SELECT ${selectParts.join(", ")} FROM ${table}`;
109
+ if (where) {
110
+ sql += ` WHERE ${where}`;
111
+ }
112
+ if (groupBy && groupBy.length > 0) {
113
+ sql += ` GROUP BY ${groupBy.join(", ")}`;
114
+ }
115
+ if (orderBy) {
116
+ sql += ` ORDER BY ${orderBy}`;
117
+ }
118
+ if (limit) {
119
+ sql += ` LIMIT ${limit}`;
120
+ }
121
+ return sql;
122
+ }
123
+ function buildWhereClause(filters, filterDefs) {
124
+ const conditions = [];
125
+ for (const [name, value] of Object.entries(filters)) {
126
+ if (value === void 0 || value === null) continue;
127
+ const def = filterDefs.find((f) => f.name === name);
128
+ if (!def) continue;
129
+ const field = def.field || name;
130
+ if (def.type === "date_range" && typeof value === "object") {
131
+ const range = value;
132
+ if (range.start) {
133
+ conditions.push(`${field} >= '${range.start.toISOString()}'`);
134
+ }
135
+ if (range.end) {
136
+ conditions.push(`${field} <= '${range.end.toISOString()}'`);
137
+ }
138
+ } else if (def.type === "select" && Array.isArray(value)) {
139
+ if (value.length > 0) {
140
+ const escaped = value.map((v) => `'${String(v).replace(/'/g, "''")}'`);
141
+ conditions.push(`${field} IN (${escaped.join(", ")})`);
142
+ }
143
+ } else if (def.type === "boolean") {
144
+ conditions.push(`${field} = ${value ? "true" : "false"}`);
145
+ } else if (def.type === "text") {
146
+ conditions.push(`${field} ILIKE '%${String(value).replace(/'/g, "''")}%'`);
147
+ }
148
+ }
149
+ return conditions.length > 0 ? conditions.join(" AND ") : null;
150
+ }
151
+ function parseDateRange(preset) {
152
+ const now = /* @__PURE__ */ new Date();
153
+ const end = new Date(now);
154
+ end.setHours(23, 59, 59, 999);
155
+ const start = new Date(now);
156
+ start.setHours(0, 0, 0, 0);
157
+ switch (preset) {
158
+ case "today":
159
+ return { start, end };
160
+ case "yesterday":
161
+ start.setDate(start.getDate() - 1);
162
+ end.setDate(end.getDate() - 1);
163
+ return { start, end };
164
+ case "last_7d":
165
+ start.setDate(start.getDate() - 7);
166
+ return { start, end };
167
+ case "last_30d":
168
+ start.setDate(start.getDate() - 30);
169
+ return { start, end };
170
+ case "this_month":
171
+ start.setDate(1);
172
+ return { start, end };
173
+ case "last_month":
174
+ start.setMonth(start.getMonth() - 1);
175
+ start.setDate(1);
176
+ end.setDate(0);
177
+ return { start, end };
178
+ case "this_quarter": {
179
+ const q = Math.floor(now.getMonth() / 3);
180
+ start.setMonth(q * 3);
181
+ start.setDate(1);
182
+ return { start, end };
183
+ }
184
+ default:
185
+ start.setDate(start.getDate() - 7);
186
+ return { start, end };
187
+ }
188
+ }
189
+
190
+ // src/lib/dashboard/renderers/base.ts
191
+ function formatValue(value, format, truncateLen) {
192
+ if (value === null || value === void 0) {
193
+ return `${colors.dim}\u2014${RESET}`;
194
+ }
195
+ const strValue = String(value);
196
+ switch (format) {
197
+ case "number": {
198
+ const num = typeof value === "number" ? value : parseFloat(strValue);
199
+ if (isNaN(num)) return strValue;
200
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
201
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
202
+ return num.toLocaleString();
203
+ }
204
+ case "currency": {
205
+ const amount = typeof value === "number" ? value : parseFloat(strValue);
206
+ if (isNaN(amount)) return strValue;
207
+ return `$${amount.toFixed(2)}`;
208
+ }
209
+ case "percent": {
210
+ const pct = typeof value === "number" ? value : parseFloat(strValue);
211
+ if (isNaN(pct)) return strValue;
212
+ return `${pct.toFixed(1)}%`;
213
+ }
214
+ case "duration": {
215
+ const secs = typeof value === "number" ? value : parseFloat(strValue);
216
+ if (isNaN(secs)) return strValue;
217
+ if (secs < 60) return `${secs.toFixed(1)}s`;
218
+ if (secs < 3600) return `${Math.floor(secs / 60)}m ${Math.round(secs % 60)}s`;
219
+ return `${Math.floor(secs / 3600)}h ${Math.floor(secs % 3600 / 60)}m`;
220
+ }
221
+ case "tokens": {
222
+ const tokens = typeof value === "number" ? value : parseInt(strValue, 10);
223
+ if (isNaN(tokens)) return strValue;
224
+ if (tokens >= 1e6) return `${(tokens / 1e6).toFixed(1)}M`;
225
+ if (tokens >= 1e3) return `${(tokens / 1e3).toFixed(0)}k`;
226
+ return tokens.toLocaleString();
227
+ }
228
+ case "relative_time":
229
+ return formatRelativeTime(value);
230
+ case "relative_date":
231
+ return formatRelativeDate(value);
232
+ case "status_badge":
233
+ return formatStatusBadge(strValue);
234
+ case "progress_bar": {
235
+ const progress = typeof value === "number" ? value : parseFloat(strValue);
236
+ if (isNaN(progress)) return strValue;
237
+ return progressBar(progress, 8);
238
+ }
239
+ default:
240
+ if (truncateLen && strValue.length > truncateLen) {
241
+ return truncate(strValue, truncateLen);
242
+ }
243
+ return strValue;
244
+ }
245
+ }
246
+ function formatRelativeTime(value) {
247
+ const date = value instanceof Date ? value : new Date(String(value));
248
+ if (isNaN(date.getTime())) return String(value);
249
+ const now = /* @__PURE__ */ new Date();
250
+ const diffMs = now.getTime() - date.getTime();
251
+ const diffSecs = Math.floor(diffMs / 1e3);
252
+ const diffMins = Math.floor(diffSecs / 60);
253
+ const diffHours = Math.floor(diffMins / 60);
254
+ const diffDays = Math.floor(diffHours / 24);
255
+ if (diffSecs < 60) return `${diffSecs}s ago`;
256
+ if (diffMins < 60) return `${diffMins}m ago`;
257
+ if (diffHours < 24) return `${diffHours}h ago`;
258
+ if (diffDays < 7) return `${diffDays}d ago`;
259
+ return date.toLocaleDateString();
260
+ }
261
+ function formatRelativeDate(value) {
262
+ const date = value instanceof Date ? value : new Date(String(value));
263
+ if (isNaN(date.getTime())) return String(value);
264
+ const now = /* @__PURE__ */ new Date();
265
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
266
+ const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
267
+ const diffDays = Math.floor((today.getTime() - target.getTime()) / (1e3 * 60 * 60 * 24));
268
+ if (diffDays === 0) return "today";
269
+ if (diffDays === 1) return "yesterday";
270
+ if (diffDays < 7) return `${diffDays}d ago`;
271
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`;
272
+ return date.toLocaleDateString();
273
+ }
274
+ function formatStatusBadge(status) {
275
+ const lower = status.toLowerCase();
276
+ switch (lower) {
277
+ case "success":
278
+ case "completed":
279
+ case "active":
280
+ return `${colors.green}${status}${RESET}`;
281
+ case "failed":
282
+ case "error":
283
+ return `${colors.red}${status}${RESET}`;
284
+ case "running":
285
+ case "pending":
286
+ case "in_progress":
287
+ return `${colors.yellow}${status}${RESET}`;
288
+ case "timeout":
289
+ case "cancelled":
290
+ return `${colors.dim}${status}${RESET}`;
291
+ default:
292
+ return status;
293
+ }
294
+ }
295
+ function calculateColumnWidths(rows, columns, maxWidth = 80) {
296
+ const widths = columns.map((col) => {
297
+ const headerLen = (col.label || col.field).length;
298
+ const maxValueLen = rows.reduce((max, row) => {
299
+ const val = String(row[col.field] ?? "");
300
+ return Math.max(max, val.length);
301
+ }, 0);
302
+ return Math.min(Math.max(headerLen, maxValueLen) + 2, 30);
303
+ });
304
+ const totalWidth = widths.reduce((sum, w) => sum + w, 0);
305
+ if (totalWidth > maxWidth) {
306
+ const scale = maxWidth / totalWidth;
307
+ return widths.map((w) => Math.max(Math.floor(w * scale), 5));
308
+ }
309
+ return widths;
310
+ }
311
+
312
+ // src/lib/dashboard/renderers/summary.ts
313
+ function renderSummary(view, data, metricDefs) {
314
+ const lines = [];
315
+ const row = data.rows[0] || {};
316
+ const parts = [];
317
+ for (const metricName of view.metrics || []) {
318
+ const def = metricDefs.find((m) => m.name === metricName);
319
+ if (!def) continue;
320
+ const value = row[metricName];
321
+ const formatted = formatValue(value, def.format);
322
+ const label = def.label || metricName;
323
+ const valueColor = def.format === "currency" ? colors.green : def.format === "percent" ? colors.purple : colors.cyan;
324
+ parts.push(`${valueColor}${formatted}${RESET} ${colors.dim}${label}${RESET}`);
325
+ }
326
+ const line = parts.join(` ${colors.dim}|${RESET} `);
327
+ lines.push(` ${line}`);
328
+ return lines;
329
+ }
330
+
331
+ // src/lib/dashboard/renderers/table.ts
332
+ function renderTable(view, data, metricDefs, dimensionDefs) {
333
+ const lines = [];
334
+ if (data.rows.length === 0) {
335
+ lines.push(` ${colors.dim}No data${RESET}`);
336
+ return lines;
337
+ }
338
+ const columns = [];
339
+ if (view.group_by) {
340
+ for (const dimName of view.group_by) {
341
+ const def = dimensionDefs.find((d) => d.name === dimName);
342
+ columns.push({
343
+ field: dimName,
344
+ label: def?.label || dimName,
345
+ format: void 0,
346
+ width: 0
347
+ // Will calculate
348
+ });
349
+ }
350
+ }
351
+ if (view.metrics) {
352
+ for (const metricName of view.metrics) {
353
+ const def = metricDefs.find((m) => m.name === metricName);
354
+ columns.push({
355
+ field: metricName,
356
+ label: def?.label || metricName,
357
+ format: def?.format,
358
+ width: 0
359
+ });
360
+ }
361
+ }
362
+ if (view.columns && view.columns.length > 0) {
363
+ columns.length = 0;
364
+ for (const col of view.columns) {
365
+ columns.push({
366
+ field: col.field,
367
+ label: col.label || col.field,
368
+ format: col.format,
369
+ width: 0
370
+ });
371
+ }
372
+ }
373
+ const widths = calculateColumnWidths(data.rows, columns, 70);
374
+ columns.forEach((col, i) => col.width = widths[i]);
375
+ const tableWidth = columns.reduce((sum, col) => sum + col.width, 0) + columns.length + 1;
376
+ if (view.title) {
377
+ lines.push(` ${bold}${view.title}${RESET}`);
378
+ lines.push("");
379
+ }
380
+ lines.push(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);
381
+ let headerRow = ` ${colors.purple}${box.vertical}${RESET}`;
382
+ for (const col of columns) {
383
+ headerRow += ` ${bold}${padEnd(col.label.toUpperCase(), col.width - 1)}${RESET}`;
384
+ }
385
+ headerRow += `${colors.purple}${box.vertical}${RESET}`;
386
+ lines.push(headerRow);
387
+ lines.push(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);
388
+ for (const row of data.rows) {
389
+ let rowStr = ` ${colors.purple}${box.vertical}${RESET}`;
390
+ for (const col of columns) {
391
+ const value = row[col.field];
392
+ const formatted = formatValue(value, col.format || "string", col.width - 3);
393
+ const visibleLen = formatted.replace(/\x1b\[[0-9;]*m/g, "").length;
394
+ const padding = Math.max(0, col.width - 1 - visibleLen);
395
+ rowStr += ` ${formatted}${" ".repeat(padding)}`;
396
+ }
397
+ rowStr += `${colors.purple}${box.vertical}${RESET}`;
398
+ lines.push(rowStr);
399
+ }
400
+ lines.push(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);
401
+ return lines;
402
+ }
403
+
404
+ // src/lib/dashboard/renderers/trend.ts
405
+ function renderTrend(view, data, metricDefs) {
406
+ const lines = [];
407
+ if (data.rows.length === 0) {
408
+ lines.push(` ${colors.dim}No data${RESET}`);
409
+ return lines;
410
+ }
411
+ if (view.title) {
412
+ lines.push(` ${bold}${view.title}${RESET}`);
413
+ lines.push("");
414
+ }
415
+ for (const metricName of view.metrics || []) {
416
+ const def = metricDefs.find((m) => m.name === metricName);
417
+ if (!def) continue;
418
+ const values = data.rows.map((row) => {
419
+ const val = row[metricName];
420
+ return typeof val === "number" ? val : parseFloat(String(val)) || 0;
421
+ });
422
+ const orderedValues = [...values].reverse();
423
+ const total = orderedValues.reduce((sum, v) => sum + v, 0);
424
+ const avg = orderedValues.length > 0 ? total / orderedValues.length : 0;
425
+ const latest = orderedValues[orderedValues.length - 1] || 0;
426
+ const previous = orderedValues[orderedValues.length - 2] || 0;
427
+ const change = previous > 0 ? (latest - previous) / previous * 100 : 0;
428
+ const spark = sparkline(orderedValues);
429
+ const label = def.label || metricName;
430
+ const totalFormatted = formatValue(total, def.format);
431
+ const avgFormatted = formatValue(avg, def.format);
432
+ const changeColor = change > 0 ? colors.green : change < 0 ? colors.red : colors.dim;
433
+ const changeSign = change > 0 ? "+" : "";
434
+ const changeStr = `${changeColor}${changeSign}${change.toFixed(0)}%${RESET}`;
435
+ lines.push(` ${colors.dim}${label}:${RESET} ${spark} ${colors.cyan}${totalFormatted}${RESET} total ${colors.dim}(avg ${avgFormatted})${RESET} ${changeStr}`);
436
+ }
437
+ return lines;
438
+ }
439
+ function renderStackedTrend(view, data, metricDefs) {
440
+ const lines = [];
441
+ if (data.rows.length === 0) {
442
+ lines.push(` ${colors.dim}No data${RESET}`);
443
+ return lines;
444
+ }
445
+ if (view.title) {
446
+ lines.push(` ${bold}${view.title}${RESET}`);
447
+ lines.push("");
448
+ }
449
+ const primaryMetric = view.metrics?.[0];
450
+ if (primaryMetric) {
451
+ const values = data.rows.map((row) => {
452
+ const val = row[primaryMetric];
453
+ return typeof val === "number" ? val : parseFloat(String(val)) || 0;
454
+ }).reverse();
455
+ const spark = sparkline(values);
456
+ lines.push(` ${spark}`);
457
+ lines.push("");
458
+ }
459
+ const legendParts = [];
460
+ for (const metricName of view.metrics || []) {
461
+ const def = metricDefs.find((m) => m.name === metricName);
462
+ if (!def) continue;
463
+ const total = data.rows.reduce((sum, row) => {
464
+ const val = row[metricName];
465
+ return sum + (typeof val === "number" ? val : parseFloat(String(val)) || 0);
466
+ }, 0);
467
+ const label = def.label || metricName;
468
+ const formatted = formatValue(total, def.format);
469
+ const metricColor = metricName === view.metrics?.[0] ? colors.green : metricName === view.metrics?.[1] ? colors.yellow : colors.red;
470
+ legendParts.push(`${metricColor}${formatted}${RESET} ${colors.dim}${label}${RESET}`);
471
+ }
472
+ lines.push(` ${legendParts.join(" ")}`);
473
+ return lines;
474
+ }
475
+
476
+ // src/lib/dashboard/renderers/bar.ts
477
+ function renderBar(view, data, metricDefs, _dimensionDefs) {
478
+ const lines = [];
479
+ if (data.rows.length === 0) {
480
+ lines.push(` ${colors.dim}No data${RESET}`);
481
+ return lines;
482
+ }
483
+ if (view.title) {
484
+ lines.push(` ${bold}${view.title}${RESET}`);
485
+ lines.push("");
486
+ }
487
+ const dimName = view.group_by?.[0];
488
+ const metricName = view.metrics?.[0];
489
+ if (!dimName || !metricName) {
490
+ lines.push(` ${colors.dim}Bar chart requires group_by and metrics${RESET}`);
491
+ return lines;
492
+ }
493
+ const metricDef = metricDefs.find((m) => m.name === metricName);
494
+ const values = data.rows.map((row) => {
495
+ const val = row[metricName];
496
+ return typeof val === "number" ? val : parseFloat(String(val)) || 0;
497
+ });
498
+ const maxValue = Math.max(...values, 1);
499
+ const labels = data.rows.map((row) => String(row[dimName] || "Unknown"));
500
+ const maxLabelWidth = Math.min(Math.max(...labels.map((l) => l.length)), 20);
501
+ for (const row of data.rows) {
502
+ const label = String(row[dimName] || "Unknown");
503
+ const value = row[metricName];
504
+ const numValue = typeof value === "number" ? value : parseFloat(String(value)) || 0;
505
+ const bar = barChart(numValue, maxValue, 20);
506
+ const formatted = formatValue(value, metricDef?.format || "number");
507
+ lines.push(` ${colors.cyan}${padEnd(label.slice(0, maxLabelWidth), maxLabelWidth + 1)}${RESET}${bar} ${colors.dim}${formatted}${RESET}`);
508
+ }
509
+ return lines;
510
+ }
511
+ function renderPie(view, data, _metricDefs, _dimensionDefs) {
512
+ const lines = [];
513
+ if (data.rows.length === 0) {
514
+ lines.push(` ${colors.dim}No data${RESET}`);
515
+ return lines;
516
+ }
517
+ if (view.title) {
518
+ lines.push(` ${bold}${view.title}${RESET}`);
519
+ lines.push("");
520
+ }
521
+ const dimName = view.group_by?.[0];
522
+ const metricName = view.metrics?.[0];
523
+ if (!dimName || !metricName) {
524
+ lines.push(` ${colors.dim}Pie chart requires group_by and metrics${RESET}`);
525
+ return lines;
526
+ }
527
+ const values = data.rows.map((row) => {
528
+ const val = row[metricName];
529
+ return {
530
+ label: String(row[dimName] || "Unknown"),
531
+ value: typeof val === "number" ? val : parseFloat(String(val)) || 0
532
+ };
533
+ });
534
+ const total = values.reduce((sum, v) => sum + v.value, 0);
535
+ if (total === 0) {
536
+ lines.push(` ${colors.dim}No data${RESET}`);
537
+ return lines;
538
+ }
539
+ const colorPalette = [colors.cyan, colors.green, colors.yellow, colors.purple, colors.red];
540
+ const barWidth = 40;
541
+ let barStr = "";
542
+ let pos = 0;
543
+ for (let i = 0; i < values.length; i++) {
544
+ const pct = values[i].value / total;
545
+ const width = Math.max(1, Math.round(pct * barWidth));
546
+ const color = colorPalette[i % colorPalette.length];
547
+ barStr += `${color}${"\u2588".repeat(width)}${RESET}`;
548
+ pos += width;
549
+ }
550
+ if (pos < barWidth) {
551
+ barStr += `${colors.dim}${"\u2591".repeat(barWidth - pos)}${RESET}`;
552
+ }
553
+ lines.push(` ${barStr}`);
554
+ lines.push("");
555
+ for (let i = 0; i < values.length; i++) {
556
+ const v = values[i];
557
+ const pct = v.value / total * 100;
558
+ const color = colorPalette[i % colorPalette.length];
559
+ lines.push(` ${color}\u25A0${RESET} ${padEnd(v.label.slice(0, 15), 16)} ${color}${pct.toFixed(1)}%${RESET} ${colors.dim}(${v.value.toLocaleString()})${RESET}`);
560
+ }
561
+ return lines;
562
+ }
563
+
564
+ // src/lib/dashboard/renderers/list.ts
565
+ function renderList(view, data) {
566
+ const lines = [];
567
+ if (data.rows.length === 0) {
568
+ lines.push(` ${colors.dim}No data${RESET}`);
569
+ return lines;
570
+ }
571
+ if (view.title) {
572
+ lines.push(` ${bold}${view.title}${RESET}`);
573
+ lines.push("");
574
+ }
575
+ const columns = view.columns || data.columns.map((c) => ({ field: c }));
576
+ for (const row of data.rows) {
577
+ const parts = [];
578
+ for (const col of columns) {
579
+ const value = row[col.field];
580
+ let formatted = formatValue(value, col.format || "string", col.truncate);
581
+ if (col.label) {
582
+ formatted = `${colors.dim}${col.label}:${RESET} ${formatted}`;
583
+ }
584
+ parts.push(formatted);
585
+ }
586
+ lines.push(` ${parts.join(" ")}`);
587
+ }
588
+ return lines;
589
+ }
590
+ function renderRecentList(view, data) {
591
+ const lines = [];
592
+ if (data.rows.length === 0) {
593
+ lines.push(` ${colors.dim}No recent items${RESET}`);
594
+ return lines;
595
+ }
596
+ if (view.title) {
597
+ lines.push(` ${bold}${view.title}${RESET}`);
598
+ lines.push("");
599
+ }
600
+ const columns = view.columns || [];
601
+ const timeCol = columns.find((c) => c.format === "relative_time" || c.field.includes("_at"));
602
+ for (const row of data.rows) {
603
+ const parts = [];
604
+ if (timeCol) {
605
+ const timeVal = formatValue(row[timeCol.field], "relative_time");
606
+ parts.push(`${colors.dim}${timeVal}${RESET}`);
607
+ }
608
+ for (const col of columns) {
609
+ if (col === timeCol) continue;
610
+ const value = row[col.field];
611
+ const formatted = formatValue(value, col.format || "string", col.truncate);
612
+ parts.push(formatted);
613
+ }
614
+ lines.push(` ${parts.join(" ")}`);
615
+ }
616
+ return lines;
617
+ }
618
+
619
+ // src/lib/dashboard/renderers/index.ts
620
+ function renderView(view, data, metricDefs, dimensionDefs) {
621
+ switch (view.type) {
622
+ case "summary":
623
+ return renderSummary(view, data, metricDefs);
624
+ case "table":
625
+ return renderTable(view, data, metricDefs, dimensionDefs);
626
+ case "trend":
627
+ if (view.stacked) {
628
+ return renderStackedTrend(view, data, metricDefs);
629
+ }
630
+ return renderTrend(view, data, metricDefs);
631
+ case "bar":
632
+ return renderBar(view, data, metricDefs, dimensionDefs);
633
+ case "pie":
634
+ return renderPie(view, data, metricDefs, dimensionDefs);
635
+ case "list": {
636
+ const hasTimeCol = view.columns?.some(
637
+ (c) => c.format === "relative_time" || c.field.includes("_at")
638
+ );
639
+ if (hasTimeCol) {
640
+ return renderRecentList(view, data);
641
+ }
642
+ return renderList(view, data);
643
+ }
644
+ case "histogram":
645
+ return [` ${colors.dim}Histogram not yet implemented${RESET}`];
646
+ case "heatmap":
647
+ return [` ${colors.dim}Heatmap not yet implemented${RESET}`];
648
+ default:
649
+ return [` ${colors.dim}Unknown view type: ${view.type}${RESET}`];
650
+ }
651
+ }
652
+
653
+ // src/lib/dashboard/engine.ts
654
+ function getDataSource(sourceName) {
655
+ switch (sourceName) {
656
+ case "postgres":
657
+ return postgresSource;
658
+ // TODO: Add other sources
659
+ case "sessions":
660
+ case "langfuse":
661
+ case "api":
662
+ return null;
663
+ default:
664
+ return null;
665
+ }
666
+ }
667
+ async function executeDashboard(name, options = {}) {
668
+ const lines = [];
669
+ const def = findDashboard(name);
670
+ if (!def) {
671
+ return {
672
+ success: false,
673
+ lines: [`${colors.red}Dashboard not found: ${name}${RESET}`]
674
+ };
675
+ }
676
+ const source = getDataSource(def.source);
677
+ if (!source) {
678
+ return {
679
+ success: false,
680
+ lines: [`${colors.red}Data source not available: ${def.source}${RESET}`]
681
+ };
682
+ }
683
+ const available = await source.isAvailable();
684
+ if (!available) {
685
+ return {
686
+ success: false,
687
+ lines: [`${colors.red}Cannot connect to ${def.source}${RESET}`]
688
+ };
689
+ }
690
+ const filters = { ...options.filters };
691
+ for (const filterDef of def.filters || []) {
692
+ if (filters[filterDef.name] === void 0 && filterDef.default) {
693
+ if (filterDef.type === "date_range") {
694
+ filters[filterDef.name] = parseDateRange(filterDef.default);
695
+ } else {
696
+ filters[filterDef.name] = filterDef.default;
697
+ }
698
+ }
699
+ }
700
+ lines.push("");
701
+ lines.push(` ${gradient("squads")} ${colors.dim}${def.title}${RESET}`);
702
+ if (def.description && options.verbose) {
703
+ lines.push(` ${colors.dim}${def.description}${RESET}`);
704
+ }
705
+ lines.push("");
706
+ const viewsToRender = options.views ? def.views.filter((v) => options.views.includes(v.id)) : def.views;
707
+ for (const view of viewsToRender) {
708
+ try {
709
+ const data = await fetchViewData(def, view, filters, source);
710
+ const viewLines = renderView(view, data, def.metrics, def.dimensions || []);
711
+ lines.push(...viewLines);
712
+ lines.push("");
713
+ } catch (err) {
714
+ if (options.verbose) {
715
+ lines.push(` ${colors.red}Error rendering ${view.id}: ${err}${RESET}`);
716
+ }
717
+ }
718
+ }
719
+ await source.close();
720
+ return { success: true, lines };
721
+ }
722
+ async function fetchViewData(def, view, filters, source) {
723
+ if (view.source) {
724
+ return source.query(view.source);
725
+ }
726
+ if (!def.table) {
727
+ throw new Error("Dashboard requires table or view.source");
728
+ }
729
+ const metrics = (view.metrics || []).map((name) => {
730
+ const m = def.metrics.find((m2) => m2.name === name);
731
+ if (!m) throw new Error(`Unknown metric: ${name}`);
732
+ return m;
733
+ });
734
+ const filterDefs = def.filters || [];
735
+ let where = buildWhereClause(filters, filterDefs);
736
+ if (view.filter) {
737
+ where = where ? `(${where}) AND (${view.filter})` : view.filter;
738
+ }
739
+ const sql = buildQuery(
740
+ def.table,
741
+ metrics,
742
+ view.group_by,
743
+ where ?? void 0,
744
+ view.sort,
745
+ view.limit
746
+ );
747
+ return source.query(sql);
748
+ }
749
+ async function renderDashboard(name, options = {}) {
750
+ const result = await executeDashboard(name, options);
751
+ for (const line of result.lines) {
752
+ writeLine(line);
753
+ }
754
+ return result.success;
755
+ }
756
+ function showAvailableDashboards() {
757
+ const names = listDashboards();
758
+ writeLine();
759
+ writeLine(` ${gradient("squads")} ${colors.dim}dashboards${RESET}`);
760
+ writeLine();
761
+ if (names.length === 0) {
762
+ writeLine(` ${colors.dim}No dashboards found${RESET}`);
763
+ writeLine(` ${colors.dim}Create dashboards in .agents/dashboards/*.yaml${RESET}`);
764
+ writeLine();
765
+ return;
766
+ }
767
+ for (const name of names) {
768
+ const def = loadDashboard(name);
769
+ if (def) {
770
+ writeLine(` ${colors.cyan}${name.padEnd(16)}${RESET} ${colors.dim}${def.title}${RESET}`);
771
+ }
772
+ }
773
+ writeLine();
774
+ writeLine(` ${colors.dim}Usage:${RESET} squads dash ${colors.cyan}<dashboard>${RESET}`);
775
+ writeLine(` ${colors.dim} squads dash ${colors.cyan}<dashboard>${RESET} ${colors.dim}--view=<view-id>${RESET}`);
776
+ writeLine();
777
+ }
778
+ async function interactiveDashboard(name, initialFilters) {
779
+ await renderDashboard(name, { filters: initialFilters });
780
+ }
781
+ export {
782
+ clearDashboardCache,
783
+ executeDashboard,
784
+ findDashboard,
785
+ findDashboardsDir,
786
+ interactiveDashboard,
787
+ listDashboards,
788
+ loadAllDashboards,
789
+ loadDashboard,
790
+ postgresSource,
791
+ renderDashboard,
792
+ showAvailableDashboards
793
+ };
794
+ //# sourceMappingURL=dashboard-MFNRLCEE.js.map