@myhpmp/cli 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +156 -0
  3. package/README.md +156 -0
  4. package/dist/adapter/claude-adapter.d.ts +9 -0
  5. package/dist/adapter/claude-adapter.js +36 -0
  6. package/dist/adapter/claude-adapter.js.map +1 -0
  7. package/dist/adapter/codex-adapter.d.ts +14 -0
  8. package/dist/adapter/codex-adapter.js +84 -0
  9. package/dist/adapter/codex-adapter.js.map +1 -0
  10. package/dist/adapter/index.d.ts +6 -0
  11. package/dist/adapter/index.js +18 -0
  12. package/dist/adapter/index.js.map +1 -0
  13. package/dist/adapter/provider.d.ts +23 -0
  14. package/dist/adapter/provider.js +6 -0
  15. package/dist/adapter/provider.js.map +1 -0
  16. package/dist/auth/auth-manager.d.ts +15 -0
  17. package/dist/auth/auth-manager.js +34 -0
  18. package/dist/auth/auth-manager.js.map +1 -0
  19. package/dist/auth/oauth.d.ts +6 -0
  20. package/dist/auth/oauth.js +113 -0
  21. package/dist/auth/oauth.js.map +1 -0
  22. package/dist/cli.d.ts +4 -0
  23. package/dist/cli.js +46 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/commands/init.d.ts +1 -0
  26. package/dist/commands/init.js +116 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/locale.d.ts +1 -0
  29. package/dist/commands/locale.js +40 -0
  30. package/dist/commands/locale.js.map +1 -0
  31. package/dist/commands/setup.d.ts +1 -0
  32. package/dist/commands/setup.js +137 -0
  33. package/dist/commands/setup.js.map +1 -0
  34. package/dist/commands/statusline-toggle.d.ts +1 -0
  35. package/dist/commands/statusline-toggle.js +61 -0
  36. package/dist/commands/statusline-toggle.js.map +1 -0
  37. package/dist/commands/sync.d.ts +1 -0
  38. package/dist/commands/sync.js +29 -0
  39. package/dist/commands/sync.js.map +1 -0
  40. package/dist/commands/usage.d.ts +1 -0
  41. package/dist/commands/usage.js +56 -0
  42. package/dist/commands/usage.js.map +1 -0
  43. package/dist/config.d.ts +2 -0
  44. package/dist/config.js +35 -0
  45. package/dist/config.js.map +1 -0
  46. package/dist/core/exp-calculator.d.ts +1 -0
  47. package/dist/core/exp-calculator.js +3 -0
  48. package/dist/core/exp-calculator.js.map +1 -0
  49. package/dist/core/level-system.d.ts +2 -0
  50. package/dist/core/level-system.js +3 -0
  51. package/dist/core/level-system.js.map +1 -0
  52. package/dist/core/stats-aggregator.d.ts +5 -0
  53. package/dist/core/stats-aggregator.js +35 -0
  54. package/dist/core/stats-aggregator.js.map +1 -0
  55. package/dist/data/auto-sync.d.ts +8 -0
  56. package/dist/data/auto-sync.js +75 -0
  57. package/dist/data/auto-sync.js.map +1 -0
  58. package/dist/data/claude-usage.d.ts +13 -0
  59. package/dist/data/claude-usage.js +82 -0
  60. package/dist/data/claude-usage.js.map +1 -0
  61. package/dist/data/exp-logger.d.ts +1 -0
  62. package/dist/data/exp-logger.js +43 -0
  63. package/dist/data/exp-logger.js.map +1 -0
  64. package/dist/data/local-store.d.ts +16 -0
  65. package/dist/data/local-store.js +41 -0
  66. package/dist/data/local-store.js.map +1 -0
  67. package/dist/data/pending-exp.d.ts +8 -0
  68. package/dist/data/pending-exp.js +42 -0
  69. package/dist/data/pending-exp.js.map +1 -0
  70. package/dist/data/providers/db-interface.d.ts +11 -0
  71. package/dist/data/providers/db-interface.js +2 -0
  72. package/dist/data/providers/db-interface.js.map +1 -0
  73. package/dist/data/providers/supabase.d.ts +16 -0
  74. package/dist/data/providers/supabase.js +84 -0
  75. package/dist/data/providers/supabase.js.map +1 -0
  76. package/dist/data/sync-engine.d.ts +15 -0
  77. package/dist/data/sync-engine.js +67 -0
  78. package/dist/data/sync-engine.js.map +1 -0
  79. package/dist/display/detail-view.d.ts +23 -0
  80. package/dist/display/detail-view.js +36 -0
  81. package/dist/display/detail-view.js.map +1 -0
  82. package/dist/display/status-line.d.ts +12 -0
  83. package/dist/display/status-line.js +17 -0
  84. package/dist/display/status-line.js.map +1 -0
  85. package/dist/hooks/claude/post-tool-use.d.ts +1 -0
  86. package/dist/hooks/claude/post-tool-use.js +82 -0
  87. package/dist/hooks/claude/post-tool-use.js.map +1 -0
  88. package/dist/hooks/claude/session-end.d.ts +1 -0
  89. package/dist/hooks/claude/session-end.js +23 -0
  90. package/dist/hooks/claude/session-end.js.map +1 -0
  91. package/dist/hooks/claude/status-line-updater.d.ts +1 -0
  92. package/dist/hooks/claude/status-line-updater.js +48 -0
  93. package/dist/hooks/claude/status-line-updater.js.map +1 -0
  94. package/dist/hooks/codex/session-end.d.ts +1 -0
  95. package/dist/hooks/codex/session-end.js +40 -0
  96. package/dist/hooks/codex/session-end.js.map +1 -0
  97. package/dist/hooks/common/session-start.d.ts +1 -0
  98. package/dist/hooks/common/session-start.js +34 -0
  99. package/dist/hooks/common/session-start.js.map +1 -0
  100. package/dist/i18n/en.d.ts +26 -0
  101. package/dist/i18n/en.js +26 -0
  102. package/dist/i18n/en.js.map +1 -0
  103. package/dist/i18n/index.d.ts +7 -0
  104. package/dist/i18n/index.js +30 -0
  105. package/dist/i18n/index.js.map +1 -0
  106. package/dist/i18n/ko.d.ts +26 -0
  107. package/dist/i18n/ko.js +26 -0
  108. package/dist/i18n/ko.js.map +1 -0
  109. package/dist/index.d.ts +1 -0
  110. package/dist/index.js +2 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/statusline.d.ts +2 -0
  113. package/dist/statusline.js +103 -0
  114. package/dist/statusline.js.map +1 -0
  115. package/package.json +55 -0
