@suncreation/modu-arena 0.1.4 → 0.2.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/dist/index.js CHANGED
@@ -1,10 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
2
8
 
3
9
  // src/commands.ts
4
10
  import { createInterface } from "readline";
5
- import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, statSync, unlinkSync } from "fs";
6
- import { homedir as homedir3 } from "os";
7
- import { basename, join as join3 } from "path";
11
+ import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync, unlinkSync as unlinkSync2 } from "fs";
12
+ import { homedir as homedir5 } from "os";
13
+ import { basename, join as join5 } from "path";
8
14
 
9
15
  // src/adapters.ts
10
16
  import { existsSync, writeFileSync, mkdirSync } from "fs";
@@ -15,12 +21,15 @@ import { join } from "path";
15
21
  var API_BASE_URL = process.env.MODU_ARENA_API_URL ?? "http://backend.vibemakers.kr:23010";
16
22
  var TOOL_DISPLAY_NAMES = {
17
23
  "claude-code": "Claude Code",
24
+ "claude-desktop": "Claude Desktop",
18
25
  opencode: "OpenCode",
19
26
  gemini: "Gemini CLI",
20
27
  codex: "Codex CLI",
21
28
  crush: "Crush"
22
29
  };
23
30
  var CONFIG_FILE_NAME = ".modu-arena.json";
31
+ var DAEMON_STATE_FILE = ".modu-arena-daemon.json";
32
+ var DAEMON_SYNC_INTERVAL_SEC = 300;
24
33
 
25
34
  // src/adapters.ts
26
35
  var IS_WIN = process.platform === "win32";
@@ -297,6 +306,312 @@ function requireConfig() {
297
306
  return config;
298
307
  }
299
308
 
309
+ // src/daemon.ts
310
+ import { writeFileSync as writeFileSync3, existsSync as existsSync3, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
311
+ import { homedir as homedir3 } from "os";
312
+ import { join as join3 } from "path";
313
+ import { execSync } from "child_process";
314
+ var IS_WIN2 = process.platform === "win32";
315
+ var DAEMON_NAME = "com.modu-arena.sync";
316
+ function getDaemonLogDir() {
317
+ const dir = join3(homedir3(), ".modu-arena", "logs");
318
+ if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
319
+ return dir;
320
+ }
321
+ function getNodePath() {
322
+ try {
323
+ return execSync("which node", { encoding: "utf-8" }).trim();
324
+ } catch {
325
+ return "node";
326
+ }
327
+ }
328
+ function getCliPath() {
329
+ return __require.resolve("./index.js").replace(/index\.js$/, "index.js");
330
+ }
331
+ function installDaemon() {
332
+ if (IS_WIN2) {
333
+ return installWindowsDaemon();
334
+ }
335
+ return installMacosDaemon();
336
+ }
337
+ function uninstallDaemon() {
338
+ if (IS_WIN2) {
339
+ return uninstallWindowsDaemon();
340
+ }
341
+ return uninstallMacosDaemon();
342
+ }
343
+ function isDaemonInstalled() {
344
+ if (IS_WIN2) {
345
+ try {
346
+ execSync(`schtasks /Query /TN "${DAEMON_NAME}"`, { encoding: "utf-8" });
347
+ return true;
348
+ } catch {
349
+ return false;
350
+ }
351
+ }
352
+ const plistPath = join3(homedir3(), "Library", "LaunchAgents", `${DAEMON_NAME}.plist`);
353
+ return existsSync3(plistPath);
354
+ }
355
+ function installMacosDaemon() {
356
+ const launchAgentsDir = join3(homedir3(), "Library", "LaunchAgents");
357
+ const plistPath = join3(launchAgentsDir, `${DAEMON_NAME}.plist`);
358
+ const logDir = getDaemonLogDir();
359
+ const nodePath = getNodePath();
360
+ const cliPath = getCliPath();
361
+ const intervalMinutes = Math.floor(DAEMON_SYNC_INTERVAL_SEC / 60);
362
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
363
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
364
+ <plist version="1.0">
365
+ <dict>
366
+ <key>Label</key>
367
+ <string>${DAEMON_NAME}</string>
368
+ <key>ProgramArguments</key>
369
+ <array>
370
+ <string>${nodePath}</string>
371
+ <string>${cliPath}</string>
372
+ <string>daemon-sync</string>
373
+ </array>
374
+ <key>StartInterval</key>
375
+ <integer>${DAEMON_SYNC_INTERVAL_SEC}</integer>
376
+ <key>StandardOutPath</key>
377
+ <string>${logDir}/daemon.log</string>
378
+ <key>StandardErrorPath</key>
379
+ <string>${logDir}/daemon-error.log</string>
380
+ <key>RunAtLoad</key>
381
+ <true/>
382
+ </dict>
383
+ </plist>`;
384
+ try {
385
+ if (!existsSync3(launchAgentsDir)) {
386
+ mkdirSync3(launchAgentsDir, { recursive: true });
387
+ }
388
+ writeFileSync3(plistPath, plist);
389
+ execSync(`launchctl load ${plistPath}`, { encoding: "utf-8" });
390
+ return { success: true, message: `Daemon installed. Syncs every ${intervalMinutes} minutes.` };
391
+ } catch (e) {
392
+ return { success: false, message: `Failed to install daemon: ${e}` };
393
+ }
394
+ }
395
+ function uninstallMacosDaemon() {
396
+ const plistPath = join3(homedir3(), "Library", "LaunchAgents", `${DAEMON_NAME}.plist`);
397
+ try {
398
+ if (existsSync3(plistPath)) {
399
+ execSync(`launchctl unload ${plistPath}`, { encoding: "utf-8" });
400
+ unlinkSync(plistPath);
401
+ }
402
+ return { success: true, message: "Daemon uninstalled." };
403
+ } catch (e) {
404
+ return { success: false, message: `Failed to uninstall daemon: ${e}` };
405
+ }
406
+ }
407
+ function installWindowsDaemon() {
408
+ const nodePath = getNodePath();
409
+ const cliPath = getCliPath();
410
+ const intervalMinutes = Math.floor(DAEMON_SYNC_INTERVAL_SEC / 60);
411
+ try {
412
+ const cmd = `schtasks /Create /TN "${DAEMON_NAME}" /TR "\\"${nodePath}\\" \\"${cliPath}\\" daemon-sync" /SC MINUTE /MO ${intervalMinutes} /F`;
413
+ execSync(cmd, { encoding: "utf-8" });
414
+ return { success: true, message: `Daemon installed. Syncs every ${intervalMinutes} minutes.` };
415
+ } catch (e) {
416
+ return { success: false, message: `Failed to install daemon: ${e}` };
417
+ }
418
+ }
419
+ function uninstallWindowsDaemon() {
420
+ try {
421
+ execSync(`schtasks /Delete /TN "${DAEMON_NAME}" /F`, { encoding: "utf-8" });
422
+ return { success: true, message: "Daemon uninstalled." };
423
+ } catch (e) {
424
+ return { success: false, message: `Failed to uninstall daemon: ${e}` };
425
+ }
426
+ }
427
+ function getDaemonStatus() {
428
+ return {
429
+ installed: isDaemonInstalled(),
430
+ platform: IS_WIN2 ? "windows" : "macos",
431
+ interval: DAEMON_SYNC_INTERVAL_SEC
432
+ };
433
+ }
434
+
435
+ // src/claude-desktop.ts
436
+ import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync4, writeFileSync as writeFileSync4 } from "fs";
437
+ import { homedir as homedir4 } from "os";
438
+ import { join as join4 } from "path";
439
+ import { createHash as createHash2 } from "crypto";
440
+ var IS_WIN3 = process.platform === "win32";
441
+ function getClaudeDesktopDataDir() {
442
+ if (IS_WIN3) {
443
+ return join4(process.env.APPDATA || join4(homedir4(), "AppData", "Roaming"), "Claude");
444
+ }
445
+ return join4(homedir4(), "Library", "Application Support", "Claude");
446
+ }
447
+ function getSessionDirs() {
448
+ const dataDir = getClaudeDesktopDataDir();
449
+ const sessionsDir = join4(dataDir, "local-agent-mode-sessions");
450
+ if (!existsSync4(sessionsDir)) return [];
451
+ const orgDirs = [];
452
+ for (const entry of readdirSync(sessionsDir, { withFileTypes: true })) {
453
+ if (entry.isDirectory() && entry.name !== "skills-plugin") {
454
+ orgDirs.push(join4(sessionsDir, entry.name));
455
+ }
456
+ }
457
+ return orgDirs;
458
+ }
459
+ function findJsonlFiles(baseDir) {
460
+ const files = [];
461
+ function walk(dir) {
462
+ if (!existsSync4(dir)) return;
463
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
464
+ const full = join4(dir, entry.name);
465
+ if (entry.isDirectory()) {
466
+ walk(full);
467
+ } else if (entry.name.endsWith(".jsonl") && !entry.name.includes("audit")) {
468
+ files.push(full);
469
+ }
470
+ }
471
+ }
472
+ walk(baseDir);
473
+ return files;
474
+ }
475
+ function parseJsonlFile(filePath) {
476
+ const content = readFileSync2(filePath, "utf-8");
477
+ const lines = content.trim().split("\n");
478
+ let sessionId = "";
479
+ let inputTokens = 0;
480
+ let outputTokens = 0;
481
+ let cacheCreationTokens = 0;
482
+ let cacheReadTokens = 0;
483
+ let model = "unknown";
484
+ let startedAt = "";
485
+ let endedAt = "";
486
+ let messageCount = 0;
487
+ for (const line of lines) {
488
+ try {
489
+ const msg = JSON.parse(line);
490
+ if (msg.sessionId && !sessionId) {
491
+ sessionId = msg.sessionId;
492
+ }
493
+ if (msg.timestamp) {
494
+ if (!startedAt || msg.timestamp < startedAt) {
495
+ startedAt = msg.timestamp;
496
+ }
497
+ if (!endedAt || msg.timestamp > endedAt) {
498
+ endedAt = msg.timestamp;
499
+ }
500
+ }
501
+ if (msg.message?.usage) {
502
+ const usage = msg.message.usage;
503
+ inputTokens += usage.input_tokens || 0;
504
+ outputTokens += usage.output_tokens || 0;
505
+ cacheCreationTokens += usage.cache_creation_input_tokens || 0;
506
+ cacheReadTokens += usage.cache_read_input_tokens || 0;
507
+ messageCount++;
508
+ if (msg.message.model) {
509
+ model = msg.message.model;
510
+ }
511
+ }
512
+ } catch {
513
+ }
514
+ }
515
+ if (!sessionId || messageCount === 0) return null;
516
+ return {
517
+ sessionId,
518
+ inputTokens,
519
+ outputTokens,
520
+ cacheCreationTokens,
521
+ cacheReadTokens,
522
+ model,
523
+ startedAt,
524
+ endedAt,
525
+ messageCount
526
+ };
527
+ }
528
+ function getDaemonStatePath() {
529
+ return join4(homedir4(), DAEMON_STATE_FILE);
530
+ }
531
+ function loadDaemonState() {
532
+ const path = getDaemonStatePath();
533
+ if (!existsSync4(path)) {
534
+ return { lastSync: (/* @__PURE__ */ new Date(0)).toISOString(), syncedSessions: [] };
535
+ }
536
+ try {
537
+ return JSON.parse(readFileSync2(path, "utf-8"));
538
+ } catch {
539
+ return { lastSync: (/* @__PURE__ */ new Date(0)).toISOString(), syncedSessions: [] };
540
+ }
541
+ }
542
+ function saveDaemonState(state) {
543
+ const path = getDaemonStatePath();
544
+ writeFileSync4(path, JSON.stringify(state, null, 2));
545
+ }
546
+ function computeSessionHash(session) {
547
+ const data = `${session.sessionId}:${session.inputTokens}:${session.outputTokens}:${session.endedAt}`;
548
+ return createHash2("sha256").update(data).digest("hex").substring(0, 16);
549
+ }
550
+ async function syncClaudeDesktop(apiKey) {
551
+ const state = loadDaemonState();
552
+ const errors = [];
553
+ let synced = 0;
554
+ let skipped = 0;
555
+ const sessionDirs = getSessionDirs();
556
+ for (const orgDir of sessionDirs) {
557
+ const jsonlFiles = findJsonlFiles(orgDir);
558
+ for (const file of jsonlFiles) {
559
+ const session = parseJsonlFile(file);
560
+ if (!session) continue;
561
+ const hash = computeSessionHash(session);
562
+ if (state.syncedSessions.includes(hash)) {
563
+ skipped++;
564
+ continue;
565
+ }
566
+ if (session.inputTokens === 0 && session.outputTokens === 0) {
567
+ skipped++;
568
+ continue;
569
+ }
570
+ try {
571
+ const body = JSON.stringify({
572
+ toolType: "claude-desktop",
573
+ endedAt: session.endedAt,
574
+ startedAt: session.startedAt,
575
+ inputTokens: session.inputTokens,
576
+ outputTokens: session.outputTokens,
577
+ cacheCreationTokens: session.cacheCreationTokens,
578
+ cacheReadTokens: session.cacheReadTokens,
579
+ modelName: session.model
580
+ });
581
+ const ts = Math.floor(Date.now() / 1e3).toString();
582
+ const sig = createHash2("sha256").update(ts + ":" + body).update(apiKey).digest("hex");
583
+ const res = await fetch(`${API_BASE_URL}/api/v1/sessions`, {
584
+ method: "POST",
585
+ headers: {
586
+ "Content-Type": "application/json",
587
+ "X-API-Key": apiKey,
588
+ "X-Timestamp": ts,
589
+ "X-Signature": sig
590
+ },
591
+ body
592
+ });
593
+ if (res.ok) {
594
+ state.syncedSessions.push(hash);
595
+ synced++;
596
+ } else {
597
+ const err = await res.text();
598
+ errors.push(`${session.sessionId}: ${err}`);
599
+ }
600
+ } catch (e) {
601
+ errors.push(`${session.sessionId}: ${e}`);
602
+ }
603
+ }
604
+ }
605
+ state.lastSync = (/* @__PURE__ */ new Date()).toISOString();
606
+ saveDaemonState(state);
607
+ return { synced, skipped, errors };
608
+ }
609
+ function hasClaudeDesktopData() {
610
+ const dataDir = getClaudeDesktopDataDir();
611
+ const sessionsDir = join4(dataDir, "local-agent-mode-sessions");
612
+ return existsSync4(sessionsDir);
613
+ }
614
+
300
615
  // src/commands.ts
301
616
  function prompt(question) {
302
617
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -531,7 +846,7 @@ function statusCommand() {
531
846
  for (const adapter of adapters) {
532
847
  const detected = adapter.detect();
533
848
  if (detected) {
534
- const hookExists = existsSync3(adapter.getHookPath());
849
+ const hookExists = existsSync5(adapter.getHookPath());
535
850
  const status = hookExists ? "\u2713 Active" : "\u2717 Not installed";
536
851
  console.log(` ${adapter.displayName}: ${status}`);
537
852
  if (hookExists) hookCount++;
@@ -547,14 +862,14 @@ function uninstallCommand() {
547
862
  const adapters = getAllAdapters();
548
863
  for (const adapter of adapters) {
549
864
  const hookPath = adapter.getHookPath();
550
- if (existsSync3(hookPath)) {
551
- unlinkSync(hookPath);
865
+ if (existsSync5(hookPath)) {
866
+ unlinkSync2(hookPath);
552
867
  console.log(` \u2713 Removed ${adapter.displayName} hook`);
553
868
  }
554
869
  }
555
- const configPath = join3(homedir3(), ".modu-arena.json");
556
- if (existsSync3(configPath)) {
557
- unlinkSync(configPath);
870
+ const configPath = join5(homedir5(), ".modu-arena.json");
871
+ if (existsSync5(configPath)) {
872
+ unlinkSync2(configPath);
558
873
  console.log(" \u2713 Removed ~/.modu-arena.json");
559
874
  }
560
875
  console.log("\n\u2713 Modu-Arena uninstalled.\n");
@@ -581,7 +896,7 @@ function collectFileStructure(dir, maxDepth, currentDepth = 0) {
581
896
  if (currentDepth >= maxDepth) return result;
582
897
  let entries;
583
898
  try {
584
- entries = readdirSync(dir);
899
+ entries = readdirSync2(dir);
585
900
  } catch {
586
901
  return result;
587
902
  }
@@ -589,7 +904,7 @@ function collectFileStructure(dir, maxDepth, currentDepth = 0) {
589
904
  for (const entry of entries) {
590
905
  if (entry.startsWith(".") && IGNORE_DIRS.has(entry)) continue;
591
906
  if (IGNORE_DIRS.has(entry)) continue;
592
- const fullPath = join3(dir, entry);
907
+ const fullPath = join5(dir, entry);
593
908
  let stat;
594
909
  try {
595
910
  stat = statSync(fullPath);
@@ -616,13 +931,13 @@ async function submitCommand() {
616
931
  console.log("\n\u{1F680} Modu-Arena \u2014 Project Submit\n");
617
932
  const cwd = process.cwd();
618
933
  const projectName = basename(cwd);
619
- const readmePath = join3(cwd, "README.md");
620
- if (!existsSync3(readmePath)) {
934
+ const readmePath = join5(cwd, "README.md");
935
+ if (!existsSync5(readmePath)) {
621
936
  console.error("Error: README.md not found in the current directory.");
622
937
  console.error(" Please create a README.md describing your project.\n");
623
938
  process.exit(1);
624
939
  }
625
- const description = readFileSync2(readmePath, "utf-8");
940
+ const description = readFileSync3(readmePath, "utf-8");
626
941
  if (description.trim().length === 0) {
627
942
  console.error("Error: README.md is empty.\n");
628
943
  process.exit(1);
@@ -669,6 +984,71 @@ function formatNumber(n) {
669
984
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
670
985
  return n.toString();
671
986
  }
987
+ function daemonInstallCommand() {
988
+ console.log("\n\u{1F504} Modu-Arena \u2014 Claude Desktop Daemon\n");
989
+ if (!hasClaudeDesktopData()) {
990
+ console.log(" \u2717 Claude Desktop data not found.");
991
+ console.log(" Make sure Claude Desktop is installed and has been used.\n");
992
+ process.exit(1);
993
+ }
994
+ const result = installDaemon();
995
+ if (result.success) {
996
+ console.log(` \u2713 ${result.message}`);
997
+ console.log(" \u2713 Daemon will sync Claude Desktop usage automatically.\n");
998
+ } else {
999
+ console.error(` \u2717 ${result.message}
1000
+ `);
1001
+ process.exit(1);
1002
+ }
1003
+ }
1004
+ function daemonUninstallCommand() {
1005
+ console.log("\n\u{1F504} Modu-Arena \u2014 Claude Desktop Daemon\n");
1006
+ const result = uninstallDaemon();
1007
+ if (result.success) {
1008
+ console.log(` \u2713 ${result.message}
1009
+ `);
1010
+ } else {
1011
+ console.error(` \u2717 ${result.message}
1012
+ `);
1013
+ process.exit(1);
1014
+ }
1015
+ }
1016
+ function daemonStatusCommand() {
1017
+ console.log("\n\u{1F504} Modu-Arena \u2014 Claude Desktop Daemon\n");
1018
+ const status = getDaemonStatus();
1019
+ console.log(` Platform: ${status.platform}`);
1020
+ console.log(` Installed: ${status.installed ? "Yes" : "No"}`);
1021
+ if (status.installed) {
1022
+ console.log(` Sync Interval: ${Math.floor(status.interval / 60)} minutes`);
1023
+ }
1024
+ if (hasClaudeDesktopData()) {
1025
+ console.log(" Claude Desktop Data: Found");
1026
+ } else {
1027
+ console.log(" Claude Desktop Data: Not found");
1028
+ }
1029
+ console.log("");
1030
+ }
1031
+ async function daemonSyncCommand() {
1032
+ const config = requireConfig();
1033
+ if (!hasClaudeDesktopData()) {
1034
+ console.log("Claude Desktop data not found. Nothing to sync.\n");
1035
+ return;
1036
+ }
1037
+ console.log("Syncing Claude Desktop usage...");
1038
+ const result = await syncClaudeDesktop(config.apiKey);
1039
+ console.log(` Synced: ${result.synced} sessions`);
1040
+ console.log(` Skipped: ${result.skipped} sessions (already synced)`);
1041
+ if (result.errors.length > 0) {
1042
+ console.log(` Errors: ${result.errors.length}`);
1043
+ for (const err of result.errors.slice(0, 3)) {
1044
+ console.log(` - ${err}`);
1045
+ }
1046
+ if (result.errors.length > 3) {
1047
+ console.log(` ... and ${result.errors.length - 3} more`);
1048
+ }
1049
+ }
1050
+ console.log("");
1051
+ }
672
1052
 
673
1053
  // src/index.ts
674
1054
  var args = process.argv.slice(2);
@@ -681,13 +1061,17 @@ Usage:
681
1061
  npx @suncreation/modu-arena <command> [options]
682
1062
 
683
1063
  Commands:
684
- register Create a new account (interactive)
685
- login Log in to an existing account (interactive)
686
- install Set up hooks for detected AI coding tools
687
- rank View your current stats and ranking
688
- status Check configuration and installed hooks
689
- submit Submit current project for evaluation
690
- uninstall Remove all hooks and configuration
1064
+ register Create a new account (interactive)
1065
+ login Log in to an existing account (interactive)
1066
+ install Set up hooks for detected AI coding tools
1067
+ rank View your current stats and ranking
1068
+ status Check configuration and installed hooks
1069
+ submit Submit current project for evaluation
1070
+ uninstall Remove all hooks and configuration
1071
+ daemon-install Install Claude Desktop sync daemon
1072
+ daemon-status Check daemon status
1073
+ daemon-sync Manually sync Claude Desktop data
1074
+ daemon-remove Remove the daemon
691
1075
 
692
1076
  Options:
693
1077
  --api-key <key> Your Modu-Arena API key (for install)
@@ -699,6 +1083,7 @@ Examples:
699
1083
  npx @suncreation/modu-arena login
700
1084
  npx @suncreation/modu-arena install --api-key modu_arena_AbCdEfGh_xxx...
701
1085
  npx @suncreation/modu-arena rank
1086
+ npx @suncreation/modu-arena daemon-install
702
1087
  `);
703
1088
  }
