sisyphi 1.1.19 → 1.1.23

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