@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,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* coding-buddy hunt — brute-force search for a specific buddy
|
|
3
3
|
*
|
|
4
4
|
* Rules:
|
|
5
5
|
* - Asks for a name before saving
|
|
@@ -7,14 +7,12 @@
|
|
|
7
7
|
* - Appends to the manifest — never overwrites an existing slot
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
type Species, type Rarity, type StatName, type SearchCriteria,
|
|
13
|
-
} from "../../../core/engine.ts";
|
|
10
|
+
import { searchBuddy, renderBuddy, SPECIES, RARITIES, STAT_NAMES,
|
|
11
|
+
type Species, type Rarity, type StatName, type SearchCriteria, } from "../core/engine.ts"
|
|
14
12
|
import {
|
|
15
13
|
saveCompanionSlot, saveActiveSlot, writeStatusState,
|
|
16
14
|
slugify, unusedName, listCompanionSlots,
|
|
17
|
-
} from "../
|
|
15
|
+
} from "../server/state.ts";
|
|
18
16
|
import { createInterface } from "readline";
|
|
19
17
|
|
|
20
18
|
const CYAN = "\x1b[36m";
|
|
@@ -47,7 +45,7 @@ function pickFromList<T extends string>(label: string, items: readonly T[]): Pro
|
|
|
47
45
|
async function main() {
|
|
48
46
|
console.log(`
|
|
49
47
|
${CYAN}╔══════════════════════════════════════════════════════════╗${NC}
|
|
50
|
-
${CYAN}║${NC} ${BOLD}
|
|
48
|
+
${CYAN}║${NC} ${BOLD}coding-buddy hunt${NC} — find your perfect companion ${CYAN}║${NC}
|
|
51
49
|
${CYAN}╚══════════════════════════════════════════════════════════╝${NC}
|
|
52
50
|
`);
|
|
53
51
|
|
|
@@ -67,8 +65,8 @@ ${CYAN}╚═══════════════════════
|
|
|
67
65
|
const statsAns = await ask(`\n Configure stats? [Y/n]: `);
|
|
68
66
|
if (statsAns.toLowerCase() !== "n") {
|
|
69
67
|
wantPeak = await pickFromList("Peak stat (highest):", STAT_NAMES);
|
|
70
|
-
const dumpOptions = STAT_NAMES.filter((s)
|
|
71
|
-
wantDump = await pickFromList("Dump stat (lowest):", dumpOptions);
|
|
68
|
+
const dumpOptions = STAT_NAMES.filter((s) => s !== wantPeak);
|
|
69
|
+
wantDump = await pickFromList("Dump stat (lowest):", dumpOptions as any);
|
|
72
70
|
console.log(`${GREEN}✓${NC} peak=${wantPeak} dump=${wantDump}`);
|
|
73
71
|
}
|
|
74
72
|
|
package/cli/index.ts
CHANGED
|
@@ -1,60 +1,62 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* coding-buddy CLI
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* npx
|
|
7
|
-
* npx
|
|
8
|
-
* npx
|
|
9
|
-
* npx
|
|
10
|
-
* npx
|
|
11
|
-
* npx
|
|
12
|
-
* npx
|
|
13
|
-
* npx
|
|
6
|
+
* npx -y @ramarivera/coding-buddy Interactive install
|
|
7
|
+
* npx -y @ramarivera/coding-buddy install Install MCP + skill + hooks + statusline
|
|
8
|
+
* npx -y @ramarivera/coding-buddy show Show current buddy
|
|
9
|
+
* npx -y @ramarivera/coding-buddy pick Interactive two-pane buddy picker (saved + search)
|
|
10
|
+
* npx -y @ramarivera/coding-buddy hunt Search for a specific buddy (non-interactive)
|
|
11
|
+
* npx -y @ramarivera/coding-buddy upgrade Pull latest + reinstall
|
|
12
|
+
* npx -y @ramarivera/coding-buddy uninstall Remove all integrations
|
|
13
|
+
* npx -y @ramarivera/coding-buddy verify Verify what buddy your ID produces
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
export {}; // Make this file a module for TypeScript top-level await
|
|
17
|
+
|
|
16
18
|
const args = process.argv.slice(2);
|
|
17
19
|
const command = args[0] || "install";
|
|
18
20
|
|
|
19
21
|
switch (command) {
|
|
20
22
|
case "install":
|
|
21
|
-
await import("
|
|
23
|
+
await import("./install.ts");
|
|
22
24
|
break;
|
|
23
25
|
case "show":
|
|
24
|
-
await import("
|
|
26
|
+
await import("./show.ts");
|
|
25
27
|
break;
|
|
26
28
|
case "pick":
|
|
27
|
-
await import("
|
|
29
|
+
await import("./pick.ts");
|
|
28
30
|
break;
|
|
29
31
|
case "hunt":
|
|
30
|
-
await import("
|
|
32
|
+
await import("./hunt.ts");
|
|
31
33
|
break;
|
|
32
34
|
case "uninstall":
|
|
33
|
-
await import("
|
|
35
|
+
await import("./uninstall.ts");
|
|
34
36
|
break;
|
|
35
37
|
case "verify":
|
|
36
38
|
await import("./verify.ts");
|
|
37
39
|
break;
|
|
38
40
|
case "doctor":
|
|
39
|
-
await import("
|
|
41
|
+
await import("./doctor.ts");
|
|
40
42
|
break;
|
|
41
43
|
case "test-statusline":
|
|
42
|
-
await import("
|
|
44
|
+
await import("./test-statusline.ts");
|
|
43
45
|
break;
|
|
44
46
|
case "backup":
|
|
45
|
-
await import("
|
|
47
|
+
await import("./backup.ts");
|
|
46
48
|
break;
|
|
47
49
|
case "settings":
|
|
48
|
-
await import("
|
|
50
|
+
await import("./settings.ts");
|
|
49
51
|
break;
|
|
50
52
|
case "disable":
|
|
51
|
-
await import("
|
|
53
|
+
await import("./disable.ts");
|
|
52
54
|
break;
|
|
53
55
|
case "upgrade":
|
|
54
56
|
await import("./upgrade.ts");
|
|
55
57
|
break;
|
|
56
58
|
case "enable":
|
|
57
|
-
await import("
|
|
59
|
+
await import("./install.ts");
|
|
58
60
|
break;
|
|
59
61
|
case "help":
|
|
60
62
|
case "--help":
|
|
@@ -69,14 +71,14 @@ switch (command) {
|
|
|
69
71
|
|
|
70
72
|
function showHelp() {
|
|
71
73
|
console.log(`
|
|
72
|
-
|
|
74
|
+
coding-buddy — permanent coding companion for Claude Code, Pi, and Oh My Pi
|
|
73
75
|
|
|
74
76
|
Setup:
|
|
75
77
|
install-buddy Set up MCP server, skill, hooks, and status line
|
|
76
78
|
upgrade Pull latest version and reinstall integrations
|
|
77
79
|
enable Same as install-buddy (re-enable after disable)
|
|
78
80
|
disable Temporarily deactivate buddy (data preserved)
|
|
79
|
-
uninstall Remove all
|
|
81
|
+
uninstall Remove all coding-buddy integrations
|
|
80
82
|
|
|
81
83
|
Buddy:
|
|
82
84
|
show Display your current buddy
|
|
@@ -94,7 +96,7 @@ Settings:
|
|
|
94
96
|
Diagnostics:
|
|
95
97
|
doctor Run diagnostic report (paste output in bug reports)
|
|
96
98
|
test-statusline Test status line rendering in Claude Code
|
|
97
|
-
backup Snapshot or restore all
|
|
99
|
+
backup Snapshot or restore all coding-buddy state
|
|
98
100
|
|
|
99
101
|
In Claude Code:
|
|
100
102
|
/buddy Show companion card with ASCII art + stats
|
|
@@ -113,10 +115,12 @@ In Claude Code:
|
|
|
113
115
|
/buddy style Show or set bubble style (tmux only)
|
|
114
116
|
/buddy position Show or set bubble position (tmux only)
|
|
115
117
|
/buddy rarity Show or hide rarity stars (tmux only)
|
|
118
|
+
/buddy width Set bubble text width in chars (10-60, tmux only)
|
|
116
119
|
|
|
117
120
|
Usage:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
npx -y @ramarivera/coding-buddy <command> Run from the registry without cloning
|
|
122
|
+
bun run <command> e.g. bun run show, bun run doctor (in repo)
|
|
123
|
+
coding-buddy <command> if globally linked (bun link)
|
|
124
|
+
bun run help Show this help
|
|
121
125
|
`);
|
|
122
126
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* coding-buddy installer
|
|
3
3
|
*
|
|
4
4
|
* Registers: MCP server (in Claude's user config), skill, hooks, status line
|
|
5
5
|
* (in settings.json). All paths resolve via server/paths.ts, so the installer
|
|
@@ -7,15 +7,20 @@
|
|
|
7
7
|
* ~/.claude/ layout when the env var is unset.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from "fs";
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
import { generateBones, renderBuddy, renderFace, RARITY_STARS } from "
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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"
|
|
19
24
|
|
|
20
25
|
const CYAN = "\x1b[36m";
|
|
21
26
|
const GREEN = "\x1b[32m";
|
|
@@ -25,17 +30,16 @@ const BOLD = "\x1b[1m";
|
|
|
25
30
|
const DIM = "\x1b[2m";
|
|
26
31
|
const NC = "\x1b[0m";
|
|
27
32
|
|
|
28
|
-
const CLAUDE_DIR =
|
|
29
|
-
const SETTINGS_FILE =
|
|
30
|
-
const BUDDY_DIR =
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
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));
|
|
34
38
|
|
|
35
39
|
function banner() {
|
|
36
40
|
console.log(`
|
|
37
41
|
${CYAN}╔══════════════════════════════════════════════════════════╗${NC}
|
|
38
|
-
${CYAN}║${NC} ${BOLD}
|
|
42
|
+
${CYAN}║${NC} ${BOLD}coding-buddy${NC} — permanent coding companion ${CYAN}║${NC}
|
|
39
43
|
${CYAN}║${NC} ${DIM}MCP + Skill + StatusLine + Hooks${NC} ${CYAN}║${NC}
|
|
40
44
|
${CYAN}╚══════════════════════════════════════════════════════════╝${NC}
|
|
41
45
|
`);
|
|
@@ -75,21 +79,20 @@ function preflight(): boolean {
|
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
// Check Claude config dir exists
|
|
78
83
|
if (!existsSync(CLAUDE_DIR)) {
|
|
79
|
-
err(
|
|
80
|
-
pass = false;
|
|
81
|
-
} else if (!statSync(CLAUDE_DIR).isDirectory()) {
|
|
82
|
-
err(`Claude config path is not a directory: ${CLAUDE_DIR}`);
|
|
84
|
+
err(`${CLAUDE_DIR} not found. Start Claude Code once first, then re-run.`);
|
|
83
85
|
pass = false;
|
|
84
86
|
} else {
|
|
85
|
-
ok(
|
|
87
|
+
ok(`${CLAUDE_DIR} found`);
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
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.`);
|
|
90
93
|
pass = false;
|
|
91
94
|
} else {
|
|
92
|
-
ok(
|
|
95
|
+
ok(`${CLAUDE_JSON_PATH} found`);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
return pass;
|
|
@@ -97,50 +100,15 @@ function preflight(): boolean {
|
|
|
97
100
|
|
|
98
101
|
// ─── Load / update settings.json ────────────────────────────────────────────
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
type: "command";
|
|
102
|
-
command: string;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
interface HookMatcherEntry {
|
|
106
|
-
matcher?: string;
|
|
107
|
-
hooks?: HookCommand[];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
interface ClaudeStatusLineConfig {
|
|
111
|
-
type: "command";
|
|
112
|
-
command: string;
|
|
113
|
-
padding: number;
|
|
114
|
-
refreshInterval: number;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
interface ClaudeSettings {
|
|
118
|
-
statusLine?: ClaudeStatusLineConfig;
|
|
119
|
-
hooks?: Partial<Record<"SessionStart" | "SessionEnd" | "PostToolUse" | "Stop" | "UserPromptSubmit", HookMatcherEntry[]>>;
|
|
120
|
-
permissions?: {
|
|
121
|
-
allow?: string[];
|
|
122
|
-
};
|
|
123
|
-
[key: string]: unknown;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
interface ClaudeJsonConfig {
|
|
127
|
-
mcpServers?: Record<string, { command: string; args: string[]; cwd: string }>;
|
|
128
|
-
[key: string]: unknown;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function hasBuddyHook(entry: HookMatcherEntry): boolean {
|
|
132
|
-
return (entry.hooks ?? []).some((hook) => hook.command.includes("claude-buddy"));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function loadSettings(): ClaudeSettings {
|
|
103
|
+
function loadSettings(): Record<string, any> {
|
|
136
104
|
try {
|
|
137
|
-
return JSON.parse(readFileSync(SETTINGS_FILE, "utf8"))
|
|
105
|
+
return JSON.parse(readFileSync(SETTINGS_FILE, "utf8"));
|
|
138
106
|
} catch {
|
|
139
107
|
return {};
|
|
140
108
|
}
|
|
141
109
|
}
|
|
142
110
|
|
|
143
|
-
function saveSettings(settings:
|
|
111
|
+
function saveSettings(settings: Record<string, any>) {
|
|
144
112
|
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
145
113
|
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
|
|
146
114
|
}
|
|
@@ -148,12 +116,11 @@ function saveSettings(settings: ClaudeSettings) {
|
|
|
148
116
|
// ─── Step 1: Register MCP server (in ~/.claude.json) ────────────────────────
|
|
149
117
|
|
|
150
118
|
function installMcp() {
|
|
151
|
-
const serverPath = join(PROJECT_ROOT, "
|
|
152
|
-
const claudeJsonPath = CLAUDE_JSON;
|
|
119
|
+
const serverPath = join(PROJECT_ROOT, "server", "index.ts");
|
|
153
120
|
|
|
154
|
-
let claudeJson:
|
|
121
|
+
let claudeJson: Record<string, any> = {};
|
|
155
122
|
try {
|
|
156
|
-
claudeJson = JSON.parse(readFileSync(
|
|
123
|
+
claudeJson = JSON.parse(readFileSync(CLAUDE_JSON_PATH, "utf8"));
|
|
157
124
|
} catch { /* fresh config */ }
|
|
158
125
|
|
|
159
126
|
if (!claudeJson.mcpServers) claudeJson.mcpServers = {};
|
|
@@ -164,14 +131,14 @@ function installMcp() {
|
|
|
164
131
|
cwd: toUnixPath(PROJECT_ROOT),
|
|
165
132
|
};
|
|
166
133
|
|
|
167
|
-
writeFileSync(
|
|
168
|
-
ok(`MCP server registered in ${
|
|
134
|
+
writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(claudeJson, null, 2));
|
|
135
|
+
ok(`MCP server registered in ${CLAUDE_JSON_PATH}`);
|
|
169
136
|
}
|
|
170
137
|
|
|
171
138
|
// ─── Step 2: Install skill ──────────────────────────────────────────────────
|
|
172
139
|
|
|
173
140
|
function installSkill() {
|
|
174
|
-
const srcSkill = join(PROJECT_ROOT, "
|
|
141
|
+
const srcSkill = join(PROJECT_ROOT, "skills", "buddy", "SKILL.md");
|
|
175
142
|
mkdirSync(BUDDY_DIR, { recursive: true });
|
|
176
143
|
cpSync(srcSkill, join(BUDDY_DIR, "SKILL.md"), { force: true });
|
|
177
144
|
ok(`Skill installed: ${join(BUDDY_DIR, "SKILL.md")}`);
|
|
@@ -179,8 +146,8 @@ function installSkill() {
|
|
|
179
146
|
|
|
180
147
|
// ─── Step 3: Configure status line (with animation refresh) ─────────────────
|
|
181
148
|
|
|
182
|
-
function installStatusLine(settings:
|
|
183
|
-
const statusScript = join(PROJECT_ROOT, "
|
|
149
|
+
function installStatusLine(settings: Record<string, any>) {
|
|
150
|
+
const statusScript = join(PROJECT_ROOT, "statusline", "buddy-status.sh");
|
|
184
151
|
|
|
185
152
|
settings.statusLine = {
|
|
186
153
|
type: "command",
|
|
@@ -213,47 +180,62 @@ function stripLegacyPopupHooks(settings: Record<string, any>) {
|
|
|
213
180
|
|
|
214
181
|
// ─── Step 4: Register hooks ─────────────────────────────────────────────────
|
|
215
182
|
|
|
216
|
-
function installHooks(settings:
|
|
217
|
-
const reactHook
|
|
218
|
-
const
|
|
219
|
-
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");
|
|
220
190
|
|
|
221
191
|
if (!settings.hooks) settings.hooks = {};
|
|
222
192
|
|
|
223
|
-
// 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.
|
|
224
195
|
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
225
196
|
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(
|
|
226
|
-
(h) => !
|
|
197
|
+
(h: any) => !h.hooks?.some((hh: any) => hh.command?.includes("coding-buddy") || hh.command?.includes("claude-buddy")),
|
|
227
198
|
);
|
|
228
199
|
settings.hooks.PostToolUse.push({
|
|
229
200
|
matcher: "Bash",
|
|
230
201
|
hooks: [{ type: "command", command: toUnixPath(reactHook) }],
|
|
231
202
|
});
|
|
203
|
+
settings.hooks.PostToolUse.push({
|
|
204
|
+
matcher: "Write|Edit",
|
|
205
|
+
hooks: [{ type: "command", command: toUnixPath(fileTypeHook) }],
|
|
206
|
+
});
|
|
232
207
|
|
|
233
208
|
// Stop: extract <!-- buddy: --> comment from Claude's response
|
|
234
209
|
if (!settings.hooks.Stop) settings.hooks.Stop = [];
|
|
235
210
|
settings.hooks.Stop = settings.hooks.Stop.filter(
|
|
236
|
-
(h) => !
|
|
211
|
+
(h: any) => !h.hooks?.some((hh: any) => hh.command?.includes("coding-buddy") || hh.command?.includes("claude-buddy")),
|
|
237
212
|
);
|
|
238
213
|
settings.hooks.Stop.push({
|
|
239
214
|
hooks: [{ type: "command", command: toUnixPath(commentHook) }],
|
|
240
215
|
});
|
|
216
|
+
settings.hooks.Stop.push({
|
|
217
|
+
hooks: [{ type: "command", command: toUnixPath(suggestHook) }],
|
|
218
|
+
});
|
|
241
219
|
|
|
242
|
-
// 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.
|
|
243
222
|
if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
|
|
244
223
|
settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(
|
|
245
|
-
(h) => !
|
|
224
|
+
(h: any) => !h.hooks?.some((hh: any) => hh.command?.includes("coding-buddy") || hh.command?.includes("claude-buddy")),
|
|
246
225
|
);
|
|
247
226
|
settings.hooks.UserPromptSubmit.push({
|
|
248
227
|
hooks: [{ type: "command", command: toUnixPath(nameHook) }],
|
|
249
228
|
});
|
|
229
|
+
settings.hooks.UserPromptSubmit.push({
|
|
230
|
+
hooks: [{ type: "command", command: toUnixPath(moodHook) }],
|
|
231
|
+
});
|
|
250
232
|
|
|
251
|
-
ok("Hooks registered: PostToolUse + Stop + UserPromptSubmit");
|
|
233
|
+
ok("Hooks registered: PostToolUse (Bash + Write/Edit) + Stop (comment + suggest) + UserPromptSubmit (name + mood)");
|
|
252
234
|
}
|
|
253
235
|
|
|
254
236
|
// ─── Step 5: Ensure MCP tools are allowed ───────────────────────────────────
|
|
255
237
|
|
|
256
|
-
function ensurePermissions(settings:
|
|
238
|
+
function ensurePermissions(settings: Record<string, any>) {
|
|
257
239
|
if (!settings.permissions) settings.permissions = {};
|
|
258
240
|
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
259
241
|
|
|
@@ -310,7 +292,7 @@ if (!preflight()) {
|
|
|
310
292
|
}
|
|
311
293
|
|
|
312
294
|
console.log("");
|
|
313
|
-
info("Installing
|
|
295
|
+
info("Installing coding-buddy...\n");
|
|
314
296
|
|
|
315
297
|
const settings = loadSettings();
|
|
316
298
|
|