claude-pet 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-pet might be problematic. Click here for more details.
- package/.claude/commands/feed.md +28 -0
- package/.claude/commands/name.md +28 -0
- package/.claude/commands/pet.md +29 -0
- package/.claude/commands/play.md +29 -0
- package/.claude/settings.local.json +41 -0
- package/.github/workflows/AGENTS.md +60 -0
- package/.github/workflows/build.yml +87 -0
- package/AGENTS.md +66 -0
- package/LICENSE +15 -0
- package/README.md +292 -0
- package/bin/claude-pet.js +42 -0
- package/build/AGENTS.md +50 -0
- package/build/dmg-background.png +0 -0
- package/build/entitlements.mac.plist +14 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/build/installerHeader.bmp +0 -0
- package/build/installerSidebar.bmp +0 -0
- package/build/tray-icon.png +0 -0
- package/dist/main/core/badge-manager.js +49 -0
- package/dist/main/core/badge-registry.js +72 -0
- package/dist/main/core/badge-triggers.js +45 -0
- package/dist/main/core/contextual-messages.js +372 -0
- package/dist/main/core/messages.js +440 -0
- package/dist/main/core/mood-engine.js +145 -0
- package/dist/main/core/pet-messages.js +612 -0
- package/dist/main/core/pet-state-engine.js +232 -0
- package/dist/main/core/quote-collection.js +60 -0
- package/dist/main/core/quote-registry.js +175 -0
- package/dist/main/core/quote-triggers.js +62 -0
- package/dist/main/core/usage-tracker.js +625 -0
- package/dist/main/main/auto-launch.js +39 -0
- package/dist/main/main/auto-updater.js +98 -0
- package/dist/main/main/event-watcher.js +174 -0
- package/dist/main/main/ipc-handlers.js +89 -0
- package/dist/main/main/main.js +422 -0
- package/dist/main/main/preload.js +93 -0
- package/dist/main/main/settings-window.js +49 -0
- package/dist/main/main/share-card.js +139 -0
- package/dist/main/main/skin-manager.js +118 -0
- package/dist/main/main/tray.js +88 -0
- package/dist/main/shared/i18n.js +392 -0
- package/dist/main/shared/types.js +25 -0
- package/dist/main/shared/utils.js +9 -0
- package/dist/renderer/assets/claude-pet.png +0 -0
- package/dist/renderer/assets/index-BMnMEuOf.js +9 -0
- package/dist/renderer/assets/index-qzlrlqpX.css +1 -0
- package/dist/renderer/index.html +30 -0
- package/dist/renderer/share-card-template/card.html +148 -0
- package/docs/AGENTS.md +42 -0
- package/docs/images/angry.png +0 -0
- package/docs/images/character.webp +0 -0
- package/docs/images/claude-mama.png +0 -0
- package/docs/images/happy.png +0 -0
- package/docs/images/proud.png +0 -0
- package/docs/images/share-card-example.png +0 -0
- package/docs/images/worried_1.png +0 -0
- package/docs/images/worried_2.png +0 -0
- package/docs/spritesheet-bugs.md +240 -0
- package/docs/superpowers/plans/2026-03-10-compact-widget.md +888 -0
- package/docs/superpowers/plans/2026-03-10-viral-features.md +1874 -0
- package/docs/superpowers/plans/2026-03-14-update-ux.md +362 -0
- package/docs/superpowers/plans/2026-03-14-v1.1-features.md +2139 -0
- package/docs/superpowers/specs/2026-03-10-compact-widget-design.md +150 -0
- package/docs/superpowers/specs/2026-03-10-viral-features-design.md +217 -0
- package/docs/superpowers/specs/2026-03-14-streak-calendar-design.md +26 -0
- package/docs/superpowers/specs/2026-03-14-update-ux-design.md +172 -0
- package/docs/superpowers/specs/2026-03-14-v1.1-features-design.md +342 -0
- package/electron-builder.yml +75 -0
- package/package.json +48 -0
- package/scripts/AGENTS.md +60 -0
- package/scripts/install.ps1 +47 -0
- package/scripts/install.sh +98 -0
- package/scripts/make-icon.js +119 -0
- package/scripts/notarize.js +18 -0
- package/src/AGENTS.md +47 -0
- package/src/core/AGENTS.md +58 -0
- package/src/core/__tests__/AGENTS.md +60 -0
- package/src/core/__tests__/badge-triggers.test.ts +83 -0
- package/src/core/__tests__/contextual-messages.test.ts +87 -0
- package/src/core/__tests__/pet-state-engine.test.ts +350 -0
- package/src/core/__tests__/quote-collection.test.ts +62 -0
- package/src/core/__tests__/quote-triggers.test.ts +110 -0
- package/src/core/badge-manager.ts +50 -0
- package/src/core/badge-registry.ts +71 -0
- package/src/core/badge-triggers.ts +41 -0
- package/src/core/contextual-messages.ts +381 -0
- package/src/core/pet-messages.ts +615 -0
- package/src/core/pet-state-engine.ts +272 -0
- package/src/core/quote-collection.ts +63 -0
- package/src/core/quote-registry.ts +181 -0
- package/src/core/quote-triggers.ts +64 -0
- package/src/core/usage-tracker.ts +680 -0
- package/src/main/AGENTS.md +70 -0
- package/src/main/auto-launch.ts +38 -0
- package/src/main/auto-updater.ts +106 -0
- package/src/main/event-watcher.ts +159 -0
- package/src/main/ipc-handlers.ts +107 -0
- package/src/main/main.ts +425 -0
- package/src/main/preload.ts +111 -0
- package/src/main/settings-window.ts +50 -0
- package/src/main/share-card.ts +153 -0
- package/src/main/skin-manager.ts +119 -0
- package/src/main/tray.ts +94 -0
- package/src/renderer/AGENTS.md +62 -0
- package/src/renderer/App.tsx +270 -0
- package/src/renderer/assets/claude-mama.png +0 -0
- package/src/renderer/assets/claude-pet.png +0 -0
- package/src/renderer/components/AGENTS.md +50 -0
- package/src/renderer/components/Character.tsx +327 -0
- package/src/renderer/components/SpeechBubble.tsx +182 -0
- package/src/renderer/components/UsageIndicator.tsx +268 -0
- package/src/renderer/electron.d.ts +34 -0
- package/src/renderer/hooks/AGENTS.md +55 -0
- package/src/renderer/hooks/usePetState.ts +59 -0
- package/src/renderer/hooks/useWidgetMode.ts +18 -0
- package/src/renderer/index.html +29 -0
- package/src/renderer/main.tsx +13 -0
- package/src/renderer/pages/AGENTS.md +53 -0
- package/src/renderer/pages/Collection.tsx +252 -0
- package/src/renderer/pages/Settings.tsx +815 -0
- package/src/renderer/share-card-template/card.html +148 -0
- package/src/renderer/styles/AGENTS.md +50 -0
- package/src/renderer/styles/styles.css +166 -0
- package/src/shared/AGENTS.md +48 -0
- package/src/shared/i18n.ts +395 -0
- package/src/shared/types.ts +163 -0
- package/src/shared/utils.ts +6 -0
- package/tsconfig.json +16 -0
- package/tsconfig.main.json +12 -0
- package/tsconfig.renderer.json +12 -0
- package/vite.config.ts +47 -0
- package/vitest.config.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 👩👦 Claude Mama
|
|
4
|
+
|
|
5
|
+
**Your API quota is rotting. Mom is disappointed.**
|
|
6
|
+
|
|
7
|
+
A desktop mascot that guilt-trips you into using your Claude Code tokens — just like a real Korean mom.
|
|
8
|
+
|
|
9
|
+
[](https://github.com/scm1400/claude-mama/actions/workflows/build.yml)
|
|
10
|
+
[](https://opensource.org/licenses/ISC)
|
|
11
|
+
[](https://www.electronjs.org/)
|
|
12
|
+
[](http://makeapullrequest.com)
|
|
13
|
+
|
|
14
|
+
<br/>
|
|
15
|
+
|
|
16
|
+
<img src="docs/images/claude-mama.png" width="100%" alt="Claude Mama character" />
|
|
17
|
+
|
|
18
|
+
<br/>
|
|
19
|
+
|
|
20
|
+
| Angry | Worried | Happy | Proud |
|
|
21
|
+
|:---:|:---:|:---:|:---:|
|
|
22
|
+
| 😡 "You haven't used any today?!" | 😟 "Everyone else is using theirs..." | 😊 "That's my kid!" | 🥹 "Mom's buying chicken tonight~" |
|
|
23
|
+
| < 15% usage | 15–50% usage | 50–85% usage | 85%+ usage |
|
|
24
|
+
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What is this?
|
|
30
|
+
|
|
31
|
+
Claude Mama is a tiny desktop widget that monitors your [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) API usage and reacts with the emotional range of a Korean mother who just found out you skipped dinner.
|
|
32
|
+
|
|
33
|
+
- **Using too little?** She's angry. She didn't raise you to waste a perfectly good API quota.
|
|
34
|
+
- **Using a moderate amount?** She's worried. The neighbors' kids are using more.
|
|
35
|
+
- **Using a healthy amount?** She's happy. Finally, some return on investment.
|
|
36
|
+
- **Maxing it out?** She's proud. Tears are streaming. Chicken is being ordered.
|
|
37
|
+
|
|
38
|
+
> *"Other moms worry their kids use too much computer. Claude Mama worries you don't use enough."*
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Real-time usage tracking** — Pulls 7-day and 5-hour utilization from the Anthropic OAuth API, with smart JSONL session fallback when rate limited
|
|
43
|
+
- **Pixel art character** — A pixel-art mama with curler hair and 6 mood expressions (angry, worried, happy, proud, confused, sleeping)
|
|
44
|
+
- **Guilt-powered messages** — Randomized mom-style messages that rotate every 2 minutes
|
|
45
|
+
- **Contextual messages** — Situation-aware messages for weekends, unused streaks, usage spikes, and reset warnings (200+ new localized messages across 4 languages)
|
|
46
|
+
- **5-hour burnout warning** — "Take a break~ You're almost at the limit!" (she cares, in her own way)
|
|
47
|
+
- **Achievement Badges** — Unlock 9 badges across Bronze, Silver, and Gold tiers for streaks, milestones, and achievements
|
|
48
|
+
- **Share Report Card** — Save a shareable PNG card with your current mood, usage stats, and reset countdown
|
|
49
|
+
- **Quote Collection (도감)** — Collect 86 unique mama quotes across 4 rarity tiers (Common, Rare, Legendary, Secret)
|
|
50
|
+
- **Custom Character Skins** — Upload your own character images with single, per-mood, or sprite sheet modes
|
|
51
|
+
- **Always on Top** — Toggle in Settings or tray menu (default ON) to keep mama visible above other windows
|
|
52
|
+
- **System tray** — Lives quietly in your taskbar, judging you silently
|
|
53
|
+
- **Settings panel** — Position, auto-start, language selection, collection viewer, and skin customization
|
|
54
|
+
- **4 languages** — 한국어, English, 日本語, 中文
|
|
55
|
+
- **Auto-start** — Boots with your OS so you can never escape mom's watchful eye
|
|
56
|
+
- **Auto-update** — Mom keeps herself up to date via GitHub Releases
|
|
57
|
+
|
|
58
|
+
### Share Report Card
|
|
59
|
+
|
|
60
|
+
Save your current mama status as a PNG image — perfect for sharing on social media.
|
|
61
|
+
|
|
62
|
+
<div align="center">
|
|
63
|
+
<img src="docs/images/share-card-example.png" width="500" alt="Share Report Card example" />
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
The card includes mood, quote, 7-day/5-hour usage bars with reset countdowns, and a UTC timestamp.
|
|
67
|
+
|
|
68
|
+
### Quote Collection
|
|
69
|
+
|
|
70
|
+
Mama has 86 unique quotes spread across 4 rarity tiers:
|
|
71
|
+
|
|
72
|
+
| Rarity | Count | How to Unlock |
|
|
73
|
+
|--------|------:|---------------|
|
|
74
|
+
| ⚪ Common | 73 | Displayed during normal use |
|
|
75
|
+
| 🔵 Rare | 5 | Hit specific usage milestones (0%, 50%, 100% of 5hr, etc.) |
|
|
76
|
+
| 🟡 Legendary | 3 | Achieve streaks and lifetime milestones |
|
|
77
|
+
| 🔴 Secret | 5 | Use the app on holidays or at 3 AM |
|
|
78
|
+
|
|
79
|
+
### Achievement Badges
|
|
80
|
+
|
|
81
|
+
Unlock badges for hitting milestones and maintaining streaks. View your collection in **Collection tab → Badges**.
|
|
82
|
+
|
|
83
|
+
| Tier | Badge | How to Unlock |
|
|
84
|
+
|------|-------|---------------|
|
|
85
|
+
| 🥉 Bronze | First Steps | Make your first API call |
|
|
86
|
+
| 🥉 Bronze | 3-Day Streak | Use Claude Code on 3 consecutive days |
|
|
87
|
+
| 🥉 Bronze | 7 Days with Mom | Use the app for 7 consecutive days |
|
|
88
|
+
| 🥈 Silver | Halfway There | Hit 50% of your weekly quota |
|
|
89
|
+
| 🥈 Silver | 7-Day Streak | Maintain a 7-day usage streak |
|
|
90
|
+
| 🥈 Silver | Mom's Pride | Get 10 "proud" expressions (85%+ usage) |
|
|
91
|
+
| 🥇 Gold | Full Power | Reach 100% of your weekly quota |
|
|
92
|
+
| 🥇 Gold | 30-Day Streak | Maintain a 30-day usage streak |
|
|
93
|
+
| 🥇 Gold | Survivor | Get 10 "angry" expressions AND keep using the app |
|
|
94
|
+
|
|
95
|
+
### Custom Character Skins
|
|
96
|
+
|
|
97
|
+
Give mama a new look with custom character images:
|
|
98
|
+
|
|
99
|
+
- **Single Image Mode** — One image displayed for all moods
|
|
100
|
+
- **Per-Mood Mode** — 6 images (one for each mood expression: angry, worried, happy, proud, confused, sleeping)
|
|
101
|
+
- **Sprite Sheet Mode** — Advanced mode with one image and grid configuration for frame positioning
|
|
102
|
+
|
|
103
|
+
Upload your custom skins in **Settings → Skins** and switch between them anytime. Mom might judge your art choices, but she'll wear them anyway.
|
|
104
|
+
|
|
105
|
+
## Installation
|
|
106
|
+
|
|
107
|
+
### Prerequisites
|
|
108
|
+
|
|
109
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) must be installed and logged in (OAuth)
|
|
110
|
+
- That's it. Mom doesn't ask for much.
|
|
111
|
+
|
|
112
|
+
### Quick Install (One-liner)
|
|
113
|
+
|
|
114
|
+
**npm** (all platforms, requires Node.js):
|
|
115
|
+
```bash
|
|
116
|
+
npm install -g claude-pet
|
|
117
|
+
claude-pet
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**macOS / Linux:**
|
|
121
|
+
```bash
|
|
122
|
+
curl -fsSL https://raw.githubusercontent.com/scm1400/claude-pet/main/scripts/install.sh | bash
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Windows (PowerShell):**
|
|
126
|
+
```powershell
|
|
127
|
+
irm https://raw.githubusercontent.com/scm1400/claude-pet/main/scripts/install.ps1 | iex
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Download Installer
|
|
131
|
+
|
|
132
|
+
Or grab the latest installer from [**Releases**](https://github.com/scm1400/claude-mama/releases):
|
|
133
|
+
|
|
134
|
+
| Platform | File | Arch |
|
|
135
|
+
|----------|------|------|
|
|
136
|
+
| Windows | `Claude Pet Setup x.x.x.exe` | x64 |
|
|
137
|
+
| macOS | `Claude Pet-x.x.x.dmg` | Universal (Intel + Apple Silicon) |
|
|
138
|
+
| Linux | `Claude Pet-x.x.x.AppImage` | x64 |
|
|
139
|
+
| Linux | `claude-pet_x.x.x_amd64.deb` | x64 |
|
|
140
|
+
|
|
141
|
+
> **⚠️ Not code-signed:** The app is not code-signed yet, so your OS will show a security warning on first install.
|
|
142
|
+
> - **Windows:** SmartScreen → **More info** → **Run anyway**
|
|
143
|
+
> - **macOS:** **System Settings** → **Privacy & Security** → **Open Anyway**
|
|
144
|
+
> - **Linux (AppImage):** `chmod +x Claude\ Pet-*.AppImage && ./Claude\ Pet-*.AppImage`
|
|
145
|
+
> - **Linux (deb):** `sudo dpkg -i claude-pet_*.deb`
|
|
146
|
+
|
|
147
|
+
### Claude Code Integration
|
|
148
|
+
|
|
149
|
+
Claude Pet includes slash commands that work inside Claude Code:
|
|
150
|
+
|
|
151
|
+
| Command | Effect |
|
|
152
|
+
|---------|--------|
|
|
153
|
+
| `/feed` | Feed your pet (hunger -30%) |
|
|
154
|
+
| `/play` | Play with your pet (happiness +25%) |
|
|
155
|
+
| `/pet` | Pet your pet (hunger -10%, happiness +10%, energy +10%) |
|
|
156
|
+
| `/name <name>` | Give your pet a name |
|
|
157
|
+
|
|
158
|
+
Each command shows a live status line with level, growth stage, and stat changes.
|
|
159
|
+
|
|
160
|
+
### First Launch
|
|
161
|
+
|
|
162
|
+
1. Make sure Claude Code is installed and logged in (`claude` in terminal)
|
|
163
|
+
2. Claude Mama will automatically detect your API usage via OAuth
|
|
164
|
+
3. If rate-limited (429), it falls back to local JSONL session parsing — no extra config needed
|
|
165
|
+
4. Mama starts polling every 5 minutes. Just let her do her thing.
|
|
166
|
+
|
|
167
|
+
### Auto-Start & Auto-Update
|
|
168
|
+
|
|
169
|
+
- **Auto-start:** Enabled by default. Claude Mama starts with your OS. Toggle in Settings if you dare.
|
|
170
|
+
- **Auto-update:** Updates are downloaded automatically from GitHub Releases. When a new version is ready, mama will ask you to restart.
|
|
171
|
+
|
|
172
|
+
## How It Works
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
┌─────────────────┐ ┌──────────────┐ ┌─────────────┐
|
|
176
|
+
│ Anthropic OAuth │────>│ Usage Tracker │───>│ Mood Engine │
|
|
177
|
+
│ Usage API │ │ (5min poll) │ │ │
|
|
178
|
+
└─────────────────┘ └──────┬───────┘ └──────┬──────┘
|
|
179
|
+
│ │
|
|
180
|
+
┌──────┴───────┐ ┌───────┘
|
|
181
|
+
│ JSONL Session│ │
|
|
182
|
+
│ Parser (429) │ v
|
|
183
|
+
└──────────────┘ ┌──────────────┐
|
|
184
|
+
│ Pixel Art │
|
|
185
|
+
┌──────────────┐ ┌──────────────┐ │ Character │
|
|
186
|
+
│ Speech │ │ Usage Bar │ └──────────────┘
|
|
187
|
+
│ Bubble │ │ Indicator │
|
|
188
|
+
└──────────────┘ └──────────────┘
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
1. **Polls** the Anthropic usage API every 5 minutes
|
|
192
|
+
2. **Falls back to JSONL session parsing** when rate limited (429) — scans `~/.claude/projects/` session files to estimate usage with dynamic calibration
|
|
193
|
+
3. **Computes mood** based on weekly utilization thresholds
|
|
194
|
+
4. **Renders** a pixel-art mama character with mood-appropriate expression and message
|
|
195
|
+
5. **Nags you** until you use your tokens like a responsible adult
|
|
196
|
+
|
|
197
|
+
### Mood Thresholds
|
|
198
|
+
|
|
199
|
+
| Weekly Usage | Mood | Mom Says |
|
|
200
|
+
|:---:|:---:|---|
|
|
201
|
+
| 0–14% | 😡 Angry | "Your quota is rotting away!" |
|
|
202
|
+
| 15–49% | 😟 Worried | "Mom is worried about you..." |
|
|
203
|
+
| 50–84% | 😊 Happy | "Now that's what I like~" |
|
|
204
|
+
| 85–100% | 🥹 Proud | "Perfect! I'm tearing up..." |
|
|
205
|
+
| 5hr > 90% | ⚠️ Warning | "Take a break~ 5-hour limit almost reached!" |
|
|
206
|
+
| Rate limited | 😵 Confused | Uses local session data, or "Collecting data..." while calibrating |
|
|
207
|
+
| API error | 😵 Confused | "Something went wrong..." |
|
|
208
|
+
| No login | 😴 Sleeping | "Log in first!" |
|
|
209
|
+
|
|
210
|
+
> **Note:** The Claude Code usage API (`api/oauth/usage`) has strict rate limits. When rate limited (429), Claude Mama parses local session JSONL files (`~/.claude/projects/`) to estimate usage. It learns the token-to-percent ratio from successful API responses (dynamic calibration), so estimates improve over time. While waiting for the first successful calibration, mama shows "Collecting data..." and retries every 10 seconds.
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Install dependencies
|
|
216
|
+
npm install
|
|
217
|
+
|
|
218
|
+
# Run in development mode
|
|
219
|
+
npm run dev
|
|
220
|
+
|
|
221
|
+
# Run tests
|
|
222
|
+
npm test
|
|
223
|
+
|
|
224
|
+
# Build for production
|
|
225
|
+
npm run build
|
|
226
|
+
|
|
227
|
+
# Build Windows installer
|
|
228
|
+
npm run build:win
|
|
229
|
+
|
|
230
|
+
# Build macOS installer (requires macOS)
|
|
231
|
+
npm run build:mac
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## FAQ
|
|
235
|
+
|
|
236
|
+
**Q: Can I hide from Claude Mama?**
|
|
237
|
+
A: No. She auto-starts with your OS. You can disable it in settings, but she'll know.
|
|
238
|
+
|
|
239
|
+
**Q: Why does she speak Korean by default?**
|
|
240
|
+
A: Because Korean moms are the gold standard of guilt-tripping. You can switch to English, Japanese, or Chinese in settings if your guilt receptors are calibrated differently.
|
|
241
|
+
|
|
242
|
+
**Q: My usage is at 0% but I've been coding all day?**
|
|
243
|
+
A: Make sure Claude Code is logged in. Mom can't monitor what she can't see.
|
|
244
|
+
|
|
245
|
+
**Q: How do I unlock secret quotes?**
|
|
246
|
+
A: Use the app on holidays (New Year, Chuseok, Christmas) or stay up coding past 3 AM. Mom notices everything.
|
|
247
|
+
|
|
248
|
+
**Q: Is this a joke?**
|
|
249
|
+
A: The guilt is real. The chicken reward is not (yet).
|
|
250
|
+
|
|
251
|
+
**Q: Will there be a Claude Dad version?**
|
|
252
|
+
A: Claude Dad went out for tokens and never came back.
|
|
253
|
+
|
|
254
|
+
## Contributing
|
|
255
|
+
|
|
256
|
+
PRs welcome! Whether it's new languages, more guilt-inducing messages, or pixel art improvements — mom appreciates the help.
|
|
257
|
+
|
|
258
|
+
1. Fork this repo
|
|
259
|
+
2. Create your feature branch (`git checkout -b feature/more-guilt`)
|
|
260
|
+
3. Commit your changes
|
|
261
|
+
4. Push to the branch
|
|
262
|
+
5. Open a Pull Request
|
|
263
|
+
|
|
264
|
+
## Privacy Policy
|
|
265
|
+
|
|
266
|
+
This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it.
|
|
267
|
+
|
|
268
|
+
- **Anthropic API**: The app calls the Anthropic OAuth Usage API to retrieve your token usage statistics. This is initiated by you when you log in with your Claude Code credentials.
|
|
269
|
+
- **GitHub (Auto-Update)**: The app checks GitHub Releases for new versions. No personal data is transmitted.
|
|
270
|
+
- **Local Storage**: Your API credentials and settings are stored locally on your machine using `electron-store`. They are never sent to any third-party server.
|
|
271
|
+
|
|
272
|
+
## Code Signing Policy
|
|
273
|
+
|
|
274
|
+
Free code signing provided by [SignPath.io](https://signpath.io), certificate by [SignPath Foundation](https://signpath.org).
|
|
275
|
+
|
|
276
|
+
**Team roles:**
|
|
277
|
+
- Committers and reviewers: [Members](https://github.com/orgs/scm1400/people)
|
|
278
|
+
- Approvers: [Owners](https://github.com/orgs/scm1400/people?query=role%3Aowner)
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
[ISC](LICENSE) — Free as in "mom's love" (unconditional, but with expectations).
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
<div align="center">
|
|
287
|
+
|
|
288
|
+
**Built with guilt and ❤️**
|
|
289
|
+
|
|
290
|
+
*If this made you mass-consume your Claude API quota, please star the repo. Mom would be proud.*
|
|
291
|
+
|
|
292
|
+
</div>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const PET_DIR = path.join(os.homedir(), '.claude-pet');
|
|
8
|
+
const LOCK_FILE = path.join(PET_DIR, 'app.pid');
|
|
9
|
+
|
|
10
|
+
// Check if process with given PID is running
|
|
11
|
+
function isRunning(pid) {
|
|
12
|
+
try {
|
|
13
|
+
process.kill(pid, 0);
|
|
14
|
+
return true;
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Check lock file for existing instance
|
|
21
|
+
if (fs.existsSync(LOCK_FILE)) {
|
|
22
|
+
const pid = parseInt(fs.readFileSync(LOCK_FILE, 'utf-8').trim(), 10);
|
|
23
|
+
if (pid && isRunning(pid)) {
|
|
24
|
+
// Already running — exit silently
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Launch Electron app
|
|
30
|
+
const electronPath = require('electron');
|
|
31
|
+
const appPath = path.join(__dirname, '..', 'dist', 'main', 'main', 'main.js');
|
|
32
|
+
|
|
33
|
+
const child = spawn(electronPath, [appPath], {
|
|
34
|
+
stdio: 'ignore',
|
|
35
|
+
detached: true,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Write PID lock file
|
|
39
|
+
fs.mkdirSync(PET_DIR, { recursive: true });
|
|
40
|
+
fs.writeFileSync(LOCK_FILE, String(child.pid));
|
|
41
|
+
|
|
42
|
+
child.unref();
|
package/build/AGENTS.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<!-- Parent: ../AGENTS.md -->
|
|
2
|
+
<!-- Generated: 2026-03-14 -->
|
|
3
|
+
|
|
4
|
+
# build/
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
Static build assets consumed by electron-builder at packaging time. All files in this directory are generated by `scripts/make-icon.js` from the source artwork at `docs/images/character.webp`. Do not edit these files manually — regenerate them by running `npm run make-icon`.
|
|
8
|
+
|
|
9
|
+
## Key Files
|
|
10
|
+
| File | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `icon.png` | 512×512 PNG — macOS application icon |
|
|
13
|
+
| `icon.ico` | 256×256 ICO — Windows application icon |
|
|
14
|
+
| `tray-icon.png` | 32×32 PNG — system tray icon (used at runtime by `src/main/tray.ts`) |
|
|
15
|
+
| `installerSidebar.bmp` | 164×314 BMP — NSIS Windows installer sidebar graphic (pink gradient + character) |
|
|
16
|
+
| `installerHeader.bmp` | 150×57 BMP — NSIS Windows installer header graphic |
|
|
17
|
+
| `dmg-background.png` | 540×380 PNG — macOS DMG window background |
|
|
18
|
+
| `entitlements.mac.plist` | macOS entitlements file for code signing (hardened runtime permissions) |
|
|
19
|
+
|
|
20
|
+
## Subdirectories
|
|
21
|
+
| Directory | Purpose |
|
|
22
|
+
|-----------|---------|
|
|
23
|
+
| *(none)* | — |
|
|
24
|
+
|
|
25
|
+
## For AI Agents
|
|
26
|
+
|
|
27
|
+
### Working In This Directory
|
|
28
|
+
- Do not edit image files directly. If the character artwork changes, update `docs/images/character.webp` and run `npm run make-icon` to regenerate everything.
|
|
29
|
+
- `entitlements.mac.plist` is hand-maintained and controls macOS sandbox permissions. Edit it only when adding new capabilities (e.g., network access, keychain access). Changes here require re-signing and re-notarizing the app.
|
|
30
|
+
- BMP dimensions are fixed requirements from NSIS: sidebar must be exactly 164×314, header exactly 150×57. The DMG background must be exactly 540×380.
|
|
31
|
+
- `tray-icon.png` is referenced at runtime by `src/main/tray.ts` via `path.join(app.getAppPath(), 'build/tray-icon.png')` — do not rename or move it.
|
|
32
|
+
- `icon.png` and `icon.ico` are referenced in `electron-builder.yml` — do not rename them.
|
|
33
|
+
|
|
34
|
+
### Testing Requirements
|
|
35
|
+
- After running `npm run make-icon`, visually confirm that all six image files render correctly and have the expected dimensions.
|
|
36
|
+
- Run `npm run build:win` or `npm run build:mac` to verify that electron-builder picks up all assets without errors.
|
|
37
|
+
|
|
38
|
+
### Common Patterns
|
|
39
|
+
- All images are generated with `sharp`'s `contain` fit mode and a transparent background, then composited onto colored SVG backgrounds for installer graphics.
|
|
40
|
+
|
|
41
|
+
## Dependencies
|
|
42
|
+
### Internal
|
|
43
|
+
- Generated from `docs/images/character.webp` by `scripts/make-icon.js`
|
|
44
|
+
- Referenced by `electron-builder.yml` (icons, installer images)
|
|
45
|
+
- Referenced at runtime by `src/main/tray.ts` (tray-icon.png)
|
|
46
|
+
|
|
47
|
+
### External
|
|
48
|
+
- electron-builder reads this directory during packaging
|
|
49
|
+
|
|
50
|
+
<!-- MANUAL: -->
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
6
|
+
<true/>
|
|
7
|
+
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
8
|
+
<true/>
|
|
9
|
+
<key>com.apple.security.cs.disable-library-validation</key>
|
|
10
|
+
<true/>
|
|
11
|
+
<key>com.apple.security.automation.apple-events</key>
|
|
12
|
+
<true/>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
package/build/icon.ico
ADDED
|
Binary file
|
package/build/icon.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BadgeManager = void 0;
|
|
4
|
+
const badge_registry_1 = require("./badge-registry");
|
|
5
|
+
class BadgeManager {
|
|
6
|
+
unlocked;
|
|
7
|
+
constructor(persisted) {
|
|
8
|
+
this.unlocked = new Map(persisted.map((b) => [b.id, b]));
|
|
9
|
+
}
|
|
10
|
+
unlock(id, now) {
|
|
11
|
+
if (this.unlocked.has(id))
|
|
12
|
+
return false;
|
|
13
|
+
if (!(0, badge_registry_1.getBadgeById)(id))
|
|
14
|
+
return false;
|
|
15
|
+
this.unlocked.set(id, { id, unlockedAt: now.toISOString() });
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
processTriggered(triggeredIds, now) {
|
|
19
|
+
const newlyUnlocked = [];
|
|
20
|
+
for (const id of triggeredIds) {
|
|
21
|
+
if (this.unlock(id, now)) {
|
|
22
|
+
newlyUnlocked.push(id);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return newlyUnlocked;
|
|
26
|
+
}
|
|
27
|
+
getState() {
|
|
28
|
+
const byTier = {
|
|
29
|
+
bronze: { unlocked: 0, total: 0 },
|
|
30
|
+
silver: { unlocked: 0, total: 0 },
|
|
31
|
+
gold: { unlocked: 0, total: 0 },
|
|
32
|
+
};
|
|
33
|
+
for (const entry of badge_registry_1.BADGE_REGISTRY) {
|
|
34
|
+
byTier[entry.tier].total++;
|
|
35
|
+
if (this.unlocked.has(entry.id)) {
|
|
36
|
+
byTier[entry.tier].unlocked++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
unlocked: [...this.unlocked.values()],
|
|
41
|
+
totalCount: badge_registry_1.BADGE_REGISTRY.length,
|
|
42
|
+
byTier,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
serialize() {
|
|
46
|
+
return [...this.unlocked.values()];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.BadgeManager = BadgeManager;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BADGE_REGISTRY = void 0;
|
|
4
|
+
exports.getBadgeById = getBadgeById;
|
|
5
|
+
exports.BADGE_REGISTRY = [
|
|
6
|
+
{
|
|
7
|
+
id: 'badge_first_call',
|
|
8
|
+
tier: 'bronze',
|
|
9
|
+
name: { ko: '첫 만남', en: 'First Meeting', ja: '初めまして', zh: '初次见面' },
|
|
10
|
+
description: { ko: '첫 API 호출', en: 'First API call', ja: '初めてのAPI呼び出し', zh: '第一次API调用' },
|
|
11
|
+
icon: '🐾',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'badge_streak_3',
|
|
15
|
+
tier: 'bronze',
|
|
16
|
+
name: { ko: '3일 함께', en: '3 Days Together', ja: '3日一緒', zh: '3天陪伴' },
|
|
17
|
+
description: { ko: '3일 연속 사용', en: '3 consecutive days', ja: '3日連続使用', zh: '连续使用3天' },
|
|
18
|
+
icon: '🔥',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'badge_7days',
|
|
22
|
+
tier: 'bronze',
|
|
23
|
+
name: { ko: '일주일 친구', en: 'Week-long Buddy', ja: '一週間の仲間', zh: '一周好友' },
|
|
24
|
+
description: { ko: '설치 후 7일', en: '7 days since install', ja: 'インストールから7日', zh: '安装后7天' },
|
|
25
|
+
icon: '🏠',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'badge_half',
|
|
29
|
+
tier: 'silver',
|
|
30
|
+
name: { ko: '반타작', en: 'Halfway There', ja: '半分達成', zh: '半程达成' },
|
|
31
|
+
description: { ko: '주간 50% 달성', en: 'Reach 50% weekly', ja: '週間50%達成', zh: '周使用率50%' },
|
|
32
|
+
icon: '⚡',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'badge_streak_7',
|
|
36
|
+
tier: 'silver',
|
|
37
|
+
name: { ko: '7일 연속', en: '7-Day Streak', ja: '7日連続', zh: '7天连续' },
|
|
38
|
+
description: { ko: '7일 연속 사용', en: '7 consecutive days', ja: '7日連続使用', zh: '连续使用7天' },
|
|
39
|
+
icon: '💪',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'badge_happy_10',
|
|
43
|
+
tier: 'silver',
|
|
44
|
+
name: { ko: '꼬리흔들기 달인', en: 'Tail Wag Master', ja: 'しっぽ振り名人', zh: '摇尾达人' },
|
|
45
|
+
description: { ko: 'happy 상태 10회', en: 'Happy mood 10 times', ja: 'happy状態10回', zh: 'happy状态10次' },
|
|
46
|
+
icon: '🏆',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'badge_full',
|
|
50
|
+
tier: 'gold',
|
|
51
|
+
name: { ko: '풀 가동', en: 'Full Power', ja: 'フル稼働', zh: '全力运转' },
|
|
52
|
+
description: { ko: '주간 100% 달성', en: 'Reach 100% weekly', ja: '週間100%達成', zh: '周使用率100%' },
|
|
53
|
+
icon: '🚀',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'badge_streak_30',
|
|
57
|
+
tier: 'gold',
|
|
58
|
+
name: { ko: '30일 동반자', en: '30-Day Companion', ja: '30日の相棒', zh: '30天伙伴' },
|
|
59
|
+
description: { ko: '30일 연속 사용', en: '30 consecutive days', ja: '30日連続使用', zh: '连续使用30天' },
|
|
60
|
+
icon: '👑',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'badge_survivor',
|
|
64
|
+
tier: 'gold',
|
|
65
|
+
name: { ko: '충실한 친구', en: 'Loyal Companion', ja: '忠実な友', zh: '忠实伙伴' },
|
|
66
|
+
description: { ko: 'worried 10회 후에도 사용', en: 'Keep using after 10 worried moods', ja: 'worried10回後も使い続ける', zh: '经历10次worried后继续' },
|
|
67
|
+
icon: '🛡️',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
function getBadgeById(id) {
|
|
71
|
+
return exports.BADGE_REGISTRY.find((b) => b.id === id);
|
|
72
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evaluateBadgeTriggers = evaluateBadgeTriggers;
|
|
4
|
+
function evaluateBadgeTriggers(ctx) {
|
|
5
|
+
const triggered = [];
|
|
6
|
+
const { weeklyUtilization, dailyHistory, installDate, firstApiCallSeen, now, happyCount, worriedCount } = ctx;
|
|
7
|
+
// Bronze: first call
|
|
8
|
+
if (!firstApiCallSeen && weeklyUtilization !== null && weeklyUtilization > 0) {
|
|
9
|
+
triggered.push('badge_first_call');
|
|
10
|
+
}
|
|
11
|
+
// Streak calculation
|
|
12
|
+
if (dailyHistory.length > 0) {
|
|
13
|
+
const sorted = [...dailyHistory].sort((a, b) => b.date.localeCompare(a.date));
|
|
14
|
+
let streak = 0;
|
|
15
|
+
for (const d of sorted) {
|
|
16
|
+
if (d.percent > 0)
|
|
17
|
+
streak++;
|
|
18
|
+
else
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
if (streak >= 3)
|
|
22
|
+
triggered.push('badge_streak_3');
|
|
23
|
+
if (streak >= 7)
|
|
24
|
+
triggered.push('badge_streak_7');
|
|
25
|
+
if (streak >= 30)
|
|
26
|
+
triggered.push('badge_streak_30');
|
|
27
|
+
}
|
|
28
|
+
// Days since install
|
|
29
|
+
const installDt = new Date(installDate);
|
|
30
|
+
const daysSince = Math.floor((now.getTime() - installDt.getTime()) / (24 * 60 * 60 * 1000));
|
|
31
|
+
if (daysSince >= 7)
|
|
32
|
+
triggered.push('badge_7days');
|
|
33
|
+
// Usage milestones
|
|
34
|
+
if (weeklyUtilization !== null && weeklyUtilization >= 50)
|
|
35
|
+
triggered.push('badge_half');
|
|
36
|
+
if (weeklyUtilization !== null && weeklyUtilization >= 100)
|
|
37
|
+
triggered.push('badge_full');
|
|
38
|
+
// Mood counts
|
|
39
|
+
if (happyCount >= 10)
|
|
40
|
+
triggered.push('badge_happy_10');
|
|
41
|
+
if (worriedCount >= 10 && weeklyUtilization !== null && weeklyUtilization > 0) {
|
|
42
|
+
triggered.push('badge_survivor');
|
|
43
|
+
}
|
|
44
|
+
return triggered;
|
|
45
|
+
}
|