@ramarivera/coding-buddy 0.4.0-alpha.8 → 0.6.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/{adapters/claude/plugin → .claude-plugin}/marketplace.json +11 -6
- package/{adapters/claude/plugin → .claude-plugin}/plugin.json +9 -7
- package/LICENSE +1 -1
- package/README.md +102 -72
- 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 +59 -44
- package/cli/biomes.ts +309 -0
- package/cli/buddy-shell.ts +818 -0
- package/{adapters/claude/install → cli}/disable.ts +14 -32
- package/{adapters/claude/install → cli}/doctor.ts +55 -57
- package/{adapters/claude/install → cli}/hunt.ts +7 -9
- package/cli/index.ts +36 -25
- package/{adapters/claude/install → cli}/install.ts +100 -145
- package/{adapters/claude/install → cli}/pick.ts +111 -50
- 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 +11 -11
- package/cli/tui.tsx +2333 -0
- package/{adapters/claude/install → cli}/uninstall.ts +23 -38
- package/cli/upgrade.ts +213 -0
- 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 +23 -2
- package/hooks/file-type-react.sh +401 -0
- package/hooks/hooks.json +52 -0
- package/hooks/mood-react.sh +208 -0
- package/{adapters/claude/hooks → hooks}/name-react.sh +5 -2
- package/hooks/react.sh +1368 -0
- package/hooks/suggest.sh +55 -0
- package/package.json +113 -59
- package/scripts/gen-emoji-widths.ts +34 -0
- package/scripts/paths.sh +56 -0
- package/scripts/postinstall.cjs +42 -0
- package/scripts/snapshot-statusline.sh +54 -0
- package/server/achievements.ts +1419 -0
- package/server/art.ts +450 -0
- package/server/award-xp.ts +48 -0
- package/server/check-suggestions.ts +102 -0
- package/server/consolidate.ts +33 -0
- package/server/index.ts +1340 -0
- package/server/mcp-launcher.sh +26 -0
- package/server/memory.ts +404 -0
- package/server/mood.ts +254 -0
- package/server/path.ts +82 -0
- package/server/shift-mood.ts +43 -0
- package/{adapters/claude/storage → server}/state.ts +200 -7
- 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 +48 -4
- package/statusline/buddy-status.sh +397 -0
- package/statusline/combined-status.sh +134 -0
- package/statusline/emoji-widths.data +4 -0
- package/adapters/claude/hooks/hooks.json +0 -35
- package/adapters/claude/hooks/react.sh +0 -204
- 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/adapters/claude/rendering/art.ts +0 -264
- package/adapters/claude/server/index.ts +0 -752
- package/adapters/claude/server/instructions.ts +0 -24
- package/adapters/claude/server/resources.ts +0 -38
- package/adapters/claude/statusline/buddy-status.sh +0 -389
- package/adapters/claude/storage/achievements.ts +0 -253
- package/adapters/claude/storage/identity.ts +0 -13
- package/adapters/claude/storage/paths.ts +0 -24
- package/adapters/claude/storage/settings.ts +0 -41
- package/adapters/pi/README.md +0 -83
- package/adapters/pi/tools.ts +0 -6
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* coding-buddy installer
|
|
3
3
|
*
|
|
4
|
-
* Registers: MCP server (in
|
|
5
|
-
*
|
|
4
|
+
* Registers: MCP server (in Claude's user config), skill, hooks, status line
|
|
5
|
+
* (in settings.json). All paths resolve via server/paths.ts, so the installer
|
|
6
|
+
* targets the active Claude profile ($CLAUDE_CONFIG_DIR) or the default
|
|
7
|
+
* ~/.claude/ layout when the env var is unset.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from "fs";
|
|
9
11
|
import { execSync } from "child_process";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
import { generateBones, renderBuddy, renderFace, RARITY_STARS } from "
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
import { resolve, dirname, join } from "path";
|
|
13
|
+
|
|
14
|
+
import { generateBones, renderBuddy, renderFace, RARITY_STARS } from "../core/engine.ts"
|
|
15
|
+
import {
|
|
16
|
+
claudeConfigDir,
|
|
17
|
+
claudeSettingsPath,
|
|
18
|
+
claudeSkillDir,
|
|
19
|
+
claudeUserConfigPath,
|
|
20
|
+
toUnixPath,
|
|
21
|
+
} from "../server/path.ts";
|
|
22
|
+
import { loadCompanion, saveCompanion, resolveUserId, writeStatusState } from "../server/state.ts";
|
|
23
|
+
import { generateFallbackName } from "../core/reactions.ts"
|
|
17
24
|
|
|
18
25
|
const CYAN = "\x1b[36m";
|
|
19
26
|
const GREEN = "\x1b[32m";
|
|
@@ -23,16 +30,16 @@ const BOLD = "\x1b[1m";
|
|
|
23
30
|
const DIM = "\x1b[2m";
|
|
24
31
|
const NC = "\x1b[0m";
|
|
25
32
|
|
|
26
|
-
const CLAUDE_DIR =
|
|
27
|
-
const SETTINGS_FILE =
|
|
28
|
-
const BUDDY_DIR =
|
|
29
|
-
const
|
|
30
|
-
const PROJECT_ROOT = resolve(import.meta.dir
|
|
33
|
+
const CLAUDE_DIR = claudeConfigDir();
|
|
34
|
+
const SETTINGS_FILE = claudeSettingsPath();
|
|
35
|
+
const BUDDY_DIR = claudeSkillDir("buddy");
|
|
36
|
+
const CLAUDE_JSON_PATH = claudeUserConfigPath();
|
|
37
|
+
const PROJECT_ROOT = resolve(dirname(import.meta.dir));
|
|
31
38
|
|
|
32
39
|
function banner() {
|
|
33
40
|
console.log(`
|
|
34
41
|
${CYAN}╔══════════════════════════════════════════════════════════╗${NC}
|
|
35
|
-
${CYAN}║${NC} ${BOLD}
|
|
42
|
+
${CYAN}║${NC} ${BOLD}coding-buddy${NC} — permanent coding companion ${CYAN}║${NC}
|
|
36
43
|
${CYAN}║${NC} ${DIM}MCP + Skill + StatusLine + Hooks${NC} ${CYAN}║${NC}
|
|
37
44
|
${CYAN}╚══════════════════════════════════════════════════════════╝${NC}
|
|
38
45
|
`);
|
|
@@ -67,28 +74,25 @@ function preflight(): boolean {
|
|
|
67
74
|
execSync("sudo apt-get install -y jq 2>/dev/null || brew install jq 2>/dev/null", { stdio: "ignore" });
|
|
68
75
|
ok("jq installed");
|
|
69
76
|
} catch {
|
|
70
|
-
err("Could not install jq. Install manually: apt install jq / brew install jq");
|
|
77
|
+
err("Could not install jq. Install manually: apt install jq / brew install jq / windows: install from https://github.com/jqlang/jq/releases/latest and add to PATH");
|
|
71
78
|
pass = false;
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
// Check Claude config dir exists
|
|
82
|
+
// Check Claude config dir exists
|
|
76
83
|
if (!existsSync(CLAUDE_DIR)) {
|
|
77
|
-
err(
|
|
78
|
-
pass = false;
|
|
79
|
-
} else if (!statSync(CLAUDE_DIR).isDirectory()) {
|
|
80
|
-
err(`Claude config path is not a directory: ${CLAUDE_DIR}`);
|
|
84
|
+
err(`${CLAUDE_DIR} not found. Start Claude Code once first, then re-run.`);
|
|
81
85
|
pass = false;
|
|
82
86
|
} else {
|
|
83
|
-
ok(
|
|
87
|
+
ok(`${CLAUDE_DIR} found`);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
|
-
// Check .claude.json exists
|
|
87
|
-
if (!existsSync(
|
|
88
|
-
err(
|
|
90
|
+
// Check Claude user config (.claude.json) exists
|
|
91
|
+
if (!existsSync(CLAUDE_JSON_PATH)) {
|
|
92
|
+
err(`${CLAUDE_JSON_PATH} not found. Start Claude Code once first, then re-run.`);
|
|
89
93
|
pass = false;
|
|
90
94
|
} else {
|
|
91
|
-
ok(
|
|
95
|
+
ok(`${CLAUDE_JSON_PATH} found`);
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
return pass;
|
|
@@ -96,50 +100,15 @@ function preflight(): boolean {
|
|
|
96
100
|
|
|
97
101
|
// ─── Load / update settings.json ────────────────────────────────────────────
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
type: "command";
|
|
101
|
-
command: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
interface HookMatcherEntry {
|
|
105
|
-
matcher?: string;
|
|
106
|
-
hooks?: HookCommand[];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
interface ClaudeStatusLineConfig {
|
|
110
|
-
type: "command";
|
|
111
|
-
command: string;
|
|
112
|
-
padding: number;
|
|
113
|
-
refreshInterval: number;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
interface ClaudeSettings {
|
|
117
|
-
statusLine?: ClaudeStatusLineConfig;
|
|
118
|
-
hooks?: Partial<Record<"SessionStart" | "SessionEnd" | "PostToolUse" | "Stop" | "UserPromptSubmit", HookMatcherEntry[]>>;
|
|
119
|
-
permissions?: {
|
|
120
|
-
allow?: string[];
|
|
121
|
-
};
|
|
122
|
-
[key: string]: unknown;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
interface ClaudeJsonConfig {
|
|
126
|
-
mcpServers?: Record<string, { command: string; args: string[]; cwd: string }>;
|
|
127
|
-
[key: string]: unknown;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function hasBuddyHook(entry: HookMatcherEntry): boolean {
|
|
131
|
-
return (entry.hooks ?? []).some((hook) => hook.command.includes("claude-buddy"));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function loadSettings(): ClaudeSettings {
|
|
103
|
+
function loadSettings(): Record<string, any> {
|
|
135
104
|
try {
|
|
136
|
-
return JSON.parse(readFileSync(SETTINGS_FILE, "utf8"))
|
|
105
|
+
return JSON.parse(readFileSync(SETTINGS_FILE, "utf8"));
|
|
137
106
|
} catch {
|
|
138
107
|
return {};
|
|
139
108
|
}
|
|
140
109
|
}
|
|
141
110
|
|
|
142
|
-
function saveSettings(settings:
|
|
111
|
+
function saveSettings(settings: Record<string, any>) {
|
|
143
112
|
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
144
113
|
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
145
114
|
}
|
|
@@ -147,30 +116,29 @@ function saveSettings(settings: ClaudeSettings) {
|
|
|
147
116
|
// ─── Step 1: Register MCP server (in ~/.claude.json) ────────────────────────
|
|
148
117
|
|
|
149
118
|
function installMcp() {
|
|
150
|
-
const serverPath = join(PROJECT_ROOT, "
|
|
151
|
-
const claudeJsonPath = CLAUDE_JSON;
|
|
119
|
+
const serverPath = join(PROJECT_ROOT, "server", "index.ts");
|
|
152
120
|
|
|
153
|
-
let claudeJson:
|
|
121
|
+
let claudeJson: Record<string, any> = {};
|
|
154
122
|
try {
|
|
155
|
-
claudeJson = JSON.parse(readFileSync(
|
|
123
|
+
claudeJson = JSON.parse(readFileSync(CLAUDE_JSON_PATH, "utf8"));
|
|
156
124
|
} catch { /* fresh config */ }
|
|
157
125
|
|
|
158
126
|
if (!claudeJson.mcpServers) claudeJson.mcpServers = {};
|
|
159
127
|
|
|
160
128
|
claudeJson.mcpServers["claude-buddy"] = {
|
|
161
129
|
command: "bun",
|
|
162
|
-
args: [serverPath],
|
|
163
|
-
cwd: PROJECT_ROOT,
|
|
130
|
+
args: [toUnixPath(serverPath)],
|
|
131
|
+
cwd: toUnixPath(PROJECT_ROOT),
|
|
164
132
|
};
|
|
165
133
|
|
|
166
|
-
writeFileSync(
|
|
167
|
-
ok(`MCP server registered in ${
|
|
134
|
+
writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(claudeJson, null, 2));
|
|
135
|
+
ok(`MCP server registered in ${CLAUDE_JSON_PATH}`);
|
|
168
136
|
}
|
|
169
137
|
|
|
170
138
|
// ─── Step 2: Install skill ──────────────────────────────────────────────────
|
|
171
139
|
|
|
172
140
|
function installSkill() {
|
|
173
|
-
const srcSkill = join(PROJECT_ROOT, "
|
|
141
|
+
const srcSkill = join(PROJECT_ROOT, "skills", "buddy", "SKILL.md");
|
|
174
142
|
mkdirSync(BUDDY_DIR, { recursive: true });
|
|
175
143
|
cpSync(srcSkill, join(BUDDY_DIR, "SKILL.md"), { force: true });
|
|
176
144
|
ok(`Skill installed: ${join(BUDDY_DIR, "SKILL.md")}`);
|
|
@@ -178,12 +146,12 @@ function installSkill() {
|
|
|
178
146
|
|
|
179
147
|
// ─── Step 3: Configure status line (with animation refresh) ─────────────────
|
|
180
148
|
|
|
181
|
-
function installStatusLine(settings:
|
|
182
|
-
const statusScript = join(PROJECT_ROOT, "
|
|
149
|
+
function installStatusLine(settings: Record<string, any>) {
|
|
150
|
+
const statusScript = join(PROJECT_ROOT, "statusline", "buddy-status.sh");
|
|
183
151
|
|
|
184
152
|
settings.statusLine = {
|
|
185
153
|
type: "command",
|
|
186
|
-
command: statusScript,
|
|
154
|
+
command: toUnixPath(statusScript),
|
|
187
155
|
padding: 1,
|
|
188
156
|
refreshInterval: 1, // 1 second — drives the buddy animation
|
|
189
157
|
};
|
|
@@ -191,90 +159,83 @@ function installStatusLine(settings: ClaudeSettings) {
|
|
|
191
159
|
ok("Status line configured (with animation refresh)");
|
|
192
160
|
}
|
|
193
161
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
162
|
+
// The tmux popup mode was removed in favour of the status line / buddy-shell
|
|
163
|
+
// — its modal `tmux display-popup` intercepted the `Ctrl+b` prefix, breaking
|
|
164
|
+
// every tmux binding while the buddy was visible (issue #57). For backwards
|
|
165
|
+
// compatibility any legacy SessionStart/SessionEnd hooks that reference the
|
|
166
|
+
// popup manager are stripped out below when re-installing.
|
|
167
|
+
|
|
168
|
+
function stripLegacyPopupHooks(settings: Record<string, any>) {
|
|
169
|
+
if (!settings.hooks) return;
|
|
170
|
+
for (const hookType of ["SessionStart", "SessionEnd"] as const) {
|
|
171
|
+
if (!settings.hooks[hookType]) continue;
|
|
172
|
+
settings.hooks[hookType] = settings.hooks[hookType].filter(
|
|
173
|
+
(h: any) => !h.hooks?.some((hh: any) =>
|
|
174
|
+
hh.command?.includes("popup-manager") || hh.command?.includes("claude-buddy/popup"),
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
if (settings.hooks[hookType].length === 0) delete settings.hooks[hookType];
|
|
206
178
|
}
|
|
207
179
|
}
|
|
208
180
|
|
|
209
|
-
function installPopupHooks(settings: ClaudeSettings) {
|
|
210
|
-
const popupManager = join(PROJECT_ROOT, "adapters", "claude", "popup", "popup-manager.sh");
|
|
211
|
-
|
|
212
|
-
if (!settings.hooks) settings.hooks = {};
|
|
213
|
-
|
|
214
|
-
// SessionStart: open popup
|
|
215
|
-
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
216
|
-
settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
|
|
217
|
-
(h) => !hasBuddyHook(h),
|
|
218
|
-
);
|
|
219
|
-
settings.hooks.SessionStart.push({
|
|
220
|
-
hooks: [{ type: "command", command: `${popupManager} start` }],
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// SessionEnd: close popup
|
|
224
|
-
if (!settings.hooks.SessionEnd) settings.hooks.SessionEnd = [];
|
|
225
|
-
settings.hooks.SessionEnd = settings.hooks.SessionEnd.filter(
|
|
226
|
-
(h) => !hasBuddyHook(h),
|
|
227
|
-
);
|
|
228
|
-
settings.hooks.SessionEnd.push({
|
|
229
|
-
hooks: [{ type: "command", command: `${popupManager} stop` }],
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
ok("Popup hooks registered: SessionStart + SessionEnd");
|
|
233
|
-
}
|
|
234
|
-
|
|
235
181
|
// ─── Step 4: Register hooks ─────────────────────────────────────────────────
|
|
236
182
|
|
|
237
|
-
function installHooks(settings:
|
|
238
|
-
const reactHook
|
|
239
|
-
const
|
|
240
|
-
const
|
|
183
|
+
function installHooks(settings: Record<string, any>) {
|
|
184
|
+
const reactHook = join(PROJECT_ROOT, "hooks", "react.sh");
|
|
185
|
+
const fileTypeHook = join(PROJECT_ROOT, "hooks", "file-type-react.sh");
|
|
186
|
+
const commentHook = join(PROJECT_ROOT, "hooks", "buddy-comment.sh");
|
|
187
|
+
const suggestHook = join(PROJECT_ROOT, "hooks", "suggest.sh");
|
|
188
|
+
const nameHook = join(PROJECT_ROOT, "hooks", "name-react.sh");
|
|
189
|
+
const moodHook = join(PROJECT_ROOT, "hooks", "mood-react.sh");
|
|
241
190
|
|
|
242
191
|
if (!settings.hooks) settings.hooks = {};
|
|
243
192
|
|
|
244
|
-
// PostToolUse: detect errors/test failures/successes in Bash output
|
|
193
|
+
// PostToolUse: detect errors/test failures/successes in Bash output,
|
|
194
|
+
// plus file-type specific reactions on Write/Edit.
|
|
245
195
|
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
246
196
|
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(
|
|
247
|
-
(h) => !
|
|
197
|
+
(h: any) => !h.hooks?.some((hh: any) => hh.command?.includes("coding-buddy") || hh.command?.includes("claude-buddy")),
|
|
248
198
|
);
|
|
249
199
|
settings.hooks.PostToolUse.push({
|
|
250
200
|
matcher: "Bash",
|
|
251
|
-
hooks: [{ type: "command", command: reactHook }],
|
|
201
|
+
hooks: [{ type: "command", command: toUnixPath(reactHook) }],
|
|
202
|
+
});
|
|
203
|
+
settings.hooks.PostToolUse.push({
|
|
204
|
+
matcher: "Write|Edit",
|
|
205
|
+
hooks: [{ type: "command", command: toUnixPath(fileTypeHook) }],
|
|
252
206
|
});
|
|
253
207
|
|
|
254
208
|
// Stop: extract <!-- buddy: --> comment from Claude's response
|
|
255
209
|
if (!settings.hooks.Stop) settings.hooks.Stop = [];
|
|
256
210
|
settings.hooks.Stop = settings.hooks.Stop.filter(
|
|
257
|
-
(h) => !
|
|
211
|
+
(h: any) => !h.hooks?.some((hh: any) => hh.command?.includes("coding-buddy") || hh.command?.includes("claude-buddy")),
|
|
258
212
|
);
|
|
259
213
|
settings.hooks.Stop.push({
|
|
260
|
-
hooks: [{ type: "command", command: commentHook }],
|
|
214
|
+
hooks: [{ type: "command", command: toUnixPath(commentHook) }],
|
|
215
|
+
});
|
|
216
|
+
settings.hooks.Stop.push({
|
|
217
|
+
hooks: [{ type: "command", command: toUnixPath(suggestHook) }],
|
|
261
218
|
});
|
|
262
219
|
|
|
263
|
-
// UserPromptSubmit: detect buddy's name in user message → instant status line
|
|
220
|
+
// UserPromptSubmit: detect buddy's name in user message → instant status line
|
|
221
|
+
// reaction, plus mood-react based on prompt content.
|
|
264
222
|
if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
|
|
265
223
|
settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(
|
|
266
|
-
(h) => !
|
|
224
|
+
(h: any) => !h.hooks?.some((hh: any) => hh.command?.includes("coding-buddy") || hh.command?.includes("claude-buddy")),
|
|
267
225
|
);
|
|
268
226
|
settings.hooks.UserPromptSubmit.push({
|
|
269
|
-
hooks: [{ type: "command", command: nameHook }],
|
|
227
|
+
hooks: [{ type: "command", command: toUnixPath(nameHook) }],
|
|
228
|
+
});
|
|
229
|
+
settings.hooks.UserPromptSubmit.push({
|
|
230
|
+
hooks: [{ type: "command", command: toUnixPath(moodHook) }],
|
|
270
231
|
});
|
|
271
232
|
|
|
272
|
-
ok("Hooks registered: PostToolUse + Stop + UserPromptSubmit");
|
|
233
|
+
ok("Hooks registered: PostToolUse (Bash + Write/Edit) + Stop (comment + suggest) + UserPromptSubmit (name + mood)");
|
|
273
234
|
}
|
|
274
235
|
|
|
275
236
|
// ─── Step 5: Ensure MCP tools are allowed ───────────────────────────────────
|
|
276
237
|
|
|
277
|
-
function ensurePermissions(settings:
|
|
238
|
+
function ensurePermissions(settings: Record<string, any>) {
|
|
278
239
|
if (!settings.permissions) settings.permissions = {};
|
|
279
240
|
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
280
241
|
|
|
@@ -319,6 +280,11 @@ function initCompanion() {
|
|
|
319
280
|
|
|
320
281
|
banner();
|
|
321
282
|
|
|
283
|
+
const profileSource = process.env.CLAUDE_CONFIG_DIR
|
|
284
|
+
? "from CLAUDE_CONFIG_DIR"
|
|
285
|
+
: "CLAUDE_CONFIG_DIR unset — single-profile default";
|
|
286
|
+
info(`Target profile: ${CLAUDE_DIR} ${DIM}(${profileSource})${NC}\n`);
|
|
287
|
+
|
|
322
288
|
info("Checking requirements...\n");
|
|
323
289
|
if (!preflight()) {
|
|
324
290
|
console.log(`\n${RED}Installation aborted. Fix the issues above and retry.${NC}\n`);
|
|
@@ -326,25 +292,15 @@ if (!preflight()) {
|
|
|
326
292
|
}
|
|
327
293
|
|
|
328
294
|
console.log("");
|
|
329
|
-
info("Installing
|
|
295
|
+
info("Installing coding-buddy...\n");
|
|
330
296
|
|
|
331
297
|
const settings = loadSettings();
|
|
332
298
|
|
|
333
299
|
installMcp();
|
|
334
300
|
installSkill();
|
|
335
301
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
info("tmux detected (>= 3.2) -- using popup overlay mode");
|
|
339
|
-
installPopupHooks(settings);
|
|
340
|
-
// Disable status line to avoid duplicate buddy rendering
|
|
341
|
-
if (settings.statusLine?.command?.includes("buddy")) {
|
|
342
|
-
delete settings.statusLine;
|
|
343
|
-
ok("Status line disabled (popup replaces it)");
|
|
344
|
-
}
|
|
345
|
-
} else {
|
|
346
|
-
installStatusLine(settings);
|
|
347
|
-
}
|
|
302
|
+
stripLegacyPopupHooks(settings);
|
|
303
|
+
installStatusLine(settings);
|
|
348
304
|
|
|
349
305
|
installHooks(settings);
|
|
350
306
|
ensurePermissions(settings);
|
|
@@ -359,10 +315,9 @@ console.log("");
|
|
|
359
315
|
console.log(` ${BOLD}${companion.name}${NC} -- ${companion.personality}`);
|
|
360
316
|
console.log("");
|
|
361
317
|
|
|
362
|
-
const modeMsg = useTmuxPopup ? "popup overlay" : "status line";
|
|
363
318
|
console.log(`${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`);
|
|
364
319
|
console.log(`${GREEN} Done! Restart Claude Code and type /buddy${NC}`);
|
|
365
|
-
console.log(`${GREEN} Display mode: ${
|
|
320
|
+
console.log(`${GREEN} Display mode: status line${NC}`);
|
|
366
321
|
console.log(`${GREEN} Your companion is now permanent -- survives any update.${NC}`);
|
|
367
322
|
console.log(`${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`);
|
|
368
323
|
console.log("");
|