@@ -0,0 +1,56 @@
1
+ import { LocalStore } from '../data/local-store.js';
2
+ import { getLevelInfo, getTierForLevel, getStars, getTierEmoji, getTierTitle } from '../core/level-system.js';
3
+ import { renderDetailView } from '../display/detail-view.js';
4
+ import { detectLocale } from '../i18n/index.js';
5
+ import { AuthManager } from '../auth/auth-manager.js';
6
+ import { fetchClaudeUsage, utilizationToPercent, resetsAtToMinutes } from '../data/claude-usage.js';
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+ const DATA_DIR = path.join(os.homedir(), '.myhpmp');
10
+ async function main() {
11
+ const authManager = new AuthManager(DATA_DIR);
12
+ const store = new LocalStore(DATA_DIR);
13
+ const stats = await store.load();
14
+ let locale;
15
+ try {
16
+ const config = await authManager.loadConfig();
17
+ locale = config.locale ?? detectLocale();
18
+ }
19
+ catch {
20
+ locale = detectLocale();
21
+ }
22
+ const levelInfo = getLevelInfo(stats.totalExp);
23
+ const tier = getTierForLevel(levelInfo.level);
24
+ const titleEmoji = getTierEmoji(tier.tierIndex);
25
+ const titleName = getTierTitle(tier.tierIndex, locale);
26
+ // Fetch real Claude usage data
27
+ const usage = await fetchClaudeUsage();
28
+ const hpPercent = usage ? utilizationToPercent(usage.fiveHour.utilization) : 100;
29
+ const mpPercent = usage ? utilizationToPercent(usage.sevenDay.utilization) : 100;
30
+ const resetMinutes = usage ? resetsAtToMinutes(usage.fiveHour.resetsAt) : 0;
31
+ const output = renderDetailView({
32
+ titleEmoji,
33
+ titleName,
34
+ level: levelInfo.level,
35
+ stars: getStars(levelInfo.level),
36
+ hpPercent,
37
+ hpUsed: hpPercent,
38
+ hpTotal: 100,
39
+ resetMinutes,
40
+ mpPercent,
41
+ mpUsed: mpPercent,
42
+ mpTotal: 100,
43
+ ctxPercent: 0, // context not available via API
44
+ ctxUsed: 0,
45
+ ctxTotal: 1000000,
46
+ expCurrent: levelInfo.currentLevelExp,
47
+ expNeeded: levelInfo.expForNextLevel,
48
+ nextLevel: levelInfo.level + 1,
49
+ totalExp: stats.totalExp,
50
+ totalSessions: stats.totalSessions,
51
+ streakDays: stats.streakDays,
52
+ }, locale);
53
+ console.log(output);
54
+ }
55
+ main().catch(console.error);
56
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/commands/usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const SUPABASE_URL: string;
2
+ export declare const SUPABASE_ANON_KEY: string;
package/dist/config.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Supabase configuration.
3
+ * Values are injected at build time via scripts/inject-config.
4
+ * Fallback: environment variables or ~/.myhpmp/config.json
5
+ */
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import os from 'node:os';
9
+ const CONFIG_PATH = path.join(os.homedir(), '.myhpmp', 'config.json');
10
+ // https://olbgoszrgheqxncnnlou.supabase.co and eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9sYmdvc3pyZ2hlcXhuY25ubG91Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzU0MjMyNzQsImV4cCI6MjA5MDk5OTI3NH0.EuHkGMGoU8XX0bQwyvDYb2WZ4OQ0ieryysvkbt3BQKE are replaced at build time
11
+ const INJECTED_URL = 'https://olbgoszrgheqxncnnlou.supabase.co';
12
+ const INJECTED_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9sYmdvc3pyZ2hlcXhuY25ubG91Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzU0MjMyNzQsImV4cCI6MjA5MDk5OTI3NH0.EuHkGMGoU8XX0bQwyvDYb2WZ4OQ0ieryysvkbt3BQKE';
13
+ function loadLocalConfig() {
14
+ try {
15
+ const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
16
+ return JSON.parse(raw);
17
+ }
18
+ catch {
19
+ return {};
20
+ }
21
+ }
22
+ function resolve(injected, envVar, configKey) {
23
+ // 1. Injected at build time (npm package)
24
+ if (injected && !injected.startsWith('__INJECT_'))
25
+ return injected;
26
+ // 2. Environment variable
27
+ if (process.env[envVar])
28
+ return process.env[envVar];
29
+ // 3. Local config file
30
+ const local = loadLocalConfig();
31
+ return local[configKey] || '';
32
+ }
33
+ export const SUPABASE_URL = resolve(INJECTED_URL, 'SUPABASE_URL', 'supabaseUrl');
34
+ export const SUPABASE_ANON_KEY = resolve(INJECTED_KEY, 'SUPABASE_ANON_KEY', 'supabaseAnonKey');
35
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAEtE,sFAAsF;AACtF,MAAM,YAAY,GAAG,yBAAyB,CAAC;AAC/C,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAEpD,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,QAAgB,EAAE,MAAc,EAAE,SAAiB;IAClE,0CAA0C;IAC1C,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,QAAQ,CAAC;IACnE,0BAA0B;IAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;IACrD,uBAAuB;IACvB,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,OAAO,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,cAAc,EAAE,aAAa,CAAC,CAAC;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export { calcTokenExp, calcSessionExp, calcStreakBonus, calcWeeklyBonus } from '@myhpmp/core';
@@ -0,0 +1,3 @@
1
+ // Re-export from @myhpmp/core (single source of truth)
2
+ export { calcTokenExp, calcSessionExp, calcStreakBonus, calcWeeklyBonus } from '@myhpmp/core';
3
+ //# sourceMappingURL=exp-calculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exp-calculator.js","sourceRoot":"","sources":["../../src/core/exp-calculator.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { getLevelInfo, getTierForLevel, getStars, TIERS, getTierEmoji, getTierTitle } from '@myhpmp/core';
2
+ export type { Tier, LevelInfo } from '@myhpmp/core';
@@ -0,0 +1,3 @@
1
+ // Re-export from @myhpmp/core (single source of truth)
2
+ export { getLevelInfo, getTierForLevel, getStars, TIERS, getTierEmoji, getTierTitle } from '@myhpmp/core';
3
+ //# sourceMappingURL=level-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"level-system.js","sourceRoot":"","sources":["../../src/core/level-system.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function computeHP(remainingTokens: number, totalTokens: number): number;
2
+ export declare function computeMP(remainingWeekly: number, totalWeekly: number): number;
3
+ export declare function computeCTX(usedContext: number, maxContext: number): number;
4
+ export declare function computeStreak(currentStreak: number, lastActiveDate: string | null): number;
5
+ export declare function formatTime(minutes: number): string;
@@ -0,0 +1,35 @@
1
+ export function computeHP(remainingTokens, totalTokens) {
2
+ if (totalTokens === 0)
3
+ return 0;
4
+ return Math.round((remainingTokens / totalTokens) * 100);
5
+ }
6
+ export function computeMP(remainingWeekly, totalWeekly) {
7
+ if (totalWeekly === 0)
8
+ return 0;
9
+ return Math.round((remainingWeekly / totalWeekly) * 100);
10
+ }
11
+ export function computeCTX(usedContext, maxContext) {
12
+ if (maxContext === 0)
13
+ return 0;
14
+ return Math.round((usedContext / maxContext) * 100);
15
+ }
16
+ export function computeStreak(currentStreak, lastActiveDate) {
17
+ if (!lastActiveDate)
18
+ return 1;
19
+ const today = new Date();
20
+ today.setHours(0, 0, 0, 0);
21
+ const [y, m, d] = lastActiveDate.split('-').map(Number);
22
+ const lastActive = new Date(y, m - 1, d);
23
+ const diffDays = Math.floor((today.getTime() - lastActive.getTime()) / (1000 * 60 * 60 * 24));
24
+ if (diffDays === 0)
25
+ return currentStreak;
26
+ if (diffDays === 1)
27
+ return currentStreak + 1;
28
+ return 1;
29
+ }
30
+ export function formatTime(minutes) {
31
+ const h = Math.floor(minutes / 60);
32
+ const m = minutes % 60;
33
+ return `${h}h${m}m`;
34
+ }
35
+ //# sourceMappingURL=stats-aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats-aggregator.js","sourceRoot":"","sources":["../../src/core/stats-aggregator.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,eAAuB,EAAE,WAAmB;IACpE,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,eAAuB,EAAE,WAAmB;IACpE,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,UAAkB;IAChE,IAAI,UAAU,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,aAAqB,EAAE,cAA6B;IAChF,IAAI,CAAC,cAAc;QAAE,OAAO,CAAC,CAAC;IAE9B,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;IACzB,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAE9F,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,aAAa,CAAC;IACzC,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,aAAa,GAAG,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,OAAO,GAAG,EAAE,CAAC;IACvB,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AACtB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sync unconditionally (used by session-start, session-end, manual sync).
3
+ */
4
+ export declare function autoSync(): Promise<void>;
5
+ /**
6
+ * Sync only if 5+ minutes have passed since last sync (used by post-tool-use).
7
+ */
8
+ export declare function autoSyncIfDue(): Promise<void>;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Auto-sync helper: flushes pending exp queue + pulls server state.
3
+ * Server is the source of truth for totalExp (computed by DB trigger).
4
+ * Silent fail — sync is optional and should never break hooks.
5
+ */
6
+ import fs from 'node:fs/promises';
7
+ import { LocalStore } from './local-store.js';
8
+ import { SyncEngine } from './sync-engine.js';
9
+ import { AuthManager } from '../auth/auth-manager.js';
10
+ import os from 'node:os';
11
+ import path from 'node:path';
12
+ const DATA_DIR = path.join(os.homedir(), '.myhpmp');
13
+ const LAST_SYNC_PATH = path.join(DATA_DIR, 'last-sync.json');
14
+ const SYNC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
15
+ async function shouldSync() {
16
+ try {
17
+ const raw = await fs.readFile(LAST_SYNC_PATH, 'utf-8');
18
+ const { timestamp } = JSON.parse(raw);
19
+ return Date.now() - timestamp >= SYNC_INTERVAL_MS;
20
+ }
21
+ catch {
22
+ return true; // No record = should sync
23
+ }
24
+ }
25
+ async function markSynced() {
26
+ await fs.mkdir(DATA_DIR, { recursive: true });
27
+ await fs.writeFile(LAST_SYNC_PATH, JSON.stringify({ timestamp: Date.now() }), 'utf-8');
28
+ }
29
+ /**
30
+ * Sync unconditionally (used by session-start, session-end, manual sync).
31
+ */
32
+ export async function autoSync() {
33
+ try {
34
+ const authManager = new AuthManager(DATA_DIR);
35
+ if (!(await authManager.isAuthenticated()))
36
+ return;
37
+ const config = await authManager.loadConfig();
38
+ const store = new LocalStore(DATA_DIR);
39
+ const { SUPABASE_URL, SUPABASE_ANON_KEY } = await import('../config.js');
40
+ const { SupabaseProvider } = await import('./providers/supabase.js');
41
+ const provider = new SupabaseProvider(SUPABASE_URL, SUPABASE_ANON_KEY);
42
+ try {
43
+ await provider.setSession(config.accessToken, config.refreshToken);
44
+ }
45
+ catch {
46
+ // Token expired — attempt refresh
47
+ const refreshed = await provider.refreshSession(config.refreshToken);
48
+ if (refreshed) {
49
+ await authManager.saveConfig({
50
+ ...config,
51
+ accessToken: refreshed.accessToken,
52
+ refreshToken: refreshed.refreshToken,
53
+ });
54
+ }
55
+ else {
56
+ return; // Cannot recover — skip sync silently
57
+ }
58
+ }
59
+ const engine = new SyncEngine(store, provider);
60
+ await engine.sync(config.userId);
61
+ await markSynced();
62
+ }
63
+ catch {
64
+ // Silent fail — sync is best-effort
65
+ }
66
+ }
67
+ /**
68
+ * Sync only if 5+ minutes have passed since last sync (used by post-tool-use).
69
+ */
70
+ export async function autoSyncIfDue() {
71
+ if (await shouldSync()) {
72
+ await autoSync();
73
+ }
74
+ }
75
+ //# sourceMappingURL=auto-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-sync.js","sourceRoot":"","sources":["../../src/data/auto-sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface ClaudeUsageData {
2
+ fiveHour: {
3
+ utilization: number;
4
+ resetsAt: string;
5
+ };
6
+ sevenDay: {
7
+ utilization: number;
8
+ resetsAt: string;
9
+ };
10
+ }
11
+ export declare function fetchClaudeUsage(): Promise<ClaudeUsageData | null>;
12
+ export declare function utilizationToPercent(utilization: number): number;
13
+ export declare function resetsAtToMinutes(resetsAt: string): number;
@@ -0,0 +1,82 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const CREDENTIALS_PATH = path.join(os.homedir(), '.claude', '.credentials.json');
5
+ const CACHE_PATH = path.join(os.homedir(), '.claude', 'usage_cache.json');
6
+ const CACHE_TTL_MS = 60_000; // 60 seconds
7
+ const API_URL = 'https://api.anthropic.com/api/oauth/usage';
8
+ async function readOAuthToken() {
9
+ try {
10
+ const raw = await fs.readFile(CREDENTIALS_PATH, 'utf-8');
11
+ const creds = JSON.parse(raw);
12
+ return creds.claudeAiOauth?.accessToken ?? null;
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ async function readCache() {
19
+ try {
20
+ const raw = await fs.readFile(CACHE_PATH, 'utf-8');
21
+ const cached = JSON.parse(raw);
22
+ if (typeof cached.fetchedAt !== 'number' || !Number.isFinite(cached.fetchedAt))
23
+ return null;
24
+ if (Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
25
+ return cached.data;
26
+ }
27
+ return null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ async function writeCache(data) {
34
+ const cached = { data, fetchedAt: Date.now() };
35
+ await fs.writeFile(CACHE_PATH, JSON.stringify(cached, null, 2), 'utf-8');
36
+ }
37
+ export async function fetchClaudeUsage() {
38
+ // Try cache first
39
+ const cached = await readCache();
40
+ if (cached)
41
+ return cached;
42
+ const token = await readOAuthToken();
43
+ if (!token)
44
+ return null;
45
+ try {
46
+ const res = await fetch(API_URL, {
47
+ headers: {
48
+ 'Authorization': `Bearer ${token}`,
49
+ 'anthropic-beta': 'oauth-2025-04-20',
50
+ 'Content-Type': 'application/json',
51
+ 'User-Agent': 'myhpmp/1.0',
52
+ },
53
+ });
54
+ if (!res.ok)
55
+ return null;
56
+ const body = await res.json();
57
+ const data = {
58
+ fiveHour: {
59
+ utilization: body.five_hour?.utilization ?? 0,
60
+ resetsAt: body.five_hour?.resets_at ?? new Date().toISOString(),
61
+ },
62
+ sevenDay: {
63
+ utilization: body.seven_day?.utilization ?? 0,
64
+ resetsAt: body.seven_day?.resets_at ?? new Date().toISOString(),
65
+ },
66
+ };
67
+ await writeCache(data);
68
+ return data;
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ export function utilizationToPercent(utilization) {
75
+ // utilization is already a percentage (0-100), we want remaining %
76
+ return Math.max(0, Math.round(100 - utilization));
77
+ }
78
+ export function resetsAtToMinutes(resetsAt) {
79
+ const diff = new Date(resetsAt).getTime() - Date.now();
80
+ return Math.max(0, Math.round(diff / 60_000));
81
+ }
82
+ //# sourceMappingURL=claude-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-usage.js","sourceRoot":"","sources":["../../src/data/claude-usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function logExp(amount: number, reason: string, metadata?: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Logs EXP gains to exp_history table in Supabase.
3
+ * On failure, queues locally for retry on next sync.
4
+ */
5
+ import { AuthManager } from '../auth/auth-manager.js';
6
+ import { enqueue } from './pending-exp.js';
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+ const DATA_DIR = path.join(os.homedir(), '.myhpmp');
10
+ export async function logExp(amount, reason, metadata) {
11
+ if (amount <= 0)
12
+ return;
13
+ try {
14
+ const authManager = new AuthManager(DATA_DIR);
15
+ if (!(await authManager.isAuthenticated()))
16
+ return;
17
+ const config = await authManager.loadConfig();
18
+ const { SUPABASE_URL, SUPABASE_ANON_KEY } = await import('../config.js');
19
+ const { SupabaseProvider } = await import('./providers/supabase.js');
20
+ const provider = new SupabaseProvider(SUPABASE_URL, SUPABASE_ANON_KEY);
21
+ try {
22
+ await provider.setSession(config.accessToken, config.refreshToken);
23
+ }
24
+ catch {
25
+ const refreshed = await provider.refreshSession(config.refreshToken);
26
+ if (!refreshed) {
27
+ await enqueue({ amount, reason, timestamp: new Date().toISOString() });
28
+ return;
29
+ }
30
+ await authManager.saveConfig({
31
+ ...config,
32
+ accessToken: refreshed.accessToken,
33
+ refreshToken: refreshed.refreshToken,
34
+ });
35
+ }
36
+ await provider.insertExpHistory(config.userId, { amount, reason, metadata });
37
+ }
38
+ catch {
39
+ // INSERT failed (network, rate limit, etc.) — queue for retry
40
+ await enqueue({ amount, reason, timestamp: new Date().toISOString() }).catch(() => { });
41
+ }
42
+ }
43
+ //# sourceMappingURL=exp-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exp-logger.js","sourceRoot":"","sources":["../../src/data/exp-logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,QAAkC;IAC7F,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO;IAExB,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,CAAC,CAAC,MAAM,WAAW,CAAC,eAAe,EAAE,CAAC;YAAE,OAAO;QAEnD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,CAAC;QAC9C,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACzE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QACrE,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;QAEvE,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACrE,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YACD,MAAM,WAAW,CAAC,UAAU,CAAC;gBAC3B,GAAG,MAAM;gBACT,WAAW,EAAE,SAAS,CAAC,WAAW;gBAClC,YAAY,EAAE,SAAS,CAAC,YAAY;aACrC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;QAC9D,MAAM,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACzF,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface UserStats {
2
+ totalExp: number;
3
+ level: number;
4
+ totalSessions: number;
5
+ streakDays: number;
6
+ lastActiveDate: string | null;
7
+ weeklyExpBonusClaimed: boolean;
8
+ updatedAt: string;
9
+ }
10
+ export declare class LocalStore {
11
+ private filePath;
12
+ constructor(dir: string);
13
+ exists(): Promise<boolean>;
14
+ load(): Promise<UserStats>;
15
+ save(stats: UserStats): Promise<void>;
16
+ }
@@ -0,0 +1,41 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const DEFAULT_STATS = {
4
+ totalExp: 0,
5
+ level: 1,
6
+ totalSessions: 0,
7
+ streakDays: 0,
8
+ lastActiveDate: null,
9
+ weeklyExpBonusClaimed: false,
10
+ updatedAt: new Date().toISOString(),
11
+ };
12
+ export class LocalStore {
13
+ filePath;
14
+ constructor(dir) {
15
+ this.filePath = path.join(dir, 'data.json');
16
+ }
17
+ async exists() {
18
+ try {
19
+ await fs.access(this.filePath);
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ async load() {
27
+ try {
28
+ const raw = await fs.readFile(this.filePath, 'utf-8');
29
+ return { ...DEFAULT_STATS, ...JSON.parse(raw) };
30
+ }
31
+ catch {
32
+ return { ...DEFAULT_STATS };
33
+ }
34
+ }
35
+ async save(stats) {
36
+ const dir = path.dirname(this.filePath);
37
+ await fs.mkdir(dir, { recursive: true });
38
+ await fs.writeFile(this.filePath, JSON.stringify(stats, null, 2), 'utf-8');
39
+ }
40
+ }
41
+ //# sourceMappingURL=local-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-store.js","sourceRoot":"","sources":["../../src/data/local-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAY7B,MAAM,aAAa,GAAc;IAC/B,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC;IACR,aAAa,EAAE,CAAC;IAChB,UAAU,EAAE,CAAC;IACb,cAAc,EAAE,IAAI;IACpB,qBAAqB,EAAE,KAAK;IAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;CACpC,CAAC;AAEF,MAAM,OAAO,UAAU;IACb,QAAQ,CAAS;IAEzB,YAAY,GAAW;QACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACtD,OAAO,EAAE,GAAG,aAAa,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAgB;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7E,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export interface PendingExpEntry {
2
+ amount: number;
3
+ reason: string;
4
+ timestamp: string;
5
+ }
6
+ export declare function loadQueue(): Promise<PendingExpEntry[]>;
7
+ export declare function enqueue(entry: PendingExpEntry): Promise<void>;
8
+ export declare function saveQueue(queue: PendingExpEntry[]): Promise<void>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Local queue for exp_history records that failed to INSERT.
3
+ * Flushed on next successful sync.
4
+ */
5
+ import fs from 'node:fs/promises';
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+ const DATA_DIR = path.join(os.homedir(), '.myhpmp');
9
+ const QUEUE_PATH = path.join(DATA_DIR, 'pending-exp.json');
10
+ export async function loadQueue() {
11
+ try {
12
+ const raw = await fs.readFile(QUEUE_PATH, 'utf-8');
13
+ return JSON.parse(raw);
14
+ }
15
+ catch {
16
+ return [];
17
+ }
18
+ }
19
+ const MAX_QUEUE_SIZE = 1000;
20
+ export async function enqueue(entry) {
21
+ const queue = await loadQueue();
22
+ if (queue.length >= MAX_QUEUE_SIZE) {
23
+ queue.shift(); // Drop oldest entry
24
+ }
25
+ queue.push(entry);
26
+ await fs.mkdir(DATA_DIR, { recursive: true });
27
+ await fs.writeFile(QUEUE_PATH, JSON.stringify(queue), 'utf-8');
28
+ }
29
+ export async function saveQueue(queue) {
30
+ if (queue.length === 0) {
31
+ try {
32
+ await fs.unlink(QUEUE_PATH);
33
+ }
34
+ catch {
35
+ // File doesn't exist, fine
36
+ }
37
+ return;
38
+ }
39
+ await fs.mkdir(DATA_DIR, { recursive: true });
40
+ await fs.writeFile(QUEUE_PATH, JSON.stringify(queue), 'utf-8');
41
+ }
42
+ //# sourceMappingURL=pending-exp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-exp.js","sourceRoot":"","sources":["../../src/data/pending-exp.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AACpD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;AAQ3D,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAsB;IAClD,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,IAAI,cAAc,EAAE,CAAC;QACnC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,oBAAoB;IACrC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAwB;IACtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { UserStats } from '../local-store.js';
2
+ export interface ExpHistoryEntry {
3
+ amount: number;
4
+ reason: string;
5
+ metadata?: Record<string, unknown>;
6
+ }
7
+ export interface DbProvider {
8
+ loadUserStats(userId: string): Promise<UserStats | null>;
9
+ saveUserStats(userId: string, stats: UserStats): Promise<void>;
10
+ insertExpHistory(userId: string, entry: ExpHistoryEntry): Promise<void>;
11
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=db-interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-interface.js","sourceRoot":"","sources":["../../../src/data/providers/db-interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import type { UserStats } from '../local-store.js';
3
+ import type { DbProvider, ExpHistoryEntry } from './db-interface.js';
4
+ export declare class SupabaseProvider implements DbProvider {
5
+ private client;
6
+ constructor(url: string, anonKey: string);
7
+ getClient(): SupabaseClient;
8
+ setSession(accessToken: string, refreshToken: string): Promise<void>;
9
+ refreshSession(refreshToken: string): Promise<{
10
+ accessToken: string;
11
+ refreshToken: string;
12
+ } | null>;
13
+ loadUserStats(userId: string): Promise<UserStats | null>;
14
+ saveUserStats(userId: string, stats: UserStats): Promise<void>;
15
+ insertExpHistory(userId: string, entry: ExpHistoryEntry): Promise<void>;
16
+ }