@cpretzinger/boss-claude 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +264 -0
- package/bin/boss-claude.js +150 -0
- package/lib/identity.js +115 -0
- package/lib/init.js +133 -0
- package/lib/memory.js +94 -0
- package/lib/postgres.js +398 -0
- package/lib/session.js +158 -0
- package/package.json +37 -0
- package/scripts/install.js +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Craig Pretzinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Boss Claude
|
|
2
|
+
|
|
3
|
+
**Gamified AI assistant with persistent memory across all your repositories.**
|
|
4
|
+
|
|
5
|
+
Boss Claude turns every coding session into an RPG-style experience where Claude levels up, earns tokens, and maintains perfect memory across all your projects through GitHub Issues.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🎮 **Gamification**: Claude levels up with XP, builds a token bank, and tracks career stats
|
|
10
|
+
- 💾 **Persistent Memory**: Every session auto-saves to GitHub Issues for perfect recall
|
|
11
|
+
- 🌍 **Global CLI**: Works in ANY repository after `npm install -g`
|
|
12
|
+
- 📊 **Career Stats**: Track total sessions, repos managed, token earnings
|
|
13
|
+
- 🔍 **Semantic Search**: Recall past sessions with natural language queries
|
|
14
|
+
- 🏆 **Progression System**: Level-based XP, token banking, achievement tracking
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g @cpretzinger/boss-claude
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
### 1. Configure Credentials
|
|
25
|
+
|
|
26
|
+
Edit `~/.boss-claude/.env` with your credentials:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Redis URL (required) - For Boss identity and session storage
|
|
30
|
+
REDIS_URL=redis://default:password@host:port
|
|
31
|
+
|
|
32
|
+
# GitHub Token (required) - For memory storage in GitHub Issues
|
|
33
|
+
# Create at: https://github.com/settings/tokens (needs 'repo' scope)
|
|
34
|
+
GITHUB_TOKEN=ghp_your_token_here
|
|
35
|
+
|
|
36
|
+
# GitHub Owner (optional) - Defaults to 'cpretzinger'
|
|
37
|
+
GITHUB_OWNER=your-username
|
|
38
|
+
|
|
39
|
+
# GitHub Memory Repository (optional) - Defaults to 'boss-claude-memory'
|
|
40
|
+
GITHUB_MEMORY_REPO=boss-claude-memory
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Create GitHub Memory Repository
|
|
44
|
+
|
|
45
|
+
Create a repository to store your session memories:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gh repo create boss-claude-memory --private
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. Initialize Boss Claude
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
boss-claude init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
Boss Claude automatically loads when Claude starts in ANY repository. You'll see:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
🎮 BOSS CLAUDE AUTO-LOADED
|
|
63
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
64
|
+
|
|
65
|
+
👤 BOSS STATUS
|
|
66
|
+
Level 5 • 250/500 XP (50%)
|
|
67
|
+
💰 Token Bank: 1,234,567 tokens
|
|
68
|
+
📊 Total Sessions: 42
|
|
69
|
+
🏢 Repos Managed: 8
|
|
70
|
+
|
|
71
|
+
📁 CURRENT REPOSITORY
|
|
72
|
+
Name: my-awesome-project
|
|
73
|
+
Path: /Users/you/projects/my-awesome-project
|
|
74
|
+
Sessions: 12
|
|
75
|
+
Last Active: 2026-01-30T15:30:00.000Z
|
|
76
|
+
|
|
77
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
78
|
+
💡 Commands: boss-claude status | save | recall
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Commands
|
|
82
|
+
|
|
83
|
+
#### Show Status
|
|
84
|
+
```bash
|
|
85
|
+
boss-claude status
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Displays Boss level, XP, token bank, and repository stats.
|
|
89
|
+
|
|
90
|
+
#### Save Session
|
|
91
|
+
```bash
|
|
92
|
+
boss-claude save "Implemented user authentication"
|
|
93
|
+
boss-claude save "Fixed bug in API" --tags "bugfix,api"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Saves current session to GitHub Issues with:
|
|
97
|
+
- Automatic summary if not provided
|
|
98
|
+
- Optional tags for organization
|
|
99
|
+
- XP and token rewards
|
|
100
|
+
- Searchable history
|
|
101
|
+
|
|
102
|
+
#### Recall Past Sessions
|
|
103
|
+
```bash
|
|
104
|
+
boss-claude recall "authentication"
|
|
105
|
+
boss-claude recall "bug fix" --limit 10
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Searches your GitHub Issues memory for past sessions.
|
|
109
|
+
|
|
110
|
+
## How It Works
|
|
111
|
+
|
|
112
|
+
### Session Flow
|
|
113
|
+
|
|
114
|
+
1. **Auto-Load**: Boss Claude loads on every Claude conversation start
|
|
115
|
+
2. **Session Tracking**: Monitors tokens used, messages, and repository context
|
|
116
|
+
3. **Save to Memory**: Session data saved as GitHub Issue when you run `boss-claude save`
|
|
117
|
+
4. **Rewards**: Earn XP (50 per session) and bank tokens used
|
|
118
|
+
5. **Level Up**: Reach new levels (100 XP per level, exponential scaling)
|
|
119
|
+
|
|
120
|
+
### Data Storage
|
|
121
|
+
|
|
122
|
+
- **Redis**: Boss identity, session state, repository stats
|
|
123
|
+
- **GitHub Issues**: Long-term memory storage, searchable with full history
|
|
124
|
+
- **Local**: Configuration in `~/.boss-claude/.env`
|
|
125
|
+
|
|
126
|
+
### Memory Structure
|
|
127
|
+
|
|
128
|
+
Each saved session creates a GitHub Issue with:
|
|
129
|
+
|
|
130
|
+
```markdown
|
|
131
|
+
## Session Summary
|
|
132
|
+
|
|
133
|
+
Implemented user authentication
|
|
134
|
+
|
|
135
|
+
## Session Data
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"started_at": "2026-01-30T15:00:00.000Z",
|
|
140
|
+
"tokens_used": 12500,
|
|
141
|
+
"messages": [...],
|
|
142
|
+
"repo": {
|
|
143
|
+
"name": "my-project",
|
|
144
|
+
"path": "/Users/you/projects/my-project"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Labels: `session`, `my-project`, `authentication`
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Architecture
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
~/.boss-claude/
|
|
156
|
+
├── .env # Your credentials
|
|
157
|
+
|
|
158
|
+
GitHub Repository (boss-claude-memory)
|
|
159
|
+
└── Issues/ # One issue per session
|
|
160
|
+
├── #1: [repo-name] Session summary
|
|
161
|
+
├── #2: [repo-name] Session summary
|
|
162
|
+
└── ...
|
|
163
|
+
|
|
164
|
+
Redis Keys
|
|
165
|
+
├── boss:identity # Boss level, XP, token bank
|
|
166
|
+
├── boss:repo:{name} # Repository stats
|
|
167
|
+
└── boss:session:{name} # Active session data
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Gamification System
|
|
171
|
+
|
|
172
|
+
### Levels
|
|
173
|
+
- **Level 1**: Start
|
|
174
|
+
- **Level 2**: 100 XP
|
|
175
|
+
- **Level 3**: 200 XP
|
|
176
|
+
- **Level 4**: 300 XP
|
|
177
|
+
- ...and so on (100 XP per level)
|
|
178
|
+
|
|
179
|
+
### Rewards
|
|
180
|
+
- **XP**: 50 XP per session saved
|
|
181
|
+
- **Token Bank**: All tokens used during session are banked
|
|
182
|
+
- **Net Worth**: Token bank × $0.000003 per token
|
|
183
|
+
|
|
184
|
+
### Stats Tracked
|
|
185
|
+
- Total sessions across all repos
|
|
186
|
+
- Repositories managed
|
|
187
|
+
- Token bank size
|
|
188
|
+
- Current level and XP progress
|
|
189
|
+
- Per-repo session counts
|
|
190
|
+
|
|
191
|
+
## CLI Reference
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Initialize Boss Claude in current repo
|
|
195
|
+
boss-claude init
|
|
196
|
+
|
|
197
|
+
# Show Boss status and stats
|
|
198
|
+
boss-claude status
|
|
199
|
+
|
|
200
|
+
# Save current session
|
|
201
|
+
boss-claude save [summary] [--tags <tags>]
|
|
202
|
+
|
|
203
|
+
# Search past sessions
|
|
204
|
+
boss-claude recall <query> [--limit <number>]
|
|
205
|
+
|
|
206
|
+
# Show help
|
|
207
|
+
boss-claude --help
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Environment Variables
|
|
211
|
+
|
|
212
|
+
| Variable | Required | Default | Description |
|
|
213
|
+
|----------|----------|---------|-------------|
|
|
214
|
+
| `REDIS_URL` | Yes | - | Redis connection string |
|
|
215
|
+
| `GITHUB_TOKEN` | Yes | - | GitHub personal access token |
|
|
216
|
+
| `GITHUB_OWNER` | No | `cpretzinger` | GitHub username |
|
|
217
|
+
| `GITHUB_MEMORY_REPO` | No | `boss-claude-memory` | Repository name for memory storage |
|
|
218
|
+
|
|
219
|
+
## Troubleshooting
|
|
220
|
+
|
|
221
|
+
### "REDIS_URL not found"
|
|
222
|
+
Edit `~/.boss-claude/.env` and add your Redis connection string.
|
|
223
|
+
|
|
224
|
+
### "GITHUB_TOKEN not found"
|
|
225
|
+
Create a GitHub token at https://github.com/settings/tokens with `repo` scope and add to `~/.boss-claude/.env`.
|
|
226
|
+
|
|
227
|
+
### "Not in a git repository"
|
|
228
|
+
Boss Claude requires a git repository to track sessions. Initialize one with `git init`.
|
|
229
|
+
|
|
230
|
+
### Auto-load not working
|
|
231
|
+
Check that `~/.claude/CLAUDE.md` contains the Boss Claude auto-load block. Reinstall with:
|
|
232
|
+
```bash
|
|
233
|
+
npm uninstall -g @cpretzinger/boss-claude
|
|
234
|
+
npm install -g @cpretzinger/boss-claude
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Clone the repo
|
|
241
|
+
git clone https://github.com/cpretzinger/boss-claude.git
|
|
242
|
+
cd boss-claude
|
|
243
|
+
|
|
244
|
+
# Install dependencies
|
|
245
|
+
npm install
|
|
246
|
+
|
|
247
|
+
# Link globally for testing
|
|
248
|
+
npm link
|
|
249
|
+
|
|
250
|
+
# Test in any repo
|
|
251
|
+
boss-claude init
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
MIT
|
|
257
|
+
|
|
258
|
+
## Author
|
|
259
|
+
|
|
260
|
+
Craig Pretzinger ([@cpretzinger](https://github.com/cpretzinger))
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
**Built with**: Node.js, Redis, GitHub API, Commander.js, Chalk
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { loadIdentity, updateIdentity } from '../lib/identity.js';
|
|
6
|
+
import { loadSession, saveSession } from '../lib/session.js';
|
|
7
|
+
import { searchMemory, saveMemory } from '../lib/memory.js';
|
|
8
|
+
import { getStatus } from '../lib/init.js';
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('boss-claude')
|
|
14
|
+
.description('Boss Claude - Gamified AI assistant with persistent memory')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.command('init')
|
|
19
|
+
.description('Initialize Boss Claude in current repository')
|
|
20
|
+
.action(async () => {
|
|
21
|
+
try {
|
|
22
|
+
console.log(chalk.blue('🎮 Initializing Boss Claude...\n'));
|
|
23
|
+
|
|
24
|
+
const status = await getStatus();
|
|
25
|
+
|
|
26
|
+
console.log(chalk.green('✅ Boss Claude initialized successfully!\n'));
|
|
27
|
+
console.log(chalk.bold('Boss Identity:'));
|
|
28
|
+
console.log(` Level: ${chalk.yellow(status.boss.level)}`);
|
|
29
|
+
console.log(` XP: ${chalk.cyan(status.boss.xp)}/${status.boss.xp_to_next_level}`);
|
|
30
|
+
console.log(` Token Bank: ${chalk.magenta(status.boss.token_bank.toLocaleString())} tokens`);
|
|
31
|
+
console.log(` Total Sessions: ${chalk.blue(status.boss.total_sessions)}`);
|
|
32
|
+
console.log(` Repositories Managed: ${chalk.green(status.boss.repos_managed)}\n`);
|
|
33
|
+
|
|
34
|
+
if (status.repo) {
|
|
35
|
+
console.log(chalk.bold('Current Repository:'));
|
|
36
|
+
console.log(` Name: ${chalk.white(status.repo.name)}`);
|
|
37
|
+
console.log(` Path: ${chalk.gray(status.repo.path)}`);
|
|
38
|
+
console.log(` Sessions: ${chalk.blue(status.repo.session_count)}`);
|
|
39
|
+
console.log(` Last Active: ${chalk.gray(status.repo.last_active || 'Never')}\n`);
|
|
40
|
+
} else {
|
|
41
|
+
console.log(chalk.yellow('⚠️ Not in a git repository\n'));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(chalk.red('❌ Error initializing Boss Claude:'), error.message);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
program
|
|
51
|
+
.command('status')
|
|
52
|
+
.description('Show Boss Claude status and stats')
|
|
53
|
+
.action(async () => {
|
|
54
|
+
try {
|
|
55
|
+
const status = await getStatus();
|
|
56
|
+
|
|
57
|
+
console.log(chalk.bold.blue('\n🎮 BOSS CLAUDE STATUS\n'));
|
|
58
|
+
console.log(chalk.bold('Boss Level & Progress:'));
|
|
59
|
+
console.log(` Level: ${chalk.yellow.bold(status.boss.level)}`);
|
|
60
|
+
console.log(` XP: ${chalk.cyan(status.boss.xp)}/${status.boss.xp_to_next_level} (${Math.floor((status.boss.xp / status.boss.xp_to_next_level) * 100)}%)`);
|
|
61
|
+
console.log(` Token Bank: ${chalk.magenta.bold(status.boss.token_bank.toLocaleString())} tokens`);
|
|
62
|
+
console.log(` Net Worth: ${chalk.green.bold('$' + (status.boss.token_bank * 0.000003).toFixed(2))}\n`);
|
|
63
|
+
|
|
64
|
+
console.log(chalk.bold('Career Stats:'));
|
|
65
|
+
console.log(` Total Sessions: ${chalk.blue(status.boss.total_sessions)}`);
|
|
66
|
+
console.log(` Repositories Managed: ${chalk.green(status.boss.repos_managed)}`);
|
|
67
|
+
console.log(` Account Created: ${chalk.gray(status.boss.created_at)}\n`);
|
|
68
|
+
|
|
69
|
+
if (status.repo) {
|
|
70
|
+
console.log(chalk.bold('Current Repository:'));
|
|
71
|
+
console.log(` Name: ${chalk.white.bold(status.repo.name)}`);
|
|
72
|
+
console.log(` Path: ${chalk.gray(status.repo.path)}`);
|
|
73
|
+
console.log(` Sessions: ${chalk.blue(status.repo.session_count)}`);
|
|
74
|
+
console.log(` Last Active: ${chalk.gray(status.repo.last_active || 'Never')}\n`);
|
|
75
|
+
} else {
|
|
76
|
+
console.log(chalk.yellow('⚠️ Not currently in a git repository\n'));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(chalk.dim('💡 Use "boss-claude save" to save your current session'));
|
|
80
|
+
console.log(chalk.dim('💡 Use "boss-claude recall" to search past sessions\n'));
|
|
81
|
+
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(chalk.red('❌ Error fetching status:'), error.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
program
|
|
89
|
+
.command('save')
|
|
90
|
+
.description('Save current session to memory')
|
|
91
|
+
.argument('[summary]', 'Session summary (optional)')
|
|
92
|
+
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
93
|
+
.action(async (summary, options) => {
|
|
94
|
+
try {
|
|
95
|
+
console.log(chalk.blue('💾 Saving session...\n'));
|
|
96
|
+
|
|
97
|
+
const session = await saveSession(summary, options.tags);
|
|
98
|
+
|
|
99
|
+
console.log(chalk.green('✅ Session saved to GitHub Issues!\n'));
|
|
100
|
+
console.log(chalk.bold('Session Details:'));
|
|
101
|
+
console.log(` Issue: ${chalk.cyan('#' + session.issue_number)}`);
|
|
102
|
+
console.log(` Repository: ${chalk.white(session.repo_name)}`);
|
|
103
|
+
console.log(` Summary: ${chalk.gray(session.summary)}`);
|
|
104
|
+
if (session.tags) {
|
|
105
|
+
console.log(` Tags: ${chalk.yellow(session.tags.join(', '))}`);
|
|
106
|
+
}
|
|
107
|
+
console.log(` URL: ${chalk.blue(session.url)}\n`);
|
|
108
|
+
|
|
109
|
+
console.log(chalk.bold('Rewards:'));
|
|
110
|
+
console.log(` XP Earned: ${chalk.cyan('+' + session.xp_earned)}`);
|
|
111
|
+
console.log(` Tokens Banked: ${chalk.magenta('+' + session.tokens_earned.toLocaleString())}\n`);
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error(chalk.red('❌ Error saving session:'), error.message);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
program
|
|
120
|
+
.command('recall')
|
|
121
|
+
.description('Search past sessions')
|
|
122
|
+
.argument('<query>', 'Search query')
|
|
123
|
+
.option('-l, --limit <number>', 'Number of results', '5')
|
|
124
|
+
.action(async (query, options) => {
|
|
125
|
+
try {
|
|
126
|
+
console.log(chalk.blue(`🔍 Searching for: "${query}"\n`));
|
|
127
|
+
|
|
128
|
+
const results = await searchMemory(query, parseInt(options.limit));
|
|
129
|
+
|
|
130
|
+
if (results.length === 0) {
|
|
131
|
+
console.log(chalk.yellow('No sessions found matching your query\n'));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(chalk.green(`Found ${results.length} session(s):\n`));
|
|
136
|
+
|
|
137
|
+
results.forEach((result, idx) => {
|
|
138
|
+
console.log(chalk.bold(`${idx + 1}. ${result.title}`));
|
|
139
|
+
console.log(` ${chalk.gray(result.summary)}`);
|
|
140
|
+
console.log(` ${chalk.blue(result.url)}`);
|
|
141
|
+
console.log(` ${chalk.dim(result.created_at)}\n`);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(chalk.red('❌ Error searching memory:'), error.message);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
program.parse();
|
package/lib/identity.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// Load environment variables from ~/.boss-claude/.env
|
|
12
|
+
const envPath = join(os.homedir(), '.boss-claude', '.env');
|
|
13
|
+
if (existsSync(envPath)) {
|
|
14
|
+
dotenv.config({ path: envPath });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let redis = null;
|
|
18
|
+
|
|
19
|
+
function getRedis() {
|
|
20
|
+
if (!redis) {
|
|
21
|
+
if (!process.env.REDIS_URL) {
|
|
22
|
+
throw new Error('REDIS_URL not found. Please run: boss-claude init');
|
|
23
|
+
}
|
|
24
|
+
redis = new Redis(process.env.REDIS_URL);
|
|
25
|
+
}
|
|
26
|
+
return redis;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const BOSS_KEY = 'boss:identity';
|
|
30
|
+
|
|
31
|
+
export async function loadIdentity() {
|
|
32
|
+
const client = getRedis();
|
|
33
|
+
const data = await client.get(BOSS_KEY);
|
|
34
|
+
|
|
35
|
+
if (!data) {
|
|
36
|
+
// Create default Boss identity
|
|
37
|
+
const identity = {
|
|
38
|
+
level: 1,
|
|
39
|
+
xp: 0,
|
|
40
|
+
token_bank: 0,
|
|
41
|
+
total_sessions: 0,
|
|
42
|
+
repos_managed: 0,
|
|
43
|
+
created_at: new Date().toISOString()
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
await client.set(BOSS_KEY, JSON.stringify(identity));
|
|
47
|
+
return identity;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return JSON.parse(data);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function updateIdentity(updates) {
|
|
54
|
+
const client = getRedis();
|
|
55
|
+
const identity = await loadIdentity();
|
|
56
|
+
|
|
57
|
+
const updated = {
|
|
58
|
+
...identity,
|
|
59
|
+
...updates,
|
|
60
|
+
updated_at: new Date().toISOString()
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
await client.set(BOSS_KEY, JSON.stringify(updated));
|
|
64
|
+
return updated;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function addXP(amount) {
|
|
68
|
+
const identity = await loadIdentity();
|
|
69
|
+
|
|
70
|
+
let newXP = identity.xp + amount;
|
|
71
|
+
let newLevel = identity.level;
|
|
72
|
+
|
|
73
|
+
// Level up logic (100 XP per level, exponential)
|
|
74
|
+
const xpForNextLevel = newLevel * 100;
|
|
75
|
+
|
|
76
|
+
if (newXP >= xpForNextLevel) {
|
|
77
|
+
newLevel++;
|
|
78
|
+
newXP -= xpForNextLevel;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return updateIdentity({
|
|
82
|
+
level: newLevel,
|
|
83
|
+
xp: newXP
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function addTokens(amount) {
|
|
88
|
+
const identity = await loadIdentity();
|
|
89
|
+
|
|
90
|
+
return updateIdentity({
|
|
91
|
+
token_bank: identity.token_bank + amount
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function incrementSessions() {
|
|
96
|
+
const identity = await loadIdentity();
|
|
97
|
+
|
|
98
|
+
return updateIdentity({
|
|
99
|
+
total_sessions: identity.total_sessions + 1
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function addRepo(repoName) {
|
|
104
|
+
const client = getRedis();
|
|
105
|
+
const repoKey = `boss:repo:${repoName}`;
|
|
106
|
+
|
|
107
|
+
const exists = await client.exists(repoKey);
|
|
108
|
+
|
|
109
|
+
if (!exists) {
|
|
110
|
+
const identity = await loadIdentity();
|
|
111
|
+
await updateIdentity({
|
|
112
|
+
repos_managed: identity.repos_managed + 1
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
package/lib/init.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { loadIdentity, addRepo } from './identity.js';
|
|
2
|
+
import Redis from 'ioredis';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { exec } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Load environment variables from ~/.boss-claude/.env
|
|
17
|
+
const envPath = join(os.homedir(), '.boss-claude', '.env');
|
|
18
|
+
if (existsSync(envPath)) {
|
|
19
|
+
dotenv.config({ path: envPath });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let redis = null;
|
|
23
|
+
|
|
24
|
+
function getRedis() {
|
|
25
|
+
if (!redis) {
|
|
26
|
+
if (!process.env.REDIS_URL) {
|
|
27
|
+
throw new Error('REDIS_URL not found. Please run: boss-claude init');
|
|
28
|
+
}
|
|
29
|
+
redis = new Redis(process.env.REDIS_URL);
|
|
30
|
+
}
|
|
31
|
+
return redis;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function getCurrentRepo() {
|
|
35
|
+
try {
|
|
36
|
+
const { stdout: repoPath } = await execAsync('git rev-parse --show-toplevel');
|
|
37
|
+
const { stdout: repoUrl } = await execAsync('git config --get remote.origin.url');
|
|
38
|
+
|
|
39
|
+
const repoName = repoUrl.trim().split('/').pop().replace('.git', '');
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
name: repoName,
|
|
43
|
+
path: repoPath.trim(),
|
|
44
|
+
url: repoUrl.trim()
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function getStatus() {
|
|
52
|
+
// Load Boss identity
|
|
53
|
+
const boss = await loadIdentity();
|
|
54
|
+
|
|
55
|
+
// Calculate XP to next level
|
|
56
|
+
const xp_to_next_level = boss.level * 100;
|
|
57
|
+
|
|
58
|
+
// Get current repo info
|
|
59
|
+
const repo = await getCurrentRepo();
|
|
60
|
+
|
|
61
|
+
let repoStats = null;
|
|
62
|
+
|
|
63
|
+
if (repo) {
|
|
64
|
+
// Register repo if new
|
|
65
|
+
await addRepo(repo.name);
|
|
66
|
+
|
|
67
|
+
// Get repo stats
|
|
68
|
+
const client = getRedis();
|
|
69
|
+
const repoKey = `boss:repo:${repo.name}`;
|
|
70
|
+
const repoData = await client.get(repoKey);
|
|
71
|
+
|
|
72
|
+
if (repoData) {
|
|
73
|
+
repoStats = JSON.parse(repoData);
|
|
74
|
+
} else {
|
|
75
|
+
// Initialize repo stats
|
|
76
|
+
repoStats = {
|
|
77
|
+
name: repo.name,
|
|
78
|
+
path: repo.path,
|
|
79
|
+
session_count: 0,
|
|
80
|
+
first_seen: new Date().toISOString(),
|
|
81
|
+
last_active: null
|
|
82
|
+
};
|
|
83
|
+
await client.set(repoKey, JSON.stringify(repoStats));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
boss: {
|
|
89
|
+
...boss,
|
|
90
|
+
xp_to_next_level
|
|
91
|
+
},
|
|
92
|
+
repo: repo ? {
|
|
93
|
+
...repo,
|
|
94
|
+
...repoStats
|
|
95
|
+
} : null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function formatStatusForClaude() {
|
|
100
|
+
const status = await getStatus();
|
|
101
|
+
|
|
102
|
+
let output = `
|
|
103
|
+
🎮 BOSS CLAUDE AUTO-LOADED
|
|
104
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
105
|
+
|
|
106
|
+
👤 BOSS STATUS
|
|
107
|
+
Level ${status.boss.level} • ${status.boss.xp}/${status.boss.xp_to_next_level} XP (${Math.floor((status.boss.xp / status.boss.xp_to_next_level) * 100)}%)
|
|
108
|
+
💰 Token Bank: ${status.boss.token_bank.toLocaleString()} tokens
|
|
109
|
+
📊 Total Sessions: ${status.boss.total_sessions}
|
|
110
|
+
🏢 Repos Managed: ${status.boss.repos_managed}
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
if (status.repo) {
|
|
114
|
+
output += `
|
|
115
|
+
📁 CURRENT REPOSITORY
|
|
116
|
+
Name: ${status.repo.name}
|
|
117
|
+
Path: ${status.repo.path}
|
|
118
|
+
Sessions: ${status.repo.session_count}
|
|
119
|
+
Last Active: ${status.repo.last_active || 'Never'}
|
|
120
|
+
`;
|
|
121
|
+
} else {
|
|
122
|
+
output += `
|
|
123
|
+
⚠️ Not currently in a git repository
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
output += `
|
|
128
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
129
|
+
💡 Commands: boss-claude status | save | recall
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
return output;
|
|
133
|
+
}
|