feat(plugin): add gitnexus-factory-plugin for droid plugin install

This commit is contained in:
joseph02 2026-07-17 17:24:59 -07:00
parent d48fd4712a
commit 17473b9c3b
4 changed files with 207 additions and 0 deletions

View file

@ -0,0 +1,11 @@
{
"name": "gitnexus",
"description": "Code intelligence powered by a knowledge graph. Provides execution flow tracing, blast radius analysis, and augmented search across your codebase.",
"version": "1.6.9",
"author": {
"name": "GitNexus"
},
"homepage": "https://github.com/abhigyanpatwari/GitNexus",
"repository": "https://github.com/abhigyanpatwari/GitNexus",
"keywords": ["code-intelligence", "knowledge-graph", "mcp", "static-analysis"]
}

View file

@ -0,0 +1,174 @@
#!/usr/bin/env node
/**
* GitNexus Factory AI (Droid) Plugin Hook
*
* PostToolUse handler — augments Grep/Glob/Execute searches with graph
* context from the GitNexus index. Self-contained: reads the tool payload from
* stdin, resolves a search pattern, and appends `gitnexus augment` output via
* hookSpecificOutput.additionalContext. No external dependencies (no jq).
*/
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
/**
* Read JSON input from stdin synchronously.
*/
function readInput() {
try {
const data = fs.readFileSync(0, 'utf-8');
return JSON.parse(data);
} catch {
return {};
}
}
/**
* Check if a directory (or ancestor) has a .gitnexus index.
*/
function findGitNexusIndex(startDir) {
let dir = startDir || process.cwd();
for (let i = 0; i < 5; i++) {
if (fs.existsSync(path.join(dir, '.gitnexus'))) {
return true;
}
const parent = path.dirname(dir);
if (parent === dir) break;
dir = parent;
}
return false;
}
/**
* Extract search pattern from tool input.
*/
function extractPattern(toolName, toolInput) {
if (toolName === 'Grep') {
return toolInput.pattern || null;
}
if (toolName === 'Glob') {
const raw = toolInput.pattern || '';
const match = raw.match(/[*\/]([a-zA-Z][a-zA-Z0-9_-]{2,})/);
return match ? match[1] : null;
}
if (toolName === 'Execute') {
const cmd = toolInput.command || '';
if (!/\brg\b|\bgrep\b/.test(cmd)) return null;
const tokens = cmd.split(/\s+/);
let foundCmd = false;
let skipNext = false;
const flagsWithValues = new Set([
'-e',
'-f',
'-m',
'-A',
'-B',
'-C',
'-g',
'--glob',
'-t',
'--type',
'--include',
'--exclude',
]);
for (const token of tokens) {
if (skipNext) {
skipNext = false;
continue;
}
if (!foundCmd) {
if (/\brg\b|\bgrep\b/.test(token)) foundCmd = true;
continue;
}
if (token.startsWith('-')) {
if (flagsWithValues.has(token)) skipNext = true;
continue;
}
const cleaned = token.replace(/['"]/g, '');
return cleaned.length >= 3 ? cleaned : null;
}
return null;
}
return null;
}
function main() {
try {
const input = readInput();
const hookEvent = input.hook_event_name || '';
if (hookEvent !== 'PostToolUse') return;
const cwd = input.cwd || process.cwd();
if (!findGitNexusIndex(cwd)) return;
const toolName = input.tool_name || '';
const toolInput = input.tool_input || {};
if (toolName !== 'Grep' && toolName !== 'Glob' && toolName !== 'Execute') return;
const pattern = extractPattern(toolName, toolInput);
if (!pattern || pattern.length < 3) return;
// augment CLI writes result to stderr (KuzuDB's native module captures
// stdout fd at OS level, making it unusable in subprocess contexts).
let result = '';
const isWin = process.platform === 'win32';
// Try direct gitnexus binary first (faster if globally installed)
try {
const child = spawnSync('gitnexus', ['augment', pattern], {
encoding: 'utf-8',
timeout: 8000,
cwd,
stdio: ['pipe', 'pipe', 'pipe'],
shell: isWin,
});
if (child.status === 0 && child.stderr && child.stderr.trim()) {
result = child.stderr;
}
} catch {
/* not on PATH */
}
// Fallback to npx if direct binary didn't produce output
if (!result || !result.trim()) {
try {
const child = spawnSync('npx', ['-y', 'gitnexus', 'augment', pattern], {
encoding: 'utf-8',
timeout: 8000,
cwd,
stdio: ['pipe', 'pipe', 'pipe'],
shell: isWin,
});
if (child.status === 0 && child.stderr && child.stderr.trim()) {
result = child.stderr;
}
} catch {
/* graceful failure */
}
}
if (result && result.trim()) {
console.log(
JSON.stringify({
hookSpecificOutput: {
hookEventName: 'PostToolUse',
additionalContext: result.trim(),
},
}),
);
}
} catch {
// Graceful failure
}
}
main();

View file

@ -0,0 +1,14 @@
{
"PostToolUse": [
{
"matcher": "Grep|Glob|Execute",
"hooks": [
{
"type": "command",
"command": "node ${DROID_PLUGIN_ROOT}/hooks/gitnexus-hook.js",
"timeout": 10
}
]
}
]
}

View file

@ -0,0 +1,8 @@
{
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@latest", "mcp"]
}
}
}