@ramarivera/coding-buddy 0.4.0-alpha.7 → 0.4.0-alpha.9
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/README.md +18 -39
- package/adapters/claude/hooks/buddy-comment.sh +4 -1
- package/adapters/claude/hooks/name-react.sh +4 -1
- package/adapters/claude/hooks/react.sh +4 -1
- package/adapters/claude/install/backup.ts +36 -118
- package/adapters/claude/install/disable.ts +9 -14
- package/adapters/claude/install/doctor.ts +26 -87
- package/adapters/claude/install/install.ts +39 -66
- package/adapters/claude/install/test-statusline.ts +8 -18
- package/adapters/claude/install/uninstall.ts +18 -26
- package/adapters/claude/plugin/marketplace.json +4 -4
- package/adapters/claude/plugin/plugin.json +3 -5
- package/adapters/claude/server/index.ts +132 -5
- package/adapters/claude/server/path.ts +12 -0
- package/adapters/claude/skills/buddy/SKILL.md +16 -1
- package/adapters/claude/statusline/buddy-status.sh +22 -3
- package/adapters/claude/storage/paths.ts +9 -0
- package/adapters/claude/storage/settings.ts +53 -3
- package/adapters/claude/storage/state.ts +22 -4
- package/adapters/pi/README.md +19 -0
- package/adapters/pi/events.ts +176 -19
- package/adapters/pi/index.ts +3 -1
- package/adapters/pi/logger.ts +52 -0
- package/adapters/pi/prompt.ts +18 -0
- package/adapters/pi/storage.ts +1 -0
- package/cli/biomes.ts +309 -0
- package/cli/buddy-shell.ts +818 -0
- package/cli/index.ts +7 -0
- package/cli/tui.tsx +2244 -0
- package/cli/upgrade.ts +213 -0
- package/core/model.ts +6 -0
- package/package.json +78 -62
- package/scripts/paths.sh +40 -0
- package/server/achievements.ts +15 -0
- package/server/art.ts +1 -0
- package/server/engine.ts +1 -0
- package/server/mcp-launcher.sh +16 -0
- package/server/path.ts +30 -0
- package/server/reactions.ts +1 -0
- package/server/state.ts +3 -0
- package/adapters/claude/popup/buddy-popup.sh +0 -92
- package/adapters/claude/popup/buddy-render.sh +0 -540
- package/adapters/claude/popup/popup-manager.sh +0 -355
package/cli/upgrade.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { join, resolve, dirname } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
|
|
8
|
+
const CYAN = "\x1b[36m";
|
|
9
|
+
const GREEN = "\x1b[32m";
|
|
10
|
+
const YELLOW = "\x1b[33m";
|
|
11
|
+
const RED = "\x1b[31m";
|
|
12
|
+
const BOLD = "\x1b[1m";
|
|
13
|
+
const DIM = "\x1b[2m";
|
|
14
|
+
const NC = "\x1b[0m";
|
|
15
|
+
|
|
16
|
+
const PROJECT_ROOT = resolve(dirname(import.meta.dir));
|
|
17
|
+
|
|
18
|
+
function ok(msg: string) { console.log(`${GREEN}✓${NC} ${msg}`); }
|
|
19
|
+
function info(msg: string) { console.log(`${CYAN}→${NC} ${msg}`); }
|
|
20
|
+
function warn(msg: string) { console.log(`${YELLOW}⚠${NC} ${msg}`); }
|
|
21
|
+
function err(msg: string) { console.log(`${RED}✗${NC} ${msg}`); }
|
|
22
|
+
|
|
23
|
+
function tryExec(cmd: string, fallback = ""): string {
|
|
24
|
+
try {
|
|
25
|
+
return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
26
|
+
} catch {
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getCurrentVersion(): string {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(join(PROJECT_ROOT, "package.json"), "utf8"));
|
|
34
|
+
return pkg.version ?? "unknown";
|
|
35
|
+
} catch {
|
|
36
|
+
return "unknown";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function banner() {
|
|
41
|
+
console.log(`
|
|
42
|
+
${CYAN}╔══════════════════════════════════════════════════════════╗${NC}
|
|
43
|
+
${CYAN}║${NC} ${BOLD}claude-buddy upgrade${NC} ${CYAN}║${NC}
|
|
44
|
+
${CYAN}╚══════════════════════════════════════════════════════════╝${NC}
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function checkGitRepo(): boolean {
|
|
49
|
+
const isRepo = tryExec("git rev-parse --is-inside-work-tree 2>/dev/null");
|
|
50
|
+
if (isRepo !== "true") {
|
|
51
|
+
err("Not inside a git repository. Upgrade requires a git clone of claude-buddy.");
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getRemoteBranch(): string {
|
|
58
|
+
const branch = tryExec("git rev-parse --abbrev-ref HEAD 2>/dev/null", "main");
|
|
59
|
+
return branch === "HEAD" ? "main" : branch;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function checkForUpdates(branch: string): { hasUpdate: boolean; local: string; remote: string; commits: string[] } {
|
|
63
|
+
info("Fetching latest from remote...\n");
|
|
64
|
+
try {
|
|
65
|
+
execSync("git fetch --quiet 2>/dev/null", { cwd: PROJECT_ROOT, stdio: "ignore" });
|
|
66
|
+
} catch {
|
|
67
|
+
warn("git fetch failed — proceeding with cached remote state");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const local = tryExec("git rev-parse HEAD 2>/dev/null");
|
|
71
|
+
const upstream = tryExec(`git rev-parse '@{upstream}' 2>/dev/null`);
|
|
72
|
+
const remote = upstream || tryExec(`git rev-parse origin/${branch} 2>/dev/null`);
|
|
73
|
+
|
|
74
|
+
if (!local || !remote) {
|
|
75
|
+
warn("Could not determine remote HEAD — no tracking branch configured");
|
|
76
|
+
info(`To set one: git branch --set-upstream-to=origin/${branch} ${branch}`);
|
|
77
|
+
return { hasUpdate: false, local, remote, commits: [] };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (local === remote) {
|
|
81
|
+
return { hasUpdate: false, local, remote, commits: [] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const commits = tryExec(
|
|
85
|
+
`git log --oneline ${local}..origin/${branch} 2>/dev/null`,
|
|
86
|
+
).split("\n").filter(Boolean);
|
|
87
|
+
|
|
88
|
+
return { hasUpdate: true, local, remote, commits };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function pullLatest(branch: string): boolean {
|
|
92
|
+
info(`Pulling latest from origin/${branch}...`);
|
|
93
|
+
try {
|
|
94
|
+
const output = execSync(`git pull --ff-only origin ${branch} 2>&1`, {
|
|
95
|
+
cwd: PROJECT_ROOT,
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
});
|
|
98
|
+
ok("Git pull successful");
|
|
99
|
+
return true;
|
|
100
|
+
} catch (e: any) {
|
|
101
|
+
err("git pull failed — you may have local changes that conflict");
|
|
102
|
+
err(e.message?.split("\n")[0] || "unknown error");
|
|
103
|
+
info("Stash or commit your local changes, then re-run upgrade");
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function installDeps(): boolean {
|
|
109
|
+
info("Installing dependencies...");
|
|
110
|
+
try {
|
|
111
|
+
execSync("bun install 2>&1", { cwd: PROJECT_ROOT, stdio: "ignore" });
|
|
112
|
+
ok("Dependencies installed");
|
|
113
|
+
return true;
|
|
114
|
+
} catch {
|
|
115
|
+
err("bun install failed");
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function reinstallBuddy(): boolean {
|
|
121
|
+
info("Re-running install-buddy to update integrations...\n");
|
|
122
|
+
try {
|
|
123
|
+
execSync("bun run install-buddy 2>&1", { cwd: PROJECT_ROOT, stdio: "inherit" });
|
|
124
|
+
return true;
|
|
125
|
+
} catch {
|
|
126
|
+
err("install-buddy failed");
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function printSummary(oldVersion: string, commits: string[]) {
|
|
132
|
+
const newVersion = getCurrentVersion();
|
|
133
|
+
|
|
134
|
+
console.log("");
|
|
135
|
+
console.log(`${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`);
|
|
136
|
+
console.log(`${GREEN} Upgrade complete!${NC}`);
|
|
137
|
+
console.log(`${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`);
|
|
138
|
+
console.log("");
|
|
139
|
+
console.log(` ${BOLD}Version:${NC} ${oldVersion} → ${BOLD}${newVersion}${NC}`);
|
|
140
|
+
|
|
141
|
+
if (commits.length > 0) {
|
|
142
|
+
console.log(` ${BOLD}Changes:${NC}`);
|
|
143
|
+
const display = commits.slice(0, 15);
|
|
144
|
+
for (const c of display) {
|
|
145
|
+
console.log(` ${DIM}${c}${NC}`);
|
|
146
|
+
}
|
|
147
|
+
if (commits.length > 15) {
|
|
148
|
+
console.log(` ${DIM}... and ${commits.length - 15} more${NC}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log("");
|
|
153
|
+
console.log(`${DIM} Restart Claude Code for changes to take effect.${NC}`);
|
|
154
|
+
console.log("");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const args = process.argv.slice(2);
|
|
158
|
+
const checkOnly = args.includes("--check");
|
|
159
|
+
|
|
160
|
+
banner();
|
|
161
|
+
|
|
162
|
+
const oldVersion = getCurrentVersion();
|
|
163
|
+
info(`Current version: ${oldVersion}\n`);
|
|
164
|
+
|
|
165
|
+
if (!checkGitRepo()) {
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const branch = getRemoteBranch();
|
|
170
|
+
const { hasUpdate, commits } = checkForUpdates(branch);
|
|
171
|
+
|
|
172
|
+
if (!hasUpdate) {
|
|
173
|
+
ok(`Already up to date (v${oldVersion})`);
|
|
174
|
+
console.log("");
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (checkOnly) {
|
|
179
|
+
warn(`Update available! ${commits.length} new commit${commits.length === 1 ? "" : "s"}:`);
|
|
180
|
+
for (const c of commits.slice(0, 10)) {
|
|
181
|
+
console.log(` ${DIM}${c}${NC}`);
|
|
182
|
+
}
|
|
183
|
+
if (commits.length > 10) {
|
|
184
|
+
console.log(` ${DIM}... and ${commits.length - 10} more${NC}`);
|
|
185
|
+
}
|
|
186
|
+
console.log("");
|
|
187
|
+
info("Run without --check to apply the update");
|
|
188
|
+
console.log("");
|
|
189
|
+
process.exit(0);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
info(`${commits.length} new commit${commits.length === 1 ? "" : ""} available:`);
|
|
193
|
+
for (const c of commits.slice(0, 10)) {
|
|
194
|
+
console.log(` ${DIM}${c}${NC}`);
|
|
195
|
+
}
|
|
196
|
+
if (commits.length > 10) {
|
|
197
|
+
console.log(` ${DIM}... and ${commits.length - 10} more${NC}`);
|
|
198
|
+
}
|
|
199
|
+
console.log("");
|
|
200
|
+
|
|
201
|
+
if (!pullLatest(branch)) {
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!installDeps()) {
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!reinstallBuddy()) {
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
printSummary(oldVersion, commits);
|
package/core/model.ts
CHANGED
|
@@ -26,6 +26,11 @@ export interface ReactionState {
|
|
|
26
26
|
reason: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export interface BuddyTurnCommentModelConfig {
|
|
30
|
+
provider: string;
|
|
31
|
+
model: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
export interface BuddyConfig {
|
|
30
35
|
commentCooldown: number;
|
|
31
36
|
reactionTTL: number;
|
|
@@ -33,6 +38,7 @@ export interface BuddyConfig {
|
|
|
33
38
|
bubblePosition: "top" | "left";
|
|
34
39
|
showRarity: boolean;
|
|
35
40
|
statusLineEnabled: boolean;
|
|
41
|
+
turnCommentModel?: BuddyTurnCommentModelConfig;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
export interface GlobalCounters {
|
package/package.json
CHANGED
|
@@ -1,64 +1,80 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
2
|
+
"name": "@ramarivera/coding-buddy",
|
|
3
|
+
"version": "0.4.0-alpha.9",
|
|
4
|
+
"description": "Persistent coding companion for Claude Code and pi",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"coding-buddy": "./cli/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"server": "bun run adapters/claude/server/index.ts",
|
|
11
|
+
"pick": "bun run adapters/claude/install/pick.ts",
|
|
12
|
+
"hunt": "bun run adapters/claude/install/hunt.ts",
|
|
13
|
+
"install-buddy": "bun run adapters/claude/install/install.ts",
|
|
14
|
+
"show": "bun run adapters/claude/install/show.ts",
|
|
15
|
+
"doctor": "bun run adapters/claude/install/doctor.ts",
|
|
16
|
+
"test-statusline": "bun run adapters/claude/install/test-statusline.ts",
|
|
17
|
+
"backup": "bun run adapters/claude/install/backup.ts",
|
|
18
|
+
"settings": "bun run adapters/claude/install/settings.ts",
|
|
19
|
+
"disable": "bun run adapters/claude/install/disable.ts",
|
|
20
|
+
"enable": "bun run adapters/claude/install/install.ts",
|
|
21
|
+
"upgrade": "bun run cli/upgrade.ts",
|
|
22
|
+
"uninstall": "bun run adapters/claude/install/uninstall.ts",
|
|
23
|
+
"help": "bun run cli/index.ts help",
|
|
24
|
+
"buddy-shell": "npx tsx cli/buddy-shell.ts",
|
|
25
|
+
"tui": "bun run cli/tui.tsx",
|
|
26
|
+
"test": "bun test",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"core/",
|
|
31
|
+
"adapters/",
|
|
32
|
+
"cli/",
|
|
33
|
+
"scripts/",
|
|
34
|
+
"server/",
|
|
35
|
+
"!**/*.test.ts"
|
|
36
|
+
],
|
|
37
|
+
"keywords": [
|
|
38
|
+
"claude-code",
|
|
39
|
+
"buddy",
|
|
40
|
+
"companion",
|
|
41
|
+
"mcp",
|
|
42
|
+
"terminal-pet",
|
|
43
|
+
"tamagotchi",
|
|
44
|
+
"pi-package"
|
|
45
|
+
],
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/ramarivera/claude-buddy.git"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/ramarivera/claude-buddy#readme",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public",
|
|
54
|
+
"registry": "https://registry.npmjs.org/"
|
|
55
|
+
},
|
|
56
|
+
"pi": {
|
|
57
|
+
"extensions": [
|
|
58
|
+
"./adapters/pi/index.ts"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
|
|
63
|
+
"@mariozechner/pi-coding-agent": "0.66.1",
|
|
64
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
65
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
66
|
+
"@xterm/headless": "^6.0.0",
|
|
67
|
+
"ink": "^7.0.0",
|
|
68
|
+
"pino": "^10.3.1",
|
|
69
|
+
"react": "^19.2.5"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/react": "^19.2.14",
|
|
73
|
+
"bun-types": "^1.3.11",
|
|
74
|
+
"tsx": "^4.21.0",
|
|
75
|
+
"typescript": "^6.0.2"
|
|
76
|
+
},
|
|
77
|
+
"trustedDependencies": [
|
|
78
|
+
"@homebridge/node-pty-prebuilt-multiarch"
|
|
79
|
+
]
|
|
64
80
|
}
|
package/scripts/paths.sh
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Path resolvers for claude-buddy shell scripts.
|
|
3
|
+
#
|
|
4
|
+
# Must stay in sync with server/path.ts. Source this file early:
|
|
5
|
+
# source "$(dirname "$0")/../scripts/paths.sh"
|
|
6
|
+
# …and consumers get BUDDY_STATE_DIR, CLAUDE_CFG_DIR, CLAUDE_SETTINGS_FILE,
|
|
7
|
+
# and CLAUDE_USER_CONFIG.
|
|
8
|
+
#
|
|
9
|
+
# Resolution rules (must match server/path.ts):
|
|
10
|
+
# - If CLAUDE_CONFIG_DIR is set → everything lives under it
|
|
11
|
+
# (settings.json, skills/, .claude.json inside the config dir, and
|
|
12
|
+
# buddy state at $CLAUDE_CONFIG_DIR/buddy-state).
|
|
13
|
+
# - Else (single-profile default) → $HOME/.claude, $HOME/.claude.json,
|
|
14
|
+
# $HOME/.claude-buddy.
|
|
15
|
+
|
|
16
|
+
if [[ -n "${CLAUDE_CONFIG_DIR:-}" ]]; then
|
|
17
|
+
CLAUDE_CFG_DIR="$CLAUDE_CONFIG_DIR"
|
|
18
|
+
else
|
|
19
|
+
CLAUDE_CFG_DIR="$HOME/.claude"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
CLAUDE_SETTINGS_FILE="$CLAUDE_CFG_DIR/settings.json"
|
|
23
|
+
|
|
24
|
+
# .claude.json: inside CLAUDE_CONFIG_DIR when set, else $HOME. We never
|
|
25
|
+
# fall back to $HOME when CLAUDE_CONFIG_DIR is set — doing so would break
|
|
26
|
+
# profile isolation (enabling buddy in one profile could mutate the
|
|
27
|
+
# home-level file that a different profile reads).
|
|
28
|
+
if [[ -n "${CLAUDE_CONFIG_DIR:-}" ]]; then
|
|
29
|
+
CLAUDE_USER_CONFIG="$CLAUDE_CONFIG_DIR/.claude.json"
|
|
30
|
+
else
|
|
31
|
+
CLAUDE_USER_CONFIG="$HOME/.claude.json"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [[ -n "${CLAUDE_CONFIG_DIR:-}" ]]; then
|
|
35
|
+
BUDDY_STATE_DIR="$CLAUDE_CONFIG_DIR/buddy-state"
|
|
36
|
+
else
|
|
37
|
+
BUDDY_STATE_DIR="$HOME/.claude-buddy"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
export CLAUDE_CFG_DIR CLAUDE_SETTINGS_FILE CLAUDE_USER_CONFIG BUDDY_STATE_DIR
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ACHIEVEMENTS,
|
|
3
|
+
type Achievement,
|
|
4
|
+
type EventCounters,
|
|
5
|
+
} from "../core/achievements.ts";
|
|
6
|
+
export type { UnlockedAchievement } from "../core/model.ts";
|
|
7
|
+
export {
|
|
8
|
+
loadUnlocked,
|
|
9
|
+
loadEvents,
|
|
10
|
+
incrementEvent,
|
|
11
|
+
checkAndAward,
|
|
12
|
+
trackActiveDay,
|
|
13
|
+
renderAchievementsCard,
|
|
14
|
+
renderAchievementsCardMarkdown,
|
|
15
|
+
} from "../adapters/claude/storage/achievements.ts";
|
package/server/art.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../core/render-model.ts";
|
package/server/engine.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../core/engine.ts";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -eu
|
|
3
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
4
|
+
cat >&2 <<'MSG'
|
|
5
|
+
[claude-buddy] ERROR: 'bun' was not found on PATH.
|
|
6
|
+
|
|
7
|
+
claude-buddy's MCP server runs on bun. Install it with:
|
|
8
|
+
|
|
9
|
+
curl -fsSL https://bun.sh/install | bash
|
|
10
|
+
|
|
11
|
+
Then open a new shell and restart Claude Code.
|
|
12
|
+
MSG
|
|
13
|
+
exit 127
|
|
14
|
+
fi
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
16
|
+
exec bun "$SCRIPT_DIR/../adapters/claude/server/index.ts"
|
package/server/path.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBuddySkillDir,
|
|
3
|
+
getBuddyStateDir,
|
|
4
|
+
getClaudeConfigDir,
|
|
5
|
+
getClaudeJsonPath,
|
|
6
|
+
getClaudeSettingsPath,
|
|
7
|
+
toUnixPath,
|
|
8
|
+
} from "../adapters/claude/storage/paths.ts";
|
|
9
|
+
|
|
10
|
+
export function claudeConfigDir(): string {
|
|
11
|
+
return getClaudeConfigDir();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function claudeSettingsPath(): string {
|
|
15
|
+
return getClaudeSettingsPath();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function claudeUserConfigPath(): string {
|
|
19
|
+
return getClaudeJsonPath();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buddyStateDir(): string {
|
|
23
|
+
return getBuddyStateDir();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function claudeSkillDir(_name: string): string {
|
|
27
|
+
return getBuddySkillDir();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { toUnixPath };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../core/reactions.ts";
|
package/server/state.ts
ADDED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# claude-buddy popup entry point -- runs INSIDE the tmux popup
|
|
3
|
-
#
|
|
4
|
-
# Architecture:
|
|
5
|
-
# - Render loop runs in BACKGROUND (only writes to stdout, never reads stdin)
|
|
6
|
-
# - Input forwarder runs in FOREGROUND (owns stdin exclusively)
|
|
7
|
-
#
|
|
8
|
-
# The reopen loop in popup-manager.sh handles:
|
|
9
|
-
# - ESC forwarding (when tmux closes popup on ESC)
|
|
10
|
-
# - CC pane death detection
|
|
11
|
-
# - Dynamic resizing on reopen
|
|
12
|
-
#
|
|
13
|
-
# Env vars (set by popup-manager.sh via -e):
|
|
14
|
-
# CC_PANE -- tmux pane ID for Claude Code (e.g. %0)
|
|
15
|
-
# BUDDY_DIR -- ~/.claude-buddy
|
|
16
|
-
# BUDDY_SID -- session ID (sanitized pane number, e.g. "0")
|
|
17
|
-
# Args: $1 = SID (fallback for tmux < 3.4 without -e support)
|
|
18
|
-
|
|
19
|
-
set -uo pipefail
|
|
20
|
-
|
|
21
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
22
|
-
|
|
23
|
-
# Session ID: from env (tmux 3.4+), $1 arg, or "default"
|
|
24
|
-
BUDDY_SID="${BUDDY_SID:-${1:-default}}"
|
|
25
|
-
|
|
26
|
-
# On tmux 3.2-3.3, env vars are passed via file (no -e flag support)
|
|
27
|
-
ENV_FILE="${HOME}/.claude-buddy/popup-env.$BUDDY_SID"
|
|
28
|
-
if [ -z "${CC_PANE:-}" ] && [ -f "$ENV_FILE" ]; then
|
|
29
|
-
. "$ENV_FILE"
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
if [ -z "${CC_PANE:-}" ]; then
|
|
33
|
-
echo "Error: CC_PANE not set" >&2
|
|
34
|
-
sleep 2
|
|
35
|
-
exit 1
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# ─── Cleanup on exit ─────────────────────────────────────────────────────────
|
|
39
|
-
cleanup() {
|
|
40
|
-
[ -n "${RENDER_PID:-}" ] && kill "$RENDER_PID" 2>/dev/null
|
|
41
|
-
tput cnorm 2>/dev/null
|
|
42
|
-
stty sane 2>/dev/null
|
|
43
|
-
}
|
|
44
|
-
trap cleanup EXIT INT TERM HUP
|
|
45
|
-
|
|
46
|
-
# Hide cursor
|
|
47
|
-
tput civis 2>/dev/null
|
|
48
|
-
|
|
49
|
-
# ─── Render loop in BACKGROUND (stdout only, no stdin) ───────────────────────
|
|
50
|
-
"$SCRIPT_DIR/buddy-render.sh" </dev/null &
|
|
51
|
-
RENDER_PID=$!
|
|
52
|
-
|
|
53
|
-
# ─── Input forwarder in FOREGROUND ───────────────────────────────────────────
|
|
54
|
-
# Raw mode: all bytes pass through without terminal interpretation.
|
|
55
|
-
# No SIGINT on Ctrl-C, no EOF on Ctrl-D, no CR-to-NL on Enter.
|
|
56
|
-
stty raw -echo 2>/dev/null
|
|
57
|
-
|
|
58
|
-
# Use perl for raw byte forwarding. bash's read -n1 internally overrides
|
|
59
|
-
# terminal settings on each call (saves/restores tty mode), which undoes
|
|
60
|
-
# stty raw and re-enables signal processing. perl's sysread doesn't touch
|
|
61
|
-
# the terminal at all -- it reads raw bytes from the file descriptor.
|
|
62
|
-
#
|
|
63
|
-
# Batching: first byte is a blocking read, then non-blocking drain of any
|
|
64
|
-
# remaining bytes (paste arrives as a burst). The batch is sent to CC in
|
|
65
|
-
# a single tmux send-keys call for efficiency.
|
|
66
|
-
exec perl -e '
|
|
67
|
-
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
|
|
68
|
-
my $pane = $ENV{CC_PANE};
|
|
69
|
-
while (1) {
|
|
70
|
-
my $buf;
|
|
71
|
-
my $n = sysread(STDIN, $buf, 1);
|
|
72
|
-
last unless $n;
|
|
73
|
-
# Non-blocking drain for paste batching
|
|
74
|
-
my $flags = fcntl(STDIN, F_GETFL, 0);
|
|
75
|
-
fcntl(STDIN, F_SETFL, $flags | O_NONBLOCK);
|
|
76
|
-
while (sysread(STDIN, my $more, 4096)) {
|
|
77
|
-
$buf .= $more;
|
|
78
|
-
}
|
|
79
|
-
fcntl(STDIN, F_SETFL, $flags);
|
|
80
|
-
|
|
81
|
-
# F12 (\e[24~) = close popup and enter scroll mode
|
|
82
|
-
if ($buf =~ /\e\[24~/) {
|
|
83
|
-
my $buddy_dir = $ENV{BUDDY_DIR} || "$ENV{HOME}/.claude-buddy";
|
|
84
|
-
my $sid = $ENV{BUDDY_SID} // "default";
|
|
85
|
-
open(my $fh, ">", "$buddy_dir/popup-scroll.$sid");
|
|
86
|
-
close($fh) if $fh;
|
|
87
|
-
exit 0;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
system("tmux", "send-keys", "-t", $pane, "-l", "--", $buf);
|
|
91
|
-
}
|
|
92
|
-
'
|