mega-minds
Version:
Enhanced multi-agent workflow system for Claude Code projects with automated handoff management and Claude Code hooks integration
163 lines (140 loc) • 5.42 kB
JavaScript
// lib/commands/record-tool-use.js
// Command: npx mega-minds record-tool-use
// Purpose: Records tool usage from Claude Code PostToolUse hook for agent coordination tracking
const AIDevTeam = require('../core/AIDevTeam');
/**
* Records tool usage from Claude Code hooks, specifically Task tool usage for agent coordination
* Implements integration between Claude's native Task tool and mega-minds tracking system
*/
class ToolUseRecorder {
constructor() {
this.projectPath = process.cwd();
}
/**
* Main execution function for recording tool use
* This is called by Claude Code PostToolUse hook
* @param {Array} args - Command arguments from hook
* @returns {Promise<void>}
*/
async run(args = []) {
try {
// Get tool use data from environment variables or stdin
const toolData = await this.getToolUseData();
if (!toolData || !toolData.tool) {
// Not a tool use we care about, exit silently
return;
}
// Only track Task tool usage for agent coordination
if (toolData.tool === 'Task') {
await this.recordTaskToolUsage(toolData);
}
} catch (error) {
// Hooks should fail silently to not interrupt Claude Code workflow
console.error('⚠️ Tool use recording failed (silent):', error.message);
}
}
/**
* Get tool use data from Claude Code hook environment
* @returns {Promise<object|null>} Tool use data or null if not available
*/
async getToolUseData() {
try {
// Try to get data from environment variables (Claude Code hook format)
const toolName = process.env.CLAUDE_TOOL_NAME;
const toolArgs = process.env.CLAUDE_TOOL_ARGS;
if (toolName) {
return {
tool: toolName,
args: toolArgs ? JSON.parse(toolArgs) : {},
timestamp: new Date().toISOString()
};
}
// Try to read from stdin (alternative hook format)
const stdinData = await this.readStdinData();
if (stdinData) {
return JSON.parse(stdinData);
}
return null;
} catch (error) {
return null;
}
}
/**
* Read data from stdin with timeout
* @returns {Promise<string|null>} Stdin data or null
*/
async readStdinData() {
return new Promise((resolve) => {
let data = '';
const timeout = setTimeout(() => resolve(null), 1000); // 1 second timeout
process.stdin.on('readable', () => {
const chunk = process.stdin.read();
if (chunk !== null) {
data += chunk;
}
});
process.stdin.on('end', () => {
clearTimeout(timeout);
resolve(data || null);
});
// Handle case where stdin is empty immediately
setTimeout(() => {
if (data === '') {
resolve(null);
}
}, 100);
});
}
/**
* Record Task tool usage for agent coordination
* @param {object} toolData - Tool usage data
*/
async recordTaskToolUsage(toolData) {
const team = new AIDevTeam(this.projectPath);
await team.initialize('lightweight'); // Lightweight init for hook performance
const args = toolData.args || {};
const subagentType = args.subagent_type || args.subagentType;
const description = args.description || args.prompt || 'Task tool usage';
if (subagentType) {
// This is an agent activation via Task tool
console.log(`🤖 Recording Task tool agent activation: ${subagentType}`);
// Record as handoff initiation (from system to agent)
const handoffId = await team.agentState.recordHandoffInitiated(
'system',
subagentType,
{
taskDescription: description,
context: 'Task tool activation',
priority: 'normal',
timestamp: toolData.timestamp,
source: 'task-tool-hook',
toolArgs: args
}
);
// Update session with agent activity
if (team.sessions && team.sessions.currentSession) {
await team.sessions.trackHandoffEvent('task_tool_activation', {
agent: subagentType,
handoffId: handoffId,
description: description,
timestamp: toolData.timestamp
});
}
console.log(`✅ Agent activation tracked: ${handoffId}`);
}
}
}
// Handle CLI execution
if (require.main === module) {
const recorder = new ToolUseRecorder();
const args = process.argv.slice(2);
recorder.run(args)
.then(() => {
// Exit silently on success (hook requirement)
})
.catch((error) => {
// Exit silently on error (hook requirement)
process.exit(0);
});
}
module.exports = ToolUseRecorder;