Skip to content

Commit 3971f96

Browse files
igorcostaAutohand Evolve
andcommitted
fix(tools): implement install_agent_skill execution handler
Wires up the previously stubbed install_agent_skill tool: - Fetches community registry with cache fallback - Finds skill by exact name or ID with similar-skill suggestions - Installs via installSkillWithSecurity in non-interactive mode - Auto-activates the skill when activate !== false Co-authored-by: Autohand Evolve <code-noreply@autohand.ai>
1 parent d97b31b commit 3971f96

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

src/core/agent.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ import { FeedbackManager } from '../feedback/FeedbackManager.js';
7979
import { TelemetryManager } from '../telemetry/TelemetryManager.js';
8080
import { SkillsRegistry } from '../skills/SkillsRegistry.js';
8181
import { CommunitySkillsClient } from '../skills/CommunitySkillsClient.js';
82+
import { CommunitySkillsCache } from '../skills/CommunitySkillsCache.js';
83+
import { GitHubRegistryFetcher } from '../skills/GitHubRegistryFetcher.js';
84+
import { fetchRegistryWithFallback, installSkillWithSecurity } from '../skills/communityInstaller.js';
8285
import { McpClientManager } from '../mcp/McpClientManager.js';
8386
import type { McpServerConfig } from '../mcp/types.js';
8487
import { AUTOHAND_PATHS } from '../constants.js';
@@ -879,6 +882,58 @@ export class AutohandAgent {
879882
const cancelled = this.repeatManager.cancel(id);
880883
result = cancelled ? `Cancelled schedule ${id}.` : `No active schedule found with ID "${id}".`;
881884
}
885+
} else if (action.type === 'install_agent_skill') {
886+
const skillName = (action as { name: string }).name;
887+
if (!skillName) {
888+
result = 'Error: install_agent_skill requires a "name" argument.';
889+
} else {
890+
const scope = (action as { scope?: 'project' | 'user' }).scope ?? 'project';
891+
const activate = (action as { activate?: boolean }).activate !== false;
892+
const cache = new CommunitySkillsCache();
893+
const fetcher = new GitHubRegistryFetcher();
894+
const registry = await fetchRegistryWithFallback(cache, fetcher);
895+
if (!registry) {
896+
result = 'Failed to fetch community skills registry. Please check your internet connection.';
897+
} else {
898+
const skill = fetcher.findSkill(registry.skills, skillName);
899+
if (!skill) {
900+
const similar = fetcher.findSimilarSkills(registry.skills, skillName, 3);
901+
let msg = `Skill not found: "${skillName}".`;
902+
if (similar.length > 0) {
903+
msg += `\nDid you mean: ${similar.map((s) => s.name).join(', ')}`;
904+
}
905+
result = msg;
906+
} else {
907+
const installResult = await installSkillWithSecurity(
908+
{
909+
skillsRegistry: this.skillsRegistry,
910+
workspaceRoot: this.runtime.workspaceRoot,
911+
hookManager: this.hookManager,
912+
isNonInteractive: true,
913+
},
914+
skill,
915+
cache,
916+
fetcher,
917+
scope,
918+
);
919+
if (activate && !installResult.includes('Failed') && !installResult.includes('Blocked') && !installResult.includes('blocked') && !installResult.includes('Denied')) {
920+
// Try to activate after successful install
921+
try {
922+
const activateResult = this.skillsRegistry.activateSkill(skill.name);
923+
if (activateResult) {
924+
result = `${installResult}\n\nActivated skill: ${skill.name}`;
925+
} else {
926+
result = `${installResult}\n\nNote: skill installed but could not be activated automatically.`;
927+
}
928+
} catch {
929+
result = `${installResult}\n\nNote: skill installed but activation failed.`;
930+
}
931+
} else {
932+
result = installResult;
933+
}
934+
}
935+
}
936+
}
882937
} else if (McpClientManager.isMcpTool(action.type)) {
883938
// Ensure MCP servers have finished connecting before dispatching
884939
if (this.mcpReady) await this.mcpReady;

0 commit comments

Comments
 (0)