usehour 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/hour.mjs +474 -0
- package/package.json +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# usehour
|
|
2
|
+
|
|
3
|
+
Your year of building with AI, as Wrapped-style cards.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx usehour
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
- Asks before it reads anything, and lists the folders it will read.
|
|
10
|
+
- Reads your Claude Code and Codex session logs on your machine, read only.
|
|
11
|
+
- Keeps counts only. Your prompts and code are never saved or sent.
|
|
12
|
+
- Opens your cards in your browser. Save any card as an image to post it.
|
|
13
|
+
- No dependencies, no network access, nothing installed.
|
|
14
|
+
|
|
15
|
+
It's one file: read [hour.mjs](https://usehour.com/hour.mjs) before you run it.
|
|
16
|
+
|
|
17
|
+
Made by [Hour](https://usehour.com/wrapped).
|
package/hour.mjs
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
Hour Wrapped: a year of building with AI, from the sessions already on your machine.
|
|
4
|
+
|
|
5
|
+
npx https://usehour.com/hour.tgz (or: node hour.mjs)
|
|
6
|
+
|
|
7
|
+
What this script does, in order:
|
|
8
|
+
1. Tells you which folders it will read, and asks before reading anything.
|
|
9
|
+
2. Reads your Claude Code (~/.claude/projects) and Codex (~/.codex/sessions)
|
|
10
|
+
session logs on this machine. Read only. Nothing is changed or deleted.
|
|
11
|
+
3. Keeps counts only: how many sessions, which hours, which models, how often
|
|
12
|
+
you asked the agent to test its work. Your prompts and your code are read
|
|
13
|
+
to count them and are never written anywhere or sent anywhere.
|
|
14
|
+
4. Writes two files into ./hour-wrapped/ : index.html (your cards) and
|
|
15
|
+
wrapped.json (the counts), then opens the cards in your browser.
|
|
16
|
+
5. Only if you say yes: sends wrapped.json to Hour so we can make your
|
|
17
|
+
shareable page. You see the exact data first. Nothing is sent otherwise.
|
|
18
|
+
|
|
19
|
+
No dependencies. No install step. No network access unless you choose to share.
|
|
20
|
+
Source: https://usehour.com/hour.mjs Questions: lush@usehour.com
|
|
21
|
+
*/
|
|
22
|
+
import fs from "node:fs";
|
|
23
|
+
import os from "node:os";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import readline from "node:readline";
|
|
26
|
+
import { spawn } from "node:child_process";
|
|
27
|
+
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
|
|
30
|
+
const VERSION = "0.1.0";
|
|
31
|
+
const SHARE_URL_DEFAULT = "https://usehour.com/";
|
|
32
|
+
|
|
33
|
+
/* ------------------------------------------------------------------ patterns */
|
|
34
|
+
const TEST = /\b(pytest|vitest|jest|mocha|playwright|go test|cargo test|npm (run )?test|pnpm (run )?test|yarn test|bun test|node --test|npm run (check|lint|typecheck|build)|tsc\b|astro check|ruff|eslint|rspec|phpunit|dotnet test|mvn test|gradle test|--check\b)/i;
|
|
35
|
+
const VERIFY = /\b(test|tests|verify|verif\w+|double[- ]check|check (that|if|whether|it|the)|make sure|screenshot|confirm|does it (work|run)|run it|try it|qa|review)\b/i;
|
|
36
|
+
const CORRECT = /^(no\b|nope|not\b|wait\b|stop\b|don'?t\b|do not\b|actually\b|instead\b|hmm|that'?s (wrong|not)|this is (wrong|not)|you (broke|missed|forgot)|why did you|undo|revert|remove|go back|but (this|that|it|you|i said|why))/i;
|
|
37
|
+
const GOAHEAD = /^(ok(ay)?|all right|alright|yes|yup|yep|sure|great|cool|nice|perfect|good|please proceed|proceed|go ahead|continue|do it|let'?s go)\b/i;
|
|
38
|
+
const QUESTION = /^(what|why|how|where|which|who|can you|could you|is it|are you|do you|does)\b/i;
|
|
39
|
+
const DONTKNOW = /\bi don'?t know\b/i;
|
|
40
|
+
|
|
41
|
+
/* ------------------------------------------------------------ file parsing */
|
|
42
|
+
/* One file in, one small summary out. Prompt text stays inside this function:
|
|
43
|
+
only word counts, timestamps and the classification of each prompt leave it.
|
|
44
|
+
The one exception is very short prompts (three words or fewer, letters only),
|
|
45
|
+
which are tallied so the "go-to prompt" card can say e.g. "please proceed". */
|
|
46
|
+
function shortKey(t) {
|
|
47
|
+
const s = t.trim().toLowerCase().replace(/[.!]+$/, "");
|
|
48
|
+
if (!/^[a-z' ]+$/.test(s)) return null;
|
|
49
|
+
const w = s.split(/\s+/).filter(Boolean);
|
|
50
|
+
return w.length >= 1 && w.length <= 3 ? w.join(" ") : null;
|
|
51
|
+
}
|
|
52
|
+
function promptFacts(ts, text) {
|
|
53
|
+
const t = text.trim();
|
|
54
|
+
return {
|
|
55
|
+
ts,
|
|
56
|
+
words: t.split(/\s+/).filter(Boolean).length,
|
|
57
|
+
verify: VERIFY.test(t),
|
|
58
|
+
dontknow: DONTKNOW.test(t),
|
|
59
|
+
kind: CORRECT.test(t) ? "correct" : (GOAHEAD.test(t) && t.split(/\s+/).length <= 12) ? "go" : (t.endsWith("?") || QUESTION.test(t)) ? "question" : "direct",
|
|
60
|
+
short: shortKey(t),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function codexUserText(p) {
|
|
64
|
+
const pick = (c) => typeof c === "string" ? c : Array.isArray(c) ? c.map((x) => x && (x.text || x.input_text) || "").filter((x) => typeof x === "string").join("\n") : "";
|
|
65
|
+
if (p.type === "user_message") return p.message || "";
|
|
66
|
+
if (p.type === "item_completed" && p.item && p.item.type === "UserMessage") return pick(p.item.content);
|
|
67
|
+
if (p.type === "message" && p.role === "user") return pick(p.content);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function cleanPrompt(t) {
|
|
71
|
+
if (!t) return null;
|
|
72
|
+
t = t.trim();
|
|
73
|
+
const m = t.match(/## My request[^\n]*\n([\s\S]*)/);
|
|
74
|
+
if (m) t = m[1].trim();
|
|
75
|
+
if (!t || t.startsWith("<") || t.startsWith("# AGENTS.md") || t.startsWith("Caveat:") || t.startsWith("[Request interrupted") || t.startsWith("# Files mentioned")) return null;
|
|
76
|
+
return t;
|
|
77
|
+
}
|
|
78
|
+
const KEEP_CODEX = /"(session_meta|turn_context|user_message|UserMessage|function_call|custom_tool_call|local_shell_call|token_count|turn_aborted)"|"role":\s*"user"/;
|
|
79
|
+
const KEEP_CLAUDE = /"type":\s*"(user|assistant)"/;
|
|
80
|
+
async function parseFile(file, src) {
|
|
81
|
+
const s = { src, sub: file.includes(`${path.sep}subagents${path.sep}`), start: null, end: null, cwd: null, models: {}, prompts: [], ids: [], patches: 0, cmds: 0, tests: 0, aborts: 0, tokens: 0 };
|
|
82
|
+
const seenMsg = new Set(); const seenText = new Set();
|
|
83
|
+
const rl = readline.createInterface({ input: fs.createReadStream(file, { encoding: "utf8" }), crlfDelay: Infinity });
|
|
84
|
+
for await (const line of rl) {
|
|
85
|
+
if (line.length > 4_000_000) continue;
|
|
86
|
+
if (src === "codex" && !KEEP_CODEX.test(line)) continue;
|
|
87
|
+
if (src === "claude" && !KEEP_CLAUDE.test(line)) continue;
|
|
88
|
+
let o; try { o = JSON.parse(line); } catch { continue; }
|
|
89
|
+
const ts = o.timestamp;
|
|
90
|
+
if (ts) { s.start = s.start || ts; s.end = ts; }
|
|
91
|
+
if (src === "claude") {
|
|
92
|
+
if (o.isSidechain) s.sub = true;
|
|
93
|
+
s.cwd = s.cwd || o.cwd || null;
|
|
94
|
+
const m = o.message && typeof o.message === "object" ? o.message : {};
|
|
95
|
+
if (o.type === "user") {
|
|
96
|
+
const c = m.content;
|
|
97
|
+
let text = typeof c === "string" ? c : Array.isArray(c) && !c.some((x) => x && x.type === "tool_result") ? c.filter((x) => x && x.type === "text").map((x) => x.text).join("\n") : null;
|
|
98
|
+
if (text && text.trim().startsWith("[Request interrupted")) s.aborts++;
|
|
99
|
+
if (o.isMeta) continue;
|
|
100
|
+
const u = cleanPrompt(text);
|
|
101
|
+
if (u) { s.prompts.push(promptFacts(ts, u)); s.ids.push(o.uuid || null); }
|
|
102
|
+
} else if (o.type === "assistant") {
|
|
103
|
+
if (m.id && !seenMsg.has(m.id)) {
|
|
104
|
+
seenMsg.add(m.id);
|
|
105
|
+
if (m.model && m.model !== "<synthetic>") s.models[m.model] = (s.models[m.model] || 0) + 1;
|
|
106
|
+
const u = m.usage || {};
|
|
107
|
+
s.tokens += (u.input_tokens || 0) + (u.output_tokens || 0) + (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
108
|
+
}
|
|
109
|
+
for (const x of m.content || []) {
|
|
110
|
+
if (!x || x.type !== "tool_use") continue;
|
|
111
|
+
if (["Edit", "Write", "MultiEdit", "NotebookEdit"].includes(x.name)) s.patches++;
|
|
112
|
+
else if (x.name === "Bash") { s.cmds++; if (TEST.test((x.input && x.input.command) || "")) s.tests++; }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
const p = o.payload || {};
|
|
117
|
+
if (o.type === "session_meta") {
|
|
118
|
+
s.cwd = p.cwd || null;
|
|
119
|
+
if ((p.source && typeof p.source === "object" && p.source.subagent) || p.parent_thread_id) s.sub = true;
|
|
120
|
+
} else if (o.type === "turn_context") {
|
|
121
|
+
if (p.model) s.models[p.model] = (s.models[p.model] || 0) + 1;
|
|
122
|
+
} else if (o.type === "event_msg" && p.type === "turn_aborted") s.aborts++;
|
|
123
|
+
else if (o.type === "event_msg" && p.type === "token_count") {
|
|
124
|
+
const t = p.info && p.info.total_token_usage && p.info.total_token_usage.total_tokens;
|
|
125
|
+
if (t) s.tokens = Math.max(s.tokens, t);
|
|
126
|
+
}
|
|
127
|
+
const ut = codexUserText(p);
|
|
128
|
+
if (ut !== null && (o.type === "event_msg" || o.type === "response_item")) {
|
|
129
|
+
const u = cleanPrompt(ut);
|
|
130
|
+
if (u && !seenText.has(u)) { seenText.add(u); s.prompts.push(promptFacts(ts, u)); s.ids.push(null); }
|
|
131
|
+
}
|
|
132
|
+
if (o.type === "response_item" && ["function_call", "custom_tool_call", "local_shell_call"].includes(p.type)) {
|
|
133
|
+
const name = p.name || "";
|
|
134
|
+
let arg = p.arguments || p.input || (p.action ? JSON.stringify(p.action) : "");
|
|
135
|
+
if (typeof arg !== "string") arg = JSON.stringify(arg);
|
|
136
|
+
if (name === "apply_patch" || arg.includes("*** Begin Patch")) s.patches++;
|
|
137
|
+
else { s.cmds++; if (TEST.test(arg)) s.tests++; }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return s;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Worker threads parse files in parallel; each returns only its summary. */
|
|
145
|
+
if (!isMainThread) {
|
|
146
|
+
parentPort.on("message", async ({ file, src }) => {
|
|
147
|
+
try { parentPort.postMessage({ ok: true, s: await parseFile(file, src) }); }
|
|
148
|
+
catch (e) { parentPort.postMessage({ ok: false, error: String(e && e.message || e) }); }
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ------------------------------------------------------------- discovery */
|
|
153
|
+
function walk(dir, out = []) {
|
|
154
|
+
let ents; try { ents = fs.readdirSync(dir, { withFileTypes: true }); } catch { return out; }
|
|
155
|
+
for (const e of ents) {
|
|
156
|
+
const p = path.join(dir, e.name);
|
|
157
|
+
if (e.isDirectory()) walk(p, out);
|
|
158
|
+
else if (e.isFile() && e.name.endsWith(".jsonl")) out.push(p);
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ---------------------------------------------------------------- metrics */
|
|
164
|
+
const median = (a) => { if (!a.length) return 0; const b = [...a].sort((x, y) => x - y), m = b.length >> 1; return b.length % 2 ? b[m] : (b[m - 1] + b[m]) / 2; };
|
|
165
|
+
const pct = (x) => Math.round(x * 100);
|
|
166
|
+
function modelName(id) {
|
|
167
|
+
const m = id.toLowerCase();
|
|
168
|
+
const pretty = m.replace(/^claude-/, "Claude ").replace(/-(\d+)-(\d+)(-\d{8})?$/, " $1.$2").replace(/-(\d+)(-\d{8})?$/, " $1").replace(/^gpt/, "GPT");
|
|
169
|
+
return pretty.replace(/\b(opus|sonnet|haiku|fable)\b/g, (w) => w[0].toUpperCase() + w.slice(1));
|
|
170
|
+
}
|
|
171
|
+
function compute(summaries) {
|
|
172
|
+
const seen = new Set();
|
|
173
|
+
for (const s of summaries) {
|
|
174
|
+
if (s.src !== "claude") continue;
|
|
175
|
+
const keep = [];
|
|
176
|
+
s.prompts.forEach((p, i) => { const k = s.ids[i] || `${p.ts}|${p.words}`; if (!seen.has(k)) { seen.add(k); keep.push(p); } });
|
|
177
|
+
s.prompts = keep;
|
|
178
|
+
}
|
|
179
|
+
const main = summaries.filter((s) => !s.sub && s.prompts.length);
|
|
180
|
+
const P = [];
|
|
181
|
+
main.forEach((s, si) => { [...s.prompts].sort((a, b) => (a.ts || "").localeCompare(b.ts || "")).forEach((p, i) => { if (p.ts) P.push({ ...p, d: new Date(p.ts), si, i }); }); });
|
|
182
|
+
if (!P.length) return null;
|
|
183
|
+
const hours = Array(24).fill(0); P.forEach((p) => hours[p.d.getHours()]++);
|
|
184
|
+
const busiest = hours.indexOf(Math.max(...hours));
|
|
185
|
+
const now = new Date(); const months = [];
|
|
186
|
+
for (let k = 12; k >= 0; k--) { const d = new Date(now.getFullYear(), now.getMonth() - k, 1); months.push({ key: `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`, label: d.toLocaleString("en", { month: "short" }), n: 0 }); }
|
|
187
|
+
P.forEach((p) => { const k = `${p.d.getFullYear()}-${String(p.d.getMonth() + 1).padStart(2, "0")}`; const m = months.find((x) => x.key === k); if (m) m.n++; });
|
|
188
|
+
const busiestMonth = months.reduce((a, b) => (b.n > a.n ? b : a), months[0]);
|
|
189
|
+
const mc = {}; main.forEach((s) => { for (const [m, c] of Object.entries(s.models)) mc[m] = (mc[m] || 0) + c; });
|
|
190
|
+
const mtot = Object.values(mc).reduce((a, b) => a + b, 0) || 1;
|
|
191
|
+
const models = Object.entries(mc).sort((a, b) => b[1] - a[1]).map(([id, c]) => ({ id, name: modelName(id), share: c / mtot }));
|
|
192
|
+
const anthropic = Object.entries(mc).filter(([m]) => m.toLowerCase().includes("claude")).reduce((a, [, c]) => a + c, 0) / mtot;
|
|
193
|
+
const firsts = P.filter((p) => p.i === 0).map((p) => p.words), follows = P.filter((p) => p.i > 0);
|
|
194
|
+
const mix = { direct: 0, question: 0, go: 0, correct: 0 }; follows.forEach((p) => mix[p.kind]++);
|
|
195
|
+
const nf = follows.length || 1;
|
|
196
|
+
const verifySessions = main.filter((s) => s.prompts.some((p) => p.verify)).length;
|
|
197
|
+
const edit = main.filter((s) => s.patches > 0), editChecked = edit.filter((s) => s.tests > 0).length;
|
|
198
|
+
const sorted = P.map((p) => [p.d.getTime(), p.si]).sort((a, b) => a[0] - b[0]);
|
|
199
|
+
let best = 0, j = 0; const win = new Map();
|
|
200
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
201
|
+
win.set(sorted[i][1], (win.get(sorted[i][1]) || 0) + 1);
|
|
202
|
+
while (sorted[i][0] - sorted[j][0] > 20 * 60e3) { const k = sorted[j][1]; const v = win.get(k) - 1; v ? win.set(k, v) : win.delete(k); j++; }
|
|
203
|
+
best = Math.max(best, win.size);
|
|
204
|
+
}
|
|
205
|
+
const shorts = {}; P.forEach((p) => { if (p.short) shorts[p.short] = (shorts[p.short] || 0) + 1; });
|
|
206
|
+
const goto = Object.entries(shorts).filter(([, c]) => c >= 5).sort((a, b) => b[1] - a[1])[0] || null;
|
|
207
|
+
const days = new Set(P.map((p) => p.d.toDateString())).size;
|
|
208
|
+
const projects = new Set(main.map((s) => s.cwd).filter(Boolean)).size;
|
|
209
|
+
const first = new Date(Math.min(...P.map((p) => p.d.getTime()))), last = new Date(Math.max(...P.map((p) => p.d.getTime())));
|
|
210
|
+
return {
|
|
211
|
+
version: VERSION,
|
|
212
|
+
generated: new Date().toISOString().slice(0, 10),
|
|
213
|
+
window: { first: first.toISOString().slice(0, 10), last: last.toISOString().slice(0, 10), activeDays: days },
|
|
214
|
+
sessions: { total: main.length, claudeCode: main.filter((s) => s.src === "claude").length, codex: main.filter((s) => s.src === "codex").length, subagentThreads: summaries.filter((s) => s.sub).length },
|
|
215
|
+
prompts: { total: P.length, medianWords: median(P.map((p) => p.words)), firstPromptMedianWords: median(firsts), followupMedianWords: median(follows.map((p) => p.words)) },
|
|
216
|
+
followups: { total: follows.length, question: mix.question / nf, correction: mix.correct / nf, goAhead: mix.go / nf, instruction: mix.direct / nf },
|
|
217
|
+
proof: { sessionsAskedToVerify: verifySessions, codingSessions: edit.length, codingSessionsWithTests: editChecked },
|
|
218
|
+
parallelMax: best,
|
|
219
|
+
hours, busiestHour: busiest, weekendShare: P.filter((p) => [0, 6].includes(p.d.getDay())).length / P.length,
|
|
220
|
+
months: months.map((m) => ({ month: m.key, label: m.label, prompts: m.n })), busiestMonth: busiestMonth.key,
|
|
221
|
+
models: models.slice(0, 5), anthropicShare: anthropic,
|
|
222
|
+
projects, stops: main.reduce((a, s) => a + s.aborts, 0),
|
|
223
|
+
goToPrompt: goto ? { text: goto[0], count: goto[1] } : null,
|
|
224
|
+
dontKnow: P.filter((p) => p.dontknow).length,
|
|
225
|
+
tokens: summaries.reduce((a, s) => a + s.tokens, 0),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* Builder types. Each signal is scored against the level where it becomes
|
|
230
|
+
notable; the highest score wins, and below 1 everywhere you are a Pair
|
|
231
|
+
Programmer. The rules are public so anyone can check their own. */
|
|
232
|
+
const TYPES = {
|
|
233
|
+
conductor: { name: "The Conductor", line: "Several agents at once. Long briefs, then you let them run." },
|
|
234
|
+
verifier: { name: "The Verifier", line: "Nothing ships until the agent has shown it works." },
|
|
235
|
+
architect: { name: "The Architect", line: "You brief first and in detail. The agent gets the plan before the work." },
|
|
236
|
+
interrogator: { name: "The Interrogator", line: "You ask more than you tell. Questions steer the work." },
|
|
237
|
+
sprinter: { name: "The Sprinter", line: "Short prompts, fast turns, lots of them." },
|
|
238
|
+
pair: { name: "The Pair Programmer", line: "A steady back-and-forth. You and the agent build it together." },
|
|
239
|
+
};
|
|
240
|
+
function builderType(m) {
|
|
241
|
+
const verify = m.sessions.total ? m.proof.sessionsAskedToVerify / m.sessions.total : 0;
|
|
242
|
+
const tested = m.proof.codingSessions ? m.proof.codingSessionsWithTests / m.proof.codingSessions : 0;
|
|
243
|
+
const scores = {
|
|
244
|
+
conductor: m.parallelMax / 6,
|
|
245
|
+
verifier: Math.max(verify / 0.45, tested / 0.75),
|
|
246
|
+
architect: m.prompts.firstPromptMedianWords / 80,
|
|
247
|
+
interrogator: m.followups.question / 0.3,
|
|
248
|
+
sprinter: m.prompts.medianWords ? 12 / m.prompts.medianWords : 0,
|
|
249
|
+
};
|
|
250
|
+
const [k, v] = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
|
|
251
|
+
return v >= 1 ? k : "pair";
|
|
252
|
+
}
|
|
253
|
+
const monthYear = (d) => new Date(d + "T12:00:00Z").toLocaleString("en", { month: "short", year: "numeric" });
|
|
254
|
+
const hourLabel = (h) => (h % 12 || 12) + (h < 12 ? " AM" : " PM");
|
|
255
|
+
const compact = (n) => n >= 1e9 ? (n / 1e9).toFixed(1).replace(/\.0$/, "") + "B" : n >= 1e6 ? (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M" : n >= 1e3 ? Math.round(n / 1e3) + "K" : String(n);
|
|
256
|
+
function cards(m, name) {
|
|
257
|
+
const type = TYPES[builderType(m)];
|
|
258
|
+
const tools = [m.sessions.claudeCode && "Claude Code", m.sessions.codex && "Codex"].filter(Boolean).join(" and ");
|
|
259
|
+
const verify = m.sessions.total ? m.proof.sessionsAskedToVerify / m.sessions.total : 0;
|
|
260
|
+
const tested = m.proof.codingSessions ? m.proof.codingSessionsWithTests / m.proof.codingSessions : 0;
|
|
261
|
+
const top = m.models[0];
|
|
262
|
+
const busy = m.months.find((x) => x.month === m.busiestMonth);
|
|
263
|
+
const C = [];
|
|
264
|
+
C.push({ id: "intro", bg: "#0B0B0A", fg: "#F4F1EA", dot: "#F4F1EA", pat: "months", ask: "Your year with AI", big: m.sessions.total.toLocaleString("en"), line: `sessions in ${tools}. ${m.prompts.total.toLocaleString("en")} prompts. Busiest month: ${busy ? busy.label : ""}.`, bs: m.sessions.total >= 1000 ? "26cqw" : "32cqw" });
|
|
265
|
+
C.push({ id: "type", bg: "#F1ECE3", fg: "#0B0B0A", dot: "#0B0B0A", pat: "sweep", ask: "Your builder type", big: type.name, line: type.line, bs: "15cqw", grad: true });
|
|
266
|
+
if (m.goToPrompt) C.push({ id: "prompt", bg: "#DBA004", fg: "#0B0B0A", dot: "#0B0B0A", pat: "rain", ask: "Your go-to prompt", big: `“${m.goToPrompt.text}”`, line: `You said it ${m.goToPrompt.count} times.`, bs: m.goToPrompt.text.length > 12 ? "14cqw" : "20cqw" });
|
|
267
|
+
const hs = [...m.hours].map((v, h) => [h, v]).sort((a, b) => b[1] - a[1]);
|
|
268
|
+
C.push({ id: "hour", bg: "#2B5FE3", fg: "#FFFFFF", dot: "#FFFFFF", pat: "hours", ask: "Your busiest hour", big: hourLabel(m.busiestHour), line: `${hourLabel(hs[1][0])} is a close second.`, bs: "26cqw" });
|
|
269
|
+
if (m.parallelMax >= 2) C.push({ id: "parallel", bg: "#F2601F", fg: "#0B0B0A", dot: "#0B0B0A", pat: "lanes", n: Math.min(13, m.parallelMax), ask: "Most agents at once", big: String(m.parallelMax), line: "sessions going inside twenty minutes.", bs: "34cqw" });
|
|
270
|
+
C.push({ id: "proof", bg: "#8B5FD9", fg: "#FFFFFF", dot: "#FFFFFF", pat: "ring", pct: verify, ask: "How often you made the agent prove it", big: `${pct(verify)}%`, line: "of sessions, you asked it to test, verify or screenshot.", bs: "30cqw" });
|
|
271
|
+
C.push({ id: "steer", bg: "#F1ECE3", fg: "#0B0B0A", dot: "#8B5FD9", pat: "share", frac: m.followups.question, ask: "How you steer", big: `${pct(m.followups.question)}%`, line: `of your follow-ups are questions. ${pct(m.followups.correction)}% are corrections.`, bs: "30cqw" });
|
|
272
|
+
if (top) C.push({ id: "model", bg: "#0B0B0A", fg: "#F4F1EA", dot: "#F2601F", pat: "stripes", shares: m.models.map((x) => x.share), ask: "Your model", big: top.name, line: `${pct(top.share)}% of your turns.` + (m.anthropicShare > 0 && m.anthropicShare < 1 ? ` ${pct(m.anthropicShare)}% Anthropic overall.` : ""), bs: top.name.length > 11 ? "11.5cqw" : "16cqw" });
|
|
273
|
+
if (m.dontKnow >= 10) C.push({ id: "honest", bg: "#FB65C2", fg: "#0B0B0A", dot: "#0B0B0A", pat: "rain", ask: "Your most honest phrase", big: "“I don't know”", line: `${m.dontKnow} times. You think out loud, with the agent.`, bs: "14cqw" });
|
|
274
|
+
if (m.tokens > 0) C.push({ id: "tokens", bg: "#2B5FE3", fg: "#FFFFFF", dot: "#FFFFFF", pat: "field", ask: "How much you built", big: compact(m.tokens), line: "tokens processed.", bs: "30cqw" });
|
|
275
|
+
if (m.proof.codingSessions >= 5) C.push({ id: "edge", bg: "#F7F3EC", fg: "#0B0B0A", dot: "#F2601F", pat: "ring", pct: tested, ask: tested < 0.75 ? "Your growth edge" : "Your strength", big: `${pct(tested)}%`, line: tested < 0.75 ? "of coding sessions ran a test. Push that higher." : "of coding sessions ran a test. Keep it that way.", bs: "30cqw" });
|
|
276
|
+
C.push({ id: "summary", bg: "#0B0B0A", fg: "#F4F1EA", summary: true, who: name || "", period: `${monthYear(m.window.first)} – ${monthYear(m.window.last)}`, type: type.name,
|
|
277
|
+
stats: [["Sessions", m.sessions.total.toLocaleString("en")], ["Busiest hour", hourLabel(m.busiestHour)], ["Go-to prompt", m.goToPrompt ? `“${m.goToPrompt.text}”` : "–"], ["Agents at once", String(m.parallelMax)], ["Made it prove it", `${pct(verify)}%`], ["Model", top ? top.name.replace(/^Claude /, "") : "–"]] });
|
|
278
|
+
return C;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* --------------------------------------------------------------- the page */
|
|
282
|
+
function pageHTML(m, C) {
|
|
283
|
+
const data = JSON.stringify({ hours: m.hours, months: m.months.map((x) => x.prompts), cards: C }).replace(/</g, "\\u003c");
|
|
284
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
285
|
+
<title>Your AI Wrapped · Hour</title>
|
|
286
|
+
<style>
|
|
287
|
+
@font-face{font-family:"Geist";src:url(data:font/woff2;base64,${FONT_GEIST}) format("woff2");font-weight:100 900}
|
|
288
|
+
:root{--paper:#F7F3EC;--ink:#0B0B0A;--ink-3:#6C6A64;--hair:rgba(11,11,10,.1);--surface:#fff;--sans:"Geist",ui-sans-serif,system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;--mono:"Geist Mono",ui-monospace,"SF Mono",Menlo,monospace}
|
|
289
|
+
*{box-sizing:border-box}body{background:var(--paper);color:var(--ink);font-family:var(--sans);margin:0;-webkit-font-smoothing:antialiased}
|
|
290
|
+
.wrap{max-width:1180px;margin:0 auto;padding:24px clamp(16px,4vw,32px) 64px}
|
|
291
|
+
.top{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:22px}
|
|
292
|
+
.brand{display:flex;align-items:center;gap:9px;font-weight:600}.brand svg{width:22px;height:22px}
|
|
293
|
+
.note{font-size:14px;color:var(--ink-3);max-width:62ch;line-height:1.5}
|
|
294
|
+
.stage{max-width:440px;margin:0 auto 14px;position:relative}
|
|
295
|
+
.bars{position:absolute;left:4%;right:4%;top:2.6%;display:flex;gap:4px;z-index:3}.bars i{flex:1;height:3px;border-radius:2px;background:rgba(255,255,255,.35)}.bars i.on{background:rgba(255,255,255,.95)}
|
|
296
|
+
.hit{position:absolute;top:0;bottom:0;z-index:4;background:transparent;border:0;cursor:pointer}.hit.l{left:0;width:40%}.hit.r{right:0;width:60%}
|
|
297
|
+
.nav{display:flex;justify-content:center;gap:10px;margin-bottom:36px}.nav button{font:500 14px var(--sans);border:1px solid var(--hair);background:var(--surface);border-radius:999px;padding:8px 16px;cursor:pointer}
|
|
298
|
+
h2{font-size:24px;letter-spacing:-.03em;margin:0 0 14px}
|
|
299
|
+
.grid{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:14px}@media(max-width:1000px){.grid{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:560px){.grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
300
|
+
.grid .card{cursor:pointer}.save{display:block;margin-top:6px;font-size:12px;color:var(--ink-3);text-align:center}
|
|
301
|
+
.card{container-type:inline-size;aspect-ratio:4/5;width:100%;border-radius:5.5%/4.4%;overflow:hidden;position:relative;background:var(--bg);color:var(--fg);isolation:isolate}
|
|
302
|
+
.card canvas{position:absolute;inset:0;width:100%;height:100%}
|
|
303
|
+
.card .in{position:absolute;inset:0;padding:9cqw 8cqw 7cqw;display:flex;flex-direction:column;z-index:1}
|
|
304
|
+
.ask{font-size:4.4cqw;font-weight:500;opacity:.85;margin-top:3cqw;max-width:62%}
|
|
305
|
+
.big{margin-top:auto;font-size:var(--bs,19cqw);line-height:.9;letter-spacing:-.055em;font-weight:650;text-wrap:balance}
|
|
306
|
+
.line{font-size:5.6cqw;line-height:1.25;letter-spacing:-.015em;margin-top:3.4cqw;max-width:92%;text-wrap:balance}
|
|
307
|
+
.ft{display:flex;justify-content:space-between;align-items:center;margin-top:6cqw;font-size:3.4cqw;opacity:.8}.ft .b{display:flex;align-items:center;gap:1.6cqw;font-weight:600}.ft svg{width:4.6cqw;height:4.6cqw}.ft .u{font-family:var(--mono)}
|
|
308
|
+
.sum{margin-top:auto;display:grid;grid-template-columns:1fr 1fr;gap:4.5cqw 5cqw}.sum div{border-top:.35cqw solid currentColor;padding-top:2cqw}.sum small{display:block;font-size:3.4cqw;opacity:.75;margin-bottom:.6cqw}.sum b{font-size:6.2cqw;letter-spacing:-.035em;font-weight:650;line-height:1}
|
|
309
|
+
.arch{font-size:11.5cqw;line-height:.92;letter-spacing:-.05em;font-weight:650;margin:2cqw 0 6cqw}
|
|
310
|
+
.grad{background:linear-gradient(100deg,#4F88FF 0%,#B0ACFE 38%,#FB65C2 72%,#FD2980 100%);-webkit-background-clip:text;background-clip:text;color:transparent}
|
|
311
|
+
</style></head><body>
|
|
312
|
+
<svg width="0" height="0" style="position:absolute" aria-hidden="true"><defs><linearGradient id="ob" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#4F88FF"/><stop offset="38%" stop-color="#B0ACFE"/><stop offset="72%" stop-color="#FB65C2"/><stop offset="100%" stop-color="#FD2980"/></linearGradient><symbol id="mark" viewBox="0 0 64 64"><path fill="url(#ob)" d="${MARK_PATH}"/></symbol></defs></svg>
|
|
313
|
+
<div class="wrap">
|
|
314
|
+
<div class="top"><div class="brand"><svg viewBox="0 0 64 64"><use href="#mark"/></svg>Hour</div><div class="note">Made on your machine from ${m.sessions.total} sessions. Nothing has been sent anywhere.</div></div>
|
|
315
|
+
<div class="stage" id="stage"><div class="bars" id="bars"></div><div id="slot"></div><button class="hit l" id="prev" aria-label="Previous"></button><button class="hit r" id="next" aria-label="Next"></button></div>
|
|
316
|
+
<div class="nav"><button id="b1">Back</button><button id="b2">Next</button></div>
|
|
317
|
+
<h2>All cards</h2><div class="grid" id="grid"></div>
|
|
318
|
+
<p class="note" style="margin-top:24px">To post a card, click "Save image" under it. The numbers are in wrapped.json next to this file. Private profile pages on usehour.com are coming soon.</p>
|
|
319
|
+
</div>
|
|
320
|
+
<script>
|
|
321
|
+
const D=${data};
|
|
322
|
+
const esc=s=>String(s).replace(/[&<>"]/g,c=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
323
|
+
function el(c){const d=document.createElement("article");d.className="card";d.style.setProperty("--bg",c.bg);d.style.setProperty("--fg",c.fg);if(c.bs)d.style.setProperty("--bs",c.bs);
|
|
324
|
+
const ft='<div class="ft"><span class="b"><svg viewBox="0 0 64 64"><use href="#mark"/></svg>Hour</span><span class="u">usehour.com</span></div>';
|
|
325
|
+
d.innerHTML='<canvas aria-hidden="true"></canvas><div class="in">'+(c.summary?('<div class="ask">'+esc(c.who?c.who+" \\u00B7 ":"")+esc(c.period)+'</div><div class="arch grad">'+esc(c.type)+'</div><div class="sum">'+c.stats.map(s=>'<div><small>'+esc(s[0])+'</small><b>'+esc(s[1])+'</b></div>').join("")+'</div>'):('<div class="ask">'+esc(c.ask)+'</div><div class="big'+(c.grad?" grad":"")+'">'+esc(c.big)+'</div><div class="line">'+esc(c.line)+'</div>'))+ft+'</div>';
|
|
326
|
+
requestAnimationFrame(()=>draw(d,c));return d}
|
|
327
|
+
function draw(card,c){const cv=card.querySelector("canvas"),r=card.getBoundingClientRect(),dpr=Math.min(devicePixelRatio||1,2),W=Math.max(1,r.width),H=Math.max(1,r.height);cv.width=W*dpr;cv.height=H*dpr;const g=cv.getContext("2d");g.scale(dpr,dpr);drawOn(g,W,H,c)}
|
|
328
|
+
function drawOn(x,W,H,c){if(!c.pat)return;
|
|
329
|
+
const cols=28,step=W/cols,rows=Math.ceil(H/step),top=H*.13,bot=H*.47;x.fillStyle=c.dot||"#fff";
|
|
330
|
+
const dot=(i,j,s)=>{if(s<=.04)return;x.globalAlpha=Math.min(1,.18+.82*s);x.beginPath();x.arc((i+.5)*step,(j+.5)*step,step*.42*Math.min(1,s),0,7);x.fill()};
|
|
331
|
+
const rand=(i,j)=>{const t=Math.sin(i*127.1+j*311.7+c.id.length*17.3)*43758.5453;return t-Math.floor(t)};
|
|
332
|
+
const band=[];for(let j=0;j<rows;j++){const y=(j+.5)*step;if(y>top&&y<bot)band.push(j)}const nb=band.length,j0=band[0]||0;
|
|
333
|
+
const colChart=v=>{const m=Math.max(1,...v),per=cols/v.length;for(let i=0;i<cols;i++){const h=Math.round(v[Math.min(v.length-1,Math.floor(i/per))]/m*nb);for(let k=0;k<nb;k++)dot(i,j0+nb-1-k,k<h?1:.12)}};
|
|
334
|
+
const ring=(p,a,b)=>{const cx=W/2,cy=(top+bot)/2,R=(bot-top)/2*.98;for(const j of band)for(let i=0;i<cols;i++){const px=(i+.5)*step-cx,py=(j+.5)*step-cy,d=Math.hypot(px,py);let an=Math.atan2(px,-py);if(an<0)an+=2*Math.PI;const t=an/(2*Math.PI),w=R*(a+(b-a)*t);if(d<=R&&d>=R-w)dot(i,j,t<=p?1:.16)}};
|
|
335
|
+
switch(c.pat){case"months":colChart(D.months);break;case"hours":colChart(D.hours);break;case"sweep":ring(1,.06,.5);break;case"ring":ring(c.pct||0,.1,.5);break;
|
|
336
|
+
case"lanes":for(let k=0;k<(c.n||5);k++){const i=Math.round((k+.5)*cols/(c.n||5)),s0=Math.floor(rand(k,3)*nb*.45),e0=nb-Math.floor(rand(k,7)*nb*.3);for(let q=0;q<nb;q++)dot(i,j0+q,q>=s0&&q<e0?1:.14)}break;
|
|
337
|
+
case"share":{const f=c.frac||0;let n=0;for(const j of band)for(let i=0;i<cols;i++){dot(i,j,rand(i,j)<f?1:.16);n++}break}
|
|
338
|
+
case"stripes":{let acc=0;const cut=(c.shares||[1]).map(v=>acc+=v);for(const j of band)for(let i=0;i<cols;i++){const f=(i+.5)/cols,k=cut.findIndex(v=>f<=v+1e-9);dot(i,j,[1,.62,.42,.3,.18][k<0?4:k])}break}
|
|
339
|
+
case"rain":for(const j of band)for(let i=0;i<cols;i++){const v=rand(i,j);dot(i,j,v>.58?.95:v>.3?.35:.1)}break;
|
|
340
|
+
case"field":for(const j of band)for(let i=0;i<cols;i++){const f=(i/cols+(j-j0)/nb)/2;dot(i,j,.15+.85*Math.pow(rand(i,j),1.6)*f*1.4)}break}x.globalAlpha=1}
|
|
341
|
+
/* Save a card as a 1080x1350 PNG. Drawn with canvas on this machine; nothing is uploaded. */
|
|
342
|
+
function wrapText(g,t,maxW){const words=String(t).split(" "),out=[];let l="";for(const w of words){const n=l?l+" "+w:w;if(g.measureText(n).width>maxW&&l){out.push(l);l=w}else l=n}if(l)out.push(l);return out}
|
|
343
|
+
function paint(g,W,H,c){g.fillStyle=c.bg;g.fillRect(0,0,W,H);drawOn(g,W,H,c);const P=W*.08,F="Geist, ui-sans-serif, system-ui, -apple-system, Helvetica, Arial, sans-serif";g.fillStyle=c.fg;g.textBaseline="alphabetic";
|
|
344
|
+
const grad=()=>{const gr=g.createLinearGradient(P,0,W-P,0);[["#4F88FF",0],["#B0ACFE",.38],["#FB65C2",.72],["#FD2980",1]].forEach(([k,o])=>gr.addColorStop(o,k));return gr};
|
|
345
|
+
/* footer */const fs=W*.034,fy=H-W*.07;g.globalAlpha=.8;g.font="600 "+fs+"px "+F;const r=W*.019;g.lineWidth=W*.008;g.strokeStyle=grad();g.beginPath();g.arc(P+r,fy-fs*.35,r,0,7);g.stroke();g.fillText("Hour",P+r*2+W*.016,fy);g.font="400 "+fs+"px ui-monospace, SFMono-Regular, Menlo, monospace";g.textAlign="right";g.fillText("usehour.com",W-P,fy);g.textAlign="left";g.globalAlpha=1;
|
|
346
|
+
let y=fy-W*.085;
|
|
347
|
+
if(c.summary){const cw=(W-2*P-W*.05)/2,rowH=W*.2;const rows=Math.ceil(c.stats.length/2);let top=y-rows*rowH+W*.02;
|
|
348
|
+
c.stats.forEach((s,k)=>{const x=P+(k%2)*(cw+W*.05),yy=top+Math.floor(k/2)*rowH;g.fillStyle=c.fg;g.globalAlpha=1;g.fillRect(x,yy,cw,W*.0035);g.globalAlpha=.75;g.font="400 "+W*.034+"px "+F;g.fillText(s[0],x,yy+W*.05);g.globalAlpha=1;g.font="650 "+W*.062+"px "+F;wrapText(g,s[1],cw).slice(0,2).forEach((l,i)=>g.fillText(l,x,yy+W*.115+i*W*.062))});
|
|
349
|
+
g.font="650 "+W*.115+"px "+F;g.fillStyle=grad();g.fillText(c.type,P,W*.33);g.fillStyle=c.fg;g.globalAlpha=.85;g.font="500 "+W*.044+"px "+F;g.fillText((c.who?c.who+" · ":"")+c.period,P,W*.16);g.globalAlpha=1;return}
|
|
350
|
+
g.font="450 "+W*.056+"px "+F;const L=wrapText(g,c.line,(W-2*P)*.92);L.reverse().forEach((l,i)=>g.fillText(l,P,y-i*W*.07));y-=L.length*W*.07+W*.034;
|
|
351
|
+
let bs=parseFloat(c.bs||"19")/100*W;g.font="650 "+bs+"px "+F;let Bl=wrapText(g,c.big,W-2*P);while(Bl.length>1&&Bl[Bl.length-1].length<=2&&bs>W*.08){bs*=.92;g.font="650 "+bs+"px "+F;Bl=wrapText(g,c.big,W-2*P)}g.fillStyle=c.grad?grad():c.fg;Bl.reverse().forEach((l,i)=>g.fillText(l,P,y-i*bs*.9));
|
|
352
|
+
g.fillStyle=c.fg;g.globalAlpha=.85;g.font="500 "+W*.044+"px "+F;wrapText(g,c.ask,(W-2*P)*.62).forEach((l,i)=>g.fillText(l,P,W*.16+i*W*.055));g.globalAlpha=1}
|
|
353
|
+
async function save(c){try{await document.fonts.ready}catch(e){}const cv=document.createElement("canvas");cv.width=1080;cv.height=1350;paint(cv.getContext("2d"),1080,1350,c);const a=document.createElement("a");a.download="hour-"+c.id+".png";a.href=cv.toDataURL("image/png");document.body.appendChild(a);a.click();a.remove()}
|
|
354
|
+
let cur=0;const slot=document.getElementById("slot"),bars=document.getElementById("bars"),grid=document.getElementById("grid");
|
|
355
|
+
D.cards.forEach(()=>bars.appendChild(document.createElement("i")));
|
|
356
|
+
function show(n){cur=(n+D.cards.length)%D.cards.length;slot.innerHTML="";slot.appendChild(el(D.cards[cur]));[...bars.children].forEach((b,k)=>b.classList.toggle("on",k<=cur))}
|
|
357
|
+
D.cards.forEach((c,k)=>{const w=document.createElement("div");const e=el(c);e.onclick=()=>show(k);const s=document.createElement("a");s.href="#";s.className="save";s.textContent="Save image";s.onclick=ev=>{ev.preventDefault();save(c)};w.append(e,s);grid.appendChild(w)});
|
|
358
|
+
prev.onclick=b1.onclick=()=>show(cur-1);next.onclick=b2.onclick=()=>show(cur+1);addEventListener("keydown",e=>{if(e.key==="ArrowRight")show(cur+1);if(e.key==="ArrowLeft")show(cur-1)});show(0);
|
|
359
|
+
</script></body></html>`;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* ------------------------------------------------------------------ the run */
|
|
363
|
+
const B = (s) => `\x1b[1m${s}\x1b[0m`, DIM = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
364
|
+
/* One reader for the whole run, so answers typed ahead are never lost. */
|
|
365
|
+
let RL = null, LINES = [], WAIT = null;
|
|
366
|
+
function ask(q) {
|
|
367
|
+
if (!RL) {
|
|
368
|
+
RL = readline.createInterface({ input: process.stdin, terminal: false });
|
|
369
|
+
RL.on("line", (l) => { if (WAIT) { const w = WAIT; WAIT = null; w(l.trim()); } else LINES.push(l.trim()); });
|
|
370
|
+
RL.on("close", () => { if (WAIT) { const w = WAIT; WAIT = null; w(""); } });
|
|
371
|
+
}
|
|
372
|
+
process.stdout.write(q);
|
|
373
|
+
if (LINES.length) return Promise.resolve(LINES.shift());
|
|
374
|
+
return new Promise((res) => { WAIT = res; });
|
|
375
|
+
}
|
|
376
|
+
function done() { if (RL) RL.close(); }
|
|
377
|
+
const yes = (a) => /^y(es)?$/i.test(a);
|
|
378
|
+
function openFile(f) {
|
|
379
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
380
|
+
const args = process.platform === "win32" ? ["/c", "start", "", f] : [f];
|
|
381
|
+
try { spawn(cmd, args, { stdio: "ignore", detached: true }).unref(); } catch { /* the path is printed anyway */ }
|
|
382
|
+
}
|
|
383
|
+
/* Sharing stays off until usehour.com can store profiles. */
|
|
384
|
+
const SHARE_OPEN = false;
|
|
385
|
+
async function main() {
|
|
386
|
+
const argv = process.argv.slice(2), flag = (n) => argv.includes(n), opt = (n) => { const i = argv.indexOf(n); return i >= 0 ? argv[i + 1] : null; };
|
|
387
|
+
if (flag("--help") || flag("-h")) {
|
|
388
|
+
console.log(`Hour Wrapped ${VERSION}\n\n npx usehour [--yes] [--no-open] [--out DIR] [--claude-dir DIR] [--codex-dir DIR] [--no-share]\n\n --yes skip the first question (reading still happens only on this machine)\n --no-open don't open the browser\n --no-share never ask to share\n --out DIR where to write hour-wrapped/ (default: current folder)\n`);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const home = os.homedir();
|
|
392
|
+
const sources = [
|
|
393
|
+
{ src: "claude", label: "Claude Code", dir: opt("--claude-dir") || path.join(home, ".claude", "projects") },
|
|
394
|
+
{ src: "codex", label: "Codex", dir: opt("--codex-dir") || path.join(process.env.CODEX_HOME || path.join(home, ".codex"), "sessions") },
|
|
395
|
+
].map((s) => ({ ...s, files: walk(s.dir) }));
|
|
396
|
+
const found = sources.filter((s) => s.files.length);
|
|
397
|
+
console.log(`\n ${B("Hour Wrapped")} ${DIM("v" + VERSION)} Your year of building with AI.\n`);
|
|
398
|
+
if (!found.length) {
|
|
399
|
+
console.log(` No Claude Code or Codex sessions found.\n Looked in:\n${sources.map((s) => " " + s.dir).join("\n")}\n\n If yours live somewhere else, run with --claude-dir or --codex-dir.\n`);
|
|
400
|
+
process.exitCode = 1; return;
|
|
401
|
+
}
|
|
402
|
+
const bytes = found.reduce((a, s) => a + s.files.reduce((b, f) => { try { return b + fs.statSync(f).size; } catch { return b; } }, 0), 0);
|
|
403
|
+
console.log(" This reads your session logs, on this machine only:");
|
|
404
|
+
for (const s of found) console.log(` ${s.label.padEnd(12)} ${s.files.length} files ${DIM(s.dir)}`);
|
|
405
|
+
console.log(`\n It keeps counts only. Your prompts and code are not saved or sent.`);
|
|
406
|
+
console.log(` Nothing leaves this computer unless you say yes at the end.\n`);
|
|
407
|
+
if (!flag("--yes")) {
|
|
408
|
+
if (!process.stdin.isTTY) { console.log(" Run this in a terminal (or add --yes)."); process.exitCode = 1; return; }
|
|
409
|
+
const a = await ask(` Read ${(bytes / 1e9).toFixed(1)} GB of logs and make your cards? [y/N] `);
|
|
410
|
+
if (!yes(a)) { console.log("\n Stopped. Nothing was read.\n"); return; }
|
|
411
|
+
}
|
|
412
|
+
const jobs = found.flatMap((s) => s.files.map((file) => ({ file, src: s.src, size: fs.statSync(file).size }))).sort((a, b) => b.size - a.size);
|
|
413
|
+
const summaries = []; let done = 0, doneBytes = 0; const t0 = Date.now();
|
|
414
|
+
const tick = () => { if (process.stdout.isTTY) process.stdout.write(`\r Reading ${done}/${jobs.length} files ${Math.round(doneBytes / Math.max(1, bytes) * 100)}% `); };
|
|
415
|
+
const nWorkers = Math.max(1, Math.min(4, (os.availableParallelism ? os.availableParallelism() : os.cpus().length) - 1));
|
|
416
|
+
await new Promise((resolve) => {
|
|
417
|
+
let next = 0, active = 0;
|
|
418
|
+
const self = fileURLToPath(import.meta.url);
|
|
419
|
+
const workers = Array.from({ length: nWorkers }, () => new Worker(self));
|
|
420
|
+
const feed = (w) => {
|
|
421
|
+
if (next >= jobs.length) { w.terminate(); if (--active === 0) resolve(); return; }
|
|
422
|
+
const job = jobs[next++];
|
|
423
|
+
w.once("message", (r) => { if (r.ok) summaries.push(r.s); done++; doneBytes += job.size; tick(); feed(w); });
|
|
424
|
+
w.postMessage({ file: job.file, src: job.src });
|
|
425
|
+
};
|
|
426
|
+
workers.forEach((w) => { active++; feed(w); });
|
|
427
|
+
});
|
|
428
|
+
if (process.stdout.isTTY) process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
429
|
+
console.log(` Read ${jobs.length} files in ${Math.round((Date.now() - t0) / 1000)}s.`);
|
|
430
|
+
const m = compute(summaries);
|
|
431
|
+
if (!m) { console.log("\n Found the logs but no prompts in them. Nothing to make cards from.\n"); return; }
|
|
432
|
+
const C = cards(m, "");
|
|
433
|
+
const outDir = path.resolve(opt("--out") || ".", "hour-wrapped");
|
|
434
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
435
|
+
fs.writeFileSync(path.join(outDir, "wrapped.json"), JSON.stringify(m, null, 2));
|
|
436
|
+
const page = path.join(outDir, "index.html");
|
|
437
|
+
fs.writeFileSync(page, pageHTML(m, C));
|
|
438
|
+
const type = TYPES[builderType(m)].name;
|
|
439
|
+
console.log(`\n ${B("You're " + type + ".")} ${m.sessions.total} sessions, ${m.prompts.total.toLocaleString("en")} prompts, busiest at ${hourLabel(m.busiestHour)}.`);
|
|
440
|
+
console.log(` Your cards: ${page}\n`);
|
|
441
|
+
if (!flag("--no-open")) openFile(page);
|
|
442
|
+
if (flag("--no-share") || !process.stdin.isTTY) return;
|
|
443
|
+
if (!SHARE_OPEN) { console.log(" Private profile pages on usehour.com are coming soon. Nothing was sent.\n"); return; }
|
|
444
|
+
|
|
445
|
+
/* Sharing is a separate, explicit choice. */
|
|
446
|
+
const a1 = await ask(` Want a shareable page on usehour.com? This sends Hour the counts in wrapped.json only. [y/N] `);
|
|
447
|
+
if (!yes(a1)) { console.log("\n Done. Nothing was sent.\n"); return; }
|
|
448
|
+
const email = await ask(" Your email (so we can send you the link): ");
|
|
449
|
+
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { console.log("\n That doesn't look like an email. Nothing was sent.\n"); return; }
|
|
450
|
+
const name = await ask(" Your name, as it should appear on the page: ");
|
|
451
|
+
const linkedin = await ask(` LinkedIn URL ${DIM("(optional, press Enter to skip)")}: `);
|
|
452
|
+
const pub = yes(await ask(" Make your page public so you can share the link? [y/N] "));
|
|
453
|
+
const feature = yes(await ask(" OK for Hour to feature your cards in posts about great AI builders? [y/N] "));
|
|
454
|
+
const roles = yes(await ask(" Open to hearing about roles where this matters? [y/N] "));
|
|
455
|
+
const payload = { email, name, linkedin, public: pub, feature, roles, wrapped: m };
|
|
456
|
+
console.log(`\n This is exactly what will be sent to ${opt("--share-url") || SHARE_URL_DEFAULT}:\n`);
|
|
457
|
+
console.log(JSON.stringify(payload, null, 2).split("\n").map((l) => " " + l).join("\n"));
|
|
458
|
+
if (!yes(await ask("\n Send it? [y/N] "))) { console.log("\n Not sent. Your cards are still on your machine.\n"); return; }
|
|
459
|
+
const body = new URLSearchParams({ "form-name": "wrapped", email, name, linkedin, public: String(pub), feature: String(feature), roles: String(roles), data: JSON.stringify(m) });
|
|
460
|
+
try {
|
|
461
|
+
const r = await fetch(opt("--share-url") || SHARE_URL_DEFAULT, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body, redirect: "manual" });
|
|
462
|
+
if (r.status >= 200 && r.status < 400) console.log(`\n Sent. We'll email ${email} your link within a day.\n`);
|
|
463
|
+
else console.log(`\n The upload didn't go through (${r.status}). Email wrapped.json to lush@usehour.com instead.\n`);
|
|
464
|
+
} catch (e) {
|
|
465
|
+
console.log(`\n Couldn't reach usehour.com (${e.message}). Email wrapped.json to lush@usehour.com instead.\n`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/* ------------------------------------------------------------- assets
|
|
469
|
+
Hour's typeface (Geist, SIL Open Font License) and logo, embedded so the
|
|
470
|
+
cards page works offline and never loads anything from the internet. */
|
|
471
|
+
const MARK_PATH = "M32.00 2.00 L32.79 2.01 L33.57 2.04 L34.35 2.09 L35.14 2.16 L35.92 2.26 L36.69 2.37 L37.47 2.50 L38.24 2.66 L39.00 2.83 L39.76 3.02 L40.52 3.24 L41.27 3.47 L42.01 3.72 L42.75 3.99 L43.48 4.28 L44.20 4.59 L44.92 4.92 L45.62 5.27 L46.31 5.64 L47.00 6.02 L47.67 6.42 L48.34 6.84 L48.99 7.28 L49.63 7.73 L50.26 8.20 L50.88 8.69 L51.48 9.19 L52.07 9.71 L52.65 10.24 L53.21 10.79 L53.76 11.35 L54.29 11.93 L54.81 12.52 L55.31 13.12 L55.80 13.74 L56.27 14.37 L56.72 15.01 L57.16 15.66 L57.58 16.33 L57.98 17.00 L58.36 17.69 L58.73 18.38 L59.08 19.08 L59.41 19.80 L59.72 20.52 L60.01 21.25 L60.28 21.99 L60.53 22.73 L60.76 23.48 L60.98 24.24 L61.17 25.00 L61.34 25.76 L61.50 26.53 L61.63 27.31 L61.74 28.08 L61.84 28.86 L61.91 29.65 L61.96 30.43 L61.99 31.21 L62.00 32.00 L61.99 32.79 L61.96 33.57 L61.91 34.35 L61.84 35.14 L61.74 35.92 L61.63 36.69 L61.50 37.47 L61.34 38.24 L61.17 39.00 L60.98 39.76 L60.76 40.52 L60.53 41.27 L60.28 42.01 L60.01 42.75 L59.72 43.48 L59.41 44.20 L59.08 44.92 L58.73 45.62 L58.36 46.31 L57.98 47.00 L57.58 47.67 L57.16 48.34 L56.72 48.99 L56.27 49.63 L55.80 50.26 L55.31 50.88 L54.81 51.48 L54.29 52.07 L53.76 52.65 L53.21 53.21 L52.65 53.76 L52.07 54.29 L51.48 54.81 L50.88 55.31 L50.26 55.80 L49.63 56.27 L48.99 56.72 L48.34 57.16 L47.67 57.58 L47.00 57.98 L46.31 58.36 L45.62 58.73 L44.92 59.08 L44.20 59.41 L43.48 59.72 L42.75 60.01 L42.01 60.28 L41.27 60.53 L40.52 60.76 L39.76 60.98 L39.00 61.17 L38.24 61.34 L37.47 61.50 L36.69 61.63 L35.92 61.74 L35.14 61.84 L34.35 61.91 L33.57 61.96 L32.79 61.99 L32.00 62.00 L31.21 61.99 L30.43 61.96 L29.65 61.91 L28.86 61.84 L28.08 61.74 L27.31 61.63 L26.53 61.50 L25.76 61.34 L25.00 61.17 L24.24 60.98 L23.48 60.76 L22.73 60.53 L21.99 60.28 L21.25 60.01 L20.52 59.72 L19.80 59.41 L19.08 59.08 L18.38 58.73 L17.69 58.36 L17.00 57.98 L16.33 57.58 L15.66 57.16 L15.01 56.72 L14.37 56.27 L13.74 55.80 L13.12 55.31 L12.52 54.81 L11.93 54.29 L11.35 53.76 L10.79 53.21 L10.24 52.65 L9.71 52.07 L9.19 51.48 L8.69 50.88 L8.20 50.26 L7.73 49.63 L7.28 48.99 L6.84 48.34 L6.42 47.67 L6.02 47.00 L5.64 46.31 L5.27 45.62 L4.92 44.92 L4.59 44.20 L4.28 43.48 L3.99 42.75 L3.72 42.01 L3.47 41.27 L3.24 40.52 L3.02 39.76 L2.83 39.00 L2.66 38.24 L2.50 37.47 L2.37 36.69 L2.26 35.92 L2.16 35.14 L2.09 34.35 L2.04 33.57 L2.01 32.79 L2.00 32.00 L2.01 31.21 L2.04 30.43 L2.09 29.65 L2.16 28.86 L2.26 28.08 L2.37 27.31 L2.50 26.53 L2.66 25.76 L2.83 25.00 L3.02 24.24 L3.24 23.48 L3.47 22.73 L3.72 21.99 L3.99 21.25 L4.28 20.52 L4.59 19.80 L4.92 19.08 L5.27 18.38 L5.64 17.69 L6.02 17.00 L6.42 16.33 L6.84 15.66 L7.28 15.01 L7.73 14.37 L8.20 13.74 L8.69 13.12 L9.19 12.52 L9.71 11.93 L10.24 11.35 L10.79 10.79 L11.35 10.24 L11.93 9.71 L12.52 9.19 L13.12 8.69 L13.74 8.20 L14.37 7.73 L15.01 7.28 L15.66 6.84 L16.33 6.42 L17.00 6.02 L17.69 5.64 L18.38 5.27 L19.08 4.92 L19.80 4.59 L20.52 4.28 L21.25 3.99 L21.99 3.72 L22.73 3.47 L23.48 3.24 L24.24 3.02 L25.00 2.83 L25.76 2.66 L26.53 2.50 L27.31 2.37 L28.08 2.26 L28.86 2.16 L29.65 2.09 L30.43 2.04 L31.21 2.01 L32.00 2.00 L32.00 14.50 L31.54 14.46 L31.08 14.44 L30.62 14.43 L30.15 14.43 L29.69 14.45 L29.22 14.47 L28.76 14.51 L28.29 14.56 L27.83 14.62 L27.36 14.70 L26.90 14.79 L26.44 14.89 L25.98 15.00 L25.52 15.12 L25.07 15.26 L24.61 15.41 L24.16 15.57 L23.72 15.75 L23.28 15.93 L22.84 16.13 L22.40 16.34 L21.97 16.56 L21.55 16.80 L21.13 17.04 L20.72 17.30 L20.31 17.57 L19.91 17.85 L19.52 18.14 L19.13 18.44 L18.75 18.75 L18.38 19.07 L18.01 19.41 L17.66 19.75 L17.31 20.10 L16.97 20.47 L16.64 20.84 L16.32 21.22 L16.01 21.62 L15.71 22.02 L15.42 22.42 L15.13 22.84 L14.86 23.27 L14.60 23.70 L14.35 24.14 L14.12 24.59 L13.89 25.05 L13.68 25.51 L13.47 25.98 L13.28 26.46 L13.10 26.94 L12.94 27.42 L12.78 27.92 L12.64 28.41 L12.52 28.91 L12.40 29.42 L12.30 29.93 L12.21 30.44 L12.13 30.96 L12.07 31.48 L12.02 32.00 L11.99 32.52 L11.97 33.05 L11.96 33.58 L11.97 34.11 L11.99 34.63 L12.03 35.16 L12.08 35.69 L12.14 36.22 L12.22 36.75 L12.31 37.28 L12.41 37.80 L12.53 38.33 L12.67 38.85 L12.81 39.37 L12.97 39.88 L13.15 40.39 L13.34 40.90 L13.54 41.41 L13.76 41.91 L13.99 42.40 L14.23 42.89 L14.49 43.37 L14.76 43.85 L15.04 44.32 L15.33 44.79 L15.64 45.25 L15.96 45.70 L16.30 46.14 L16.64 46.57 L17.00 47.00 L17.37 47.42 L17.75 47.83 L18.14 48.22 L18.55 48.61 L18.96 48.99 L19.39 49.36 L19.82 49.72 L20.27 50.07 L20.72 50.40 L21.19 50.73 L21.66 51.04 L22.15 51.34 L22.64 51.63 L23.14 51.91 L23.65 52.17 L24.16 52.42 L24.69 52.66 L25.22 52.88 L25.75 53.09 L26.30 53.29 L26.85 53.47 L27.40 53.64 L27.96 53.79 L28.53 53.93 L29.10 54.05 L29.67 54.16 L30.25 54.26 L30.83 54.34 L31.41 54.40 L32.00 54.45 L32.59 54.48 L33.18 54.50 L33.77 54.50 L34.36 54.49 L34.96 54.46 L35.55 54.42 L36.14 54.36 L36.74 54.28 L37.33 54.19 L37.92 54.08 L38.51 53.96 L39.09 53.82 L39.67 53.67 L40.25 53.50 L40.83 53.31 L41.40 53.11 L41.97 52.90 L42.53 52.66 L43.09 52.42 L43.64 52.16 L44.18 51.88 L44.72 51.59 L45.25 51.28 L45.78 50.96 L46.29 50.63 L46.80 50.28 L47.30 49.92 L47.79 49.54 L48.28 49.15 L48.75 48.75 L49.21 48.33 L49.66 47.91 L50.11 47.46 L50.54 47.01 L50.96 46.55 L51.36 46.07 L51.76 45.58 L52.14 45.08 L52.51 44.57 L52.87 44.05 L53.22 43.52 L53.55 42.98 L53.86 42.43 L54.17 41.87 L54.46 41.30 L54.73 40.73 L54.99 40.14 L55.23 39.55 L55.46 38.95 L55.68 38.34 L55.88 37.73 L56.06 37.11 L56.22 36.49 L56.37 35.86 L56.51 35.23 L56.62 34.59 L56.72 33.95 L56.81 33.30 L56.88 32.65 L56.92 32.00 L56.96 31.35 L56.97 30.69 L56.97 30.03 L56.95 29.38 L56.92 28.72 L56.86 28.06 L56.79 27.41 L56.70 26.75 L56.60 26.09 L56.47 25.44 L56.33 24.79 L56.18 24.14 L56.00 23.50 L55.81 22.86 L55.60 22.22 L55.37 21.59 L55.13 20.97 L54.87 20.35 L54.59 19.73 L54.30 19.12 L53.99 18.52 L53.66 17.93 L53.32 17.34 L52.97 16.77 L52.59 16.20 L52.20 15.64 L51.80 15.09 L51.38 14.55 L50.95 14.02 L50.50 13.50 L50.04 12.99 L49.56 12.50 L49.07 12.01 L48.57 11.54 L48.05 11.08 L47.52 10.63 L46.98 10.20 L46.43 9.78 L45.86 9.38 L45.29 8.99 L44.70 8.61 L44.10 8.25 L43.49 7.90 L42.88 7.57 L42.25 7.26 L41.61 6.96 L40.97 6.68 L40.31 6.41 L39.65 6.16 L38.98 5.93 L38.31 5.72 L37.63 5.52 L36.94 5.34 L36.25 5.18 L35.55 5.04 L34.85 4.91 L34.14 4.81 L33.43 4.72 L32.72 4.65 L32.00 4.60 Z";
|
|
472
|
+
const FONT_GEIST = "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";
|
|
473
|
+
|
|
474
|
+
if (isMainThread) main().then(done, (e) => { done(); throw e; }).catch((e) => { console.error("\n Something went wrong: " + (e && e.message || e) + "\n Nothing was sent. Tell lush@usehour.com and we'll fix it.\n"); process.exitCode = 1; });
|
package/package.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "usehour",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Your year of building with AI, as Wrapped-style cards. Reads your Claude Code and Codex logs locally. Nothing is sent.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": { "usehour": "hour.mjs" },
|
|
7
|
+
"files": ["hour.mjs"],
|
|
8
|
+
"engines": { "node": ">=18.17" },
|
|
9
|
+
"keywords": ["claude-code", "codex", "wrapped", "ai", "cli"],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"homepage": "https://usehour.com/wrapped"
|
|
12
|
+
}
|