sisyphi 1.1.17 → 1.1.18

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.
@@ -0,0 +1,1052 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ACHIEVEMENTS
4
+ } from "./chunk-V36NXMHP.js";
5
+ import {
6
+ goalPath,
7
+ roadmapPath,
8
+ sessionsDir,
9
+ socketPath,
10
+ statePath
11
+ } from "./chunk-TMBAVPHH.js";
12
+
13
+ // src/shared/utils.ts
14
+ function computeActiveTimeMs(session) {
15
+ return session.activeMs;
16
+ }
17
+
18
+ // src/shared/format.ts
19
+ function formatDuration(startOrMs, endIso) {
20
+ let totalMs;
21
+ if (typeof startOrMs === "number") {
22
+ totalMs = startOrMs;
23
+ } else {
24
+ const start = new Date(startOrMs).getTime();
25
+ const end = endIso ? new Date(endIso).getTime() : Date.now();
26
+ totalMs = end - start;
27
+ }
28
+ const totalSeconds = Math.floor(totalMs / 1e3);
29
+ if (totalSeconds < 0) return "0s";
30
+ const hours = Math.floor(totalSeconds / 3600);
31
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
32
+ const seconds = totalSeconds % 60;
33
+ if (hours > 0) return `${hours}h${minutes}m`;
34
+ if (minutes > 0) return `${minutes}m${seconds}s`;
35
+ return `${seconds}s`;
36
+ }
37
+ function statusColor(status) {
38
+ switch (status) {
39
+ case "active":
40
+ case "running":
41
+ return "green";
42
+ case "completed":
43
+ return "cyan";
44
+ case "paused":
45
+ return "yellow";
46
+ case "killed":
47
+ case "crashed":
48
+ return "red";
49
+ case "lost":
50
+ return "gray";
51
+ default:
52
+ return "white";
53
+ }
54
+ }
55
+
56
+ // src/tui/lib/reports.ts
57
+ import { readFileSync } from "fs";
58
+ function loadReportContent(report) {
59
+ try {
60
+ return readFileSync(report.filePath, "utf-8");
61
+ } catch {
62
+ return report.summary;
63
+ }
64
+ }
65
+ function resolveReports(reports) {
66
+ return [...reports].reverse().map((r) => ({
67
+ type: r.type,
68
+ timestamp: r.timestamp,
69
+ content: loadReportContent(r),
70
+ summary: r.summary
71
+ }));
72
+ }
73
+
74
+ // src/tui/lib/context.ts
75
+ import { readFileSync as readFileSync2, readdirSync } from "fs";
76
+ function readFileSafe(filePath) {
77
+ try {
78
+ return readFileSync2(filePath, "utf-8");
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ function escapeXml(s) {
84
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
85
+ }
86
+ function buildCompanionContext(cwd) {
87
+ let sessionDirs;
88
+ try {
89
+ sessionDirs = readdirSync(sessionsDir(cwd));
90
+ } catch {
91
+ return "<sessions>No sessions found.</sessions>";
92
+ }
93
+ const now = Date.now();
94
+ const sevenDaysMs = 7 * 24 * 60 * 60 * 1e3;
95
+ const sessionBlocks = [];
96
+ for (const sessionId of sessionDirs) {
97
+ const stateRaw = readFileSafe(statePath(cwd, sessionId));
98
+ if (!stateRaw) continue;
99
+ let session;
100
+ try {
101
+ session = JSON.parse(stateRaw);
102
+ } catch {
103
+ continue;
104
+ }
105
+ if (session.status === "completed" && session.completedAt) {
106
+ if (now - new Date(session.completedAt).getTime() > sevenDaysMs) continue;
107
+ }
108
+ const lines = [];
109
+ const nameAttr = session.name ? ` name="${escapeXml(session.name)}"` : "";
110
+ lines.push(` <session id="${escapeXml(session.id)}"${nameAttr} status="${escapeXml(session.status)}">`);
111
+ lines.push(` <task>${escapeXml(session.task)}</task>`);
112
+ lines.push(` <created>${escapeXml(session.createdAt)}</created>`);
113
+ lines.push(` <cycles>${session.orchestratorCycles.length}</cycles>`);
114
+ if (session.status === "completed") {
115
+ if (session.completionReport) {
116
+ const snippet = session.completionReport.slice(0, 300).replace(/\n+/g, " ").trim();
117
+ lines.push(` <completion-report>${escapeXml(snippet)}${session.completionReport.length > 300 ? "\u2026" : ""}</completion-report>`);
118
+ }
119
+ } else {
120
+ if (session.agents.length > 0) {
121
+ const counts = /* @__PURE__ */ new Map();
122
+ for (const agent of session.agents) {
123
+ counts.set(agent.status, (counts.get(agent.status) ?? 0) + 1);
124
+ }
125
+ const summary = [...counts.entries()].map(([status, n]) => `${n} ${status}`).join(", ");
126
+ lines.push(` <agents>${escapeXml(summary)}</agents>`);
127
+ }
128
+ const goalContent = readFileSafe(goalPath(cwd, session.id));
129
+ if (goalContent) {
130
+ const firstLine = goalContent.split("\n").map((l) => l.trim()).find((l) => l.length > 0 && !l.startsWith("#"));
131
+ if (firstLine) lines.push(` <goal>${escapeXml(firstLine)}</goal>`);
132
+ }
133
+ const roadmapContent = readFileSafe(roadmapPath(cwd, session.id));
134
+ if (roadmapContent) {
135
+ const todos = roadmapContent.split("\n").filter((l) => l.includes("- [ ]")).slice(0, 5).map((l) => l.trim());
136
+ if (todos.length > 0) {
137
+ lines.push(" <todos>");
138
+ for (const todo of todos) lines.push(` ${escapeXml(todo)}`);
139
+ lines.push(" </todos>");
140
+ }
141
+ }
142
+ }
143
+ lines.push(" </session>");
144
+ sessionBlocks.push(lines.join("\n"));
145
+ }
146
+ if (sessionBlocks.length === 0) {
147
+ return "<sessions>No sessions found.</sessions>";
148
+ }
149
+ return ["<sessions>", ...sessionBlocks, "</sessions>"].join("\n");
150
+ }
151
+ function buildSessionContext(session, cwd) {
152
+ const goal = readFileSafe(goalPath(cwd, session.id));
153
+ const roadmap = readFileSafe(roadmapPath(cwd, session.id));
154
+ const agentsXml = session.agents.map((agent) => {
155
+ const reportBlocks = resolveReports(agent.reports);
156
+ const reportsXml = [...reportBlocks].reverse().map((block) => {
157
+ return ` <report type="${block.type}" time="${escapeXml(block.timestamp)}">${escapeXml(block.content)}</report>`;
158
+ }).join("\n");
159
+ return [
160
+ ` <agent id="${escapeXml(agent.id)}" name="${escapeXml(agent.name)}" type="${escapeXml(agent.agentType)}" status="${escapeXml(agent.status)}">`,
161
+ ` <instruction>${escapeXml(agent.instruction)}</instruction>`,
162
+ ...reportsXml ? [reportsXml] : [],
163
+ ` </agent>`
164
+ ].join("\n");
165
+ }).join("\n");
166
+ const cyclesXml = session.orchestratorCycles.map((cycle) => {
167
+ const agents = cycle.agentsSpawned.join(", ");
168
+ const mode = cycle.mode ? ` mode="${escapeXml(cycle.mode)}"` : "";
169
+ return ` <cycle number="${cycle.cycle}"${mode} agents="${escapeXml(agents)}" />`;
170
+ }).join("\n");
171
+ const lines = [
172
+ "<context>",
173
+ `<session id="${escapeXml(session.id)}" status="${escapeXml(session.status)}">`,
174
+ ` <task>${escapeXml(session.task)}</task>`,
175
+ ` <cwd>${escapeXml(session.cwd)}</cwd>`
176
+ ];
177
+ if (goal) lines.push(` <goal>${escapeXml(goal)}</goal>`);
178
+ if (roadmap) lines.push(` <roadmap>${escapeXml(roadmap)}</roadmap>`);
179
+ if (session.agents.length > 0) {
180
+ lines.push(" <agents>");
181
+ lines.push(agentsXml);
182
+ lines.push(" </agents>");
183
+ }
184
+ if (session.orchestratorCycles.length > 0) {
185
+ lines.push(" <cycles>");
186
+ lines.push(cyclesXml);
187
+ lines.push(" </cycles>");
188
+ }
189
+ if (session.completionReport) {
190
+ lines.push(` <completion-report>${escapeXml(session.completionReport)}</completion-report>`);
191
+ }
192
+ lines.push("</session>");
193
+ lines.push("</context>");
194
+ return lines.join("\n");
195
+ }
196
+
197
+ // src/shared/companion-badges.ts
198
+ var BADGE_ART = {
199
+ // ── Milestone ─────────────────────────────────────────────────────────────
200
+ "first-blood": [
201
+ " \u2571\u2572 ",
202
+ " \u2571 \u2572 ",
203
+ " \u2571 \u25C6\u25C6 \u2572 ",
204
+ " \u2571 \u25C6\u25C6 \u2572 ",
205
+ " \u2571 \u25C6\u25C6 \u2572 ",
206
+ " \u2571 \u25C6\u25C6 \u2572 ",
207
+ " \u2571\u2500\u2500\u2500\u2500\u2500\u25C6\u25C6\u2500\u2500\u2500\u2500\u2500\u2572 ",
208
+ " \u2572 \u25C6\u25C6 \u2571 ",
209
+ " \u2572 \u25C6\u25C6 \u2571 ",
210
+ " \u2572\u2500\u2500\u2500\u25C6\u25C6\u2500\u2500\u2500\u2571 ",
211
+ " \u2502\u2502 ",
212
+ " \u2550\u2550\u256A\u256A\u2550\u2550 "
213
+ ],
214
+ "centurion": [
215
+ " \u250C\u2500\u2500\u2550\u2550\u2550\u2550\u2550\u2500\u2500\u2510 ",
216
+ " \u2502 \u2554\u2550\u2550\u2550\u2557 \u2502 ",
217
+ " \u2571\u2502 \u2551100\u2551 \u2502\u2572 ",
218
+ " \u2571 \u2502 \u255A\u2550\u2550\u2550\u255D \u2502 \u2572 ",
219
+ " \u2571 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2572 ",
220
+ " \u2572 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2571 ",
221
+ " \u2572 \u2502 \u2605 \u2605 \u2605 \u2502 \u2571 ",
222
+ " \u2572 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2571 ",
223
+ " \u2572\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2571 "
224
+ ],
225
+ "thousand-boulder": [
226
+ " \u256D\u2501\u2501\u2501\u256E ",
227
+ " \u256D\u2501\u252B1K \u2523\u2501\u256E ",
228
+ " \u256D\u2501\u252B \u2570\u2501\u2501\u2501\u256F \u2523\u2501\u256E ",
229
+ " \u2503 \u25C9\u25C9\u25C9\u25C9\u25C9\u25C9 \u2503 ",
230
+ " \u2503 \u25C9\u25C9\u25C9\u25C9\u25C9\u25C9\u25C9\u25C9 \u2503 ",
231
+ " \u2503 \u25C9\u25C9\u25C9\u25C9\u25C9\u25C9 \u2503 ",
232
+ " \u2570\u2501\u252B \u2523\u2501\u256F ",
233
+ " \u2570\u2501\u252B \u2523\u2501\u256F ",
234
+ " \u2570\u2501\u2501\u2501\u256F "
235
+ ],
236
+ "cartographer": [
237
+ " N ",
238
+ " \u25B3 ",
239
+ " \u256D\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u256E ",
240
+ " \u2502 \xB7 \u2502 \xB7 \u2502 ",
241
+ " W\u253C\xB7\xB7\xB7\xB7+\xB7\xB7\xB7\xB7\u253CE ",
242
+ " \u2502 \xB7 \u2502 \xB7 \u2502 ",
243
+ " \u2570\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u256F ",
244
+ " \u25BD ",
245
+ " S "
246
+ ],
247
+ "world-traveler": [
248
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
249
+ " \u256D\u2500\u2500\u2524 \u251C\u2500\u2500\u256E ",
250
+ " \u2571 \xB7\u2502 \u25E0\u25E1\u25E0 \u2502\xB7 \u2572 ",
251
+ " \u2502 \xB7 \u2502\u25E0 \u25E1\u2502 \xB7 \u2502 ",
252
+ " \u2502 \xB7 \u2502 \u25E1\u25E0\u25E1 \u2502 \xB7 \u2502 ",
253
+ " \u2572 \xB7\u2502 \u2502\xB7 \u2571 ",
254
+ " \u2570\u2500\u2500\u2524 \u251C\u2500\u2500\u256F ",
255
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F "
256
+ ],
257
+ "hive-mind": [
258
+ " \u2571\u2572 \u2571\u2572 \u2571\u2572 ",
259
+ " \u2571\u25C6\u25C6\u2572\u2571\u25C6\u25C6\u2572\u2571\u25C6\u25C6\u2572 ",
260
+ " \u2572\u25C6\u25C6\u2571\u2572\u25C6\u25C6\u2571\u2572\u25C6\u25C6\u2571 ",
261
+ " \u2571\u2572\u2571\u2571\u25C6\u2572\u2571\u2571\u25C6\u2572\u2572\u2571\u2572 ",
262
+ " \u2571\u25C6\u25C6\u2572\u2572\u25C6\u25C6\u2572\u2572\u25C6\u25C6\u2571\u25C6\u25C6\u2572 ",
263
+ " \u2572\u25C6\u25C6\u2571\u2571\u25C6\u25C6\u2571\u2571\u25C6\u25C6\u2572\u25C6\u25C6\u2571 ",
264
+ " \u2572\u2571\u2572\u2572\u25C6\u2571\u2572\u2572\u25C6\u2571\u2571\u2572\u2571 ",
265
+ " \u2572\u25C6\u25C6\u2571\u2572\u25C6\u25C6\u2571\u2572\u25C6\u25C6\u2571 ",
266
+ " \u2572\u2571 \u2572\u2571 \u2572\u2571 "
267
+ ],
268
+ "old-growth": [
269
+ " \u2571\u2572 ",
270
+ " \u2571\u2571\u2572\u2572 ",
271
+ " \u2571\u2571 \u2572\u2572 ",
272
+ " \u2571\u2571\u2571\u2572\u2571\u2572\u2572\u2572 ",
273
+ " \u2571\u2571\u2571 \u2572\u2572\u2572 ",
274
+ " \u2571\u2571\u2571\u2571\u2572\u2571\u2572\u2571\u2572\u2572\u2572\u2572 ",
275
+ " \u2551\u2551\u2551 ",
276
+ " \u2551\u2551\u2551 ",
277
+ " \u2550\u2550\u2550\u2569\u2569\u2569\u2550\u2550\u2550 "
278
+ ],
279
+ "ancient": [
280
+ " \u250C\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2510 ",
281
+ " \u2502 \u2502 \u25C9 \u25C9 \u2502 \u2502 ",
282
+ " \u2502 \u2502 \u25BD \u2502 \u2502 ",
283
+ " \u2554\u2550\u2567\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2567\u2550\u2557 ",
284
+ " \u2551 A N C I E N \u2551 ",
285
+ " \u2551 T \u2551 ",
286
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D ",
287
+ " \u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571 ",
288
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
289
+ ],
290
+ "regular": [
291
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u256E ",
292
+ " \u256D\u2500\u2524 10 \u251C\u2500\u256E ",
293
+ " \u2571 \u2570\u2500\u2500\u2500\u2500\u2500\u256F \u2572 ",
294
+ " \u2502 \u256D\u2500\u2500\u2500\u2500\u2500\u256E \u2502 ",
295
+ " \u2502 \u2502 \u25C9 \u2502 \u2502 ",
296
+ " \u2502 \u2570\u2500\u2500\u2500\u2500\u2500\u256F \u2502 ",
297
+ " \u2572 \u2571 ",
298
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F "
299
+ ],
300
+ "veteran": [
301
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
302
+ " \u2551 \u2554\u2550\u2550\u2550\u2550\u2550\u2557 \u2551 ",
303
+ " \u2551 \u2551 V \u2551 \u2551 ",
304
+ " \u2551 \u2551 500 \u2551 \u2551 ",
305
+ " \u2551 \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u2551 ",
306
+ " \u2551 \u2605 \u2605 \u2605 \u2605 \u2605 \u2551 ",
307
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D ",
308
+ " \u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571 ",
309
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
310
+ ],
311
+ "swarm-starter": [
312
+ " ",
313
+ " \u25C6 \u25C6 \u25C6 \u25C6 \u25C6 ",
314
+ " \u25C6 \u25C6 \u25C6 \u25C6 \u25C6 ",
315
+ " \u25C6 \u25C6 \u25C6 \u25C6 \u25C6 ",
316
+ " \u25C6 \u25C6 \u25C6 \u25C6 \u25C6 ",
317
+ " \u25C6 \u25C6 \u25C6 \u25C6 \u25C6 ",
318
+ " ",
319
+ " \xB7 50 out \xB7 "
320
+ ],
321
+ "legion": [
322
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
323
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
324
+ " \u25C6\u25C6\u25C6 2K \u25C6\u25C6\u25C6\u25C6\u25C6 ",
325
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
326
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
327
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
328
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 "
329
+ ],
330
+ "army-of-thousands": [
331
+ " \xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7 ",
332
+ " \xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6 ",
333
+ " \xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7 ",
334
+ " \xB7\u25C6\xB7\u25C6\xB75K\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7 ",
335
+ " \xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7 ",
336
+ " \xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6 ",
337
+ " \xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7 ",
338
+ " \xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6 ",
339
+ " \xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7\xB7 "
340
+ ],
341
+ "singularity": [
342
+ " \u2572 \u2502 \u2571 \u2500 \xB7 ",
343
+ " \u2572 \u2502 \u2571 ",
344
+ " \u2572\u2502\u2571 ",
345
+ " \u2500\u2500\u2500\u2500\u25C6\u2500\u2500\u2500\u2500 ",
346
+ " \u2571\u2502\u2572 ",
347
+ " \u2571 \u2502 \u2572 ",
348
+ " \u2571 \u2502 \u2572 \u2500 \xB7 ",
349
+ " 10000 agents "
350
+ ],
351
+ "first-shift": [
352
+ " \u250C\u2500\u2500\u2500\u2500\u2500\u2510 ",
353
+ " \u2502 \u2572 \u2502 ",
354
+ " \u2571\u2502 \u2572 \u2502\u2572 ",
355
+ " \u2571 \u2502 \u2572 \u2502 \u2572 ",
356
+ " \u2572 \u2502 \u2572\u2502 \u2571 ",
357
+ " \u2572\u2502 \u2502\u2571 ",
358
+ " \u2502 \u2571 \u2502 ",
359
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2518 ",
360
+ " 10 hrs "
361
+ ],
362
+ "workaholic": [
363
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
364
+ " \u2571 12 \u2572 ",
365
+ " \u2502 \xB7 \u2502 \u2502 ",
366
+ " \u25029 \xB7 \u2502 3 \u2502 ",
367
+ " \u2502 \u2572 \u2502 ",
368
+ " \u2572 6 \xB7 \u2571 ",
369
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
370
+ " 100 hrs "
371
+ ],
372
+ "time-lord": [
373
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
374
+ " \u2571 \u2572 ",
375
+ " \u2502 \xB7 11 12 1 \xB7 \u2502 ",
376
+ " \u2502 10 \u2572 2 \u2502 ",
377
+ " \u2502 9 \xB7 3 \u2502 ",
378
+ " \u2502 8 4 \u2502 ",
379
+ " \u2572 7 5 \u2571 ",
380
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D ",
381
+ " 500 hrs "
382
+ ],
383
+ "eternal-grind": [
384
+ " \u256D\u2500\u2500\u2500\u256E \u256D\u2500\u2500\u2500\u256E ",
385
+ " \u2571 \u2572 \u2571 \u2572 ",
386
+ " \u2502 \u221E \u2573 \u221E \u2502 ",
387
+ " \u2572 \u2571 \u2572 \u2571 ",
388
+ " \u2570\u2500\u2500\u2500\u256F \u2570\u2500\u2500\u2500\u256F ",
389
+ " \u250C\u2500\u2500\u2500\u2500\u2500\u2510 ",
390
+ " \u2502 \u2572 \u2571 \u2502 ",
391
+ " \u2502 \xD7 \u2502 ",
392
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2518 "
393
+ ],
394
+ "epoch": [
395
+ " \xB7 \u2605 \xB7 ",
396
+ " \xB7 \u2571\u2502\u2572 \xB7 ",
397
+ " \u2572 \u2571 \u2502 \u2572 \u2571 ",
398
+ " \u2572\u2571 \u2502 \u2572\u2571 ",
399
+ " \u2500\u2500\u2500\u2500\u25C6\u2500\u2500\u253C\u2500\u2500\u25C6\u2500\u2500\u2500\u2500 ",
400
+ " \u2571\u2572 \u2502 \u2571\u2572 ",
401
+ " \u2571 \u2572 \u2502 \u2571 \u2572 ",
402
+ " \xB7 \u2572\u2502\u2571 \xB7 ",
403
+ " 5000 hrs "
404
+ ],
405
+ "seasoned": [
406
+ " \u2571\u2572 ",
407
+ " \u2571 \u2572 ",
408
+ " \u2571\u2571\u2572\u2571\u2572\u2572 ",
409
+ " \u2571\u2571 \u2572\u2572 ",
410
+ " \u2571\u2571\u2571\u2571\u2572\u2571\u2572\u2572\u2572\u2572 ",
411
+ " \u2551\u2551 ",
412
+ " \u2500\u2500\u2500\u2500\u2500\u2568\u2568\u2500\u2500\u2500\u2500\u2500 ",
413
+ " \u2572\u2572\u2572\u2572\u2572\u2572\u2572\u2572\u2572\u2572\u2572\u2572\u2572 ",
414
+ " 90 day roots "
415
+ ],
416
+ "omnipresent": [
417
+ " N ",
418
+ " \xB7 \u25B3 \xB7 ",
419
+ " NW \u256D\u2500\u253C\u2500\u256E NE ",
420
+ " \xB7 \u256D\u2500\u2524\xB7+\xB7\u251C\u2500\u256E \xB7 ",
421
+ " W\u2500\u2500\u2524\xB7\u2502 \xB7 \u2502\xB7\u251C\u2500\u2500E ",
422
+ " \xB7 \u2570\u2500\u2524\xB7+\xB7\u251C\u2500\u256F \xB7 ",
423
+ " SW \u2570\u2500\u253C\u2500\u256F SE ",
424
+ " \xB7 \u25BD \xB7 ",
425
+ " S "
426
+ ],
427
+ "apprentice": [
428
+ " ",
429
+ " \u2571\u2572 ",
430
+ " \u2571 \u2572 ",
431
+ " \u2571 \u2572 ",
432
+ " \u2571 \u2572 ",
433
+ " \u2571\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2572 ",
434
+ " \u2571 level 5 \u2572 ",
435
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
436
+ ],
437
+ "journeyman": [
438
+ " \u2571\u2572 ",
439
+ " \u2571 \u2572 \u2571\u2572 ",
440
+ " \u2571 \u2572 \u2571 \u2572 ",
441
+ " \u2571 \u2573 \u2572 ",
442
+ " \u2571 \u2571 \u2572 \u2572 ",
443
+ " \u2571\u2500\u2500\u2500\u2500\u2500\u2500\u2571\u2500\u2500\u2500\u2572\u2500\u2500\u2500\u2500\u2572",
444
+ " \u2571 level 15 ",
445
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
446
+ ],
447
+ "master": [
448
+ " \u2605\u2572\u2571\u2605 ",
449
+ " \u2571\u2572 ",
450
+ " \u2571 \u2572 ",
451
+ " \u2571\u2571\u2572\u2571\u2572\u2572 ",
452
+ " \u2571\u2571 \u2572\u2572 ",
453
+ " \u2571\u2571 \u2572\u2572 ",
454
+ " \u2571\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2572\u2572 ",
455
+ " \u2571 level 30 \u2572 ",
456
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
457
+ ],
458
+ "grandmaster": [
459
+ " \u2605 \u2605 \u2605 ",
460
+ " \u2572\u2502\u2571 ",
461
+ " \u2571\u2572 ",
462
+ " \u2571 \u2572 ",
463
+ " \u2571\u2571\u2572\u2571\u2572\u2572 ",
464
+ " \u2571\u2571 \u2572\u2572 ",
465
+ " \u2571\u2571 \u2605 \u2572\u2572 ",
466
+ " \u2571\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2572\u2572 ",
467
+ " \u2571 level 50 \u2572 "
468
+ ],
469
+ // ── Session ───────────────────────────────────────────────────────────────
470
+ "marathon": [
471
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
472
+ " \u2502 \u2571\u2572 \u2571\u2572 \u2571\u2572 \u2571\u2572 \u2502 ",
473
+ " \u2502\u2571 \u2573 \u2573 \u2573 \u2572 \u2502 ",
474
+ " \u2502 \u2571\u2572 \u2571\u2572 \u2571\u2572 \u2502 ",
475
+ " \u2502 \u2571 \u2573 \u2573 \u2572 \u2502 ",
476
+ " \u2502 \u2571 \u2571 \u2572\u2571 \u2572 \u2572 \u2502 ",
477
+ " \u2502\u2571 \u2571 \u2572 \u2572\u2502 ",
478
+ " \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 ",
479
+ " \u2502 ~ ^ ~ \u2502 ",
480
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F "
481
+ ],
482
+ "blitz": [
483
+ " \u2571\u2571 ",
484
+ " \u2571\u2571 ",
485
+ " \u2571\u2571 ",
486
+ " \u2571\u2571\u2571\u2571\u2571\u2571 ",
487
+ " \u2571\u2571 ",
488
+ " \u2571\u2571 ",
489
+ " \u2571\u2571 ",
490
+ " \u2571\u2571 "
491
+ ],
492
+ "speed-run": [
493
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
494
+ " \u2571 \u2572 ",
495
+ " \u2502 10 \u2571\u2571 \u2502 ",
496
+ " \u2502 \xB7 \u2571\u2571 \u2502 ",
497
+ " \u2502 \u2571 \u2571\u2571 \u2502 ",
498
+ " \u2502 \u2571 \u2571\u2571 \u2502 ",
499
+ " \u2572 \u2571\u2571 \u2571 ",
500
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F "
501
+ ],
502
+ "flawless": [
503
+ " \u2726 \u2726 ",
504
+ " \u2726 \u2571\u2572 \u2726 ",
505
+ " \u2554\u2550\u2550\u2567\u2550\u2550\u2550\u2557 ",
506
+ " \u2726 \u2551 \u2551 \u2726 ",
507
+ " \u2551 \u25C6 \u2551 ",
508
+ " \u2551 \u2551 ",
509
+ " \u2726 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2726 ",
510
+ " \u2726 \u2726 "
511
+ ],
512
+ "iron-will": [
513
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
514
+ " \u2551 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2551 ",
515
+ " \u2551 \u2502\u2554\u2550\u2550\u2550\u2550\u2550\u2557\u2502 \u2551 ",
516
+ " \u2551 \u2502\u2551 \u25C6\u25C6\u25C6 \u2551\u2502 \u2551 ",
517
+ " \u2551 \u2502\u2551 \u25C6\u25C6\u25C6 \u2551\u2502 \u2551 ",
518
+ " \u2551 \u2502\u255A\u2550\u2550\u2550\u2550\u2550\u255D\u2502 \u2551 ",
519
+ " \u2551 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2551 ",
520
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D "
521
+ ],
522
+ "glass-cannon": [
523
+ " \u2571\u2572 ",
524
+ " \u2571\u2571\u2572\u2572 ",
525
+ " \u2571\u2571 \u2572\u2572 ",
526
+ " \u2571\u2571 \u2573\u2573 \u2572\u2572 ",
527
+ " \u2571\u2571 \u2573\u2573 \u2572\u2572 ",
528
+ " \u2572\u2572 \u2573\u2573 \u2571\u2571 ",
529
+ " \u2572\u2572 \u2571\u2571 ",
530
+ " \u2572\u2572 \u2571\u2571 ",
531
+ " \u2572\u2572\u2571\u2571 ",
532
+ " \u2550\u2550\u2550\u2567\u2567\u2550\u2550\u2550 "
533
+ ],
534
+ "solo": [
535
+ " ",
536
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
537
+ " \u2502 \u2502 ",
538
+ " \u2502 \u25C6 \u2502 ",
539
+ " \u2502 \u2502 ",
540
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
541
+ " "
542
+ ],
543
+ "one-more-cycle": [
544
+ " \u256D\u2500\u2500\u2192\u2500\u2500\u256E ",
545
+ " \u2502 \u2502 ",
546
+ " \u2191 \u221E \u2193 ",
547
+ " \u2502 \u2502 ",
548
+ " \u2570\u2500\u2500\u2190\u2500\u2500\u256F ",
549
+ " ",
550
+ " \u256D\u2500\u2500\u2192\u2500\u2500\u256E ",
551
+ " \u2502 \u2502 ",
552
+ " \u2191 \u221E \u2193 ",
553
+ " \u2502 \u2502 ",
554
+ " \u2570\u2500\u2500\u2190\u2500\u2500\u256F "
555
+ ],
556
+ "quick-draw": [
557
+ " \u2571\u2502 ",
558
+ " \u2571 \u2502 ",
559
+ " \u2500\u2500\u2571\u2500\u2500\u2502\u2500\u2500 ",
560
+ " \u2571 \u2502 ",
561
+ " \u2571 \u256D\u2500\u256F ",
562
+ " \u2571 \u2502 ",
563
+ " \u2571 \u256D\u2500\u256F ",
564
+ " \u2502 ",
565
+ " \u2500\u2500\u2568\u2500\u2500 "
566
+ ],
567
+ "squad": [
568
+ " ",
569
+ " \u25C6 \u25C6 \u25C6 \u25C6 ",
570
+ " ",
571
+ " \u25C6 \u25C6 \u25C6 \u25C6 ",
572
+ " ",
573
+ " \u25C6 \u25C6 ",
574
+ " ",
575
+ " \xB7 10 strong \xB7 "
576
+ ],
577
+ "battalion": [
578
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
579
+ " ",
580
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
581
+ " ",
582
+ " \u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6\u25C6 ",
583
+ " ",
584
+ " \xB7 25 strong \xB7 "
585
+ ],
586
+ "swarm": [
587
+ " \u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7 ",
588
+ " \xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6 ",
589
+ " \u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7 ",
590
+ " \xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6 ",
591
+ " \u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7 ",
592
+ " \xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6\xB7\u25C6 ",
593
+ " \xB7 50 strong \xB7 "
594
+ ],
595
+ "deep-dive": [
596
+ " \u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248 ",
597
+ " \u2193 ",
598
+ " \u2571\u2502\u2572 ",
599
+ " \u2502 ",
600
+ " \u2502 ",
601
+ " \u25BC ",
602
+ " \xB7 \xB7 \xB7 \xB7 \xB7 \xB7 \xB7 ",
603
+ " 15 deep "
604
+ ],
605
+ "abyss": [
606
+ " \u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248 ",
607
+ " \u2502 ",
608
+ " \u2502 ",
609
+ " \u2502 ",
610
+ " \u25BC ",
611
+ " \u2502 ",
612
+ " \u2502 ",
613
+ " \u25BC ",
614
+ " 25 deep "
615
+ ],
616
+ "eternal-recurrence": [
617
+ " \u256D\u2500\u2500\u2192\u2500\u2500\u256E ",
618
+ " \u2571 \u221E \u2572 ",
619
+ " \u2502 \u256D\u2500\u2500\u2500\u256E \u2502 ",
620
+ " \u2502 \u2502 \u221E \u2502 \u2502 ",
621
+ " \u2502 \u2570\u2500\u2500\u2500\u256F \u2502 ",
622
+ " \u2572 \u221E \u2571 ",
623
+ " \u2570\u2500\u2500\u2190\u2500\u2500\u256F ",
624
+ " 40 cycles "
625
+ ],
626
+ "endurance": [
627
+ " ",
628
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
629
+ " \u2551\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2551 ",
630
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D ",
631
+ " ",
632
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
633
+ " \u2551\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593 \u2551 ",
634
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D ",
635
+ " 4 hours "
636
+ ],
637
+ "ultramarathon": [
638
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
639
+ " \u2551\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2551 ",
640
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D ",
641
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 ",
642
+ " \xB7 ",
643
+ " \xB7 ",
644
+ " \xB7 ",
645
+ " \xB7 ",
646
+ " 6 hours "
647
+ ],
648
+ "one-shot": [
649
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
650
+ " \u2502 \u256D\u2500\u2500\u2500\u256E \u2502 ",
651
+ " \u2502 \u2502 \u25C9 \u2502 \u2502 ",
652
+ " \u2502 \u2570\u2500\u2500\u2500\u256F \u2502 ",
653
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
654
+ " \u2191 ",
655
+ " \u2571\u2502 ",
656
+ " \u2571 \u2502 ",
657
+ " \u2192 \u25C9 \u2502 "
658
+ ],
659
+ "flash": [
660
+ " \u2571\u2571 ",
661
+ " \u2571\u2571 ",
662
+ " \u2571\u2571 ",
663
+ " \u2571\u2571\u2571\u2571\u2571\u2571 ",
664
+ " \u2571\u2571 ",
665
+ " \u2571\u2571 ",
666
+ " \u2571\u2571 ",
667
+ " \u2571\u2571 ",
668
+ " < 2 min "
669
+ ],
670
+ // ── Time ──────────────────────────────────────────────────────────────────
671
+ "night-owl": [
672
+ " \u263D ",
673
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u256E \xB7 ",
674
+ " \u2571 \u25C9 \u25C9 \u2572 \xB7 ",
675
+ " \u2502 \u25BD \u2502 \xB7 ",
676
+ " \u2502 \u2571\u2500\u2500\u2500\u2572 \u2502 ",
677
+ " \u2572\u2571 \u2572\u2571 ",
678
+ " \u2571\u2572 \u2571\u2572 \xB7 ",
679
+ " \u2571 \u2572\u2500\u2500\u2500\u2571 \u2572 \xB7 ",
680
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
681
+ ],
682
+ "dawn-patrol": [
683
+ " ",
684
+ " \u2500 \u2500 \u2500 \u2500 \u2500 \u2500 \u2500 \u2500 ",
685
+ " \u2572 \u2502 \u2571 ",
686
+ " \u2572 \u2502 \u2571 ",
687
+ " \u2500\u2500\u2500\u2500\u25D1\u2500\u2500\u2500\u2500 ",
688
+ " \u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593 ",
689
+ " \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 ",
690
+ " "
691
+ ],
692
+ "early-bird": [
693
+ " \u2571\u2571 ",
694
+ " \u25E0 \u2571\u2571 ",
695
+ " \u2571 \u2572 \u2571\u2571 ",
696
+ " \u2502 \u25C9 \u2572>\u2571 ",
697
+ " \u2502 \u2550\u2571 ",
698
+ " \u2572 \u2571 ",
699
+ " \u2572\u2571\u2572 ",
700
+ " \u2572 \u2572 ",
701
+ " \u2594\u2594 "
702
+ ],
703
+ "weekend-warrior": [
704
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 ",
705
+ " \u2551 S M T W T \u2551 ",
706
+ " \u2551 \u2551 ",
707
+ " \u2551 \u25C6 \u2551 ",
708
+ " \u2551 \u25C6 \u2551 ",
709
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D "
710
+ ],
711
+ "all-nighter": [
712
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
713
+ " \u2571 12 \u2572 ",
714
+ " \u2502 \xB7 \u2502 \u2502 ",
715
+ " \u25029 \u2502 3 \u2502 ",
716
+ " \u2502 \xB7 \u2502 ",
717
+ " \u2572 6 \u2571 ",
718
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
719
+ " \u221E \u221E \u221E "
720
+ ],
721
+ "witching-hour": [
722
+ " \xB7 \xB7 \u2726 \xB7 \xB7 ",
723
+ " \xB7 \u2571\u2572 \xB7 ",
724
+ " \u2571 \u2572 ",
725
+ " \u2502 3:00 \u2502 ",
726
+ " \u2502 AM \u2502 ",
727
+ " \u2572 \u2571 ",
728
+ " \xB7 \u2572\u2571 \xB7 ",
729
+ " \xB7 \xB7 \u2726 \xB7 \xB7 "
730
+ ],
731
+ // ── Behavioral ────────────────────────────────────────────────────────────
732
+ "sisyphean": [
733
+ " \u2571 ",
734
+ " \u2571 \u25C9 ",
735
+ " \u2571 \u25C9\u25C9\u25C9 ",
736
+ " \u2571 \u25C9\u25C9\u25C9 ",
737
+ " \u2571 \u25C9 ",
738
+ " \u2571 ; ",
739
+ " \u2571 (>.<) ",
740
+ " \u2571 \u2571\u2571 \u2572\u2572 ",
741
+ " \u2571 \u2571 \u2572 "
742
+ ],
743
+ "stubborn": [
744
+ " \u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571 ",
745
+ " \u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571 ",
746
+ " \u25C9 ",
747
+ " \u25C9\u25C9\u25C9 ",
748
+ " (>.<) ",
749
+ " \u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571 ",
750
+ " \u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571 ",
751
+ " \u2713 "
752
+ ],
753
+ "creature-of-habit": [
754
+ " \u256D\u2500\u2500\u2500\u256E \u256D\u2500\u2500\u2500\u256E ",
755
+ " \u2502 \u2192 \u2502\u2192\u2502 \u2192 \u2502\u2192 ",
756
+ " \u2570\u2500\u2500\u2500\u256F \u2570\u2500\u2500\u2500\u256F ",
757
+ " \u256D\u2500\u2500\u2500\u256E \u256D\u2500\u2500\u2500\u256E ",
758
+ " \u2502 \u2192 \u2502\u2192\u2502 \u2192 \u2502\u2192 ",
759
+ " \u2570\u2500\u2500\u2500\u256F \u2570\u2500\u2500\u2500\u256F ",
760
+ " \u256D\u2500\u2500\u2500\u256E \u256D\u2500\u2500\u2500\u256E ",
761
+ " \u2502 \u2192 \u2502\u2192\u2502 \u2192 \u2502\u2192 ",
762
+ " \u2570\u2500\u2500\u2500\u256F \u2570\u2500\u2500\u2500\u256F "
763
+ ],
764
+ "loyal": [
765
+ " \u2571\u2572 ",
766
+ " \u2571 \u2572 ",
767
+ " \u2571 \u2665\u2665 \u2572 ",
768
+ " \u2571 \u2665\u2665 \u2572 ",
769
+ " \u2572 50 \u2571 ",
770
+ " \u2572 \u2571 ",
771
+ " \u2572 \u2571 ",
772
+ " \u2572\u2571 "
773
+ ],
774
+ "wanderer": [
775
+ " \xB7 \xB7 \xB7 ",
776
+ " \u2572 \u2502 \u2571 ",
777
+ " \u2572 \u2502 \u2571 ",
778
+ " \xB7 + \xB7 ",
779
+ " \u2571 \u2502 \u2572 ",
780
+ " \u2571 \u2502 \u2572 ",
781
+ " \xB7 \xB7 \xB7 "
782
+ ],
783
+ "streak": [
784
+ " \u2554\u2550\u2550\u2557\u2554\u2550\u2550\u2557\u2554\u2550\u2550\u2557\u2554\u2550\u2550\u2557 ",
785
+ " \u2551M \u2551\u2551T \u2551\u2551W \u2551\u2551T \u2551 ",
786
+ " \u2551\u25C6 \u2551\u2551\u25C6 \u2551\u2551\u25C6 \u2551\u2551\u25C6 \u2551 ",
787
+ " \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D ",
788
+ " \u2554\u2550\u2550\u2557\u2554\u2550\u2550\u2557\u2554\u2550\u2550\u2557 ",
789
+ " \u2551F \u2551\u2551S \u2551\u2551S \u2551 ",
790
+ " \u2551\u25C6 \u2551\u2551\u25C6 \u2551\u2551\u25C6 \u2551 ",
791
+ " \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D "
792
+ ],
793
+ "hot-streak": [
794
+ " \u2571\u2572 ",
795
+ " \u2571\u2571\u2572\u2572 ",
796
+ " \u2571\u2571\u25C6\u25C6\u2572\u2572 ",
797
+ " \u2571\u2571 \u25C6\u25C6 \u2572\u2572 ",
798
+ " \u2572\u2572 \u25C6\u25C6 \u2571\u2571 ",
799
+ " \u2572\u2572\u25C6\u25C6\u2571\u2571 ",
800
+ " \u2572\u2572\u2571\u2571 ",
801
+ " \xD77 clean "
802
+ ],
803
+ "momentum": [
804
+ " \u25C9 \u25C9 ",
805
+ " \u2502\u2572 \u2571\u2502 ",
806
+ " \u2502 \u2572 \u2571 \u2502 ",
807
+ " \u2502 \u2572 \u2571 \u2502 ",
808
+ " \u2502 \u25C9\u25C9 \u2502 ",
809
+ " \u2502 \u2571 \u2572 \u2502 ",
810
+ " \u2502 \u2571 \u2572 \u2502 ",
811
+ " \u2502\u2571 \u2572\u2502 ",
812
+ " \u25C9 \u25C9 "
813
+ ],
814
+ "patient-one": [
815
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
816
+ " \u2502 \u203E.\u203E \u2502 ",
817
+ " \u2502 \u2502 ",
818
+ " \u2502 zzz \u2502 ",
819
+ " \u2502 zz \u2502 ",
820
+ " \u2502 z \u2502 ",
821
+ " \u2502 \u2502 ",
822
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
823
+ " 30 min+ "
824
+ ],
825
+ "message-in-a-bottle": [
826
+ " \u256D\u2500\u256E ",
827
+ " \u2502\xD7\u2502 ",
828
+ " \u256D\u2500\u2534\u2500\u2534\u2500\u256E ",
829
+ " \u2502 \u2591\u2591\u2591 \u2502 ",
830
+ " \u2502 \u2591\u2591\u2591 \u2502 ",
831
+ " \u2502 \u2591\u2591\u2591 \u2502 ",
832
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u256F ",
833
+ " \u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248\u2248 ",
834
+ " \u2248\u2248\u2248\u2248\u2248\u2248\u2248 "
835
+ ],
836
+ "comeback-kid": [
837
+ " \u2571\u2572 \u2571\u2572 ",
838
+ " \u2571 \u2572 \u2571 \u2572 ",
839
+ " \u2571 \u2572 \u2571 \u2572 ",
840
+ " \u2571 \u2573 \u2572 ",
841
+ " \u2572 \u2571\u2572 \u2571 ",
842
+ " \u2572 \u2571 \u2572 \u2571 ",
843
+ " \u2572 \u2571 \u2572 \u2571 ",
844
+ " \u2572\u2571 \u2572\u2571 \u2713 "
845
+ ],
846
+ "pair-programming": [
847
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u256E\u256D\u2500\u2500\u2500\u2500\u2500\u256E ",
848
+ " \u2502 \u25C9 \u25C9 \u2502\u2502 \u25C9 \u25C9 \u2502 ",
849
+ " \u2502 \u25BD \u2502\u2502 \u25BD \u2502 ",
850
+ " \u2570\u2500\u2500\u252C\u2500\u2500\u256F\u2570\u2500\u2500\u252C\u2500\u2500\u256F ",
851
+ " \u2502 \u2571\u2500\u2500\u2572 \u2502 ",
852
+ " \u2502\u2571 \u2572\u2502 ",
853
+ " \u2571 \u2550\u2550 \u2572 ",
854
+ " \u2571 \u2550\u2550\u2550\u2550 \u2572 ",
855
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 "
856
+ ],
857
+ "overdrive": [
858
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
859
+ " \u2571 \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E \u2572 ",
860
+ " \u2502 \u2571 \xD76 \u2572 \u2502 ",
861
+ " \u2502 \u2502 \u25C9 \u2502 \u2502 ",
862
+ " \u2502 \u2572 \u2571 \u2502 ",
863
+ " \u2572 \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F \u2571 ",
864
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
865
+ " same day \xD76 "
866
+ ],
867
+ "iron-streak": [
868
+ " \u2554\u2550\u2550\u2557\u2500\u2554\u2550\u2550\u2557\u2500\u2554\u2550\u2550\u2557 ",
869
+ " \u2551\u25C6 \u2551 \u2551\u25C6 \u2551 \u2551\u25C6 \u2551 ",
870
+ " \u255A\u2550\u2550\u255D\u2500\u255A\u2550\u2550\u255D\u2500\u255A\u2550\u2550\u255D ",
871
+ " \u2502 \u2502 ",
872
+ " \u2554\u2550\u2550\u2557\u2500\u2554\u2550\u2550\u2557\u2500\u2554\u2550\u2550\u2557 ",
873
+ " \u2551\u25C6 \u2551 \u2551\u25C6 \u2551 \u2551\u25C6 \u2551 ",
874
+ " \u255A\u2550\u2550\u255D\u2500\u255A\u2550\u2550\u255D\u2500\u255A\u2550\u2550\u255D ",
875
+ " 14 day chain "
876
+ ],
877
+ "deep-conversation": [
878
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E ",
879
+ " \u2571 \u256D\u2500\u2500\u256E 20 \u2572 ",
880
+ " \u2502 \u2502 \u2502 \u2502 ",
881
+ " \u2502 \u2570\u2500\u2500\u256F \u2502 ",
882
+ " \u2502 \xB7 \xB7 \xB7 \xB7 \xB7 \u2502 ",
883
+ " \u2572 \u2571 ",
884
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F ",
885
+ " \u2572 ",
886
+ " \u25CF "
887
+ ],
888
+ "one-must-imagine": [
889
+ " \u25C9\u25C9\u25C9\u25C9\u25C9 ",
890
+ " \u2571 \u25C9\u25C9\u25C9\u25C9 ",
891
+ " \u2571 \u25C9 \u2571 \u2190\u2500\u256E ",
892
+ "\u2571 (>.<) \u2502 ",
893
+ " \u2571\u2571 \u2572\u2572 \u2502 ",
894
+ " \u2571 \u2572 \u2502 ",
895
+ " \u2571 \u2572\u2500\u256F ",
896
+ " \u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594\u2594 ",
897
+ " \xD710 restarts "
898
+ ]
899
+ };
900
+ var CARD_WIDTH = 34;
901
+ var CARD_HEIGHT = 18;
902
+ var CARD_INNER = CARD_WIDTH - 2;
903
+ function centerLine(text, width) {
904
+ const stripped = stripAnsiForWidth(text);
905
+ if (stripped.length >= width) return text.slice(0, width);
906
+ const pad = Math.floor((width - stripped.length) / 2);
907
+ return " ".repeat(pad) + text + " ".repeat(width - stripped.length - pad);
908
+ }
909
+ function stripAnsiForWidth(s) {
910
+ return s.replace(/\x1b\[[0-9;]*m/g, "");
911
+ }
912
+ function renderBadgeCard(def, unlock, opts) {
913
+ const dim = opts?.dim === true || unlock === null;
914
+ const art = BADGE_ART[def.id] ?? [];
915
+ const lines = [];
916
+ const category = def.category.toUpperCase();
917
+ const borderLabel = ` ${category} `;
918
+ const topPad = CARD_INNER - borderLabel.length - 2;
919
+ const topLeft = Math.floor(topPad / 2);
920
+ const topRight = topPad - topLeft;
921
+ lines.push(`\u250C${"\u2500".repeat(topLeft)}${borderLabel}${"\u2500".repeat(topRight)}\u2510`);
922
+ lines.push(`\u2502${" ".repeat(CARD_INNER)}\u2502`);
923
+ const artMaxLines = 9;
924
+ const artSlice = art.slice(0, artMaxLines);
925
+ for (const artLine of artSlice) {
926
+ const centered = centerLine(artLine, CARD_INNER);
927
+ lines.push(`\u2502${dim ? dimText(centered) : centered}\u2502`);
928
+ }
929
+ for (let i = artSlice.length; i < artMaxLines; i++) {
930
+ lines.push(`\u2502${" ".repeat(CARD_INNER)}\u2502`);
931
+ }
932
+ lines.push(`\u2502${" ".repeat(CARD_INNER)}\u2502`);
933
+ const nameText = unlock !== null ? def.name : `? ${def.name} ?`;
934
+ lines.push(`\u2502${centerLine(nameText, CARD_INNER)}\u2502`);
935
+ const descLines = wrapText(def.description, CARD_INNER - 4);
936
+ for (const dl of descLines.slice(0, 2)) {
937
+ const centered = centerLine(dl, CARD_INNER);
938
+ lines.push(`\u2502${dim ? dimText(centered) : centered}\u2502`);
939
+ }
940
+ const usedContent = 1 + 1 + artMaxLines + 1 + 1 + Math.min(descLines.length, 2);
941
+ const remaining = CARD_HEIGHT - 2 - usedContent;
942
+ for (let i = 0; i < remaining; i++) {
943
+ if (i === remaining - 1 && unlock !== null) {
944
+ const dateStr = unlock.unlockedAt.slice(0, 10);
945
+ lines.push(`\u2502${centerLine(dimText(dateStr), CARD_INNER)}\u2502`);
946
+ } else {
947
+ lines.push(`\u2502${" ".repeat(CARD_INNER)}\u2502`);
948
+ }
949
+ }
950
+ lines.push(`\u2514${"\u2500".repeat(CARD_INNER)}\u2518`);
951
+ return { lines, width: CARD_WIDTH, height: lines.length };
952
+ }
953
+ function dimText(text) {
954
+ return `\x1B[2m${text}\x1B[22m`;
955
+ }
956
+ function wrapText(text, maxWidth) {
957
+ const words = text.split(" ");
958
+ const lines = [];
959
+ let current = "";
960
+ for (const word of words) {
961
+ if (current.length + word.length + 1 > maxWidth && current.length > 0) {
962
+ lines.push(current);
963
+ current = word;
964
+ } else {
965
+ current = current.length > 0 ? `${current} ${word}` : word;
966
+ }
967
+ }
968
+ if (current.length > 0) lines.push(current);
969
+ return lines;
970
+ }
971
+ function createBadgeGallery(unlockedAchievements, startIndex) {
972
+ const unlocked = /* @__PURE__ */ new Map();
973
+ for (const a of unlockedAchievements) {
974
+ unlocked.set(a.id, a);
975
+ }
976
+ const sorted = [...ACHIEVEMENTS].sort((a, b) => {
977
+ const aUnlocked = unlocked.has(a.id);
978
+ const bUnlocked = unlocked.has(b.id);
979
+ if (aUnlocked && !bUnlocked) return -1;
980
+ if (!aUnlocked && bUnlocked) return 1;
981
+ if (aUnlocked && bUnlocked) {
982
+ const aDate = unlocked.get(a.id).unlockedAt;
983
+ const bDate = unlocked.get(b.id).unlockedAt;
984
+ return aDate.localeCompare(bDate);
985
+ }
986
+ return 0;
987
+ });
988
+ return {
989
+ achievements: sorted,
990
+ unlocked,
991
+ currentIndex: startIndex ?? 0,
992
+ total: sorted.length
993
+ };
994
+ }
995
+ function galleryNext(gallery) {
996
+ return (gallery.currentIndex + 1) % gallery.total;
997
+ }
998
+ function galleryPrev(gallery) {
999
+ return (gallery.currentIndex - 1 + gallery.total) % gallery.total;
1000
+ }
1001
+
1002
+ // src/shared/client.ts
1003
+ import { connect } from "net";
1004
+ function rawSend(request, timeoutMs = 1e4) {
1005
+ const sock = socketPath();
1006
+ return new Promise((resolve, reject) => {
1007
+ const socket = connect(sock);
1008
+ let data = "";
1009
+ const timeout = setTimeout(() => {
1010
+ socket.destroy();
1011
+ reject(new Error(`Request timed out after ${(timeoutMs / 1e3).toFixed(0)}s. The daemon may be overloaded.
1012
+ Check: sisyphus doctor
1013
+ Logs: tail -20 ~/.sisyphus/daemon.log`));
1014
+ }, timeoutMs);
1015
+ socket.on("connect", () => {
1016
+ socket.write(JSON.stringify(request) + "\n");
1017
+ });
1018
+ socket.on("data", (chunk) => {
1019
+ data += chunk.toString();
1020
+ const newlineIdx = data.indexOf("\n");
1021
+ if (newlineIdx !== -1) {
1022
+ clearTimeout(timeout);
1023
+ const line = data.slice(0, newlineIdx);
1024
+ socket.destroy();
1025
+ try {
1026
+ resolve(JSON.parse(line));
1027
+ } catch {
1028
+ reject(new Error(`Invalid JSON response from daemon: ${line}`));
1029
+ }
1030
+ }
1031
+ });
1032
+ socket.on("error", (err) => {
1033
+ clearTimeout(timeout);
1034
+ reject(err);
1035
+ });
1036
+ });
1037
+ }
1038
+
1039
+ export {
1040
+ rawSend,
1041
+ computeActiveTimeMs,
1042
+ formatDuration,
1043
+ statusColor,
1044
+ resolveReports,
1045
+ buildCompanionContext,
1046
+ buildSessionContext,
1047
+ renderBadgeCard,
1048
+ createBadgeGallery,
1049
+ galleryNext,
1050
+ galleryPrev
1051
+ };
1052
+ //# sourceMappingURL=chunk-C2XKXERJ.js.map