Add unit tests for skill capabilities, skill planner, and UV setup

- Implement tests for random ID generation, ensuring preference for crypto.randomUUID.
- Create tests for runtime context capabilities, validating the injection of enabled skill capabilities.
- Add tests for skill capability parsing, including classification and command example extraction.
- Introduce tests for the skill planner, verifying tool call planning based on user requests and attachment requirements.
- Establish tests for UV setup, ensuring proper handling of Python installation scenarios and environment checks.
This commit is contained in:
DEV_DSW
2026-04-24 17:02:59 +08:00
parent e11a2296cc
commit 4c61e93c3e
42 changed files with 12560 additions and 224 deletions

View File

@@ -1,4 +1,10 @@
import { BrowserWindow } from 'electron';
import { ClawHubService } from '@electron/gateway/clawhub';
import {
hydrateSkillCapabilityRegistry,
refreshSkillCapabilityRegistry,
} from '@electron/gateway/skill-capability-registry';
import { windowManager } from '@electron/service/window-service';
import {
SkillInstallService,
SkillInstallServiceError,
@@ -9,17 +15,34 @@ import type { GatewayEvent, GatewayRpcParams, GatewayRpcReturns } from '../types
type GatewayBroadcast = (event: GatewayEvent) => void;
function broadcastGatewayEvent(event: GatewayEvent): void {
const mainWindow = BrowserWindow.getAllWindows().find(
(win) => windowManager.getName(win) === 'main',
) ?? BrowserWindow.getAllWindows().find((win) => !win.isDestroyed());
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('gateway:event', event);
}
}
function broadcastSkillsRuntimeChanged(broadcast?: GatewayBroadcast, reason = 'skills:changed'): void {
broadcast?.({
const event: GatewayEvent = {
type: 'runtime:changed',
topics: ['skills'],
reason,
syncedAt: new Date().toISOString(),
});
};
if (broadcast) {
broadcast(event);
return;
}
broadcastGatewayEvent(event);
}
export async function handleSkillsStatus(): Promise<GatewayRpcReturns['skills.status']> {
const configs = await getAllSkillConfigs();
hydrateSkillCapabilityRegistry(configs);
return {
skills: Object.entries(configs).map(([skillKey, config]) => ({
@@ -47,6 +70,7 @@ export async function handleSkillsStatus(): Promise<GatewayRpcReturns['skills.st
export async function handleSkillsUpdate(
params: { skillKey: string; enabled?: boolean },
broadcast?: GatewayBroadcast,
): Promise<GatewayRpcReturns['skills.update']> {
const { skillKey, enabled } = params;
if (!skillKey || !String(skillKey).trim()) {
@@ -58,6 +82,17 @@ export async function handleSkillsUpdate(
throw new Error(result.error || 'Failed to update skill');
}
try {
const configs = await getAllSkillConfigs();
hydrateSkillCapabilityRegistry(configs);
} catch (error) {
console.warn('Failed to refresh skill capability registry after skills.update:', error);
}
const normalizedSkillKey = String(skillKey).trim();
const action = enabled === false ? 'disabled' : enabled === true ? 'enabled' : 'updated';
broadcastSkillsRuntimeChanged(broadcast, `skills:update:${normalizedSkillKey}:${action}`);
return { success: true };
}
@@ -72,6 +107,11 @@ export async function handleSkillsInstall(
try {
const result = await installService.install(request);
try {
await refreshSkillCapabilityRegistry();
} catch (error) {
console.warn('Failed to refresh skill capability registry after skills.install:', error);
}
broadcastSkillsRuntimeChanged(
broadcast,
`skills:install:${result.source}:${result.slug}`,