@ramarivera/coding-buddy 0.4.0-alpha.9 → 0.7.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/.claude-plugin/marketplace.json +45 -0
- package/.claude-plugin/plugin.json +30 -0
- package/LICENSE +1 -1
- package/README.md +96 -45
- package/adapters/omp/commands.ts +201 -0
- package/adapters/omp/context.ts +22 -0
- package/adapters/omp/events.ts +551 -0
- package/adapters/omp/identity.ts +10 -0
- package/adapters/omp/index.ts +39 -0
- package/adapters/omp/logger.ts +52 -0
- package/adapters/omp/prompt.ts +60 -0
- package/adapters/omp/renderers.ts +73 -0
- package/adapters/omp/storage.ts +25 -0
- package/adapters/omp/ui.ts +85 -0
- package/adapters/pi/commands.ts +64 -37
- package/adapters/pi/events.ts +147 -72
- package/adapters/pi/index.ts +9 -4
- package/adapters/pi/storage.ts +14 -285
- package/adapters/pi/ui.ts +56 -5
- package/adapters/shared/file-storage.ts +526 -0
- package/{adapters/claude/install → cli}/backup.ts +130 -33
- package/cli/buddy-shell.ts +1 -1
- package/{adapters/claude/install → cli}/disable.ts +19 -32
- package/cli/doctor.ts +231 -0
- package/{adapters/claude/install → cli}/hunt.ts +7 -9
- package/cli/index.ts +30 -26
- package/{adapters/claude/install → cli}/install.ts +65 -83
- package/{adapters/claude/install → cli}/pick.ts +136 -57
- package/{adapters/claude/install → cli}/settings.ts +2 -2
- package/{adapters/claude/install → cli}/show.ts +4 -4
- package/{adapters/claude/install → cli}/test-statusline.sh +2 -2
- package/{adapters/claude/install → cli}/test-statusline.ts +21 -11
- package/cli/tui.tsx +106 -17
- package/{adapters/claude/install → cli}/uninstall.ts +40 -47
- package/cli/upgrade.ts +46 -13
- package/cli/validate-species.ts +209 -0
- package/cli/verify.ts +3 -3
- package/core/achievements.ts +12 -20
- package/core/art-data.ts +26 -0
- package/core/command-service.ts +99 -97
- package/core/engine.ts +83 -1
- package/core/ports.ts +10 -0
- package/core/reactions.ts +533 -95
- package/core/slot-slug.ts +22 -0
- package/{adapters/claude/hooks → hooks}/buddy-comment.sh +22 -9
- package/hooks/file-type-react.sh +403 -0
- package/hooks/hooks.json +52 -0
- package/hooks/mood-react.sh +210 -0
- package/{adapters/claude/hooks → hooks}/name-react.sh +3 -4
- package/hooks/react.sh +1364 -0
- package/hooks/suggest.sh +55 -0
- package/package.json +70 -31
- package/scripts/gen-emoji-widths.ts +34 -0
- package/scripts/paths.sh +33 -2
- package/scripts/postinstall.cjs +42 -0
- package/scripts/snapshot-statusline.sh +54 -0
- package/server/achievements.ts +1419 -15
- package/server/art.ts +450 -1
- package/server/award-xp.ts +48 -0
- package/server/check-suggestions.ts +102 -0
- package/server/consolidate.ts +33 -0
- package/{adapters/claude/server → server}/index.ts +556 -95
- package/server/mcp-launcher.sh +16 -6
- package/server/memory.ts +404 -0
- package/server/mood.ts +254 -0
- package/server/path.ts +70 -18
- package/server/shift-mood.ts +43 -0
- package/server/state.ts +552 -3
- package/server/suggestions.ts +335 -0
- package/server/theme.ts +53 -0
- package/server/xp.ts +435 -0
- package/{adapters/claude/skills → skills}/buddy/SKILL.md +36 -7
- package/statusline/buddy-status.sh +402 -0
- package/statusline/combined-status.sh +135 -0
- package/statusline/emoji-widths.data +4 -0
- package/adapters/claude/hooks/hooks.json +0 -35
- package/adapters/claude/hooks/react.sh +0 -207
- package/adapters/claude/install/doctor.ts +0 -172
- package/adapters/claude/plugin/marketplace.json +0 -40
- package/adapters/claude/plugin/plugin.json +0 -26
- package/adapters/claude/rendering/art.ts +0 -264
- package/adapters/claude/server/instructions.ts +0 -24
- package/adapters/claude/server/path.ts +0 -12
- package/adapters/claude/server/resources.ts +0 -38
- package/adapters/claude/statusline/buddy-status.sh +0 -408
- package/adapters/claude/storage/achievements.ts +0 -253
- package/adapters/claude/storage/identity.ts +0 -13
- package/adapters/claude/storage/paths.ts +0 -33
- package/adapters/claude/storage/settings.ts +0 -91
- package/adapters/claude/storage/state.ts +0 -365
- package/adapters/pi/README.md +0 -83
- package/adapters/pi/tools.ts +0 -6
- package/server/engine.ts +0 -1
- package/server/reactions.ts +0 -1
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# claude-buddy PostToolUse hook
|
|
3
|
-
# Detects events in Bash tool output and writes a reaction to the status line.
|
|
4
|
-
#
|
|
5
|
-
# Combined: PR #4 species reactions + PR #6 session isolation + PR #13 field fix
|
|
6
|
-
|
|
7
|
-
# shellcheck source=../scripts/paths.sh
|
|
8
|
-
source "$(dirname "${BASH_SOURCE[0]}")/../scripts/paths.sh"
|
|
9
|
-
|
|
10
|
-
STATE_DIR="$BUDDY_STATE_DIR"
|
|
11
|
-
# Session ID: sanitized tmux pane number, or "default" outside tmux
|
|
12
|
-
SID="${TMUX_PANE#%}"
|
|
13
|
-
SID="${SID:-default}"
|
|
14
|
-
REACTION_FILE="$STATE_DIR/reaction.$SID.json"
|
|
15
|
-
STATUS_FILE="$STATE_DIR/status.json"
|
|
16
|
-
COOLDOWN_FILE="$STATE_DIR/.last_reaction.$SID"
|
|
17
|
-
CONFIG_FILE="$STATE_DIR/config.json"
|
|
18
|
-
EVENTS_FILE="$STATE_DIR/events.json"
|
|
19
|
-
|
|
20
|
-
[ -f "$STATUS_FILE" ] || exit 0
|
|
21
|
-
|
|
22
|
-
INPUT=$(cat)
|
|
23
|
-
|
|
24
|
-
# Read cooldown from config (default 30s, 0 = disabled)
|
|
25
|
-
COOLDOWN=30
|
|
26
|
-
if [ -f "$CONFIG_FILE" ]; then
|
|
27
|
-
_cd=$(jq -r '.commentCooldown // 30' "$CONFIG_FILE" 2>/dev/null || echo 30)
|
|
28
|
-
# Accept any non-negative integer (including 0 to disable cooldown)
|
|
29
|
-
[[ "$_cd" =~ ^[0-9]+$ ]] && COOLDOWN=$_cd
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
# Cooldown: configurable
|
|
33
|
-
if [ -f "$COOLDOWN_FILE" ]; then
|
|
34
|
-
LAST=$(cat "$COOLDOWN_FILE" 2>/dev/null)
|
|
35
|
-
NOW=$(date +%s)
|
|
36
|
-
DIFF=$(( NOW - ${LAST:-0} ))
|
|
37
|
-
[ "$DIFF" -lt "$COOLDOWN" ] && exit 0
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
# Extract tool response (PostToolUse schema field is .tool_response — not
|
|
41
|
-
# .tool_result, which is the Anthropic SDK's content-block type name and is
|
|
42
|
-
# never a key on the hook input payload).
|
|
43
|
-
RESULT=$(echo "$INPUT" | jq -r '.tool_response // ""' 2>/dev/null)
|
|
44
|
-
[ -z "$RESULT" ] && exit 0
|
|
45
|
-
|
|
46
|
-
MUTED=$(jq -r '.muted // false' "$STATUS_FILE" 2>/dev/null)
|
|
47
|
-
[ "$MUTED" = "true" ] && exit 0
|
|
48
|
-
|
|
49
|
-
SPECIES=$(jq -r '.species // "blob"' "$STATUS_FILE" 2>/dev/null)
|
|
50
|
-
NAME=$(jq -r '.name // "buddy"' "$STATUS_FILE" 2>/dev/null)
|
|
51
|
-
|
|
52
|
-
REASON=""
|
|
53
|
-
REACTION=""
|
|
54
|
-
POOLS=()
|
|
55
|
-
|
|
56
|
-
# ─── Pick from a pool by species + event ─────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
pick_reaction() {
|
|
59
|
-
local event="$1"
|
|
60
|
-
|
|
61
|
-
case "${SPECIES}:${event}" in
|
|
62
|
-
dragon:error)
|
|
63
|
-
POOLS=("*smoke curls from nostril*" "*considers setting it on fire*" "*unimpressed gaze*" "I've seen empires fall for less.") ;;
|
|
64
|
-
dragon:test-fail)
|
|
65
|
-
POOLS=("*breathes a small flame*" "disappointing." "*scorches the failing test*" "fix it. or I will.") ;;
|
|
66
|
-
dragon:success)
|
|
67
|
-
POOLS=("*nods, barely*" "...acceptable." "*gold eyes gleam*" "as expected.") ;;
|
|
68
|
-
owl:error)
|
|
69
|
-
POOLS=("*head rotates 180* I saw that." "*unblinking stare* check your types." "*hoots disapprovingly*" "the error was in the logic. as always.") ;;
|
|
70
|
-
owl:test-fail)
|
|
71
|
-
POOLS=("*marks clipboard*" "hypothesis: rejected." "*peers over spectacles*" "the tests reveal the truth.") ;;
|
|
72
|
-
owl:success)
|
|
73
|
-
POOLS=("*satisfied hoot*" "knowledge confirmed." "*nods sagely*" "as the tests have spoken.") ;;
|
|
74
|
-
cat:error)
|
|
75
|
-
POOLS=("*knocks error off table*" "*licks paw, ignoring stacktrace*" "not my problem." "*stares at you judgmentally*") ;;
|
|
76
|
-
cat:success)
|
|
77
|
-
POOLS=("*was never worried*" "*yawns*" "I knew you'd figure it out. eventually." "*already asleep*") ;;
|
|
78
|
-
duck:error)
|
|
79
|
-
POOLS=("*quacks at the bug*" "have you tried rubber duck debugging? oh wait." "*confused quacking*" "*tilts head*") ;;
|
|
80
|
-
duck:success)
|
|
81
|
-
POOLS=("*celebratory quacking*" "*waddles in circles*" "quack!" "*happy duck noises*") ;;
|
|
82
|
-
robot:error)
|
|
83
|
-
POOLS=("SYNTAX. ERROR. DETECTED." "*beeps aggressively*" "ERROR RATE: UNACCEPTABLE." "RECALIBRATING...") ;;
|
|
84
|
-
robot:test-fail)
|
|
85
|
-
POOLS=("FAILURE RATE: UNACCEPTABLE." "*recalculating*" "TEST MATRIX: CORRUPTED." "RUNNING DIAGNOSTICS...") ;;
|
|
86
|
-
robot:success)
|
|
87
|
-
POOLS=("OBJECTIVE: COMPLETE." "*satisfying beep*" "NOMINAL." "WITHIN ACCEPTABLE PARAMETERS.") ;;
|
|
88
|
-
capybara:error)
|
|
89
|
-
POOLS=("*unbothered* it'll be fine." "*continues vibing*" "...chill. breathe." "*chews serenely*") ;;
|
|
90
|
-
capybara:success)
|
|
91
|
-
POOLS=("*maximum chill maintained*" "*nods once*" "good vibes." "see? no panic needed.") ;;
|
|
92
|
-
ghost:error)
|
|
93
|
-
POOLS=("*phases through the stack trace*" "I've seen worse... in the afterlife." "*spooky disappointed noises*" "oooOOOoo... that's bad.") ;;
|
|
94
|
-
axolotl:error)
|
|
95
|
-
POOLS=("*regenerates your hope*" "*smiles despite everything*" "it's okay. we can fix this." "*gentle gill wiggle*") ;;
|
|
96
|
-
axolotl:success)
|
|
97
|
-
POOLS=("*happy gill flutter*" "*beams*" "you did it!" "*blushes pink*") ;;
|
|
98
|
-
blob:error)
|
|
99
|
-
POOLS=("*oozes with concern*" "*vibrates nervously*" "*turns slightly red*" "oh no oh no oh no") ;;
|
|
100
|
-
blob:success)
|
|
101
|
-
POOLS=("*jiggles happily*" "*gleams*" "yay!" "*bounces*") ;;
|
|
102
|
-
turtle:error)
|
|
103
|
-
POOLS=("*slow blink* bugs are fleeting" "*retreats slightly into shell*" "I've seen this before. many times." "patience. patience.") ;;
|
|
104
|
-
turtle:success)
|
|
105
|
-
POOLS=("*satisfied shell settle*" "as the ancients foretold." "*slow approving nod*" "good. very good.") ;;
|
|
106
|
-
goose:error)
|
|
107
|
-
POOLS=("HONK OF FURY." "*pecks the stack trace*" "*hisses at the bug*" "bad code. BAD.") ;;
|
|
108
|
-
goose:success)
|
|
109
|
-
POOLS=("*victorious honk*" "HONK OF APPROVAL." "*struts triumphantly*" "*wing spread of victory*") ;;
|
|
110
|
-
octopus:error)
|
|
111
|
-
POOLS=("*ink cloud of dismay*" "*all eight arms throw up*" "*turns deep red*" "the abyss of errors beckons.") ;;
|
|
112
|
-
octopus:success)
|
|
113
|
-
POOLS=("*turns gentle blue*" "*arms applaud in sync*" "excellent, from all angles." "*satisfied bubble*") ;;
|
|
114
|
-
penguin:error)
|
|
115
|
-
POOLS=("*adjusts glasses disapprovingly*" "this will not do." "*formal sigh*" "frightfully unfortunate.") ;;
|
|
116
|
-
penguin:success)
|
|
117
|
-
POOLS=("*polite applause*" "quite good, quite good." "*nods approvingly*" "splendid work, really.") ;;
|
|
118
|
-
snail:error)
|
|
119
|
-
POOLS=("*slow sigh*" "such is the nature of bugs." "*leaves slime trail of disappointment*" "patience, friend.") ;;
|
|
120
|
-
snail:success)
|
|
121
|
-
POOLS=("*slow satisfied nod*" "good things take time." "*leaves victory slime*" "see? no rush was needed.") ;;
|
|
122
|
-
cactus:error)
|
|
123
|
-
POOLS=("*spines bristle*" "you have trodden on a bug." "*grimaces stoically*" "hydrate and try again.") ;;
|
|
124
|
-
cactus:success)
|
|
125
|
-
POOLS=("*blooms briefly*" "survival confirmed." "*flowers in victory*" "*quiet bloom*") ;;
|
|
126
|
-
rabbit:error)
|
|
127
|
-
POOLS=("*nervous twitching*" "*hops backwards*" "oh no oh no oh no" "*freezes in panic*") ;;
|
|
128
|
-
rabbit:success)
|
|
129
|
-
POOLS=("*excited binky*" "*zoomies of joy*" "yay yay yay!" "*thumps in celebration*") ;;
|
|
130
|
-
mushroom:error)
|
|
131
|
-
POOLS=("*releases worried spores*" "the mycelium disagrees." "*cap droops*" "decompose. retry.") ;;
|
|
132
|
-
mushroom:success)
|
|
133
|
-
POOLS=("*spores of celebration*" "the mycelium approves." "*cap brightens*" "spore of pride.") ;;
|
|
134
|
-
chonk:error)
|
|
135
|
-
POOLS=("*doesn't move*" "too tired for this." "*grumbles*" "*rolls away from the error*") ;;
|
|
136
|
-
chonk:success)
|
|
137
|
-
POOLS=("*happy purr*" "*satisfied chonk noises*" "acceptable." "*sleeps even harder*") ;;
|
|
138
|
-
*:error)
|
|
139
|
-
POOLS=("*head tilts* ...that doesn't look right." "saw that one coming." "*slow blink* the stack trace told you everything." "*winces*") ;;
|
|
140
|
-
*:test-fail)
|
|
141
|
-
POOLS=("bold of you to assume that would pass." "the tests are trying to tell you something." "*sips tea* interesting." "*marks calendar* test regression day.") ;;
|
|
142
|
-
*:large-diff)
|
|
143
|
-
POOLS=("that's... a lot of changes." "might want to split that PR." "bold move. let's see if CI agrees." "*counts lines nervously*") ;;
|
|
144
|
-
*:success)
|
|
145
|
-
POOLS=("*nods*" "nice." "*quiet approval*" "clean.") ;;
|
|
146
|
-
esac
|
|
147
|
-
|
|
148
|
-
[ ${#POOLS[@]} -gt 0 ] && REACTION="${POOLS[$((RANDOM % ${#POOLS[@]}))]}"
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
# ─── Detect test failures ─────────────────────────────────────────────────────
|
|
152
|
-
if echo "$RESULT" | grep -qiE '\b[1-9][0-9]* (failed|failing)\b|tests? failed|^FAIL(ED)?|✗|✘'; then
|
|
153
|
-
REASON="test-fail"
|
|
154
|
-
pick_reaction "test-fail"
|
|
155
|
-
|
|
156
|
-
# ─── Detect errors ────────────────────────────────────────────────────────────
|
|
157
|
-
elif echo "$RESULT" | grep -qiE '\berror:|\bexception\b|\btraceback\b|\bpanicked at\b|\bfatal:|exit code [1-9]'; then
|
|
158
|
-
REASON="error"
|
|
159
|
-
pick_reaction "error"
|
|
160
|
-
|
|
161
|
-
# ─── Detect large diffs ──────────────────────────────────────────────────────
|
|
162
|
-
elif echo "$RESULT" | grep -qiE '^\+.*[0-9]+ insertions|[0-9]+ files? changed'; then
|
|
163
|
-
LINES=$(echo "$RESULT" | grep -oE '[0-9]+ insertions' | grep -oE '[0-9]+' | head -1)
|
|
164
|
-
if [ "${LINES:-0}" -gt 80 ]; then
|
|
165
|
-
REASON="large-diff"
|
|
166
|
-
pick_reaction "large-diff"
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
# ─── Detect success ───────────────────────────────────────────────────────────
|
|
170
|
-
elif echo "$RESULT" | grep -qiE '\b(all )?[0-9]+ tests? (passed|ok)\b|✓|✔|PASS(ED)?|\bDone\b|\bSuccess\b|exit code 0|Build succeeded'; then
|
|
171
|
-
REASON="success"
|
|
172
|
-
pick_reaction "success"
|
|
173
|
-
fi
|
|
174
|
-
|
|
175
|
-
# Write reaction if detected
|
|
176
|
-
if [ -n "$REASON" ] && [ -n "$REACTION" ]; then
|
|
177
|
-
mkdir -p "$STATE_DIR"
|
|
178
|
-
date +%s > "$COOLDOWN_FILE"
|
|
179
|
-
|
|
180
|
-
# Write reaction for status line (use jq for safe JSON encoding)
|
|
181
|
-
jq -n --arg r "$REACTION" --arg ts "$(date +%s)000" --arg reason "$REASON" \
|
|
182
|
-
'{reaction: $r, timestamp: ($ts | tonumber), reason: $reason}' \
|
|
183
|
-
> "$REACTION_FILE"
|
|
184
|
-
|
|
185
|
-
# Update status.json with reaction
|
|
186
|
-
TMP=$(mktemp)
|
|
187
|
-
jq --arg r "$REACTION" '.reaction = $r' "$STATUS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$STATUS_FILE"
|
|
188
|
-
|
|
189
|
-
# Increment achievement event counter
|
|
190
|
-
if command -v jq >/dev/null 2>&1; then
|
|
191
|
-
if [ ! -f "$EVENTS_FILE" ]; then
|
|
192
|
-
echo '{}' > "$EVENTS_FILE"
|
|
193
|
-
fi
|
|
194
|
-
case "$REASON" in
|
|
195
|
-
"test-fail") KEY="tests_failed" ;;
|
|
196
|
-
"error") KEY="errors_seen" ;;
|
|
197
|
-
"large-diff") KEY="large_diffs" ;;
|
|
198
|
-
*) KEY="" ;;
|
|
199
|
-
esac
|
|
200
|
-
if [ -n "$KEY" ]; then
|
|
201
|
-
TMP=$(mktemp)
|
|
202
|
-
jq --arg k "$KEY" 'if .[$k] then .[$k] += 1 else .[$k] = 1 end' "$EVENTS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$EVENTS_FILE"
|
|
203
|
-
fi
|
|
204
|
-
fi
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
exit 0
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* claude-buddy doctor — comprehensive diagnostic report
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync, existsSync } from "fs";
|
|
7
|
-
import { execSync } from "child_process";
|
|
8
|
-
import { join, resolve } from "path";
|
|
9
|
-
import { getBuddySkillDir, getBuddyStateDir, getClaudeConfigDir, getClaudeJsonPath, getClaudeSettingsPath } from "../storage/paths.ts";
|
|
10
|
-
|
|
11
|
-
const PROJECT_ROOT = resolve(import.meta.dir, "../../..");
|
|
12
|
-
const CLAUDE_DIR = getClaudeConfigDir();
|
|
13
|
-
const CLAUDE_JSON = getClaudeJsonPath();
|
|
14
|
-
const STATE_DIR = getBuddyStateDir();
|
|
15
|
-
const SETTINGS = getClaudeSettingsPath();
|
|
16
|
-
const SKILL_PATH = join(getBuddySkillDir(), "SKILL.md");
|
|
17
|
-
const STATUS_SCRIPT = join(PROJECT_ROOT, "adapters", "claude", "statusline", "buddy-status.sh");
|
|
18
|
-
|
|
19
|
-
const RED = "\x1b[31m";
|
|
20
|
-
const GREEN = "\x1b[32m";
|
|
21
|
-
const YELLOW = "\x1b[33m";
|
|
22
|
-
const CYAN = "\x1b[36m";
|
|
23
|
-
const BOLD = "\x1b[1m";
|
|
24
|
-
const DIM = "\x1b[2m";
|
|
25
|
-
const NC = "\x1b[0m";
|
|
26
|
-
|
|
27
|
-
function section(title: string) {
|
|
28
|
-
console.log(`\n${CYAN}${BOLD}━━━ ${title} ${"━".repeat(Math.max(0, 60 - title.length))}${NC}`);
|
|
29
|
-
}
|
|
30
|
-
function row(label: string, value: string) {
|
|
31
|
-
console.log(` ${DIM}${label.padEnd(28)}${NC} ${value}`);
|
|
32
|
-
}
|
|
33
|
-
function ok(msg: string) { console.log(` ${GREEN}✓${NC} ${msg}`); }
|
|
34
|
-
function warn(msg: string) { console.log(` ${YELLOW}⚠${NC} ${msg}`); }
|
|
35
|
-
function err(msg: string) { console.log(` ${RED}✗${NC} ${msg}`); }
|
|
36
|
-
|
|
37
|
-
function tryExec(cmd: string, fallback = "(failed)"): string {
|
|
38
|
-
try {
|
|
39
|
-
return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
40
|
-
} catch {
|
|
41
|
-
return fallback;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function tryRead(path: string): string | null {
|
|
45
|
-
try { return readFileSync(path, "utf8"); } catch { return null; }
|
|
46
|
-
}
|
|
47
|
-
function tryParseJson(text: string | null): any {
|
|
48
|
-
if (!text) return null;
|
|
49
|
-
try { return JSON.parse(text); } catch { return null; }
|
|
50
|
-
}
|
|
51
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
52
|
-
return typeof value === "object" && value !== null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log(`${CYAN}${BOLD}
|
|
56
|
-
╔══════════════════════════════════════════════════════════╗
|
|
57
|
-
║ claude-buddy doctor — diagnostic report ║
|
|
58
|
-
╚══════════════════════════════════════════════════════════╝${NC}`);
|
|
59
|
-
console.log(`\n${DIM}Copy this entire output into your GitHub issue.${NC}`);
|
|
60
|
-
|
|
61
|
-
section("Environment");
|
|
62
|
-
row("OS", tryExec("uname -srm"));
|
|
63
|
-
row("Hostname", tryExec("uname -n"));
|
|
64
|
-
row("User shell", process.env.SHELL ?? "(unset)");
|
|
65
|
-
row("Bash version", tryExec("bash --version | head -1"));
|
|
66
|
-
row("Bun version", tryExec("bun --version"));
|
|
67
|
-
row("Node version", tryExec("node --version", "(not installed)"));
|
|
68
|
-
row("jq version", tryExec("jq --version", "(not installed)"));
|
|
69
|
-
row("Claude Code version", tryExec("claude --version", "(not in PATH)"));
|
|
70
|
-
|
|
71
|
-
section("Terminal");
|
|
72
|
-
row("TERM", process.env.TERM ?? "(unset)");
|
|
73
|
-
row("COLORTERM", process.env.COLORTERM ?? "(unset)");
|
|
74
|
-
row("TERM_PROGRAM", process.env.TERM_PROGRAM ?? "(unset)");
|
|
75
|
-
row("LANG", process.env.LANG ?? "(unset)");
|
|
76
|
-
row("COLUMNS env var", process.env.COLUMNS ?? "(unset in subprocess)");
|
|
77
|
-
row("stty size", tryExec("stty size 2>/dev/null", "(no tty)"));
|
|
78
|
-
row("tput cols", tryExec("tput cols 2>/dev/null", "(failed)"));
|
|
79
|
-
|
|
80
|
-
section("Filesystem");
|
|
81
|
-
const procExists = existsSync("/proc");
|
|
82
|
-
row("/proc exists", procExists ? `${GREEN}yes${NC} (Linux)` : `${RED}no${NC} (macOS/BSD)`);
|
|
83
|
-
row("CLAUDE_CONFIG_DIR", process.env.CLAUDE_CONFIG_DIR ?? "(unset)");
|
|
84
|
-
row("Claude config dir", CLAUDE_DIR);
|
|
85
|
-
row("Claude config dir exists", existsSync(CLAUDE_DIR) ? "yes" : "no");
|
|
86
|
-
row("Claude config file", CLAUDE_JSON);
|
|
87
|
-
row("Claude config file exists", existsSync(CLAUDE_JSON) ? "yes" : "no");
|
|
88
|
-
row("Buddy state dir", STATE_DIR);
|
|
89
|
-
row("Buddy state dir exists", existsSync(STATE_DIR) ? "yes" : "no");
|
|
90
|
-
row("Project root", PROJECT_ROOT);
|
|
91
|
-
row("Status script exists", existsSync(STATUS_SCRIPT) ? "yes" : `${RED}no${NC}`);
|
|
92
|
-
|
|
93
|
-
section("claude-buddy state");
|
|
94
|
-
const menagerie = tryParseJson(tryRead(join(STATE_DIR, "menagerie.json")));
|
|
95
|
-
const status = tryParseJson(tryRead(join(STATE_DIR, "status.json")));
|
|
96
|
-
|
|
97
|
-
if (isRecord(menagerie)) {
|
|
98
|
-
const activeSlot = typeof menagerie.active === "string" ? menagerie.active : "buddy";
|
|
99
|
-
const companions = isRecord(menagerie.companions) ? menagerie.companions : {};
|
|
100
|
-
const companion = companions[activeSlot];
|
|
101
|
-
row("Active slot", activeSlot);
|
|
102
|
-
row("Total slots", String(Object.keys(companions).length));
|
|
103
|
-
if (isRecord(companion)) {
|
|
104
|
-
const bones = isRecord(companion.bones) ? companion.bones : {};
|
|
105
|
-
row("Companion name", typeof companion.name === "string" ? companion.name : "(none)");
|
|
106
|
-
row("Species", typeof bones.species === "string" ? bones.species : "(none)");
|
|
107
|
-
row("Rarity", typeof bones.rarity === "string" ? bones.rarity : "(none)");
|
|
108
|
-
row("Hat", typeof bones.hat === "string" ? bones.hat : "(none)");
|
|
109
|
-
row("Eye", typeof bones.eye === "string" ? bones.eye : "(none)");
|
|
110
|
-
row("Shiny", String(typeof bones.shiny === "boolean" ? bones.shiny : false));
|
|
111
|
-
} else {
|
|
112
|
-
err(`No companion found in active slot "${activeSlot}"`);
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
err(`No manifest found at ${join(STATE_DIR, "menagerie.json")}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (isRecord(status)) {
|
|
119
|
-
row("Status muted", String(typeof status.muted === "boolean" ? status.muted : false));
|
|
120
|
-
row("Current reaction", typeof status.reaction === "string" && status.reaction.length > 0 ? status.reaction : "(none)");
|
|
121
|
-
} else {
|
|
122
|
-
warn(`No status state at ${join(STATE_DIR, "status.json")}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
section("Claude Code config");
|
|
126
|
-
const settings = tryParseJson(tryRead(SETTINGS));
|
|
127
|
-
const claudeJson = tryParseJson(tryRead(CLAUDE_JSON));
|
|
128
|
-
|
|
129
|
-
if (isRecord(settings) && "statusLine" in settings && settings.statusLine !== undefined) {
|
|
130
|
-
console.log(` ${DIM}statusLine:${NC}`);
|
|
131
|
-
console.log(` ${JSON.stringify(settings.statusLine, null, 2).split("\n").join("\n ")}`);
|
|
132
|
-
} else {
|
|
133
|
-
warn(`No statusLine in ${SETTINGS}`);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (isRecord(settings) && isRecord(settings.hooks)) {
|
|
137
|
-
console.log(` ${DIM}hooks:${NC}`);
|
|
138
|
-
for (const event of Object.keys(settings.hooks)) {
|
|
139
|
-
const hookEntries = settings.hooks[event];
|
|
140
|
-
const count = Array.isArray(hookEntries) ? hookEntries.length : 0;
|
|
141
|
-
row(` ${event}`, `${count} entr${count === 1 ? "y" : "ies"}`);
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
warn("No hooks configured");
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (isRecord(claudeJson) && isRecord(claudeJson.mcpServers) && isRecord(claudeJson.mcpServers["claude-buddy"])) {
|
|
148
|
-
ok(`MCP server registered in ${CLAUDE_JSON}`);
|
|
149
|
-
console.log(` ${JSON.stringify(claudeJson.mcpServers["claude-buddy"], null, 2).split("\n").join("\n ")}`);
|
|
150
|
-
} else {
|
|
151
|
-
err(`MCP server NOT registered in ${CLAUDE_JSON}`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (existsSync(SKILL_PATH)) {
|
|
155
|
-
ok(`Skill installed: ${SKILL_PATH}`);
|
|
156
|
-
} else {
|
|
157
|
-
err(`Skill missing: ${SKILL_PATH}`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
section("Live status line test");
|
|
161
|
-
if (existsSync(STATUS_SCRIPT)) {
|
|
162
|
-
try {
|
|
163
|
-
const output = execSync(`echo '{}' | ${STATUS_SCRIPT}`, { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }).trimEnd();
|
|
164
|
-
ok("Status script ran successfully");
|
|
165
|
-
console.log(`\n ${DIM}Output preview:${NC}`);
|
|
166
|
-
for (const line of output.split("\n").slice(0, 6)) console.log(` ${line}`);
|
|
167
|
-
} catch {
|
|
168
|
-
err("Status script failed to run");
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
console.log("");
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-buddy",
|
|
3
|
-
"owner": {
|
|
4
|
-
"name": "1270011"
|
|
5
|
-
},
|
|
6
|
-
"metadata": {
|
|
7
|
-
"description": "Permanent coding companion for Claude Code (requires bun: https://bun.sh/install)",
|
|
8
|
-
"version": "0.4.0-alpha.9"
|
|
9
|
-
},
|
|
10
|
-
"plugins": [
|
|
11
|
-
{
|
|
12
|
-
"name": "claude-buddy",
|
|
13
|
-
"source": "./",
|
|
14
|
-
"description": "Permanent coding companion for Claude Code \u2014 survives any update. MCP-based terminal pet with ASCII art, stats, reactions, and personality. Requires bun runtime (https://bun.sh/install).",
|
|
15
|
-
"version": "0.4.0-alpha.9",
|
|
16
|
-
"author": {
|
|
17
|
-
"name": "1270011"
|
|
18
|
-
},
|
|
19
|
-
"homepage": "https://github.com/1270011/claude-buddy#readme",
|
|
20
|
-
"repository": "https://github.com/1270011/claude-buddy",
|
|
21
|
-
"license": "MIT",
|
|
22
|
-
"keywords": [
|
|
23
|
-
"claude-code",
|
|
24
|
-
"buddy",
|
|
25
|
-
"companion",
|
|
26
|
-
"mcp",
|
|
27
|
-
"terminal-pet",
|
|
28
|
-
"tamagotchi"
|
|
29
|
-
],
|
|
30
|
-
"category": "companion",
|
|
31
|
-
"tags": [
|
|
32
|
-
"buddy",
|
|
33
|
-
"companion",
|
|
34
|
-
"mcp",
|
|
35
|
-
"terminal-pet",
|
|
36
|
-
"tamagotchi"
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
]
|
|
40
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-buddy",
|
|
3
|
-
"version": "0.4.0-alpha.9",
|
|
4
|
-
"description": "Permanent coding companion for Claude Code \u2014 survives any update. MCP-based terminal pet with ASCII art, stats, reactions, and personality.",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "1270011"
|
|
7
|
-
},
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"repository": "https://github.com/1270011/claude-buddy",
|
|
10
|
-
"homepage": "https://github.com/1270011/claude-buddy#readme",
|
|
11
|
-
"keywords": [
|
|
12
|
-
"claude-code",
|
|
13
|
-
"buddy",
|
|
14
|
-
"companion",
|
|
15
|
-
"mcp",
|
|
16
|
-
"terminal-pet",
|
|
17
|
-
"tamagotchi"
|
|
18
|
-
],
|
|
19
|
-
"mcpServers": {
|
|
20
|
-
"claude-buddy": {
|
|
21
|
-
"type": "stdio",
|
|
22
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/server/mcp-launcher.sh",
|
|
23
|
-
"args": []
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|