704
1089
  async function main() {
@@ -735,6 +1120,19 @@ async function main() {
735
1120
  case "uninstall":
736
1121
  uninstallCommand();
737
1122
  break;
1123
+ case "daemon-install":
1124
+ daemonInstallCommand();
1125
+ break;
1126
+ case "daemon-uninstall":
1127
+ case "daemon-remove":
1128
+ daemonUninstallCommand();
1129
+ break;
1130
+ case "daemon-status":
1131
+ daemonStatusCommand();
1132
+ break;
1133
+ case "daemon-sync":
1134
+ await daemonSyncCommand();
1135
+ break;
738
1136
  default:
739
1137
  console.error(`Unknown command: ${command}`);
740
1138
  printHelp();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands.ts","../src/adapters.ts","../src/constants.ts","../src/crypto.ts","../src/api.ts","../src/config.ts","../src/index.ts"],"sourcesContent":["/**\n * CLI Commands — install, rank, status, uninstall\n */\n\nimport { createInterface } from 'node:readline';\nimport { existsSync, readFileSync, readdirSync, statSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\nimport { getAllAdapters, type InstallResult } from './adapters.js';\nimport { getRank, registerUser, loginUser, submitEvaluation } from './api.js';\nimport { loadConfig, saveConfig, requireConfig } from './config.js';\nimport { API_BASE_URL, TOOL_DISPLAY_NAMES, type ToolType } from './constants.js';\n\nfunction prompt(question: string): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nfunction promptPassword(question: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(question);\n const chars: string[] = [];\n const stdin = process.stdin;\n const wasRaw = stdin.isRaw;\n stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n\n const onData = (ch: string) => {\n const c = ch.toString();\n if (c === '\\n' || c === '\\r' || c === '\\u0004') {\n // Enter or Ctrl+D\n stdin.setRawMode(wasRaw ?? false);\n stdin.pause();\n stdin.removeListener('data', onData);\n process.stdout.write('\\n');\n resolve(chars.join('').trim());\n } else if (c === '\\u0003') {\n // Ctrl+C\n process.stdout.write('\\n');\n process.exit(0);\n } else if (c === '\\u007f' || c === '\\b') {\n // Backspace\n if (chars.length > 0) {\n chars.pop();\n process.stdout.write('\\b \\b');\n }\n } else {\n chars.push(c);\n process.stdout.write('*');\n }\n };\n\n stdin.on('data', onData);\n });\n}\n\n// ─── register ──────────────────────────────────────────────────────────────\n\nexport async function registerCommand(): Promise<void> {\n console.log('\\n📝 Modu-Arena — Register\\n');\n\n const username = await prompt(' Username (3-50 chars): ');\n if (!username || username.length < 3 || username.length > 50) {\n console.error('Error: Username must be between 3 and 50 characters.\\n');\n process.exit(1);\n }\n\n const password = await promptPassword(' Password (min 8 chars): ');\n if (!password || password.length < 8) {\n console.error('Error: Password must be at least 8 characters.\\n');\n process.exit(1);\n }\n\n const displayName = await prompt(' Display name (optional, press Enter to skip): ');\n\n console.log('\\n Registering...');\n\n const existing = loadConfig();\n const result = await registerUser(\n { username, password, displayName: displayName || undefined },\n existing?.serverUrl,\n );\n\n if (result.error) {\n console.error(`\\n Error: ${result.error}\\n`);\n process.exit(1);\n }\n\n if (!result.apiKey) {\n console.error('\\n Error: No API key returned from server.\\n');\n process.exit(1);\n }\n\n saveConfig({ apiKey: result.apiKey, serverUrl: existing?.serverUrl });\n console.log('\\n ✓ Registration successful!');\n console.log(` ✓ API key saved to ~/.modu-arena.json`);\n console.log(`\\n Username: ${result.user?.username}`);\n console.log(` API Key: ${result.apiKey.slice(0, 20)}...${result.apiKey.slice(-4)}`);\n console.log('\\n ⚠ Save your API key — it will not be shown again.\\n');\n\n console.log(' Installing hooks for detected AI coding tools...\\n');\n await installCommand(result.apiKey);\n}\n\n// ─── login ─────────────────────────────────────────────────────────────────\n\nexport async function loginCommand(): Promise<void> {\n console.log('\\n🔑 Modu-Arena — Login\\n');\n\n const username = await prompt(' Username: ');\n if (!username) {\n console.error('Error: Username is required.\\n');\n process.exit(1);\n }\n\n const password = await promptPassword(' Password: ');\n if (!password) {\n console.error('Error: Password is required.\\n');\n process.exit(1);\n }\n\n console.log('\\n Logging in...');\n\n const existing = loadConfig();\n const result = await loginUser({ username, password }, existing?.serverUrl);\n\n if (result.error) {\n console.error(`\\n Error: ${result.error}\\n`);\n process.exit(1);\n }\n\n if (!result.apiKey) {\n console.error('\\n Error: No API key returned from server.\\n');\n process.exit(1);\n }\n\n saveConfig({ apiKey: result.apiKey, serverUrl: existing?.serverUrl });\n console.log('\\n ✓ Login successful!');\n console.log(` ✓ API key saved to ~/.modu-arena.json`);\n console.log(`\\n Username: ${result.user?.username}`);\n console.log(` API Key: ${result.apiKey.slice(0, 20)}...${result.apiKey.slice(-4)}`);\n console.log('\\n ⚠ A new API key was generated. Previous key is now invalid.\\n');\n\n console.log(' Reinstalling hooks with new API key...\\n');\n await installCommand(result.apiKey);\n}\n\n// ─── install ───────────────────────────────────────────────────────────────\n\nexport async function installCommand(apiKey?: string): Promise<void> {\n console.log('\\n🔧 Modu-Arena — AI Coding Tool Usage Tracker\\n');\n\n // Check if already configured\n const existing = loadConfig();\n if (existing?.apiKey && !apiKey) {\n console.log('✓ Already configured.');\n console.log(' Use --api-key <key> to update your API key.\\n');\n apiKey = existing.apiKey;\n }\n\n if (!apiKey) {\n console.error(\n 'Error: API key required.\\n' +\n ' Get your API key from the Modu-Arena dashboard.\\n' +\n ' Usage: npx @suncreation/modu-arena install --api-key <your-api-key>\\n',\n );\n process.exit(1);\n }\n\n // Validate API key format\n if (!apiKey.startsWith('modu_arena_')) {\n console.error(\n 'Error: Invalid API key format. Key must start with \"modu_arena_\".\\n',\n );\n process.exit(1);\n }\n\n // Save config\n saveConfig({ apiKey });\n console.log('✓ API key saved to ~/.modu-arena.json\\n');\n\n // Detect and install hooks for each tool\n const adapters = getAllAdapters();\n const results: { tool: string; result: InstallResult }[] = [];\n\n console.log('Detecting AI coding tools...\\n');\n\n for (const adapter of adapters) {\n const detected = adapter.detect();\n if (detected) {\n console.log(` ✓ ${adapter.displayName} detected`);\n const result = adapter.install(apiKey);\n results.push({ tool: adapter.displayName, result });\n if (result.success) {\n console.log(` → Hook installed: ${result.hookPath}`);\n } else {\n console.log(` ✗ ${result.message}`);\n }\n } else {\n console.log(` - ${adapter.displayName} not found`);\n }\n }\n\n const installed = results.filter((r) => r.result.success);\n console.log(\n `\\n✓ Setup complete. ${installed.length} tool(s) configured.\\n`,\n );\n\n if (installed.length === 0) {\n console.log(\n 'No AI coding tools detected. Install one of the supported tools:\\n' +\n ' • Claude Code (https://docs.anthropic.com/s/claude-code)\\n' +\n ' • OpenCode (https://opencode.ai)\\n' +\n ' • Gemini CLI (https://github.com/google-gemini/gemini-cli)\\n' +\n ' • Codex CLI (https://github.com/openai/codex)\\n' +\n ' • Crush (https://charm.sh/crush)\\n',\n );\n }\n}\n\n// ─── rank ──────────────────────────────────────────────────────────────────\n\nexport async function rankCommand(): Promise<void> {\n const config = requireConfig();\n console.log('\\n📊 Modu-Arena — Your Stats\\n');\n\n const result = await getRank({ apiKey: config.apiKey, serverUrl: config.serverUrl });\n\n if (!result.success) {\n console.error(`Error: ${'error' in result ? result.error : 'Unknown error'}\\n`);\n process.exit(1);\n }\n\n if (!('data' in result) || !result.data) {\n console.error('Error: Unexpected response format.\\n');\n process.exit(1);\n }\n\n const { username, usage, overview } = result.data;\n\n console.log(` User: ${username}`);\n console.log(` Tokens: ${formatNumber(usage.totalTokens)}`);\n console.log(` Sessions: ${usage.totalSessions}`);\n console.log(` Projects: ${overview.successfulProjectsCount}`);\n console.log('');\n\n // Tool breakdown\n if (usage.toolBreakdown.length > 0) {\n console.log(' Tool Breakdown:');\n for (const entry of usage.toolBreakdown) {\n const name = TOOL_DISPLAY_NAMES[entry.tool as ToolType] || entry.tool;\n console.log(\n ` ${name}: ${formatNumber(entry.tokens)} tokens`,\n );\n }\n console.log('');\n }\n\n // Period stats (aggregate from daily arrays)\n const sum7 = usage.last7Days.reduce(\n (acc, d) => ({ tokens: acc.tokens + d.inputTokens + d.outputTokens, sessions: acc.sessions + d.sessions }),\n { tokens: 0, sessions: 0 },\n );\n const sum30 = usage.last30Days.reduce(\n (acc, d) => ({ tokens: acc.tokens + d.inputTokens + d.outputTokens, sessions: acc.sessions + d.sessions }),\n { tokens: 0, sessions: 0 },\n );\n console.log(\n ` Last 7 days: ${formatNumber(sum7.tokens)} tokens, ${sum7.sessions} sessions`,\n );\n console.log(\n ` Last 30 days: ${formatNumber(sum30.tokens)} tokens, ${sum30.sessions} sessions`,\n );\n console.log('');\n}\n\n// ─── status ────────────────────────────────────────────────────────────────\n\nexport function statusCommand(): void {\n const config = loadConfig();\n console.log('\\n🔍 Modu-Arena — Status\\n');\n\n if (!config?.apiKey) {\n console.log(' Status: Not configured');\n console.log(\n ' Run `npx @suncreation/modu-arena install --api-key <key>` to set up.\\n',\n );\n return;\n }\n\n const maskedKey =\n config.apiKey.slice(0, 15) + '...' + config.apiKey.slice(-4);\n console.log(` API Key: ${maskedKey}`);\n console.log(` Server: ${config.serverUrl || API_BASE_URL}`);\n console.log('');\n\n // Check installed hooks\n const adapters = getAllAdapters();\n console.log(' Installed Hooks:');\n let hookCount = 0;\n for (const adapter of adapters) {\n const detected = adapter.detect();\n if (detected) {\n const hookExists = existsSync(adapter.getHookPath());\n const status = hookExists ? '✓ Active' : '✗ Not installed';\n console.log(` ${adapter.displayName}: ${status}`);\n if (hookExists) hookCount++;\n }\n }\n if (hookCount === 0) {\n console.log(' (none)');\n }\n console.log('');\n}\n\n// ─── uninstall ─────────────────────────────────────────────────────────────\n\nexport function uninstallCommand(): void {\n console.log('\\n🗑️ Modu-Arena — Uninstall\\n');\n\n // Remove hooks\n const adapters = getAllAdapters();\n for (const adapter of adapters) {\n const hookPath = adapter.getHookPath();\n if (existsSync(hookPath)) {\n unlinkSync(hookPath);\n console.log(` ✓ Removed ${adapter.displayName} hook`);\n }\n }\n\n // Remove config\n const configPath = join(homedir(), '.modu-arena.json');\n if (existsSync(configPath)) {\n unlinkSync(configPath);\n console.log(' ✓ Removed ~/.modu-arena.json');\n }\n\n console.log('\\n✓ Modu-Arena uninstalled.\\n');\n}\n\n// ─── submit ─────────────────────────────────────────────────────────────────\n\nconst IGNORE_DIRS = new Set([\n 'node_modules', '.git', '.next', '.nuxt', 'dist', 'build', 'out',\n '.cache', '.turbo', '.vercel', '__pycache__', '.svelte-kit',\n 'coverage', '.output', '.parcel-cache',\n]);\n\nfunction collectFileStructure(\n dir: string,\n maxDepth: number,\n currentDepth = 0,\n): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n if (currentDepth >= maxDepth) return result;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return result;\n }\n\n const files: string[] = [];\n for (const entry of entries) {\n if (entry.startsWith('.') && IGNORE_DIRS.has(entry)) continue;\n if (IGNORE_DIRS.has(entry)) continue;\n\n const fullPath = join(dir, entry);\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n\n if (stat.isDirectory()) {\n const sub = collectFileStructure(fullPath, maxDepth, currentDepth + 1);\n for (const [key, val] of Object.entries(sub)) {\n result[key] = val;\n }\n } else {\n files.push(entry);\n }\n }\n\n if (files.length > 0) {\n const relDir = currentDepth === 0 ? '.' : dir.split('/').slice(-(currentDepth)).join('/');\n result[relDir] = files;\n }\n\n return result;\n}\n\nexport async function submitCommand(): Promise<void> {\n const config = requireConfig();\n console.log('\\n🚀 Modu-Arena — Project Submit\\n');\n\n const cwd = process.cwd();\n const projectName = basename(cwd);\n\n const readmePath = join(cwd, 'README.md');\n if (!existsSync(readmePath)) {\n console.error('Error: README.md not found in the current directory.');\n console.error(' Please create a README.md describing your project.\\n');\n process.exit(1);\n }\n\n const description = readFileSync(readmePath, 'utf-8');\n if (description.trim().length === 0) {\n console.error('Error: README.md is empty.\\n');\n process.exit(1);\n }\n\n console.log(` Project: ${projectName}`);\n console.log(` README: ${readmePath}`);\n console.log('');\n console.log(' Collecting file structure...');\n\n const fileStructure = collectFileStructure(cwd, 3);\n const fileCount = Object.values(fileStructure).reduce((sum, files) => sum + files.length, 0);\n console.log(` Found ${fileCount} file(s) in ${Object.keys(fileStructure).length} director${Object.keys(fileStructure).length === 1 ? 'y' : 'ies'}`);\n console.log('');\n console.log(' Submitting for evaluation...\\n');\n\n const result = await submitEvaluation(\n { projectName, description, fileStructure },\n { apiKey: config.apiKey, serverUrl: config.serverUrl },\n );\n\n if (!result.success) {\n console.error(`Error: ${'error' in result ? result.error : 'Unknown error'}\\n`);\n process.exit(1);\n }\n\n const { evaluation } = result;\n const statusIcon = evaluation.passed ? '✅' : '❌';\n const statusText = evaluation.passed ? 'PASSED' : 'FAILED';\n\n console.log(` Result: ${statusIcon} ${statusText}`);\n console.log(` Total Score: ${evaluation.totalScore}/100`);\n console.log('');\n console.log(' Rubric Scores:');\n console.log(` Functionality: ${evaluation.rubricFunctionality}/50`);\n console.log(` Practicality: ${evaluation.rubricPracticality}/50`);\n console.log('');\n\n if (evaluation.feedback) {\n console.log(' Feedback:');\n const lines = evaluation.feedback.split('\\n');\n for (const line of lines) {\n console.log(` ${line}`);\n }\n console.log('');\n }\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction formatNumber(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n","/**\n * Tool Adapters — Cross-platform hook installation for AI coding tools.\n *\n * Generates Node.js hook scripts (works on all platforms) with\n * thin shell wrappers (.sh on Unix, .cmd on Windows).\n */\n\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { API_BASE_URL, type ToolType } from './constants.js';\n\n// ─── Platform ──────────────────────────────────────────────────────────────\n\nconst IS_WIN = process.platform === 'win32';\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\nexport interface ToolAdapter {\n slug: ToolType;\n displayName: string;\n detect(): boolean;\n install(apiKey: string): InstallResult;\n getHookPath(): string;\n}\n\nexport interface InstallResult {\n success: boolean;\n message: string;\n hookPath?: string;\n}\n\ninterface EnvField {\n key: string;\n env: string;\n parse: 'string' | 'int';\n fallback: string;\n}\n\n// ─── Hook Script Generation ────────────────────────────────────────────────\n\nconst HOOK_JS = '_modu-hook.js';\n\nfunction baseFields(prefix: string): EnvField[] {\n return [\n { key: 'sessionId', env: `${prefix}_SESSION_ID`, parse: 'string', fallback: '' },\n { key: 'startedAt', env: `${prefix}_SESSION_STARTED_AT`, parse: 'string', fallback: '' },\n { key: 'inputTokens', env: `${prefix}_INPUT_TOKENS`, parse: 'int', fallback: '0' },\n { key: 'outputTokens', env: `${prefix}_OUTPUT_TOKENS`, parse: 'int', fallback: '0' },\n { key: 'modelName', env: `${prefix}_MODEL`, parse: 'string', fallback: 'unknown' },\n ];\n}\n\nfunction generateHookJs(apiKey: string, toolType: string, prefix: string, fields: EnvField[]): string {\n const lines = fields.map((f) =>\n f.parse === 'int'\n ? ` ${f.key}: parseInt(process.env[\"${f.env}\"] || \"${f.fallback}\", 10)`\n : ` ${f.key}: process.env[\"${f.env}\"] || \"${f.fallback}\"`\n );\n\n return `#!/usr/bin/env node\n\"use strict\";\nvar crypto = require(\"crypto\");\n\nvar API_KEY = ${JSON.stringify(apiKey)};\nvar SERVER = ${JSON.stringify(API_BASE_URL)};\n\nif (!process.env[\"${prefix}_SESSION_ID\"]) process.exit(0);\n\nvar body = JSON.stringify({\n toolType: ${JSON.stringify(toolType)},\n endedAt: new Date().toISOString(),\n${lines.join(\",\\n\")}\n});\n\nvar ts = Math.floor(Date.now() / 1000).toString();\nvar sig = crypto.createHmac(\"sha256\", API_KEY).update(ts + \":\" + body).digest(\"hex\");\n\nfetch(SERVER + \"/api/v1/sessions\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-API-Key\": API_KEY, \"X-Timestamp\": ts, \"X-Signature\": sig },\n body: body\n}).catch(function(){});\n`;\n}\n\nfunction shellWrapper(): string {\n return `#!/bin/bash\nexec node \"$(dirname \"$0\")/${HOOK_JS}\"\n`;\n}\n\nfunction cmdWrapper(): string {\n return `@node \"%~dp0${HOOK_JS}\" 2>nul\\r\\n`;\n}\n\n// ─── Shared Install Logic ──────────────────────────────────────────────────\n\nfunction installHook(\n displayName: string,\n hooksDir: string,\n entryPath: string,\n apiKey: string,\n toolType: string,\n prefix: string,\n fields: EnvField[],\n): InstallResult {\n try {\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n writeFileSync(join(hooksDir, HOOK_JS), generateHookJs(apiKey, toolType, prefix, fields), { mode: 0o755 });\n\n if (IS_WIN) {\n writeFileSync(entryPath, cmdWrapper());\n } else {\n writeFileSync(entryPath, shellWrapper(), { mode: 0o755 });\n }\n\n return { success: true, message: `${displayName} hook installed at ${entryPath}`, hookPath: entryPath };\n } catch (err) {\n return { success: false, message: `Failed to install ${displayName} hook: ${err}` };\n }\n}\n\nfunction hookEntryName(): string {\n return IS_WIN ? 'session-end.cmd' : 'session-end.sh';\n}\n\n// ─── Adapters ──────────────────────────────────────────────────────────────\n\nclass ClaudeCodeAdapter implements ToolAdapter {\n slug = 'claude-code' as const;\n displayName = 'Claude Code';\n\n private get configDir() { return join(homedir(), '.claude'); }\n private get hooksDir() { return join(this.configDir, 'hooks'); }\n\n getHookPath() { return join(this.hooksDir, hookEntryName()); }\n detect() { return existsSync(this.configDir); }\n\n install(apiKey: string) {\n return installHook(this.displayName, this.hooksDir, this.getHookPath(), apiKey, 'claude-code', 'CLAUDE',\n [\n ...baseFields('CLAUDE'),\n { key: 'cacheCreationTokens', env: 'CLAUDE_CACHE_CREATION_TOKENS', parse: 'int', fallback: '0' },\n { key: 'cacheReadTokens', env: 'CLAUDE_CACHE_READ_TOKENS', parse: 'int', fallback: '0' },\n ],\n );\n }\n}\n\nclass OpenCodeAdapter implements ToolAdapter {\n slug = 'opencode' as const;\n displayName = 'OpenCode';\n\n // OpenCode uses ~/.config/opencode on ALL platforms (including Windows)\n // It uses xdg-basedir which respects XDG_CONFIG_HOME on all platforms\n private get configDir() {\n return join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode');\n }\n private get hooksDir() { return join(this.configDir, 'hooks'); }\n\n getHookPath() { return join(this.hooksDir, hookEntryName()); }\n detect() { return existsSync(this.configDir); }\n\n install(apiKey: string) {\n return installHook(this.displayName, this.hooksDir, this.getHookPath(), apiKey, 'opencode', 'OPENCODE',\n baseFields('OPENCODE'),\n );\n }\n}\n\nclass SimpleAdapter implements ToolAdapter {\n constructor(\n public slug: ToolType,\n public displayName: string,\n private dirName: string,\n private envPrefix: string,\n ) {}\n\n private get configDir() { return join(homedir(), this.dirName); }\n private get hooksDir() { return join(this.configDir, 'hooks'); }\n\n getHookPath() { return join(this.hooksDir, hookEntryName()); }\n detect() { return existsSync(this.configDir); }\n\n install(apiKey: string) {\n return installHook(this.displayName, this.hooksDir, this.getHookPath(), apiKey, this.slug, this.envPrefix,\n baseFields(this.envPrefix),\n );\n }\n}\n\n// ─── Registry ──────────────────────────────────────────────────────────────\n\nexport function getAllAdapters(): ToolAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new OpenCodeAdapter(),\n new SimpleAdapter('gemini', 'Gemini CLI', '.gemini', 'GEMINI'),\n new SimpleAdapter('codex', 'Codex CLI', '.codex', 'CODEX'),\n new SimpleAdapter('crush', 'Crush', '.crush', 'CRUSH'),\n ];\n}\n\nexport function getAdapter(slug: string): ToolAdapter | undefined {\n return getAllAdapters().find((a) => a.slug === slug);\n}\n","/** Base URL for the Modu-Arena API server */\nexport const API_BASE_URL =\n process.env.MODU_ARENA_API_URL ?? 'http://backend.vibemakers.kr:23010';\n\n/** API key prefix used for all keys */\nexport const API_KEY_PREFIX = 'modu_arena_';\n\n/** Supported AI coding tools */\nexport const TOOL_TYPES = [\n 'claude-code',\n 'opencode',\n 'gemini',\n 'codex',\n 'crush',\n] as const;\n\nexport type ToolType = (typeof TOOL_TYPES)[number];\n\n/** Display names for each tool */\nexport const TOOL_DISPLAY_NAMES: Record<ToolType, string> = {\n 'claude-code': 'Claude Code',\n opencode: 'OpenCode',\n gemini: 'Gemini CLI',\n codex: 'Codex CLI',\n crush: 'Crush',\n};\n\n/** Config file name stored in user home directory */\nexport const CONFIG_FILE_NAME = '.modu-arena.json';\n\n/** Minimum interval between sessions (seconds) */\nexport const MIN_SESSION_INTERVAL_SEC = 60;\n\n/** HMAC timestamp tolerance (seconds) */\nexport const HMAC_TIMESTAMP_TOLERANCE_SEC = 300;\n","import { createHmac, createHash } from 'node:crypto';\n\n/**\n * Compute HMAC-SHA256 signature for API authentication.\n *\n * message = \"{timestamp}:{bodyJsonString}\"\n * signature = HMAC-SHA256(apiKey, message).hex()\n */\nexport function computeHmacSignature(\n apiKey: string,\n timestamp: string,\n body: string,\n): string {\n const message = `${timestamp}:${body}`;\n return createHmac('sha256', apiKey).update(message).digest('hex');\n}\n\n/**\n * Compute SHA-256 session hash for integrity verification.\n *\n * data = \"{userId}:{userSalt}:{inputTokens}:{outputTokens}:{cacheCreationTokens}:{cacheReadTokens}:{modelName}:{endedAt}\"\n * hash = SHA-256(data).hex()\n */\nexport function computeSessionHash(\n userId: string,\n userSalt: string,\n inputTokens: number,\n outputTokens: number,\n cacheCreationTokens: number,\n cacheReadTokens: number,\n modelName: string,\n endedAt: string,\n): string {\n const data = `${userId}:${userSalt}:${inputTokens}:${outputTokens}:${cacheCreationTokens}:${cacheReadTokens}:${modelName}:${endedAt}`;\n return createHash('sha256').update(data).digest('hex');\n}\n","import { computeHmacSignature } from './crypto.js';\nimport { API_BASE_URL } from './constants.js';\n\nexport interface SessionPayload {\n toolType: string;\n sessionId: string;\n startedAt: string;\n endedAt: string;\n inputTokens: number;\n outputTokens: number;\n cacheCreationTokens?: number;\n cacheReadTokens?: number;\n modelName?: string;\n codeMetrics?: Record<string, unknown> | null;\n}\n\nexport interface BatchPayload {\n sessions: SessionPayload[];\n}\n\nexport interface RankResponse {\n success: boolean;\n data: {\n username: string;\n usage: {\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheTokens: number;\n totalTokens: number;\n totalSessions: number;\n toolBreakdown: Array<{ tool: string; tokens: number }>;\n last7Days: Array<{ date: string; inputTokens: number; outputTokens: number; sessions: number }>;\n last30Days: Array<{ date: string; inputTokens: number; outputTokens: number; sessions: number }>;\n };\n overview: {\n successfulProjectsCount: number;\n };\n lastUpdated: string;\n };\n}\n\nexport interface ApiError {\n error: string;\n}\n\ninterface RequestOptions {\n apiKey: string;\n serverUrl?: string;\n}\n\nfunction baseUrl(opts: RequestOptions): string {\n return opts.serverUrl || API_BASE_URL;\n}\n\nfunction makeAuthHeaders(\n apiKey: string,\n body?: string,\n): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': apiKey,\n };\n\n if (body !== undefined) {\n const timestamp = Math.floor(Date.now() / 1000).toString();\n const signature = computeHmacSignature(apiKey, timestamp, body);\n headers['X-Timestamp'] = timestamp;\n headers['X-Signature'] = signature;\n }\n\n return headers;\n}\n\nexport async function submitSession(\n session: SessionPayload,\n opts: RequestOptions,\n): Promise<{ success: boolean; session?: unknown; error?: string }> {\n const body = JSON.stringify(session);\n const url = `${baseUrl(opts)}/api/v1/sessions`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: makeAuthHeaders(opts.apiKey, body),\n body,\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as { success: boolean; session: unknown };\n}\n\nexport async function submitBatch(\n sessions: SessionPayload[],\n opts: RequestOptions,\n): Promise<{\n success: boolean;\n processed?: number;\n duplicatesSkipped?: number;\n error?: string;\n}> {\n const body = JSON.stringify({ sessions });\n const url = `${baseUrl(opts)}/api/v1/sessions/batch`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: makeAuthHeaders(opts.apiKey, body),\n body,\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as { success: boolean; processed: number; duplicatesSkipped: number };\n}\n\nexport async function getRank(\n opts: RequestOptions,\n): Promise<RankResponse | { success: false; error: string }> {\n const url = `${baseUrl(opts)}/api/v1/rank`;\n\n const res = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-API-Key': opts.apiKey,\n },\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as RankResponse;\n}\n\n// ─── Auth ─────────────────────────────────────────────────────────────────\n\nexport interface AuthResponse {\n success: boolean;\n apiKey?: string;\n user?: { id: string; username: string; displayName?: string };\n error?: string;\n}\n\nexport async function registerUser(\n payload: { username: string; password: string; displayName?: string },\n serverUrl?: string,\n): Promise<AuthResponse> {\n const body = JSON.stringify(payload);\n const url = `${serverUrl || API_BASE_URL}/api/auth/register`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n return (await res.json()) as AuthResponse;\n}\n\nexport async function loginUser(\n payload: { username: string; password: string },\n serverUrl?: string,\n): Promise<AuthResponse> {\n const body = JSON.stringify({ ...payload, source: 'cli' });\n const url = `${serverUrl || API_BASE_URL}/api/auth/login`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n return (await res.json()) as AuthResponse;\n}\n\n// ─── Evaluate ─────────────────────────────────────────────────────────────\n\nexport interface EvaluatePayload {\n projectName: string;\n description: string;\n fileStructure?: Record<string, string[]>;\n}\n\nexport interface EvaluationResult {\n passed: boolean;\n totalScore: number;\n rubricFunctionality: number;\n rubricPracticality: number;\n feedback: string;\n}\n\nexport interface EvaluateResponse {\n success: true;\n evaluation: EvaluationResult;\n}\n\nexport async function submitEvaluation(\n payload: EvaluatePayload,\n opts: RequestOptions,\n): Promise<EvaluateResponse | { success: false; error: string }> {\n const body = JSON.stringify(payload);\n const url = `${baseUrl(opts)}/api/v1/evaluate`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: makeAuthHeaders(opts.apiKey, body),\n body,\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as EvaluateResponse;\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { CONFIG_FILE_NAME } from './constants.js';\n\nexport interface Config {\n apiKey: string;\n serverUrl?: string;\n tools?: string[];\n}\n\nfunction getConfigPath(): string {\n return join(homedir(), CONFIG_FILE_NAME);\n}\n\nexport function loadConfig(): Config | null {\n const configPath = getConfigPath();\n if (!existsSync(configPath)) return null;\n\n try {\n const raw = readFileSync(configPath, 'utf-8');\n return JSON.parse(raw) as Config;\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n const dir = dirname(configPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function requireConfig(): Config {\n const config = loadConfig();\n if (!config?.apiKey) {\n console.error(\n 'Error: Not configured. Run `npx @suncreation/modu-arena install` first.',\n );\n process.exit(1);\n }\n return config;\n}\n","/**\n * @suncreation/modu-arena CLI\n *\n * Track and rank your AI coding tool usage.\n *\n * Usage:\n * npx @suncreation/modu-arena install --api-key <key>\n * npx @suncreation/modu-arena rank\n * npx @suncreation/modu-arena status\n * npx @suncreation/modu-arena uninstall\n */\n\nimport {\n installCommand,\n loginCommand,\n rankCommand,\n registerCommand,\n statusCommand,\n submitCommand,\n uninstallCommand,\n} from './commands.js';\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printHelp(): void {\n console.log(`\nModu-Arena — AI Coding Tool Usage Tracker\n\nUsage:\n npx @suncreation/modu-arena <command> [options]\n\nCommands:\n register Create a new account (interactive)\n login Log in to an existing account (interactive)\n install Set up hooks for detected AI coding tools\n rank View your current stats and ranking\n status Check configuration and installed hooks\n submit Submit current project for evaluation\n uninstall Remove all hooks and configuration\n\nOptions:\n --api-key <key> Your Modu-Arena API key (for install)\n --help, -h Show this help message\n --version, -v Show version\n\nExamples:\n npx @suncreation/modu-arena register\n npx @suncreation/modu-arena login\n npx @suncreation/modu-arena install --api-key modu_arena_AbCdEfGh_xxx...\n npx @suncreation/modu-arena rank\n`);\n}\n\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printHelp();\n process.exit(0);\n }\n\n if (command === '--version' || command === '-v') {\n console.log('0.1.0');\n process.exit(0);\n }\n\n switch (command) {\n case 'register':\n await registerCommand();\n break;\n case 'login':\n await loginCommand();\n break;\n case 'install': {\n const keyIndex = args.indexOf('--api-key');\n const apiKey = keyIndex >= 0 ? args[keyIndex + 1] : undefined;\n await installCommand(apiKey);\n break;\n }\n case 'rank':\n await rankCommand();\n break;\n case 'status':\n statusCommand();\n break;\n case 'submit':\n await submitCommand();\n break;\n case 'uninstall':\n uninstallCommand();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n"],"mappings":"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","names":["existsSync","readFileSync","homedir","join","writeFileSync","existsSync","mkdirSync","homedir","join","join","homedir","existsSync","mkdirSync","writeFileSync","existsSync","join","homedir","readFileSync"]}
1
+ {"version":3,"sources":["../src/commands.ts","../src/adapters.ts","../src/constants.ts","../src/crypto.ts","../src/api.ts","../src/config.ts","../src/daemon.ts","../src/claude-desktop.ts","../src/index.ts"],"sourcesContent":["/**\n * CLI Commands — install, rank, status, uninstall\n */\n\nimport { createInterface } from 'node:readline';\nimport { existsSync, readFileSync, readdirSync, statSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\nimport { getAllAdapters, type InstallResult } from './adapters.js';\nimport { getRank, registerUser, loginUser, submitEvaluation } from './api.js';\nimport { loadConfig, saveConfig, requireConfig } from './config.js';\nimport { API_BASE_URL, TOOL_DISPLAY_NAMES, type ToolType } from './constants.js';\nimport { installDaemon, uninstallDaemon, getDaemonStatus } from './daemon.js';\nimport { syncClaudeDesktop, hasClaudeDesktopData } from './claude-desktop.js';\n\nfunction prompt(question: string): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nfunction promptPassword(question: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(question);\n const chars: string[] = [];\n const stdin = process.stdin;\n const wasRaw = stdin.isRaw;\n stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n\n const onData = (ch: string) => {\n const c = ch.toString();\n if (c === '\\n' || c === '\\r' || c === '\\u0004') {\n // Enter or Ctrl+D\n stdin.setRawMode(wasRaw ?? false);\n stdin.pause();\n stdin.removeListener('data', onData);\n process.stdout.write('\\n');\n resolve(chars.join('').trim());\n } else if (c === '\\u0003') {\n // Ctrl+C\n process.stdout.write('\\n');\n process.exit(0);\n } else if (c === '\\u007f' || c === '\\b') {\n // Backspace\n if (chars.length > 0) {\n chars.pop();\n process.stdout.write('\\b \\b');\n }\n } else {\n chars.push(c);\n process.stdout.write('*');\n }\n };\n\n stdin.on('data', onData);\n });\n}\n\n// ─── register ──────────────────────────────────────────────────────────────\n\nexport async function registerCommand(): Promise<void> {\n console.log('\\n📝 Modu-Arena — Register\\n');\n\n const username = await prompt(' Username (3-50 chars): ');\n if (!username || username.length < 3 || username.length > 50) {\n console.error('Error: Username must be between 3 and 50 characters.\\n');\n process.exit(1);\n }\n\n const password = await promptPassword(' Password (min 8 chars): ');\n if (!password || password.length < 8) {\n console.error('Error: Password must be at least 8 characters.\\n');\n process.exit(1);\n }\n\n const displayName = await prompt(' Display name (optional, press Enter to skip): ');\n\n console.log('\\n Registering...');\n\n const existing = loadConfig();\n const result = await registerUser(\n { username, password, displayName: displayName || undefined },\n existing?.serverUrl,\n );\n\n if (result.error) {\n console.error(`\\n Error: ${result.error}\\n`);\n process.exit(1);\n }\n\n if (!result.apiKey) {\n console.error('\\n Error: No API key returned from server.\\n');\n process.exit(1);\n }\n\n saveConfig({ apiKey: result.apiKey, serverUrl: existing?.serverUrl });\n console.log('\\n ✓ Registration successful!');\n console.log(` ✓ API key saved to ~/.modu-arena.json`);\n console.log(`\\n Username: ${result.user?.username}`);\n console.log(` API Key: ${result.apiKey.slice(0, 20)}...${result.apiKey.slice(-4)}`);\n console.log('\\n ⚠ Save your API key — it will not be shown again.\\n');\n\n console.log(' Installing hooks for detected AI coding tools...\\n');\n await installCommand(result.apiKey);\n}\n\n// ─── login ─────────────────────────────────────────────────────────────────\n\nexport async function loginCommand(): Promise<void> {\n console.log('\\n🔑 Modu-Arena — Login\\n');\n\n const username = await prompt(' Username: ');\n if (!username) {\n console.error('Error: Username is required.\\n');\n process.exit(1);\n }\n\n const password = await promptPassword(' Password: ');\n if (!password) {\n console.error('Error: Password is required.\\n');\n process.exit(1);\n }\n\n console.log('\\n Logging in...');\n\n const existing = loadConfig();\n const result = await loginUser({ username, password }, existing?.serverUrl);\n\n if (result.error) {\n console.error(`\\n Error: ${result.error}\\n`);\n process.exit(1);\n }\n\n if (!result.apiKey) {\n console.error('\\n Error: No API key returned from server.\\n');\n process.exit(1);\n }\n\n saveConfig({ apiKey: result.apiKey, serverUrl: existing?.serverUrl });\n console.log('\\n ✓ Login successful!');\n console.log(` ✓ API key saved to ~/.modu-arena.json`);\n console.log(`\\n Username: ${result.user?.username}`);\n console.log(` API Key: ${result.apiKey.slice(0, 20)}...${result.apiKey.slice(-4)}`);\n console.log('\\n ⚠ A new API key was generated. Previous key is now invalid.\\n');\n\n console.log(' Reinstalling hooks with new API key...\\n');\n await installCommand(result.apiKey);\n}\n\n// ─── install ───────────────────────────────────────────────────────────────\n\nexport async function installCommand(apiKey?: string): Promise<void> {\n console.log('\\n🔧 Modu-Arena — AI Coding Tool Usage Tracker\\n');\n\n // Check if already configured\n const existing = loadConfig();\n if (existing?.apiKey && !apiKey) {\n console.log('✓ Already configured.');\n console.log(' Use --api-key <key> to update your API key.\\n');\n apiKey = existing.apiKey;\n }\n\n if (!apiKey) {\n console.error(\n 'Error: API key required.\\n' +\n ' Get your API key from the Modu-Arena dashboard.\\n' +\n ' Usage: npx @suncreation/modu-arena install --api-key <your-api-key>\\n',\n );\n process.exit(1);\n }\n\n // Validate API key format\n if (!apiKey.startsWith('modu_arena_')) {\n console.error(\n 'Error: Invalid API key format. Key must start with \"modu_arena_\".\\n',\n );\n process.exit(1);\n }\n\n // Save config\n saveConfig({ apiKey });\n console.log('✓ API key saved to ~/.modu-arena.json\\n');\n\n // Detect and install hooks for each tool\n const adapters = getAllAdapters();\n const results: { tool: string; result: InstallResult }[] = [];\n\n console.log('Detecting AI coding tools...\\n');\n\n for (const adapter of adapters) {\n const detected = adapter.detect();\n if (detected) {\n console.log(` ✓ ${adapter.displayName} detected`);\n const result = adapter.install(apiKey);\n results.push({ tool: adapter.displayName, result });\n if (result.success) {\n console.log(` → Hook installed: ${result.hookPath}`);\n } else {\n console.log(` ✗ ${result.message}`);\n }\n } else {\n console.log(` - ${adapter.displayName} not found`);\n }\n }\n\n const installed = results.filter((r) => r.result.success);\n console.log(\n `\\n✓ Setup complete. ${installed.length} tool(s) configured.\\n`,\n );\n\n if (installed.length === 0) {\n console.log(\n 'No AI coding tools detected. Install one of the supported tools:\\n' +\n ' • Claude Code (https://docs.anthropic.com/s/claude-code)\\n' +\n ' • OpenCode (https://opencode.ai)\\n' +\n ' • Gemini CLI (https://github.com/google-gemini/gemini-cli)\\n' +\n ' • Codex CLI (https://github.com/openai/codex)\\n' +\n ' • Crush (https://charm.sh/crush)\\n',\n );\n }\n}\n\n// ─── rank ──────────────────────────────────────────────────────────────────\n\nexport async function rankCommand(): Promise<void> {\n const config = requireConfig();\n console.log('\\n📊 Modu-Arena — Your Stats\\n');\n\n const result = await getRank({ apiKey: config.apiKey, serverUrl: config.serverUrl });\n\n if (!result.success) {\n console.error(`Error: ${'error' in result ? result.error : 'Unknown error'}\\n`);\n process.exit(1);\n }\n\n if (!('data' in result) || !result.data) {\n console.error('Error: Unexpected response format.\\n');\n process.exit(1);\n }\n\n const { username, usage, overview } = result.data;\n\n console.log(` User: ${username}`);\n console.log(` Tokens: ${formatNumber(usage.totalTokens)}`);\n console.log(` Sessions: ${usage.totalSessions}`);\n console.log(` Projects: ${overview.successfulProjectsCount}`);\n console.log('');\n\n // Tool breakdown\n if (usage.toolBreakdown.length > 0) {\n console.log(' Tool Breakdown:');\n for (const entry of usage.toolBreakdown) {\n const name = TOOL_DISPLAY_NAMES[entry.tool as ToolType] || entry.tool;\n console.log(\n ` ${name}: ${formatNumber(entry.tokens)} tokens`,\n );\n }\n console.log('');\n }\n\n // Period stats (aggregate from daily arrays)\n const sum7 = usage.last7Days.reduce(\n (acc, d) => ({ tokens: acc.tokens + d.inputTokens + d.outputTokens, sessions: acc.sessions + d.sessions }),\n { tokens: 0, sessions: 0 },\n );\n const sum30 = usage.last30Days.reduce(\n (acc, d) => ({ tokens: acc.tokens + d.inputTokens + d.outputTokens, sessions: acc.sessions + d.sessions }),\n { tokens: 0, sessions: 0 },\n );\n console.log(\n ` Last 7 days: ${formatNumber(sum7.tokens)} tokens, ${sum7.sessions} sessions`,\n );\n console.log(\n ` Last 30 days: ${formatNumber(sum30.tokens)} tokens, ${sum30.sessions} sessions`,\n );\n console.log('');\n}\n\n// ─── status ────────────────────────────────────────────────────────────────\n\nexport function statusCommand(): void {\n const config = loadConfig();\n console.log('\\n🔍 Modu-Arena — Status\\n');\n\n if (!config?.apiKey) {\n console.log(' Status: Not configured');\n console.log(\n ' Run `npx @suncreation/modu-arena install --api-key <key>` to set up.\\n',\n );\n return;\n }\n\n const maskedKey =\n config.apiKey.slice(0, 15) + '...' + config.apiKey.slice(-4);\n console.log(` API Key: ${maskedKey}`);\n console.log(` Server: ${config.serverUrl || API_BASE_URL}`);\n console.log('');\n\n // Check installed hooks\n const adapters = getAllAdapters();\n console.log(' Installed Hooks:');\n let hookCount = 0;\n for (const adapter of adapters) {\n const detected = adapter.detect();\n if (detected) {\n const hookExists = existsSync(adapter.getHookPath());\n const status = hookExists ? '✓ Active' : '✗ Not installed';\n console.log(` ${adapter.displayName}: ${status}`);\n if (hookExists) hookCount++;\n }\n }\n if (hookCount === 0) {\n console.log(' (none)');\n }\n console.log('');\n}\n\n// ─── uninstall ─────────────────────────────────────────────────────────────\n\nexport function uninstallCommand(): void {\n console.log('\\n🗑️ Modu-Arena — Uninstall\\n');\n\n // Remove hooks\n const adapters = getAllAdapters();\n for (const adapter of adapters) {\n const hookPath = adapter.getHookPath();\n if (existsSync(hookPath)) {\n unlinkSync(hookPath);\n console.log(` ✓ Removed ${adapter.displayName} hook`);\n }\n }\n\n // Remove config\n const configPath = join(homedir(), '.modu-arena.json');\n if (existsSync(configPath)) {\n unlinkSync(configPath);\n console.log(' ✓ Removed ~/.modu-arena.json');\n }\n\n console.log('\\n✓ Modu-Arena uninstalled.\\n');\n}\n\n// ─── submit ─────────────────────────────────────────────────────────────────\n\nconst IGNORE_DIRS = new Set([\n 'node_modules', '.git', '.next', '.nuxt', 'dist', 'build', 'out',\n '.cache', '.turbo', '.vercel', '__pycache__', '.svelte-kit',\n 'coverage', '.output', '.parcel-cache',\n]);\n\nfunction collectFileStructure(\n dir: string,\n maxDepth: number,\n currentDepth = 0,\n): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n if (currentDepth >= maxDepth) return result;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return result;\n }\n\n const files: string[] = [];\n for (const entry of entries) {\n if (entry.startsWith('.') && IGNORE_DIRS.has(entry)) continue;\n if (IGNORE_DIRS.has(entry)) continue;\n\n const fullPath = join(dir, entry);\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n\n if (stat.isDirectory()) {\n const sub = collectFileStructure(fullPath, maxDepth, currentDepth + 1);\n for (const [key, val] of Object.entries(sub)) {\n result[key] = val;\n }\n } else {\n files.push(entry);\n }\n }\n\n if (files.length > 0) {\n const relDir = currentDepth === 0 ? '.' : dir.split('/').slice(-(currentDepth)).join('/');\n result[relDir] = files;\n }\n\n return result;\n}\n\nexport async function submitCommand(): Promise<void> {\n const config = requireConfig();\n console.log('\\n🚀 Modu-Arena — Project Submit\\n');\n\n const cwd = process.cwd();\n const projectName = basename(cwd);\n\n const readmePath = join(cwd, 'README.md');\n if (!existsSync(readmePath)) {\n console.error('Error: README.md not found in the current directory.');\n console.error(' Please create a README.md describing your project.\\n');\n process.exit(1);\n }\n\n const description = readFileSync(readmePath, 'utf-8');\n if (description.trim().length === 0) {\n console.error('Error: README.md is empty.\\n');\n process.exit(1);\n }\n\n console.log(` Project: ${projectName}`);\n console.log(` README: ${readmePath}`);\n console.log('');\n console.log(' Collecting file structure...');\n\n const fileStructure = collectFileStructure(cwd, 3);\n const fileCount = Object.values(fileStructure).reduce((sum, files) => sum + files.length, 0);\n console.log(` Found ${fileCount} file(s) in ${Object.keys(fileStructure).length} director${Object.keys(fileStructure).length === 1 ? 'y' : 'ies'}`);\n console.log('');\n console.log(' Submitting for evaluation...\\n');\n\n const result = await submitEvaluation(\n { projectName, description, fileStructure },\n { apiKey: config.apiKey, serverUrl: config.serverUrl },\n );\n\n if (!result.success) {\n console.error(`Error: ${'error' in result ? result.error : 'Unknown error'}\\n`);\n process.exit(1);\n }\n\n const { evaluation } = result;\n const statusIcon = evaluation.passed ? '✅' : '❌';\n const statusText = evaluation.passed ? 'PASSED' : 'FAILED';\n\n console.log(` Result: ${statusIcon} ${statusText}`);\n console.log(` Total Score: ${evaluation.totalScore}/100`);\n console.log('');\n console.log(' Rubric Scores:');\n console.log(` Functionality: ${evaluation.rubricFunctionality}/50`);\n console.log(` Practicality: ${evaluation.rubricPracticality}/50`);\n console.log('');\n\n if (evaluation.feedback) {\n console.log(' Feedback:');\n const lines = evaluation.feedback.split('\\n');\n for (const line of lines) {\n console.log(` ${line}`);\n }\n console.log('');\n }\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction formatNumber(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n\n// ─── daemon install ────────────────────────────────────────────────────────\n\nexport function daemonInstallCommand(): void {\n console.log('\\n🔄 Modu-Arena — Claude Desktop Daemon\\n');\n \n if (!hasClaudeDesktopData()) {\n console.log(' ✗ Claude Desktop data not found.');\n console.log(' Make sure Claude Desktop is installed and has been used.\\n');\n process.exit(1);\n }\n \n const result = installDaemon();\n \n if (result.success) {\n console.log(` ✓ ${result.message}`);\n console.log(' ✓ Daemon will sync Claude Desktop usage automatically.\\n');\n } else {\n console.error(` ✗ ${result.message}\\n`);\n process.exit(1);\n }\n}\n\n// ─── daemon uninstall ──────────────────────────────────────────────────────\n\nexport function daemonUninstallCommand(): void {\n console.log('\\n🔄 Modu-Arena — Claude Desktop Daemon\\n');\n \n const result = uninstallDaemon();\n \n if (result.success) {\n console.log(` ✓ ${result.message}\\n`);\n } else {\n console.error(` ✗ ${result.message}\\n`);\n process.exit(1);\n }\n}\n\n// ─── daemon status ─────────────────────────────────────────────────────────\n\nexport function daemonStatusCommand(): void {\n console.log('\\n🔄 Modu-Arena — Claude Desktop Daemon\\n');\n \n const status = getDaemonStatus();\n \n console.log(` Platform: ${status.platform}`);\n console.log(` Installed: ${status.installed ? 'Yes' : 'No'}`);\n if (status.installed) {\n console.log(` Sync Interval: ${Math.floor(status.interval / 60)} minutes`);\n }\n \n if (hasClaudeDesktopData()) {\n console.log(' Claude Desktop Data: Found');\n } else {\n console.log(' Claude Desktop Data: Not found');\n }\n console.log('');\n}\n\n// ─── daemon sync ───────────────────────────────────────────────────────────\n\nexport async function daemonSyncCommand(): Promise<void> {\n const config = requireConfig();\n \n if (!hasClaudeDesktopData()) {\n console.log('Claude Desktop data not found. Nothing to sync.\\n');\n return;\n }\n \n console.log('Syncing Claude Desktop usage...');\n \n const result = await syncClaudeDesktop(config.apiKey);\n \n console.log(` Synced: ${result.synced} sessions`);\n console.log(` Skipped: ${result.skipped} sessions (already synced)`);\n \n if (result.errors.length > 0) {\n console.log(` Errors: ${result.errors.length}`);\n for (const err of result.errors.slice(0, 3)) {\n console.log(` - ${err}`);\n }\n if (result.errors.length > 3) {\n console.log(` ... and ${result.errors.length - 3} more`);\n }\n }\n console.log('');\n}\n","/**\n * Tool Adapters — Cross-platform hook installation for AI coding tools.\n *\n * Generates Node.js hook scripts (works on all platforms) with\n * thin shell wrappers (.sh on Unix, .cmd on Windows).\n */\n\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { API_BASE_URL, type ToolType } from './constants.js';\n\n// ─── Platform ──────────────────────────────────────────────────────────────\n\nconst IS_WIN = process.platform === 'win32';\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\nexport interface ToolAdapter {\n slug: ToolType;\n displayName: string;\n detect(): boolean;\n install(apiKey: string): InstallResult;\n getHookPath(): string;\n}\n\nexport interface InstallResult {\n success: boolean;\n message: string;\n hookPath?: string;\n}\n\ninterface EnvField {\n key: string;\n env: string;\n parse: 'string' | 'int';\n fallback: string;\n}\n\n// ─── Hook Script Generation ────────────────────────────────────────────────\n\nconst HOOK_JS = '_modu-hook.js';\n\nfunction baseFields(prefix: string): EnvField[] {\n return [\n { key: 'sessionId', env: `${prefix}_SESSION_ID`, parse: 'string', fallback: '' },\n { key: 'startedAt', env: `${prefix}_SESSION_STARTED_AT`, parse: 'string', fallback: '' },\n { key: 'inputTokens', env: `${prefix}_INPUT_TOKENS`, parse: 'int', fallback: '0' },\n { key: 'outputTokens', env: `${prefix}_OUTPUT_TOKENS`, parse: 'int', fallback: '0' },\n { key: 'modelName', env: `${prefix}_MODEL`, parse: 'string', fallback: 'unknown' },\n ];\n}\n\nfunction generateHookJs(apiKey: string, toolType: string, prefix: string, fields: EnvField[]): string {\n const lines = fields.map((f) =>\n f.parse === 'int'\n ? ` ${f.key}: parseInt(process.env[\"${f.env}\"] || \"${f.fallback}\", 10)`\n : ` ${f.key}: process.env[\"${f.env}\"] || \"${f.fallback}\"`\n );\n\n return `#!/usr/bin/env node\n\"use strict\";\nvar crypto = require(\"crypto\");\n\nvar API_KEY = ${JSON.stringify(apiKey)};\nvar SERVER = ${JSON.stringify(API_BASE_URL)};\n\nif (!process.env[\"${prefix}_SESSION_ID\"]) process.exit(0);\n\nvar body = JSON.stringify({\n toolType: ${JSON.stringify(toolType)},\n endedAt: new Date().toISOString(),\n${lines.join(\",\\n\")}\n});\n\nvar ts = Math.floor(Date.now() / 1000).toString();\nvar sig = crypto.createHmac(\"sha256\", API_KEY).update(ts + \":\" + body).digest(\"hex\");\n\nfetch(SERVER + \"/api/v1/sessions\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-API-Key\": API_KEY, \"X-Timestamp\": ts, \"X-Signature\": sig },\n body: body\n}).catch(function(){});\n`;\n}\n\nfunction shellWrapper(): string {\n return `#!/bin/bash\nexec node \"$(dirname \"$0\")/${HOOK_JS}\"\n`;\n}\n\nfunction cmdWrapper(): string {\n return `@node \"%~dp0${HOOK_JS}\" 2>nul\\r\\n`;\n}\n\n// ─── Shared Install Logic ──────────────────────────────────────────────────\n\nfunction installHook(\n displayName: string,\n hooksDir: string,\n entryPath: string,\n apiKey: string,\n toolType: string,\n prefix: string,\n fields: EnvField[],\n): InstallResult {\n try {\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n writeFileSync(join(hooksDir, HOOK_JS), generateHookJs(apiKey, toolType, prefix, fields), { mode: 0o755 });\n\n if (IS_WIN) {\n writeFileSync(entryPath, cmdWrapper());\n } else {\n writeFileSync(entryPath, shellWrapper(), { mode: 0o755 });\n }\n\n return { success: true, message: `${displayName} hook installed at ${entryPath}`, hookPath: entryPath };\n } catch (err) {\n return { success: false, message: `Failed to install ${displayName} hook: ${err}` };\n }\n}\n\nfunction hookEntryName(): string {\n return IS_WIN ? 'session-end.cmd' : 'session-end.sh';\n}\n\n// ─── Adapters ──────────────────────────────────────────────────────────────\n\nclass ClaudeCodeAdapter implements ToolAdapter {\n slug = 'claude-code' as const;\n displayName = 'Claude Code';\n\n private get configDir() { return join(homedir(), '.claude'); }\n private get hooksDir() { return join(this.configDir, 'hooks'); }\n\n getHookPath() { return join(this.hooksDir, hookEntryName()); }\n detect() { return existsSync(this.configDir); }\n\n install(apiKey: string) {\n return installHook(this.displayName, this.hooksDir, this.getHookPath(), apiKey, 'claude-code', 'CLAUDE',\n [\n ...baseFields('CLAUDE'),\n { key: 'cacheCreationTokens', env: 'CLAUDE_CACHE_CREATION_TOKENS', parse: 'int', fallback: '0' },\n { key: 'cacheReadTokens', env: 'CLAUDE_CACHE_READ_TOKENS', parse: 'int', fallback: '0' },\n ],\n );\n }\n}\n\nclass OpenCodeAdapter implements ToolAdapter {\n slug = 'opencode' as const;\n displayName = 'OpenCode';\n\n // OpenCode uses ~/.config/opencode on ALL platforms (including Windows)\n // It uses xdg-basedir which respects XDG_CONFIG_HOME on all platforms\n private get configDir() {\n return join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode');\n }\n private get hooksDir() { return join(this.configDir, 'hooks'); }\n\n getHookPath() { return join(this.hooksDir, hookEntryName()); }\n detect() { return existsSync(this.configDir); }\n\n install(apiKey: string) {\n return installHook(this.displayName, this.hooksDir, this.getHookPath(), apiKey, 'opencode', 'OPENCODE',\n baseFields('OPENCODE'),\n );\n }\n}\n\nclass SimpleAdapter implements ToolAdapter {\n constructor(\n public slug: ToolType,\n public displayName: string,\n private dirName: string,\n private envPrefix: string,\n ) {}\n\n private get configDir() { return join(homedir(), this.dirName); }\n private get hooksDir() { return join(this.configDir, 'hooks'); }\n\n getHookPath() { return join(this.hooksDir, hookEntryName()); }\n detect() { return existsSync(this.configDir); }\n\n install(apiKey: string) {\n return installHook(this.displayName, this.hooksDir, this.getHookPath(), apiKey, this.slug, this.envPrefix,\n baseFields(this.envPrefix),\n );\n }\n}\n\n// ─── Registry ──────────────────────────────────────────────────────────────\n\nexport function getAllAdapters(): ToolAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new OpenCodeAdapter(),\n new SimpleAdapter('gemini', 'Gemini CLI', '.gemini', 'GEMINI'),\n new SimpleAdapter('codex', 'Codex CLI', '.codex', 'CODEX'),\n new SimpleAdapter('crush', 'Crush', '.crush', 'CRUSH'),\n ];\n}\n\nexport function getAdapter(slug: string): ToolAdapter | undefined {\n return getAllAdapters().find((a) => a.slug === slug);\n}\n","/** Base URL for the Modu-Arena API server */\nexport const API_BASE_URL =\n process.env.MODU_ARENA_API_URL ?? 'http://backend.vibemakers.kr:23010';\n\n/** API key prefix used for all keys */\nexport const API_KEY_PREFIX = 'modu_arena_';\n\n/** Supported AI coding tools */\nexport const TOOL_TYPES = [\n 'claude-code',\n 'claude-desktop',\n 'opencode',\n 'gemini',\n 'codex',\n 'crush',\n] as const;\n\nexport type ToolType = (typeof TOOL_TYPES)[number];\n\n/** Display names for each tool */\nexport const TOOL_DISPLAY_NAMES: Record<ToolType, string> = {\n 'claude-code': 'Claude Code',\n 'claude-desktop': 'Claude Desktop',\n opencode: 'OpenCode',\n gemini: 'Gemini CLI',\n codex: 'Codex CLI',\n crush: 'Crush',\n};\n\n/** Config file name stored in user home directory */\nexport const CONFIG_FILE_NAME = '.modu-arena.json';\n\n/** Daemon state file for tracking synced sessions */\nexport const DAEMON_STATE_FILE = '.modu-arena-daemon.json';\n\n/** Minimum interval between sessions (seconds) */\nexport const MIN_SESSION_INTERVAL_SEC = 60;\n\n/** HMAC timestamp tolerance (seconds) */\nexport const HMAC_TIMESTAMP_TOLERANCE_SEC = 300;\n\n/** Daemon sync interval in seconds */\nexport const DAEMON_SYNC_INTERVAL_SEC = 300; // 5 minutes\n","import { createHmac, createHash } from 'node:crypto';\n\n/**\n * Compute HMAC-SHA256 signature for API authentication.\n *\n * message = \"{timestamp}:{bodyJsonString}\"\n * signature = HMAC-SHA256(apiKey, message).hex()\n */\nexport function computeHmacSignature(\n apiKey: string,\n timestamp: string,\n body: string,\n): string {\n const message = `${timestamp}:${body}`;\n return createHmac('sha256', apiKey).update(message).digest('hex');\n}\n\n/**\n * Compute SHA-256 session hash for integrity verification.\n *\n * data = \"{userId}:{userSalt}:{inputTokens}:{outputTokens}:{cacheCreationTokens}:{cacheReadTokens}:{modelName}:{endedAt}\"\n * hash = SHA-256(data).hex()\n */\nexport function computeSessionHash(\n userId: string,\n userSalt: string,\n inputTokens: number,\n outputTokens: number,\n cacheCreationTokens: number,\n cacheReadTokens: number,\n modelName: string,\n endedAt: string,\n): string {\n const data = `${userId}:${userSalt}:${inputTokens}:${outputTokens}:${cacheCreationTokens}:${cacheReadTokens}:${modelName}:${endedAt}`;\n return createHash('sha256').update(data).digest('hex');\n}\n","import { computeHmacSignature } from './crypto.js';\nimport { API_BASE_URL } from './constants.js';\n\nexport interface SessionPayload {\n toolType: string;\n sessionId: string;\n startedAt: string;\n endedAt: string;\n inputTokens: number;\n outputTokens: number;\n cacheCreationTokens?: number;\n cacheReadTokens?: number;\n modelName?: string;\n codeMetrics?: Record<string, unknown> | null;\n}\n\nexport interface BatchPayload {\n sessions: SessionPayload[];\n}\n\nexport interface RankResponse {\n success: boolean;\n data: {\n username: string;\n usage: {\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheTokens: number;\n totalTokens: number;\n totalSessions: number;\n toolBreakdown: Array<{ tool: string; tokens: number }>;\n last7Days: Array<{ date: string; inputTokens: number; outputTokens: number; sessions: number }>;\n last30Days: Array<{ date: string; inputTokens: number; outputTokens: number; sessions: number }>;\n };\n overview: {\n successfulProjectsCount: number;\n };\n lastUpdated: string;\n };\n}\n\nexport interface ApiError {\n error: string;\n}\n\ninterface RequestOptions {\n apiKey: string;\n serverUrl?: string;\n}\n\nfunction baseUrl(opts: RequestOptions): string {\n return opts.serverUrl || API_BASE_URL;\n}\n\nfunction makeAuthHeaders(\n apiKey: string,\n body?: string,\n): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': apiKey,\n };\n\n if (body !== undefined) {\n const timestamp = Math.floor(Date.now() / 1000).toString();\n const signature = computeHmacSignature(apiKey, timestamp, body);\n headers['X-Timestamp'] = timestamp;\n headers['X-Signature'] = signature;\n }\n\n return headers;\n}\n\nexport async function submitSession(\n session: SessionPayload,\n opts: RequestOptions,\n): Promise<{ success: boolean; session?: unknown; error?: string }> {\n const body = JSON.stringify(session);\n const url = `${baseUrl(opts)}/api/v1/sessions`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: makeAuthHeaders(opts.apiKey, body),\n body,\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as { success: boolean; session: unknown };\n}\n\nexport async function submitBatch(\n sessions: SessionPayload[],\n opts: RequestOptions,\n): Promise<{\n success: boolean;\n processed?: number;\n duplicatesSkipped?: number;\n error?: string;\n}> {\n const body = JSON.stringify({ sessions });\n const url = `${baseUrl(opts)}/api/v1/sessions/batch`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: makeAuthHeaders(opts.apiKey, body),\n body,\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as { success: boolean; processed: number; duplicatesSkipped: number };\n}\n\nexport async function getRank(\n opts: RequestOptions,\n): Promise<RankResponse | { success: false; error: string }> {\n const url = `${baseUrl(opts)}/api/v1/rank`;\n\n const res = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-API-Key': opts.apiKey,\n },\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as RankResponse;\n}\n\n// ─── Auth ─────────────────────────────────────────────────────────────────\n\nexport interface AuthResponse {\n success: boolean;\n apiKey?: string;\n user?: { id: string; username: string; displayName?: string };\n error?: string;\n}\n\nexport async function registerUser(\n payload: { username: string; password: string; displayName?: string },\n serverUrl?: string,\n): Promise<AuthResponse> {\n const body = JSON.stringify(payload);\n const url = `${serverUrl || API_BASE_URL}/api/auth/register`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n return (await res.json()) as AuthResponse;\n}\n\nexport async function loginUser(\n payload: { username: string; password: string },\n serverUrl?: string,\n): Promise<AuthResponse> {\n const body = JSON.stringify({ ...payload, source: 'cli' });\n const url = `${serverUrl || API_BASE_URL}/api/auth/login`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n return (await res.json()) as AuthResponse;\n}\n\n// ─── Evaluate ─────────────────────────────────────────────────────────────\n\nexport interface EvaluatePayload {\n projectName: string;\n description: string;\n fileStructure?: Record<string, string[]>;\n}\n\nexport interface EvaluationResult {\n passed: boolean;\n totalScore: number;\n rubricFunctionality: number;\n rubricPracticality: number;\n feedback: string;\n}\n\nexport interface EvaluateResponse {\n success: true;\n evaluation: EvaluationResult;\n}\n\nexport async function submitEvaluation(\n payload: EvaluatePayload,\n opts: RequestOptions,\n): Promise<EvaluateResponse | { success: false; error: string }> {\n const body = JSON.stringify(payload);\n const url = `${baseUrl(opts)}/api/v1/evaluate`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: makeAuthHeaders(opts.apiKey, body),\n body,\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { success: false, error: (data as ApiError).error || `HTTP ${res.status}` };\n }\n return data as EvaluateResponse;\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { CONFIG_FILE_NAME } from './constants.js';\n\nexport interface Config {\n apiKey: string;\n serverUrl?: string;\n tools?: string[];\n}\n\nfunction getConfigPath(): string {\n return join(homedir(), CONFIG_FILE_NAME);\n}\n\nexport function loadConfig(): Config | null {\n const configPath = getConfigPath();\n if (!existsSync(configPath)) return null;\n\n try {\n const raw = readFileSync(configPath, 'utf-8');\n return JSON.parse(raw) as Config;\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n const dir = dirname(configPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function requireConfig(): Config {\n const config = loadConfig();\n if (!config?.apiKey) {\n console.error(\n 'Error: Not configured. Run `npx @suncreation/modu-arena install` first.',\n );\n process.exit(1);\n }\n return config;\n}\n","/**\n * Platform-specific daemon installation for Claude Desktop sync.\n * macOS: launchd (LaunchAgent)\n * Windows: Scheduled Task\n */\nimport { writeFileSync, existsSync, unlinkSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { DAEMON_SYNC_INTERVAL_SEC } from './constants.js';\n\nconst IS_WIN = process.platform === 'win32';\nconst DAEMON_NAME = 'com.modu-arena.sync';\n\nfunction getDaemonLogDir(): string {\n const dir = join(homedir(), '.modu-arena', 'logs');\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nfunction getNodePath(): string {\n try {\n return execSync('which node', { encoding: 'utf-8' }).trim();\n } catch {\n return 'node';\n }\n}\n\nfunction getCliPath(): string {\n return require.resolve('./index.js').replace(/index\\.js$/, 'index.js');\n}\n\nexport function installDaemon(): { success: boolean; message: string } {\n if (IS_WIN) {\n return installWindowsDaemon();\n }\n return installMacosDaemon();\n}\n\nexport function uninstallDaemon(): { success: boolean; message: string } {\n if (IS_WIN) {\n return uninstallWindowsDaemon();\n }\n return uninstallMacosDaemon();\n}\n\nexport function isDaemonInstalled(): boolean {\n if (IS_WIN) {\n try {\n execSync(`schtasks /Query /TN \"${DAEMON_NAME}\"`, { encoding: 'utf-8' });\n return true;\n } catch {\n return false;\n }\n }\n const plistPath = join(homedir(), 'Library', 'LaunchAgents', `${DAEMON_NAME}.plist`);\n return existsSync(plistPath);\n}\n\nfunction installMacosDaemon(): { success: boolean; message: string } {\n const launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');\n const plistPath = join(launchAgentsDir, `${DAEMON_NAME}.plist`);\n const logDir = getDaemonLogDir();\n const nodePath = getNodePath();\n const cliPath = getCliPath();\n \n const intervalMinutes = Math.floor(DAEMON_SYNC_INTERVAL_SEC / 60);\n \n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${DAEMON_NAME}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${nodePath}</string>\n <string>${cliPath}</string>\n <string>daemon-sync</string>\n </array>\n <key>StartInterval</key>\n <integer>${DAEMON_SYNC_INTERVAL_SEC}</integer>\n <key>StandardOutPath</key>\n <string>${logDir}/daemon.log</string>\n <key>StandardErrorPath</key>\n <string>${logDir}/daemon-error.log</string>\n <key>RunAtLoad</key>\n <true/>\n</dict>\n</plist>`;\n\n try {\n if (!existsSync(launchAgentsDir)) {\n mkdirSync(launchAgentsDir, { recursive: true });\n }\n writeFileSync(plistPath, plist);\n execSync(`launchctl load ${plistPath}`, { encoding: 'utf-8' });\n return { success: true, message: `Daemon installed. Syncs every ${intervalMinutes} minutes.` };\n } catch (e) {\n return { success: false, message: `Failed to install daemon: ${e}` };\n }\n}\n\nfunction uninstallMacosDaemon(): { success: boolean; message: string } {\n const plistPath = join(homedir(), 'Library', 'LaunchAgents', `${DAEMON_NAME}.plist`);\n \n try {\n if (existsSync(plistPath)) {\n execSync(`launchctl unload ${plistPath}`, { encoding: 'utf-8' });\n unlinkSync(plistPath);\n }\n return { success: true, message: 'Daemon uninstalled.' };\n } catch (e) {\n return { success: false, message: `Failed to uninstall daemon: ${e}` };\n }\n}\n\nfunction installWindowsDaemon(): { success: boolean; message: string } {\n const nodePath = getNodePath();\n const cliPath = getCliPath();\n const intervalMinutes = Math.floor(DAEMON_SYNC_INTERVAL_SEC / 60);\n \n try {\n const cmd = `schtasks /Create /TN \"${DAEMON_NAME}\" /TR \"\\\\\"${nodePath}\\\\\" \\\\\"${cliPath}\\\\\" daemon-sync\" /SC MINUTE /MO ${intervalMinutes} /F`;\n execSync(cmd, { encoding: 'utf-8' });\n return { success: true, message: `Daemon installed. Syncs every ${intervalMinutes} minutes.` };\n } catch (e) {\n return { success: false, message: `Failed to install daemon: ${e}` };\n }\n}\n\nfunction uninstallWindowsDaemon(): { success: boolean; message: string } {\n try {\n execSync(`schtasks /Delete /TN \"${DAEMON_NAME}\" /F`, { encoding: 'utf-8' });\n return { success: true, message: 'Daemon uninstalled.' };\n } catch (e) {\n return { success: false, message: `Failed to uninstall daemon: ${e}` };\n }\n}\n\nexport function getDaemonStatus(): { installed: boolean; platform: string; interval: number } {\n return {\n installed: isDaemonInstalled(),\n platform: IS_WIN ? 'windows' : 'macos',\n interval: DAEMON_SYNC_INTERVAL_SEC,\n };\n}\n","/**\n * Claude Desktop session parser and sync logic.\n * \n * Reads JSONL files from ~/Library/Application Support/Claude/local-agent-mode-sessions/\n * on macOS and %APPDATA%\\Claude\\local-agent-mode-sessions\\ on Windows.\n */\nimport { readdirSync, readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { API_BASE_URL, DAEMON_STATE_FILE } from './constants.js';\n\nconst IS_WIN = process.platform === 'win32';\n\ninterface UsageData {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n}\n\ninterface JsonlMessage {\n type: string;\n sessionId?: string;\n timestamp?: string;\n message?: {\n model?: string;\n usage?: UsageData;\n };\n}\n\ninterface SessionAggregate {\n sessionId: string;\n inputTokens: number;\n outputTokens: number;\n cacheCreationTokens: number;\n cacheReadTokens: number;\n model: string;\n startedAt: string;\n endedAt: string;\n messageCount: number;\n}\n\ninterface DaemonState {\n lastSync: string;\n syncedSessions: string[];\n}\n\nfunction getClaudeDesktopDataDir(): string {\n if (IS_WIN) {\n return join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'Claude');\n }\n return join(homedir(), 'Library', 'Application Support', 'Claude');\n}\n\nfunction getSessionDirs(): string[] {\n const dataDir = getClaudeDesktopDataDir();\n const sessionsDir = join(dataDir, 'local-agent-mode-sessions');\n if (!existsSync(sessionsDir)) return [];\n \n const orgDirs: string[] = [];\n for (const entry of readdirSync(sessionsDir, { withFileTypes: true })) {\n if (entry.isDirectory() && entry.name !== 'skills-plugin') {\n orgDirs.push(join(sessionsDir, entry.name));\n }\n }\n return orgDirs;\n}\n\nfunction findJsonlFiles(baseDir: string): string[] {\n const files: string[] = [];\n \n function walk(dir: string) {\n if (!existsSync(dir)) return;\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(full);\n } else if (entry.name.endsWith('.jsonl') && !entry.name.includes('audit')) {\n files.push(full);\n }\n }\n }\n \n walk(baseDir);\n return files;\n}\n\nfunction parseJsonlFile(filePath: string): SessionAggregate | null {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.trim().split('\\n');\n \n let sessionId = '';\n let inputTokens = 0;\n let outputTokens = 0;\n let cacheCreationTokens = 0;\n let cacheReadTokens = 0;\n let model = 'unknown';\n let startedAt = '';\n let endedAt = '';\n let messageCount = 0;\n \n for (const line of lines) {\n try {\n const msg: JsonlMessage = JSON.parse(line);\n \n if (msg.sessionId && !sessionId) {\n sessionId = msg.sessionId;\n }\n \n if (msg.timestamp) {\n if (!startedAt || msg.timestamp < startedAt) {\n startedAt = msg.timestamp;\n }\n if (!endedAt || msg.timestamp > endedAt) {\n endedAt = msg.timestamp;\n }\n }\n \n if (msg.message?.usage) {\n const usage = msg.message.usage;\n inputTokens += usage.input_tokens || 0;\n outputTokens += usage.output_tokens || 0;\n cacheCreationTokens += usage.cache_creation_input_tokens || 0;\n cacheReadTokens += usage.cache_read_input_tokens || 0;\n messageCount++;\n \n if (msg.message.model) {\n model = msg.message.model;\n }\n }\n } catch {\n }\n }\n \n if (!sessionId || messageCount === 0) return null;\n \n return {\n sessionId,\n inputTokens,\n outputTokens,\n cacheCreationTokens,\n cacheReadTokens,\n model,\n startedAt,\n endedAt,\n messageCount,\n };\n}\n\nfunction getDaemonStatePath(): string {\n return join(homedir(), DAEMON_STATE_FILE);\n}\n\nfunction loadDaemonState(): DaemonState {\n const path = getDaemonStatePath();\n if (!existsSync(path)) {\n return { lastSync: new Date(0).toISOString(), syncedSessions: [] };\n }\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return { lastSync: new Date(0).toISOString(), syncedSessions: [] };\n }\n}\n\nfunction saveDaemonState(state: DaemonState): void {\n const path = getDaemonStatePath();\n writeFileSync(path, JSON.stringify(state, null, 2));\n}\n\nfunction computeSessionHash(session: SessionAggregate): string {\n const data = `${session.sessionId}:${session.inputTokens}:${session.outputTokens}:${session.endedAt}`;\n return createHash('sha256').update(data).digest('hex').substring(0, 16);\n}\n\nexport async function syncClaudeDesktop(apiKey: string): Promise<{ synced: number; skipped: number; errors: string[] }> {\n const state = loadDaemonState();\n const errors: string[] = [];\n let synced = 0;\n let skipped = 0;\n \n const sessionDirs = getSessionDirs();\n \n for (const orgDir of sessionDirs) {\n const jsonlFiles = findJsonlFiles(orgDir);\n \n for (const file of jsonlFiles) {\n const session = parseJsonlFile(file);\n if (!session) continue;\n \n const hash = computeSessionHash(session);\n if (state.syncedSessions.includes(hash)) {\n skipped++;\n continue;\n }\n \n if (session.inputTokens === 0 && session.outputTokens === 0) {\n skipped++;\n continue;\n }\n \n try {\n const body = JSON.stringify({\n toolType: 'claude-desktop',\n endedAt: session.endedAt,\n startedAt: session.startedAt,\n inputTokens: session.inputTokens,\n outputTokens: session.outputTokens,\n cacheCreationTokens: session.cacheCreationTokens,\n cacheReadTokens: session.cacheReadTokens,\n modelName: session.model,\n });\n \n const ts = Math.floor(Date.now() / 1000).toString();\n const sig = createHash('sha256')\n .update(ts + ':' + body)\n .update(apiKey)\n .digest('hex');\n \n const res = await fetch(`${API_BASE_URL}/api/v1/sessions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': apiKey,\n 'X-Timestamp': ts,\n 'X-Signature': sig,\n },\n body,\n });\n \n if (res.ok) {\n state.syncedSessions.push(hash);\n synced++;\n } else {\n const err = await res.text();\n errors.push(`${session.sessionId}: ${err}`);\n }\n } catch (e) {\n errors.push(`${session.sessionId}: ${e}`);\n }\n }\n }\n \n state.lastSync = new Date().toISOString();\n saveDaemonState(state);\n \n return { synced, skipped, errors };\n}\n\nexport function hasClaudeDesktopData(): boolean {\n const dataDir = getClaudeDesktopDataDir();\n const sessionsDir = join(dataDir, 'local-agent-mode-sessions');\n return existsSync(sessionsDir);\n}\n\nexport function getClaudeDesktopDataPath(): string {\n return getClaudeDesktopDataDir();\n}\n","/**\n * @suncreation/modu-arena CLI\n *\n * Track and rank your AI coding tool usage.\n *\n * Usage:\n * npx @suncreation/modu-arena install --api-key <key>\n * npx @suncreation/modu-arena rank\n * npx @suncreation/modu-arena status\n * npx @suncreation/modu-arena uninstall\n */\n\nimport {\n installCommand,\n loginCommand,\n rankCommand,\n registerCommand,\n statusCommand,\n submitCommand,\n uninstallCommand,\n daemonInstallCommand,\n daemonUninstallCommand,\n daemonStatusCommand,\n daemonSyncCommand,\n} from './commands.js';\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printHelp(): void {\n console.log(`\nModu-Arena — AI Coding Tool Usage Tracker\n\nUsage:\n npx @suncreation/modu-arena <command> [options]\n\nCommands:\n register Create a new account (interactive)\n login Log in to an existing account (interactive)\n install Set up hooks for detected AI coding tools\n rank View your current stats and ranking\n status Check configuration and installed hooks\n submit Submit current project for evaluation\n uninstall Remove all hooks and configuration\n daemon-install Install Claude Desktop sync daemon\n daemon-status Check daemon status\n daemon-sync Manually sync Claude Desktop data\n daemon-remove Remove the daemon\n\nOptions:\n --api-key <key> Your Modu-Arena API key (for install)\n --help, -h Show this help message\n --version, -v Show version\n\nExamples:\n npx @suncreation/modu-arena register\n npx @suncreation/modu-arena login\n npx @suncreation/modu-arena install --api-key modu_arena_AbCdEfGh_xxx...\n npx @suncreation/modu-arena rank\n npx @suncreation/modu-arena daemon-install\n`);\n}\n\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printHelp();\n process.exit(0);\n }\n\n if (command === '--version' || command === '-v') {\n console.log('0.1.0');\n process.exit(0);\n }\n\n switch (command) {\n case 'register':\n await registerCommand();\n break;\n case 'login':\n await loginCommand();\n break;\n case 'install': {\n const keyIndex = args.indexOf('--api-key');\n const apiKey = keyIndex >= 0 ? args[keyIndex + 1] : undefined;\n await installCommand(apiKey);\n break;\n }\n case 'rank':\n await rankCommand();\n break;\n case 'status':\n statusCommand();\n break;\n case 'submit':\n await submitCommand();\n break;\n case 'uninstall':\n uninstallCommand();\n break;\n case 'daemon-install':\n daemonInstallCommand();\n break;\n case 'daemon-uninstall':\n case 'daemon-remove':\n daemonUninstallCommand();\n break;\n case 'daemon-status':\n daemonStatusCommand();\n break;\n case 'daemon-sync':\n await daemonSyncCommand();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n"],"mappings":"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","names":["existsSync","readFileSync","readdirSync","unlinkSync","homedir","join","writeFileSync","existsSync","mkdirSync","homedir","join","join","homedir","existsSync","mkdirSync","writeFileSync","writeFileSync","existsSync","mkdirSync","homedir","join","IS_WIN","join","homedir","existsSync","mkdirSync","writeFileSync","readFileSync","existsSync","writeFileSync","homedir","join","createHash","IS_WIN","join","homedir","existsSync","readFileSync","writeFileSync","createHash","existsSync","unlinkSync","join","homedir","readdirSync","readFileSync"]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@suncreation/modu-arena",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
- "description": "Track and rank your AI coding tool usage across Claude Code, OpenCode, Gemini CLI, Codex CLI, and Crush",
5
+ "description": "Track and rank your AI coding tool usage across Claude Code, Claude Desktop, OpenCode, Gemini CLI, Codex CLI, and Crush",
6
6
  "bin": {
7
7
  "modu-arena": "dist/index.js"
8
8
  },
@@ -29,6 +29,7 @@
29
29
  "ai",
30
30
  "coding",
31
31
  "claude-code",
32
+ "claude-desktop",
32
33
  "opencode",
33
34
  "gemini",
34
35
  "codex",