mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
feat(setup): add CodeBuddy IDE integration
Add MCP config, skills, and hooks support for CodeBuddy IDE in the `gitnexus setup` command. - MCP: writes `~/.codebuddy/.mcp.json` with stdio transport - Skills: installs 7 GitNexus skills to `~/.codebuddy/skills/` - Hooks: installs PreToolUse/PostToolUse hooks to `~/.codebuddy/settings.json` (fully compatible with Claude Code hooks spec, reuses same hook scripts) - Adds `gitnexus-codebuddy-integration/README.md` with integration docs Detection: checks for `~/.codebuddy/` directory existence.
This commit is contained in:
parent
e01f0912bc
commit
18bb6137f5
2 changed files with 268 additions and 0 deletions
104
gitnexus-codebuddy-integration/README.md
Normal file
104
gitnexus-codebuddy-integration/README.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# GitNexus — CodeBuddy integration
|
||||
|
||||
Static config that adds GitNexus knowledge-graph augmentation and skill files to CodeBuddy.
|
||||
|
||||
## What you get
|
||||
|
||||
| Layer | What it does | How it's installed |
|
||||
| ---------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| **MCP** | `gitnexus` MCP server with 16 tools (`query`, `context`, `impact`, `detect_changes`, `rename`, …) | `npx gitnexus setup` writes `~/.codebuddy/.mcp.json` automatically. |
|
||||
| **Skills** | `/gitnexus-exploring`, `/gitnexus-debugging`, `/gitnexus-impact-analysis`, `/gitnexus-refactoring`, `/gitnexus-cli`, `/gitnexus-pr-review`, `/gitnexus-guide` markdown skills | `npx gitnexus setup` copies them to `~/.codebuddy/skills/`. |
|
||||
| **Hooks** | `PreToolUse` / `PostToolUse` hooks that enrich `Grep` / `Glob` / `Bash` tool calls with graph context, and detect stale index after git mutations | `npx gitnexus setup` writes `~/.codebuddy/settings.json` and copies hook scripts. |
|
||||
|
||||
### What's installed by `gitnexus setup`
|
||||
|
||||
| Step | Automated? |
|
||||
| --------------------------------- | ---------- |
|
||||
| `~/.codebuddy/.mcp.json` | ✅ |
|
||||
| `~/.codebuddy/skills/gitnexus-*` | ✅ |
|
||||
| `~/.codebuddy/settings.json` (hooks) | ✅ |
|
||||
| `~/.codebuddy/hooks/gitnexus/` | ✅ |
|
||||
|
||||
Both MCP config, skills, and hooks are global — they apply to all projects opened in CodeBuddy.
|
||||
|
||||
## Hook contract
|
||||
|
||||
CodeBuddy hooks are **fully compatible with the Claude Code hooks spec**. The hook receives a JSON event on stdin:
|
||||
|
||||
```json
|
||||
{
|
||||
"hook_event_name": "PreToolUse" | "PostToolUse",
|
||||
"tool_name": "Grep" | "Glob" | "Bash",
|
||||
"tool_input": { /* tool-specific */ },
|
||||
"cwd": "/absolute/path/to/project"
|
||||
}
|
||||
```
|
||||
|
||||
It writes augmentation context to stdout as:
|
||||
|
||||
```json
|
||||
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "additionalContext": "[GitNexus] …" } }
|
||||
```
|
||||
|
||||
Empty stdout means "no augmentation, continue normally" — the hook never blocks the tool.
|
||||
|
||||
| Event | Matcher | Behavior |
|
||||
| -------------- | ------------------- | ------------------------------------------------------------------------- |
|
||||
| `PreToolUse` | `Grep\|Glob\|Bash` | Extracts search pattern from tool input, runs `gitnexus augment`, injects graph context before tool executes |
|
||||
| `PostToolUse` | `Bash` | Detects `git commit/merge/rebase` → checks index staleness → notifies agent to reindex |
|
||||
|
||||
## Verify
|
||||
|
||||
1. Index the project: `npx gitnexus analyze`
|
||||
2. Restart CodeBuddy or reload the window so it picks up the new MCP config.
|
||||
3. In CodeBuddy, check the MCP panel — GitNexus should appear as a connected server.
|
||||
4. Try a query: ask CodeBuddy "Show me the auth flow" — GitNexus tools will be available.
|
||||
|
||||
## MCP config format
|
||||
|
||||
CodeBuddy uses `~/.codebuddy/.mcp.json` (JSONC format):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"mcpServers": {
|
||||
"gitnexus": {
|
||||
"type": "stdio",
|
||||
"command": "gitnexus",
|
||||
"args": ["mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Skills format
|
||||
|
||||
Skills follow the Agent Skills standard installed at `~/.codebuddy/skills/gitnexus-*/SKILL.md`. Each skill includes YAML frontmatter with `name` and `description` fields that CodeBuddy uses to decide when to invoke the skill.
|
||||
|
||||
## Configuration priority
|
||||
|
||||
CodeBuddy uses two levels of configuration for hooks — project-level overrides user-level:
|
||||
|
||||
| Level | Path | Priority |
|
||||
| ------- | ----------------------------------------- | -------- |
|
||||
| Project | `<workspace>/.codebuddy/settings.json` | High |
|
||||
| User | `~/.codebuddy/settings.json` | Low |
|
||||
|
||||
`gitnexus setup` writes hooks to **user-level** (`~/.codebuddy/settings.json`), so they apply across all projects. To override or add project-specific hooks, create `<workspace>/.codebuddy/settings.json` in your project.
|
||||
|
||||
## Project-level setup (optional)
|
||||
|
||||
For project-specific configuration, place these files in your project:
|
||||
|
||||
```
|
||||
<your-project>/
|
||||
└── .codebuddy/
|
||||
└── skills/ ← Project-specific skills
|
||||
```
|
||||
|
||||
MCP servers configured at the project level (`.mcp.json` in project root) take precedence over user-level configuration.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **GitNexus not visible in MCP panel** — Check `~/.codebuddy/.mcp.json` exists and is valid JSONC. Run `npx gitnexus setup` to regenerate.
|
||||
- **`gitnexus` command not found** — Install globally with `npm i -g gitnexus` so the `command: "gitnexus"` entry resolves from PATH.
|
||||
- **No tools available** — Run `npx gitnexus analyze` in your project first, then reload CodeBuddy.
|
||||
|
|
@ -662,6 +662,167 @@ async function installCodexSkills(result: SetupResult): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
// ─── CodeBuddy ──────────────────────────────────────────────────────
|
||||
|
||||
async function setupCodeBuddy(result: SetupResult): Promise<void> {
|
||||
const codebuddyDir = path.join(os.homedir(), '.codebuddy');
|
||||
if (!(await dirExists(codebuddyDir))) {
|
||||
result.skipped.push('CodeBuddy (not installed)');
|
||||
return;
|
||||
}
|
||||
|
||||
// CodeBuddy uses ~/.codebuddy/.mcp.json (JSONC format, supports comments)
|
||||
const mcpPath = path.join(codebuddyDir, '.mcp.json');
|
||||
try {
|
||||
const entry = getMcpEntry();
|
||||
const ok = await mergeJsoncFile(mcpPath, ['mcpServers', 'gitnexus'], {
|
||||
type: 'stdio',
|
||||
...entry,
|
||||
});
|
||||
if (ok) {
|
||||
result.configured.push('CodeBuddy');
|
||||
} else {
|
||||
result.errors.push(
|
||||
'CodeBuddy: .mcp.json is corrupt — skipping to preserve existing content',
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
result.errors.push(`CodeBuddy: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install global CodeBuddy skills to ~/.codebuddy/skills/
|
||||
*/
|
||||
async function installCodeBuddySkills(result: SetupResult): Promise<void> {
|
||||
const codebuddyDir = path.join(os.homedir(), '.codebuddy');
|
||||
if (!(await dirExists(codebuddyDir))) return;
|
||||
|
||||
const skillsDir = path.join(codebuddyDir, 'skills');
|
||||
try {
|
||||
const installed = await installSkillsTo(skillsDir);
|
||||
if (installed.length > 0) {
|
||||
result.configured.push(
|
||||
`CodeBuddy skills (${installed.length} skills → ~/.codebuddy/skills/)`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
result.errors.push(`CodeBuddy skills: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install GitNexus hooks to ~/.codebuddy/settings.json for CodeBuddy.
|
||||
* CodeBuddy hooks are fully compatible with the Claude Code hooks spec,
|
||||
* so we reuse the same hook scripts and event structure.
|
||||
*/
|
||||
async function installCodeBuddyHooks(result: SetupResult): Promise<void> {
|
||||
const codebuddyDir = path.join(os.homedir(), '.codebuddy');
|
||||
if (!(await dirExists(codebuddyDir))) return;
|
||||
|
||||
const settingsPath = path.join(codebuddyDir, 'settings.json');
|
||||
|
||||
// Source hooks bundled within the gitnexus package (hooks/claude/)
|
||||
const pluginHooksPath = path.join(__dirname, '..', '..', 'hooks', 'claude');
|
||||
|
||||
// Copy unified hook script to ~/.codebuddy/hooks/gitnexus/
|
||||
const destHooksDir = path.join(codebuddyDir, 'hooks', 'gitnexus');
|
||||
|
||||
try {
|
||||
await fs.mkdir(destHooksDir, { recursive: true });
|
||||
|
||||
const src = path.join(pluginHooksPath, 'gitnexus-hook.cjs');
|
||||
const dest = path.join(destHooksDir, 'gitnexus-hook.cjs');
|
||||
try {
|
||||
let content = await fs.readFile(src, 'utf-8');
|
||||
const resolvedCli = path.join(__dirname, '..', 'cli', 'index.js');
|
||||
const normalizedCli = path.resolve(resolvedCli).replace(/\\/g, '/');
|
||||
const jsonCli = JSON.stringify(normalizedCli);
|
||||
content = content.replace(
|
||||
"let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');",
|
||||
`let cliPath = ${jsonCli};`,
|
||||
);
|
||||
await fs.writeFile(dest, content, 'utf-8');
|
||||
} catch {
|
||||
// Script not found in source — skip
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.copyFile(
|
||||
path.join(pluginHooksPath, 'hook-lock.cjs'),
|
||||
path.join(destHooksDir, 'hook-lock.cjs'),
|
||||
);
|
||||
} catch {
|
||||
// Helper not found in source — skip
|
||||
}
|
||||
|
||||
const hookPath = path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/');
|
||||
const escapedHookPath = hookPath.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||
const hookCmd = `node "${escapedHookPath}"`;
|
||||
|
||||
// Check which hook events need entries (idempotent: skip if already registered)
|
||||
const parsed = await (async () => {
|
||||
try {
|
||||
const r = await fs.readFile(settingsPath, 'utf-8');
|
||||
return parseJsonc(r);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
const hookEntries: Array<{ eventName: string; value: unknown }> = [];
|
||||
|
||||
if (!hasGitnexusHook(parsed?.hooks, 'PreToolUse')) {
|
||||
hookEntries.push({
|
||||
eventName: 'PreToolUse',
|
||||
value: {
|
||||
matcher: 'Grep|Glob|Bash',
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: hookCmd,
|
||||
timeout: 10,
|
||||
statusMessage: 'Enriching with GitNexus graph context...',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!hasGitnexusHook(parsed?.hooks, 'PostToolUse')) {
|
||||
hookEntries.push({
|
||||
eventName: 'PostToolUse',
|
||||
value: {
|
||||
matcher: 'Bash',
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: hookCmd,
|
||||
timeout: 10,
|
||||
statusMessage: 'Checking GitNexus index freshness...',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (hookEntries.length === 0) {
|
||||
result.configured.push('CodeBuddy hooks (already configured)');
|
||||
return;
|
||||
}
|
||||
|
||||
const ok = await mergeHooksJsonc(settingsPath, hookEntries);
|
||||
if (ok) {
|
||||
result.configured.push('CodeBuddy hooks (PreToolUse, PostToolUse)');
|
||||
} else {
|
||||
result.errors.push(
|
||||
'CodeBuddy hooks: settings.json is corrupt — skipping to preserve existing content',
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
result.errors.push(`CodeBuddy hooks: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Main command ──────────────────────────────────────────────────
|
||||
|
||||
export const setupCommand = async () => {
|
||||
|
|
@ -685,6 +846,7 @@ export const setupCommand = async () => {
|
|||
await setupClaudeCode(result);
|
||||
await setupOpenCode(result);
|
||||
await setupCodex(result);
|
||||
await setupCodeBuddy(result);
|
||||
|
||||
// Install global skills for platforms that support them
|
||||
await installClaudeCodeSkills(result);
|
||||
|
|
@ -692,6 +854,8 @@ export const setupCommand = async () => {
|
|||
await installCursorSkills(result);
|
||||
await installOpenCodeSkills(result);
|
||||
await installCodexSkills(result);
|
||||
await installCodeBuddySkills(result);
|
||||
await installCodeBuddyHooks(result);
|
||||
|
||||
// Print results
|
||||
if (result.configured.length > 0) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue