@suncreation/modu-arena 0.1.1 β†’ 0.1.2

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,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/commands.ts
4
+ import { createInterface } from "readline";
4
5
  import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync, unlinkSync } from "fs";
5
6
  import { homedir as homedir3 } from "os";
6
7
  import { basename, join as join3 } from "path";
@@ -11,7 +12,7 @@ import { homedir } from "os";
11
12
  import { join } from "path";
12
13
 
13
14
  // src/constants.ts
14
- var API_BASE_URL = process.env.MODU_ARENA_API_URL ?? "http://localhost:8989";
15
+ var API_BASE_URL = process.env.MODU_ARENA_API_URL ?? "http://backend.vibemakers.kr:23010";
15
16
  var TOOL_DISPLAY_NAMES = {
16
17
  "claude-code": "Claude Code",
17
18
  opencode: "OpenCode",
@@ -413,6 +414,26 @@ async function getRank(opts) {
413
414
  }
414
415
  return data;
415
416
  }
417
+ async function registerUser(payload, serverUrl) {
418
+ const body = JSON.stringify(payload);
419
+ const url = `${serverUrl || API_BASE_URL}/api/auth/register`;
420
+ const res = await fetch(url, {
421
+ method: "POST",
422
+ headers: { "Content-Type": "application/json" },
423
+ body
424
+ });
425
+ return await res.json();
426
+ }
427
+ async function loginUser(payload, serverUrl) {
428
+ const body = JSON.stringify({ ...payload, source: "cli" });
429
+ const url = `${serverUrl || API_BASE_URL}/api/auth/login`;
430
+ const res = await fetch(url, {
431
+ method: "POST",
432
+ headers: { "Content-Type": "application/json" },
433
+ body
434
+ });
435
+ return await res.json();
436
+ }
416
437
  async function submitEvaluation(payload, opts) {
417
438
  const body = JSON.stringify(payload);
418
439
  const url = `${baseUrl(opts)}/api/v1/evaluate`;
@@ -463,6 +484,89 @@ function requireConfig() {
463
484
  }
464
485
 
465
486
  // src/commands.ts
487
+ function prompt(question) {
488
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
489
+ return new Promise((resolve) => {
490
+ rl.question(question, (answer) => {
491
+ rl.close();
492
+ resolve(answer.trim());
493
+ });
494
+ });
495
+ }
496
+ async function registerCommand() {
497
+ console.log("\n\u{1F4DD} Modu-Arena \u2014 Register\n");
498
+ const username = await prompt(" Username (3-50 chars): ");
499
+ if (!username || username.length < 3 || username.length > 50) {
500
+ console.error("Error: Username must be between 3 and 50 characters.\n");
501
+ process.exit(1);
502
+ }
503
+ const password = await prompt(" Password (min 8 chars): ");
504
+ if (!password || password.length < 8) {
505
+ console.error("Error: Password must be at least 8 characters.\n");
506
+ process.exit(1);
507
+ }
508
+ const displayName = await prompt(" Display name (optional, press Enter to skip): ");
509
+ console.log("\n Registering...");
510
+ const existing = loadConfig();
511
+ const result = await registerUser(
512
+ { username, password, displayName: displayName || void 0 },
513
+ existing?.serverUrl
514
+ );
515
+ if (result.error) {
516
+ console.error(`
517
+ Error: ${result.error}
518
+ `);
519
+ process.exit(1);
520
+ }
521
+ if (!result.apiKey) {
522
+ console.error("\n Error: No API key returned from server.\n");
523
+ process.exit(1);
524
+ }
525
+ saveConfig({ apiKey: result.apiKey, serverUrl: existing?.serverUrl });
526
+ console.log("\n \u2713 Registration successful!");
527
+ console.log(` \u2713 API key saved to ~/.modu-arena.json`);
528
+ console.log(`
529
+ Username: ${result.user?.username}`);
530
+ console.log(` API Key: ${result.apiKey.slice(0, 20)}...${result.apiKey.slice(-4)}`);
531
+ console.log("\n \u26A0 Save your API key \u2014 it will not be shown again.\n");
532
+ console.log(" Installing hooks for detected AI coding tools...\n");
533
+ await installCommand(result.apiKey);
534
+ }
535
+ async function loginCommand() {
536
+ console.log("\n\u{1F511} Modu-Arena \u2014 Login\n");
537
+ const username = await prompt(" Username: ");
538
+ if (!username) {
539
+ console.error("Error: Username is required.\n");
540
+ process.exit(1);
541
+ }
542
+ const password = await prompt(" Password: ");
543
+ if (!password) {
544
+ console.error("Error: Password is required.\n");
545
+ process.exit(1);
546
+ }
547
+ console.log("\n Logging in...");
548
+ const existing = loadConfig();
549
+ const result = await loginUser({ username, password }, existing?.serverUrl);
550
+ if (result.error) {
551
+ console.error(`
552
+ Error: ${result.error}
553
+ `);
554
+ process.exit(1);
555
+ }
556
+ if (!result.apiKey) {
557
+ console.error("\n Error: No API key returned from server.\n");
558
+ process.exit(1);
559
+ }
560
+ saveConfig({ apiKey: result.apiKey, serverUrl: existing?.serverUrl });
561
+ console.log("\n \u2713 Login successful!");
562
+ console.log(` \u2713 API key saved to ~/.modu-arena.json`);
563
+ console.log(`
564
+ Username: ${result.user?.username}`);
565
+ console.log(` API Key: ${result.apiKey.slice(0, 20)}...${result.apiKey.slice(-4)}`);
566
+ console.log("\n \u26A0 A new API key was generated. Previous key is now invalid.\n");
567
+ console.log(" Reinstalling hooks with new API key...\n");
568
+ await installCommand(result.apiKey);
569
+ }
466
570
  async function installCommand(apiKey) {
467
571
  console.log("\n\u{1F527} Modu-Arena \u2014 AI Coding Tool Usage Tracker\n");
468
572
  const existing = loadConfig();
@@ -730,6 +834,8 @@ Usage:
730
834
  npx @suncreation/modu-arena <command> [options]
731
835
 
732
836
  Commands:
837
+ register Create a new account (interactive)
838
+ login Log in to an existing account (interactive)
733
839
  install Set up hooks for detected AI coding tools
734
840
  rank View your current stats and ranking
735
841
  status Check configuration and installed hooks
@@ -742,9 +848,10 @@ Options:
742
848
  --version, -v Show version
743
849
 
744
850
  Examples:
851
+ npx @suncreation/modu-arena register
852
+ npx @suncreation/modu-arena login
745
853
  npx @suncreation/modu-arena install --api-key modu_arena_AbCdEfGh_xxx...
746
854
  npx @suncreation/modu-arena rank
747
- npx @suncreation/modu-arena status
748
855
  `);
749
856
  }
750
857
  async function main() {
@@ -757,6 +864,12 @@ async function main() {
757
864
  process.exit(0);
758
865
  }
759
866
  switch (command) {
867
+ case "register":
868
+ await registerCommand();
869
+ break;
870
+ case "login":
871
+ await loginCommand();
872
+ break;
760
873
  case "install": {
761
874
  const keyIndex = args.indexOf("--api-key");
762
875
  const apiKey = keyIndex >= 0 ? args[keyIndex + 1] : void 0;
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 { 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, submitEvaluation } from './api.js';\nimport { loadConfig, saveConfig, requireConfig } from './config.js';\nimport { API_BASE_URL, TOOL_DISPLAY_NAMES, type ToolType } from './constants.js';\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 β€” Hook installation for each supported AI coding tool.\n *\n * Each adapter knows how to:\n * 1. Detect if the tool is installed\n * 2. Install a session-end hook to capture token usage\n * 3. Parse session data from tool-specific formats\n */\n\nimport { existsSync, readFileSync, 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\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\n// ─── Claude Code Adapter ───────────────────────────────────────────────────\n\nclass ClaudeCodeAdapter implements ToolAdapter {\n slug = 'claude-code' as const;\n displayName = 'Claude Code';\n\n private get configDir(): string {\n return join(homedir(), '.claude');\n }\n\n private get hooksDir(): string {\n return join(this.configDir, 'hooks');\n }\n\n getHookPath(): string {\n return join(this.hooksDir, 'session-end.sh');\n }\n\n detect(): boolean {\n // Check for ~/.claude directory or claude binary\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n if (!existsSync(this.hooksDir)) {\n mkdirSync(this.hooksDir, { recursive: true });\n }\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Claude Code hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Claude Code hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Claude Code session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\n# Claude Code passes session data via environment variables\n# This hook fires at session end\nif [ -n \"$CLAUDE_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"claude-code\",\n \"sessionId\": \"$CLAUDE_SESSION_ID\",\n \"startedAt\": \"$CLAUDE_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${CLAUDE_INPUT_TOKENS:-0},\n \"outputTokens\": \\${CLAUDE_OUTPUT_TOKENS:-0},\n \"cacheCreationTokens\": \\${CLAUDE_CACHE_CREATION_TOKENS:-0},\n \"cacheReadTokens\": \\${CLAUDE_CACHE_READ_TOKENS:-0},\n \"modelName\": \"\\${CLAUDE_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── OpenCode Adapter ──────────────────────────────────────────────────────\n\nclass OpenCodeAdapter implements ToolAdapter {\n slug = 'opencode' as const;\n displayName = 'OpenCode';\n\n private get configDir(): string {\n return join(homedir(), '.opencode');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `OpenCode hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install OpenCode hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: OpenCode session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$OPENCODE_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"opencode\",\n \"sessionId\": \"$OPENCODE_SESSION_ID\",\n \"startedAt\": \"$OPENCODE_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${OPENCODE_INPUT_TOKENS:-0},\n \"outputTokens\": \\${OPENCODE_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${OPENCODE_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Gemini CLI Adapter ────────────────────────────────────────────────────\n\nclass GeminiAdapter implements ToolAdapter {\n slug = 'gemini' as const;\n displayName = 'Gemini CLI';\n\n private get configDir(): string {\n return join(homedir(), '.gemini');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Gemini CLI hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Gemini CLI hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Gemini CLI session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$GEMINI_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"gemini\",\n \"sessionId\": \"$GEMINI_SESSION_ID\",\n \"startedAt\": \"$GEMINI_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${GEMINI_INPUT_TOKENS:-0},\n \"outputTokens\": \\${GEMINI_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${GEMINI_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Codex CLI Adapter ─────────────────────────────────────────────────────\n\nclass CodexAdapter implements ToolAdapter {\n slug = 'codex' as const;\n displayName = 'Codex CLI';\n\n private get configDir(): string {\n return join(homedir(), '.codex');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Codex CLI hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Codex CLI hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Codex CLI session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$CODEX_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"codex\",\n \"sessionId\": \"$CODEX_SESSION_ID\",\n \"startedAt\": \"$CODEX_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${CODEX_INPUT_TOKENS:-0},\n \"outputTokens\": \\${CODEX_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${CODEX_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Crush Adapter ─────────────────────────────────────────────────────────\n\nclass CrushAdapter implements ToolAdapter {\n slug = 'crush' as const;\n displayName = 'Crush';\n\n private get configDir(): string {\n return join(homedir(), '.crush');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Crush hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Crush hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Crush session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$CRUSH_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"crush\",\n \"sessionId\": \"$CRUSH_SESSION_ID\",\n \"startedAt\": \"$CRUSH_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${CRUSH_INPUT_TOKENS:-0},\n \"outputTokens\": \\${CRUSH_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${CRUSH_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Registry ──────────────────────────────────────────────────────────────\n\nexport function getAllAdapters(): ToolAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new OpenCodeAdapter(),\n new GeminiAdapter(),\n new CodexAdapter(),\n new CrushAdapter(),\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://localhost:8989';\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// ─── 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 rankCommand,\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 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 install --api-key modu_arena_AbCdEfGh_xxx...\n npx @suncreation/modu-arena rank\n npx @suncreation/modu-arena status\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 '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","readFileSync","writeFileSync","existsSync","mkdirSync","homedir","join","join","homedir","existsSync","readFileSync","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/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\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 prompt(' 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 prompt(' 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 β€” Hook installation for each supported AI coding tool.\n *\n * Each adapter knows how to:\n * 1. Detect if the tool is installed\n * 2. Install a session-end hook to capture token usage\n * 3. Parse session data from tool-specific formats\n */\n\nimport { existsSync, readFileSync, 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\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\n// ─── Claude Code Adapter ───────────────────────────────────────────────────\n\nclass ClaudeCodeAdapter implements ToolAdapter {\n slug = 'claude-code' as const;\n displayName = 'Claude Code';\n\n private get configDir(): string {\n return join(homedir(), '.claude');\n }\n\n private get hooksDir(): string {\n return join(this.configDir, 'hooks');\n }\n\n getHookPath(): string {\n return join(this.hooksDir, 'session-end.sh');\n }\n\n detect(): boolean {\n // Check for ~/.claude directory or claude binary\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n if (!existsSync(this.hooksDir)) {\n mkdirSync(this.hooksDir, { recursive: true });\n }\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Claude Code hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Claude Code hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Claude Code session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\n# Claude Code passes session data via environment variables\n# This hook fires at session end\nif [ -n \"$CLAUDE_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"claude-code\",\n \"sessionId\": \"$CLAUDE_SESSION_ID\",\n \"startedAt\": \"$CLAUDE_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${CLAUDE_INPUT_TOKENS:-0},\n \"outputTokens\": \\${CLAUDE_OUTPUT_TOKENS:-0},\n \"cacheCreationTokens\": \\${CLAUDE_CACHE_CREATION_TOKENS:-0},\n \"cacheReadTokens\": \\${CLAUDE_CACHE_READ_TOKENS:-0},\n \"modelName\": \"\\${CLAUDE_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── OpenCode Adapter ──────────────────────────────────────────────────────\n\nclass OpenCodeAdapter implements ToolAdapter {\n slug = 'opencode' as const;\n displayName = 'OpenCode';\n\n private get configDir(): string {\n return join(homedir(), '.opencode');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `OpenCode hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install OpenCode hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: OpenCode session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$OPENCODE_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"opencode\",\n \"sessionId\": \"$OPENCODE_SESSION_ID\",\n \"startedAt\": \"$OPENCODE_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${OPENCODE_INPUT_TOKENS:-0},\n \"outputTokens\": \\${OPENCODE_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${OPENCODE_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Gemini CLI Adapter ────────────────────────────────────────────────────\n\nclass GeminiAdapter implements ToolAdapter {\n slug = 'gemini' as const;\n displayName = 'Gemini CLI';\n\n private get configDir(): string {\n return join(homedir(), '.gemini');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Gemini CLI hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Gemini CLI hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Gemini CLI session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$GEMINI_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"gemini\",\n \"sessionId\": \"$GEMINI_SESSION_ID\",\n \"startedAt\": \"$GEMINI_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${GEMINI_INPUT_TOKENS:-0},\n \"outputTokens\": \\${GEMINI_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${GEMINI_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Codex CLI Adapter ─────────────────────────────────────────────────────\n\nclass CodexAdapter implements ToolAdapter {\n slug = 'codex' as const;\n displayName = 'Codex CLI';\n\n private get configDir(): string {\n return join(homedir(), '.codex');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Codex CLI hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Codex CLI hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Codex CLI session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$CODEX_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"codex\",\n \"sessionId\": \"$CODEX_SESSION_ID\",\n \"startedAt\": \"$CODEX_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${CODEX_INPUT_TOKENS:-0},\n \"outputTokens\": \\${CODEX_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${CODEX_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Crush Adapter ─────────────────────────────────────────────────────────\n\nclass CrushAdapter implements ToolAdapter {\n slug = 'crush' as const;\n displayName = 'Crush';\n\n private get configDir(): string {\n return join(homedir(), '.crush');\n }\n\n getHookPath(): string {\n return join(this.configDir, 'hooks', 'session-end.sh');\n }\n\n detect(): boolean {\n return existsSync(this.configDir);\n }\n\n install(apiKey: string): InstallResult {\n try {\n const hooksDir = join(this.configDir, 'hooks');\n if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });\n\n const hookScript = this.generateHookScript(apiKey);\n const hookPath = this.getHookPath();\n writeFileSync(hookPath, hookScript, { mode: 0o755 });\n\n return {\n success: true,\n message: `Crush hook installed at ${hookPath}`,\n hookPath,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to install Crush hook: ${err}`,\n };\n }\n }\n\n private generateHookScript(apiKey: string): string {\n return `#!/bin/bash\n# Modu-Arena: Crush session tracking hook\n# Auto-generated β€” do not edit manually\n\nMODU_API_KEY=\"${apiKey}\"\nMODU_SERVER=\"${API_BASE_URL}\"\n\nif [ -n \"$CRUSH_SESSION_ID\" ]; then\n SESSION_DATA=$(cat <<EOF\n{\n \"toolType\": \"crush\",\n \"sessionId\": \"$CRUSH_SESSION_ID\",\n \"startedAt\": \"$CRUSH_SESSION_STARTED_AT\",\n \"endedAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",\n \"inputTokens\": \\${CRUSH_INPUT_TOKENS:-0},\n \"outputTokens\": \\${CRUSH_OUTPUT_TOKENS:-0},\n \"modelName\": \"\\${CRUSH_MODEL:-unknown}\"\n}\nEOF\n)\n\n TIMESTAMP=$(date +%s)\n MESSAGE=\"\\${TIMESTAMP}:\\${SESSION_DATA}\"\n SIGNATURE=$(echo -n \"$MESSAGE\" | openssl dgst -sha256 -hmac \"$MODU_API_KEY\" | sed 's/.*= //')\n\n curl -s -X POST \"\\${MODU_SERVER}/api/v1/sessions\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"X-API-Key: \\${MODU_API_KEY}\" \\\\\n -H \"X-Timestamp: \\${TIMESTAMP}\" \\\\\n -H \"X-Signature: \\${SIGNATURE}\" \\\\\n -d \"\\${SESSION_DATA}\" > /dev/null 2>&1 &\nfi\n`;\n }\n}\n\n// ─── Registry ──────────────────────────────────────────────────────────────\n\nexport function getAllAdapters(): ToolAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new OpenCodeAdapter(),\n new GeminiAdapter(),\n new CodexAdapter(),\n new CrushAdapter(),\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","readFileSync","writeFileSync","existsSync","mkdirSync","homedir","join","join","homedir","existsSync","readFileSync","mkdirSync","writeFileSync","existsSync","join","homedir","readFileSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suncreation/modu-arena",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Track and rank your AI coding tool usage across Claude Code, OpenCode, Gemini CLI, Codex CLI, and Crush",
6
6
  "bin": {