cc-personality 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/cli.mjs +405 -0
  4. package/package.json +34 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yurukusa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # cc-personality
2
+
3
+ **What kind of Claude Code developer are you?**
4
+
5
+ Diagnoses your coding archetype from real usage patterns in your session logs.
6
+
7
+ ```
8
+ npx cc-personality
9
+ ```
10
+
11
+ Zero dependencies. Reads `~/.claude/projects/` locally. Nothing sent anywhere.
12
+
13
+ ---
14
+
15
+ ## Your archetype could be...
16
+
17
+ | Archetype | Trigger |
18
+ |-----------|---------|
19
+ | 🌙 The Midnight Beast | 30%+ of sessions between midnight and 5 AM |
20
+ | 🌅 The Dawn Coder | 40%+ sessions in early morning (5-9 AM) |
21
+ | ðŸĪ– The Unstoppable Machine | 30+ consecutive active days |
22
+ | ⚔ïļ The Weekend Warrior | Weekend sessions 1.8x more than weekdays |
23
+ | ⚡ The Burst Genius | Lots of inactive days + intense bursts |
24
+ | 📐 The Disciplined Architect | Consistent daily usage without burnout |
25
+ | ðŸ”Ĩ The Session Monster | Average session over 2.5 hours |
26
+ | ðŸ“Ķ The Micro-Shipper | 15+ sessions per active day |
27
+ | 🗚ïļ The Code Explorer | Active across 20+ different projects |
28
+ | ðŸŽŊ The Mono-Focused | 100+ sessions, 3 or fewer projects |
29
+
30
+ ---
31
+
32
+ ## Sample output
33
+
34
+ ```
35
+ ╔════════════════════════════════════════════════════╗
36
+ ║ YOUR CLAUDE CODE DEVELOPER ARCHETYPE ║
37
+ ╚════════════════════════════════════════════════════╝
38
+
39
+ 🌙 The Midnight Beast
40
+ ã€Œæ·ąåΜãŪ怊į‰Đ」
41
+
42
+ "The compiler doesn't sleep, and neither do I."
43
+
44
+ Your code runs on moonlight and caffeine. Peak hours: 0-5 AM.
45
+
46
+ ──────────────────────────────────────────────────────
47
+ Your data:
48
+ ⏱ 116h total â€Ē 3,485 sessions â€Ē 47 active days
49
+ ðŸ”Ĩ Longest streak: 35 days
50
+ 📊 Avg session: 120 min
51
+
52
+ Activity by hour (0h → 23h):
53
+ ▆▃▆▁▁▁▁▆▁▂▁▁▁▁▄▂▄▁▄▁▅▄█▇
54
+ 0 6 12 18 23
55
+
56
+ 🌙 Night owl (38% night sessions)
57
+ ```
58
+
59
+ ---
60
+
61
+ ## How it works
62
+
63
+ Reads timestamps from JSONL session files in `~/.claude/projects/`.
64
+ Computes 10 behavioral signals and matches your dominant pattern.
65
+ The archetype tweet link is pre-formatted for easy sharing.
66
+
67
+ For full usage statistics: [`cc-session-stats`](https://github.com/yurukusa/cc-session-stats)
68
+
69
+ ---
70
+
71
+ ## Requirements
72
+
73
+ - Node.js 18+
74
+ - Claude Code with some session history
75
+
76
+ ## License
77
+
78
+ MIT
package/cli.mjs ADDED
@@ -0,0 +1,405 @@
1
+ #!/usr/bin/env node
2
+
3
+ // cc-personality — What kind of Claude Code developer are you?
4
+ // Zero dependencies. Reads ~/.claude/projects/ session transcripts.
5
+ // Diagnoses your coding archetype from real usage patterns.
6
+
7
+ import { readdir, stat, open } from 'node:fs/promises';
8
+ import { join } from 'node:path';
9
+ import { homedir } from 'node:os';
10
+
11
+ // ── Color helpers ──────────────────────────────────────────────
12
+
13
+ const C = {
14
+ reset: '\x1b[0m',
15
+ bold: '\x1b[1m',
16
+ dim: '\x1b[2m',
17
+ red: '\x1b[31m',
18
+ green: '\x1b[32m',
19
+ yellow: '\x1b[33m',
20
+ blue: '\x1b[34m',
21
+ magenta: '\x1b[35m',
22
+ cyan: '\x1b[36m',
23
+ white: '\x1b[37m',
24
+ };
25
+
26
+ // ── File reading (reused from cc-session-stats) ────────────────
27
+
28
+ async function readFirstLastLine(filePath) {
29
+ const fh = await open(filePath, 'r');
30
+ try {
31
+ const buf = Buffer.alloc(8192);
32
+ const { bytesRead: firstBytes } = await fh.read(buf, 0, 8192, 0);
33
+ if (firstBytes === 0) return null;
34
+ const firstChunk = buf.toString('utf8', 0, firstBytes);
35
+ const firstNewline = firstChunk.indexOf('\n');
36
+ const firstLine = firstNewline >= 0 ? firstChunk.substring(0, firstNewline) : firstChunk;
37
+
38
+ const fileStat = await fh.stat();
39
+ const fileSize = fileStat.size;
40
+ if (fileSize < 2) return { firstLine, lastLine: firstLine };
41
+
42
+ const readSize = Math.min(65536, fileSize);
43
+ const tailBuf = Buffer.alloc(readSize);
44
+ const { bytesRead: tailBytes } = await fh.read(tailBuf, 0, readSize, fileSize - readSize);
45
+ const tailChunk = tailBuf.toString('utf8', 0, tailBytes);
46
+ const lines = tailChunk.split('\n').filter(l => l.trim());
47
+ const lastLine = lines[lines.length - 1] || firstLine;
48
+
49
+ return { firstLine, lastLine };
50
+ } finally {
51
+ await fh.close();
52
+ }
53
+ }
54
+
55
+ function parseTimestamp(jsonLine) {
56
+ try {
57
+ const data = JSON.parse(jsonLine);
58
+ const ts = data.timestamp || data.ts;
59
+ if (ts) return new Date(ts);
60
+ } catch {}
61
+ return null;
62
+ }
63
+
64
+ // ── Scan sessions ──────────────────────────────────────────────
65
+
66
+ const SESSION_GAP_HOURS = 0.5;
67
+
68
+ async function scanSessions(claudeDir) {
69
+ const projectsDir = join(claudeDir, 'projects');
70
+ const sessions = [];
71
+
72
+ let projectDirs;
73
+ try {
74
+ projectDirs = await readdir(projectsDir);
75
+ } catch {
76
+ return sessions;
77
+ }
78
+
79
+ for (const proj of projectDirs) {
80
+ const projPath = join(projectsDir, proj);
81
+ let files;
82
+ try {
83
+ files = await readdir(projPath);
84
+ } catch { continue; }
85
+
86
+ const jsonlFiles = files.filter(f => f.endsWith('.jsonl'));
87
+ for (const file of jsonlFiles) {
88
+ const filePath = join(projPath, file);
89
+ try {
90
+ const lines = await readFirstLastLine(filePath);
91
+ if (!lines) continue;
92
+ const start = parseTimestamp(lines.firstLine);
93
+ const end = parseTimestamp(lines.lastLine);
94
+ if (!start) continue;
95
+ const endTime = end || start;
96
+
97
+ const fileStat = await stat(filePath);
98
+ // Split into sub-sessions if gap > SESSION_GAP_HOURS
99
+ const durationHours = (endTime - start) / 3600000;
100
+ if (durationHours < SESSION_GAP_HOURS * 2) {
101
+ sessions.push({ start, end: endTime, hours: Math.max(durationHours, 0.01) });
102
+ } else {
103
+ // Estimate sub-sessions
104
+ const numSubs = Math.max(1, Math.ceil(durationHours / 2));
105
+ for (let i = 0; i < numSubs; i++) {
106
+ const subStart = new Date(start.getTime() + (i / numSubs) * (endTime - start));
107
+ sessions.push({ start: subStart, end: subStart, hours: durationHours / numSubs });
108
+ }
109
+ }
110
+ } catch { continue; }
111
+ }
112
+ }
113
+
114
+ return sessions;
115
+ }
116
+
117
+ // ── Personality archetypes ─────────────────────────────────────
118
+
119
+ const ARCHETYPES = [
120
+ {
121
+ id: 'night_beast',
122
+ name: '🌙 The Midnight Beast',
123
+ jp: 'æ·ąåΜãŪ怊į‰Đ',
124
+ tagline: '"The compiler doesn\'t sleep, and neither do I."',
125
+ condition: (s) => s.nightPct >= 0.30,
126
+ description: 'Your code runs on moonlight and caffeine. Peak hours: 0-5 AM.',
127
+ },
128
+ {
129
+ id: 'dawn_coder',
130
+ name: '🌅 The Dawn Coder',
131
+ jp: 'åĪœæ˜Žã‘ãŪã‚ģマダマ',
132
+ tagline: '"Fresh mind, fresh commits."',
133
+ condition: (s) => s.morningPct >= 0.40,
134
+ description: 'You code when the world sleeps. Clear mind, elegant solutions.',
135
+ },
136
+ {
137
+ id: 'machine',
138
+ name: 'ðŸĪ– The Unstoppable Machine',
139
+ jp: 'äļæŧ…ãŪæĐŸæĒ°',
140
+ tagline: '"Rest days are a human concept."',
141
+ condition: (s) => s.maxStreak >= 30,
142
+ description: 'Consecutive days without a break. You simply don\'t stop.',
143
+ },
144
+ {
145
+ id: 'weekend_warrior',
146
+ name: '⚔ïļ The Weekend Warrior',
147
+ jp: '週æœŦãŪæˆĶåĢŦ',
148
+ tagline: '"Monday to Friday is warmup."',
149
+ condition: (s) => s.weekendRatio >= 1.8,
150
+ description: 'Saturday and Sunday are your real working days.',
151
+ },
152
+ {
153
+ id: 'burst_genius',
154
+ name: '⚡ The Burst Genius',
155
+ jp: 'バマã‚đト型åĪĐæ‰',
156
+ tagline: '"I do nothing for days, then ship everything at once."',
157
+ condition: (s) => s.activeDays > 0 && (s.totalDays / s.activeDays) >= 2.5,
158
+ description: 'Irregular patterns. Long silences. Then explosive productivity.',
159
+ },
160
+ {
161
+ id: 'disciplined',
162
+ name: '📐 The Disciplined Architect',
163
+ jp: 'čĶåū‹ãŪį”ģし子',
164
+ tagline: '"Consistency beats intensity."',
165
+ condition: (s) => s.activeDays > 0 && (s.totalDays / s.activeDays) < 1.3 && s.totalHours / s.activeDays < 6,
166
+ description: 'Steady pace every day. You finish what you start.',
167
+ },
168
+ {
169
+ id: 'session_monster',
170
+ name: 'ðŸ”Ĩ The Session Monster',
171
+ jp: 'ã‚ŧッショãƒģ怊įĢ',
172
+ tagline: '"One more context window..."',
173
+ condition: (s) => s.avgSessionHours >= 2.5,
174
+ description: 'Long, deep sessions. You go down the rabbit hole and don\'t come back.',
175
+ },
176
+ {
177
+ id: 'micro_shipper',
178
+ name: 'ðŸ“Ķ The Micro-Shipper',
179
+ jp: 'čķ…éŦ˜é€Ÿå‡šč·č€…',
180
+ tagline: '"Ship small, ship often."',
181
+ condition: (s) => s.totalSessions / s.activeDays >= 15,
182
+ description: 'Dozens of sessions per day. Fast, iterative, relentless.',
183
+ },
184
+ {
185
+ id: 'explorer',
186
+ name: '🗚ïļ The Code Explorer',
187
+ jp: 'ã‚ģマドæŽĒæĪœåŪķ',
188
+ tagline: '"Every project is a new world."',
189
+ condition: (s) => s.projectCount >= 20,
190
+ description: 'Many projects, curious mind. You explore, you don\'t settle.',
191
+ },
192
+ {
193
+ id: 'mono_focused',
194
+ name: 'ðŸŽŊ The Mono-Focused',
195
+ jp: 'ãƒĒノフã‚Đマã‚Ŧã‚đãŪ匠',
196
+ tagline: '"One project. Total mastery."',
197
+ condition: (s) => s.projectCount <= 3 && s.totalSessions >= 100,
198
+ description: 'Deep obsession with one thing. Mastery over breadth.',
199
+ },
200
+ ];
201
+
202
+ // ── Compute stats ─────────────────────────────────────────────
203
+
204
+ function computeStats(sessions) {
205
+ if (sessions.length === 0) return null;
206
+
207
+ const totalSessions = sessions.length;
208
+ const totalHours = sessions.reduce((s, x) => s + x.hours, 0);
209
+
210
+ // Hour distribution
211
+ const hourBuckets = new Array(24).fill(0);
212
+ for (const s of sessions) {
213
+ hourBuckets[s.start.getHours()]++;
214
+ }
215
+ const nightPct = (hourBuckets.slice(0, 5).reduce((a, b) => a + b, 0) +
216
+ hourBuckets.slice(22).reduce((a, b) => a + b, 0)) / totalSessions;
217
+ const morningPct = hourBuckets.slice(5, 9).reduce((a, b) => a + b, 0) / totalSessions;
218
+ const afternoonPct = hourBuckets.slice(9, 17).reduce((a, b) => a + b, 0) / totalSessions;
219
+ const eveningPct = hourBuckets.slice(17, 22).reduce((a, b) => a + b, 0) / totalSessions;
220
+
221
+ // Day distribution
222
+ const dayBuckets = new Array(7).fill(0); // 0=Sun
223
+ for (const s of sessions) {
224
+ dayBuckets[s.start.getDay()]++;
225
+ }
226
+ const weekdayTotal = dayBuckets.slice(1, 6).reduce((a, b) => a + b, 0) / 5;
227
+ const weekendTotal = (dayBuckets[0] + dayBuckets[6]) / 2;
228
+ const weekendRatio = weekdayTotal > 0 ? weekendTotal / weekdayTotal : 0;
229
+
230
+ // Active days & streak
231
+ const daySet = new Set(sessions.map(s => s.start.toDateString()));
232
+ const activeDays = daySet.size;
233
+ const allDates = [...daySet].map(d => new Date(d)).sort((a, b) => a - b);
234
+ const firstDate = allDates[0];
235
+ const lastDate = allDates[allDates.length - 1];
236
+ const totalDays = Math.ceil((lastDate - firstDate) / 86400000) + 1;
237
+
238
+ let maxStreak = 1, curStreak = 1;
239
+ for (let i = 1; i < allDates.length; i++) {
240
+ const diff = (allDates[i] - allDates[i - 1]) / 86400000;
241
+ if (diff === 1) {
242
+ curStreak++;
243
+ if (curStreak > maxStreak) maxStreak = curStreak;
244
+ } else {
245
+ curStreak = 1;
246
+ }
247
+ }
248
+
249
+ const avgSessionHours = totalHours / totalSessions;
250
+
251
+ return {
252
+ totalSessions,
253
+ totalHours,
254
+ activeDays,
255
+ totalDays,
256
+ maxStreak,
257
+ nightPct,
258
+ morningPct,
259
+ afternoonPct,
260
+ eveningPct,
261
+ weekendRatio,
262
+ avgSessionHours,
263
+ hourBuckets,
264
+ dayBuckets,
265
+ projectCount: 0, // filled later
266
+ peakHour: hourBuckets.indexOf(Math.max(...hourBuckets)),
267
+ };
268
+ }
269
+
270
+ // ── Determine archetype ───────────────────────────────────────
271
+
272
+ function getArchetype(stats) {
273
+ // Machine check needs maxStreak
274
+ const machineArchetype = {
275
+ id: 'machine',
276
+ name: 'ðŸĪ– The Unstoppable Machine',
277
+ jp: 'äļæŧ…ãŪæĐŸæĒ°',
278
+ tagline: '"Rest days are a human concept."',
279
+ condition: (s) => s.maxStreak >= 30,
280
+ description: `${stats.maxStreak} consecutive days of coding. You simply don't stop.`,
281
+ };
282
+
283
+ const allArchetypes = ARCHETYPES.map(a =>
284
+ a.id === 'machine' ? machineArchetype : a
285
+ );
286
+
287
+ for (const archetype of allArchetypes) {
288
+ if (archetype.condition(stats)) {
289
+ return archetype;
290
+ }
291
+ }
292
+
293
+ // Default
294
+ return {
295
+ id: 'balanced',
296
+ name: '⚖ïļ The Balanced Developer',
297
+ jp: 'バãƒĐãƒģã‚đåž‹é–‹į™šč€…',
298
+ tagline: '"Sustainable velocity wins the race."',
299
+ description: 'You code consistently without burning out. Rare and admirable.',
300
+ };
301
+ }
302
+
303
+ // ── Render ────────────────────────────────────────────────────
304
+
305
+ function renderHourBar(buckets, peakHour) {
306
+ const max = Math.max(...buckets);
307
+ if (max === 0) return '';
308
+ const blocks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
309
+ return buckets.map((v, h) => {
310
+ const level = Math.round((v / max) * 7);
311
+ const block = blocks[level];
312
+ if (h === peakHour) return `${C.yellow}${block}${C.reset}`;
313
+ if (h >= 0 && h <= 4) return `${C.dim}${block}${C.reset}`;
314
+ return block;
315
+ }).join('');
316
+ }
317
+
318
+ function pct(n) { return `${Math.round(n * 100)}%`; }
319
+
320
+ // ── Main ──────────────────────────────────────────────────────
321
+
322
+ async function main() {
323
+ const claudeDir = join(homedir(), '.claude');
324
+
325
+ process.stdout.write(`${C.dim}Scanning your Claude Code sessions...${C.reset}\n`);
326
+
327
+ const sessions = await scanSessions(claudeDir);
328
+
329
+ if (sessions.length === 0) {
330
+ console.log(`\n${C.red}No Claude Code sessions found.${C.reset}`);
331
+ console.log('Make sure you have sessions in ~/.claude/projects/');
332
+ process.exit(1);
333
+ }
334
+
335
+ // Count projects
336
+ let projectCount = 0;
337
+ try {
338
+ const projectsDir = join(claudeDir, 'projects');
339
+ const dirs = await readdir(projectsDir);
340
+ projectCount = dirs.length;
341
+ } catch {}
342
+
343
+ const stats = computeStats(sessions);
344
+ stats.projectCount = projectCount;
345
+
346
+ const archetype = getArchetype(stats);
347
+
348
+ // Render
349
+ const width = 52;
350
+ const border = '═'.repeat(width);
351
+
352
+ console.log(`\n${C.bold}${C.cyan}╔${border}╗${C.reset}`);
353
+ console.log(`${C.bold}${C.cyan}║${C.reset}${C.bold} YOUR CLAUDE CODE DEVELOPER ARCHETYPE ${C.cyan}║${C.reset}`);
354
+ console.log(`${C.bold}${C.cyan}╚${border}╝${C.reset}`);
355
+
356
+ console.log(`\n ${C.bold}${C.yellow}${archetype.name}${C.reset}`);
357
+ if (archetype.jp) {
358
+ console.log(` ${C.dim}「${archetype.jp}」${C.reset}`);
359
+ }
360
+ console.log(`\n ${C.cyan}${archetype.tagline || ''}${C.reset}`);
361
+ console.log(`\n ${archetype.description || ''}`);
362
+
363
+ // Stats summary
364
+ console.log(`\n${C.dim}${'─'.repeat(width + 2)}${C.reset}`);
365
+ console.log(` ${C.bold}Your data:${C.reset}`);
366
+ console.log(` ⏱ ${stats.totalHours.toFixed(0)}h total â€Ē ${stats.totalSessions} sessions â€Ē ${stats.activeDays} active days`);
367
+ console.log(` ðŸ”Ĩ Longest streak: ${stats.maxStreak} days`);
368
+ console.log(` 📊 Avg session: ${(stats.avgSessionHours * 60).toFixed(0)} min`);
369
+
370
+ // Hour heatmap
371
+ console.log(`\n Activity by hour (0h → 23h):`);
372
+ console.log(` ${renderHourBar(stats.hourBuckets, stats.peakHour)}`);
373
+ console.log(` ${C.dim}0 6 12 18 23${C.reset}`);
374
+
375
+ // Time-of-day breakdown
376
+ const timeLabel =
377
+ stats.nightPct >= 0.25 ? `${C.blue}🌙 Night owl (${pct(stats.nightPct)} night sessions)${C.reset}` :
378
+ stats.morningPct >= 0.30 ? `${C.yellow}🌅 Early bird (${pct(stats.morningPct)} morning sessions)${C.reset}` :
379
+ stats.eveningPct >= 0.40 ? `${C.magenta}🌆 Evening coder (${pct(stats.eveningPct)} evening)${C.reset}` :
380
+ `${C.green}☀ïļ Day coder (${pct(stats.afternoonPct)} afternoon)${C.reset}`;
381
+ console.log(`\n ${timeLabel}`);
382
+
383
+ // Weekend info
384
+ if (stats.weekendRatio >= 1.5) {
385
+ console.log(` ⚔ïļ Weekend warrior (${stats.weekendRatio.toFixed(1)}x weekend activity)`);
386
+ }
387
+
388
+ // Share prompt
389
+ const shareText = encodeURIComponent(
390
+ `My Claude Code archetype: ${archetype.name} (${archetype.jp})\n` +
391
+ `${stats.totalHours.toFixed(0)}h â€Ē ${stats.totalSessions} sessions â€Ē ${stats.maxStreak}-day streak\n` +
392
+ `What's yours? npx cc-personality\n#claudecode`
393
+ );
394
+
395
+ console.log(`\n${C.dim}${'─'.repeat(width + 2)}${C.reset}`);
396
+ console.log(` ${C.bold}Share your archetype:${C.reset}`);
397
+ console.log(` ${C.dim}https://x.com/intent/tweet?text=${shareText.substring(0, 80)}...${C.reset}`);
398
+ console.log(`\n Full stats: ${C.cyan}npx cc-session-stats${C.reset}`);
399
+ console.log(` GitHub: ${C.cyan}https://github.com/yurukusa/cc-personality${C.reset}\n`);
400
+ }
401
+
402
+ main().catch(err => {
403
+ console.error(`Error: ${err.message}`);
404
+ process.exit(1);
405
+ });
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "cc-personality",
3
+ "version": "1.0.0",
4
+ "description": "Discover your Claude Code developer archetype. Diagnoses your coding style from real usage patterns.",
5
+ "bin": {
6
+ "cc-personality": "cli.mjs"
7
+ },
8
+ "type": "module",
9
+ "keywords": [
10
+ "claude-code",
11
+ "claude",
12
+ "ai",
13
+ "developer",
14
+ "personality",
15
+ "archetype",
16
+ "coding-style",
17
+ "productivity",
18
+ "time-tracking"
19
+ ],
20
+ "author": "yurukusa",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/yurukusa/cc-personality.git"
25
+ },
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "files": [
30
+ "cli.mjs",
31
+ "README.md",
32
+ "LICENSE"
33
+ ]
34
+ }