Alvin Unreal 2 months ago
parent
commit
85c60fb06e

+ 7 - 7
src/cli/install.ts

@@ -218,14 +218,12 @@ async function runInteractiveMode(
     // Skills prompt
     console.log(`${BOLD}Recommended Skills:${RESET}`);
     for (const skill of RECOMMENDED_SKILLS) {
-      console.log(`  ${SYMBOLS.bullet} ${BOLD}${skill.name}${RESET}: ${skill.description}`);
+      console.log(
+        `  ${SYMBOLS.bullet} ${BOLD}${skill.name}${RESET}: ${skill.description}`,
+      );
     }
     console.log();
-    const skills = await askYesNo(
-      rl,
-      'Install recommended skills?',
-      'yes',
-    );
+    const skills = await askYesNo(rl, 'Install recommended skills?', 'yes');
     console.log();
 
     return {
@@ -289,7 +287,9 @@ async function runInstall(config: InstallConfig): Promise<number> {
         printWarning(`Failed to install: ${skill.name}`);
       }
     }
-    printSuccess(`${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills installed`);
+    printSuccess(
+      `${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills installed`,
+    );
   }
 
   // Summary

+ 0 - 1
src/cli/providers.test.ts

@@ -141,7 +141,6 @@ describe('providers', () => {
     expect(agents.fixer.skills).toEqual([]);
   });
 
-
   test('generateLiteConfig includes mcps field', () => {
     const config = generateLiteConfig({
       hasAntigravity: true,

+ 10 - 7
src/cli/providers.ts

@@ -2,7 +2,6 @@ import { DEFAULT_AGENT_MCPS } from '../config/agent-mcps';
 import { RECOMMENDED_SKILLS } from './skills';
 import type { InstallConfig } from './types';
 
-
 /**
  * Provider configurations for Cliproxy (Antigravity via cliproxy)
  */
@@ -87,17 +86,20 @@ export function generateLiteConfig(
 
   config.preset = activePreset;
 
-  const createAgentConfig = (agentName: string, modelInfo: { model: string; variant?: string }) => {
+  const createAgentConfig = (
+    agentName: string,
+    modelInfo: { model: string; variant?: string },
+  ) => {
     const isOrchestrator = agentName === 'orchestrator';
 
     // Skills: orchestrator gets "*", others get recommended skills for their role
     const skills = isOrchestrator
       ? ['*']
       : RECOMMENDED_SKILLS.filter(
-        (s) =>
-          s.allowedAgents.includes('*') ||
-          s.allowedAgents.includes(agentName),
-      ).map((s) => s.skillName);
+          (s) =>
+            s.allowedAgents.includes('*') ||
+            s.allowedAgents.includes(agentName),
+        ).map((s) => s.skillName);
 
     // Special case for designer and agent-browser skill
     if (agentName === 'designer' && !skills.includes('agent-browser')) {
@@ -108,7 +110,8 @@ export function generateLiteConfig(
       model: modelInfo.model,
       variant: modelInfo.variant,
       skills,
-      mcps: DEFAULT_AGENT_MCPS[agentName as keyof typeof DEFAULT_AGENT_MCPS] ?? [],
+      mcps:
+        DEFAULT_AGENT_MCPS[agentName as keyof typeof DEFAULT_AGENT_MCPS] ?? [],
     };
   };
 

+ 34 - 31
src/cli/skills.test.ts

@@ -2,41 +2,44 @@ import { describe, expect, it } from 'bun:test';
 import { getSkillPermissionsForAgent } from './skills';
 
 describe('skills permissions', () => {
-    it('should allow all skills for orchestrator by default', () => {
-        const permissions = getSkillPermissionsForAgent('orchestrator');
-        expect(permissions['*']).toBe('allow');
-    });
+  it('should allow all skills for orchestrator by default', () => {
+    const permissions = getSkillPermissionsForAgent('orchestrator');
+    expect(permissions['*']).toBe('allow');
+  });
 
-    it('should deny all skills for other agents by default', () => {
-        const permissions = getSkillPermissionsForAgent('designer');
-        expect(permissions['*']).toBe('deny');
-    });
+  it('should deny all skills for other agents by default', () => {
+    const permissions = getSkillPermissionsForAgent('designer');
+    expect(permissions['*']).toBe('deny');
+  });
 
-    it('should allow recommended skills for specific agents', () => {
-        // Designer should have agent-browser allowed
-        const designerPerms = getSkillPermissionsForAgent('designer');
-        expect(designerPerms['agent-browser']).toBe('allow');
+  it('should allow recommended skills for specific agents', () => {
+    // Designer should have agent-browser allowed
+    const designerPerms = getSkillPermissionsForAgent('designer');
+    expect(designerPerms['agent-browser']).toBe('allow');
 
-        // Developer (orchestrator) should have simplify allowed (and everything else via *)
-        const orchPerms = getSkillPermissionsForAgent('orchestrator');
-        expect(orchPerms.simplify).toBe('allow');
-    });
+    // Developer (orchestrator) should have simplify allowed (and everything else via *)
+    const orchPerms = getSkillPermissionsForAgent('orchestrator');
+    expect(orchPerms.simplify).toBe('allow');
+  });
 
-    it('should honor explicit skill list overrides', () => {
-        // Override with empty list
-        const emptyPerms = getSkillPermissionsForAgent('orchestrator', []);
-        expect(emptyPerms['*']).toBe('deny');
-        expect(Object.keys(emptyPerms).length).toBe(1);
+  it('should honor explicit skill list overrides', () => {
+    // Override with empty list
+    const emptyPerms = getSkillPermissionsForAgent('orchestrator', []);
+    expect(emptyPerms['*']).toBe('deny');
+    expect(Object.keys(emptyPerms).length).toBe(1);
 
-        // Override with specific list
-        const specificPerms = getSkillPermissionsForAgent('designer', ['my-skill', '!bad-skill']);
-        expect(specificPerms['*']).toBe('deny');
-        expect(specificPerms['my-skill']).toBe('allow');
-        expect(specificPerms['bad-skill']).toBe('deny');
-    });
+    // Override with specific list
+    const specificPerms = getSkillPermissionsForAgent('designer', [
+      'my-skill',
+      '!bad-skill',
+    ]);
+    expect(specificPerms['*']).toBe('deny');
+    expect(specificPerms['my-skill']).toBe('allow');
+    expect(specificPerms['bad-skill']).toBe('deny');
+  });
 
-    it('should honor wildcard in explicit list', () => {
-        const wildcardPerms = getSkillPermissionsForAgent('designer', ['*']);
-        expect(wildcardPerms['*']).toBe('allow');
-    });
+  it('should honor wildcard in explicit list', () => {
+    const wildcardPerms = getSkillPermissionsForAgent('designer', ['*']);
+    expect(wildcardPerms['*']).toBe('allow');
+  });
 });

+ 90 - 92
src/cli/skills.ts

@@ -4,18 +4,18 @@ import { spawnSync } from 'node:child_process';
  * A recommended skill to install via `npx skills add`.
  */
 export interface RecommendedSkill {
-    /** Human-readable name for prompts */
-    name: string;
-    /** GitHub repo URL for `npx skills add` */
-    repo: string;
-    /** Skill name within the repo (--skill flag) */
-    skillName: string;
-    /** List of agents that should auto-allow this skill */
-    allowedAgents: string[];
-    /** Description shown to user during install */
-    description: string;
-    /** Optional commands to run after the skill is added */
-    postInstallCommands?: string[];
+  /** Human-readable name for prompts */
+  name: string;
+  /** GitHub repo URL for `npx skills add` */
+  repo: string;
+  /** Skill name within the repo (--skill flag) */
+  skillName: string;
+  /** List of agents that should auto-allow this skill */
+  allowedAgents: string[];
+  /** Description shown to user during install */
+  description: string;
+  /** Optional commands to run after the skill is added */
+  postInstallCommands?: string[];
 }
 
 /**
@@ -23,24 +23,24 @@ export interface RecommendedSkill {
  * Add new skills here to include them in the installation flow.
  */
 export const RECOMMENDED_SKILLS: RecommendedSkill[] = [
-    {
-        name: 'simplify',
-        repo: 'https://github.com/brianlovin/claude-config',
-        skillName: 'simplify',
-        allowedAgents: ['orchestrator'],
-        description: 'YAGNI code simplification expert',
-    },
-    {
-        name: 'agent-browser',
-        repo: 'https://github.com/vercel-labs/agent-browser',
-        skillName: 'agent-browser',
-        allowedAgents: ['designer'],
-        description: 'High-performance browser automation',
-        postInstallCommands: [
-            'npm install -g agent-browser',
-            'agent-browser install',
-        ],
-    },
+  {
+    name: 'simplify',
+    repo: 'https://github.com/brianlovin/claude-config',
+    skillName: 'simplify',
+    allowedAgents: ['orchestrator'],
+    description: 'YAGNI code simplification expert',
+  },
+  {
+    name: 'agent-browser',
+    repo: 'https://github.com/vercel-labs/agent-browser',
+    skillName: 'agent-browser',
+    allowedAgents: ['designer'],
+    description: 'High-performance browser automation',
+    postInstallCommands: [
+      'npm install -g agent-browser',
+      'agent-browser install',
+    ],
+  },
 ];
 
 /**
@@ -49,42 +49,42 @@ export const RECOMMENDED_SKILLS: RecommendedSkill[] = [
  * @returns True if installation succeeded, false otherwise
  */
 export function installSkill(skill: RecommendedSkill): boolean {
-    const args = [
-        'skills',
-        'add',
-        skill.repo,
-        '--skill',
-        skill.skillName,
-        '-a',
-        'opencode',
-        '-y',
-        '--global',
-    ];
+  const args = [
+    'skills',
+    'add',
+    skill.repo,
+    '--skill',
+    skill.skillName,
+    '-a',
+    'opencode',
+    '-y',
+    '--global',
+  ];
 
-    try {
-        const result = spawnSync('npx', args, { stdio: 'inherit' });
-        if (result.status !== 0) {
-            return false;
-        }
+  try {
+    const result = spawnSync('npx', args, { stdio: 'inherit' });
+    if (result.status !== 0) {
+      return false;
+    }
 
-        // Run post-install commands if any
-        if (skill.postInstallCommands && skill.postInstallCommands.length > 0) {
-            console.log(`Running post-install commands for ${skill.name}...`);
-            for (const cmd of skill.postInstallCommands) {
-                console.log(`> ${cmd}`);
-                const [command, ...cmdArgs] = cmd.split(' ');
-                const cmdResult = spawnSync(command, cmdArgs, { stdio: 'inherit' });
-                if (cmdResult.status !== 0) {
-                    console.warn(`Post-install command failed: ${cmd}`);
-                }
-            }
+    // Run post-install commands if any
+    if (skill.postInstallCommands && skill.postInstallCommands.length > 0) {
+      console.log(`Running post-install commands for ${skill.name}...`);
+      for (const cmd of skill.postInstallCommands) {
+        console.log(`> ${cmd}`);
+        const [command, ...cmdArgs] = cmd.split(' ');
+        const cmdResult = spawnSync(command, cmdArgs, { stdio: 'inherit' });
+        if (cmdResult.status !== 0) {
+          console.warn(`Post-install command failed: ${cmd}`);
         }
-
-        return true;
-    } catch (error) {
-        console.error(`Failed to install skill: ${skill.name}`, error);
-        return false;
+      }
     }
+
+    return true;
+  } catch (error) {
+    console.error(`Failed to install skill: ${skill.name}`, error);
+    return false;
+  }
 }
 
 /**
@@ -94,40 +94,38 @@ export function installSkill(skill: RecommendedSkill): boolean {
  * @returns Permission rules for the skill permission type
  */
 export function getSkillPermissionsForAgent(
-    agentName: string,
-    skillList?: string[],
+  agentName: string,
+  skillList?: string[],
 ): Record<string, 'allow' | 'ask' | 'deny'> {
-    // Orchestrator gets all skills by default, others are restricted
-    const permissions: Record<string, 'allow' | 'ask' | 'deny'> = {
-        '*': agentName === 'orchestrator' ? 'allow' : 'deny',
-    };
+  // Orchestrator gets all skills by default, others are restricted
+  const permissions: Record<string, 'allow' | 'ask' | 'deny'> = {
+    '*': agentName === 'orchestrator' ? 'allow' : 'deny',
+  };
 
-    // If the user provided an explicit skill list (even empty), honor it
-    if (skillList) {
-        permissions['*'] = 'deny';
-        for (const name of skillList) {
-            if (name === '*') {
-                permissions['*'] = 'allow';
-            } else if (name.startsWith('!')) {
-                permissions[name.slice(1)] = 'deny';
-            } else {
-                permissions[name] = 'allow';
-            }
-        }
-        return permissions;
+  // If the user provided an explicit skill list (even empty), honor it
+  if (skillList) {
+    permissions['*'] = 'deny';
+    for (const name of skillList) {
+      if (name === '*') {
+        permissions['*'] = 'allow';
+      } else if (name.startsWith('!')) {
+        permissions[name.slice(1)] = 'deny';
+      } else {
+        permissions[name] = 'allow';
+      }
     }
+    return permissions;
+  }
 
-    // Otherwise, use recommended defaults
-    for (const skill of RECOMMENDED_SKILLS) {
-        const isAllowed =
-            skill.allowedAgents.includes('*') ||
-            skill.allowedAgents.includes(agentName);
-        if (isAllowed) {
-            permissions[skill.skillName] = 'allow';
-        }
+  // Otherwise, use recommended defaults
+  for (const skill of RECOMMENDED_SKILLS) {
+    const isAllowed =
+      skill.allowedAgents.includes('*') ||
+      skill.allowedAgents.includes(agentName);
+    if (isAllowed) {
+      permissions[skill.skillName] = 'allow';
     }
+  }
 
-    return permissions;
+  return permissions;
 }
-
-

+ 0 - 1
src/cli/types.ts

@@ -23,7 +23,6 @@ export interface InstallConfig {
   installSkills: boolean;
 }
 
-
 export interface ConfigMergeResult {
   success: boolean;
   configPath: string;

+ 33 - 33
src/config/agent-mcps.ts

@@ -1,64 +1,64 @@
 import {
-    type AgentName,
-    getAgentOverride,
-    McpNameSchema,
-    type PluginConfig,
+  type AgentName,
+  getAgentOverride,
+  McpNameSchema,
+  type PluginConfig,
 } from '.';
 
 /** Default MCPs per agent - "*" means all MCPs, "!item" excludes specific MCPs */
 
 export const DEFAULT_AGENT_MCPS: Record<AgentName, string[]> = {
-    orchestrator: ['websearch'],
-    designer: [],
-    oracle: [],
-    librarian: ['websearch', 'context7', 'grep_app'],
-    explorer: [],
-    fixer: [],
+  orchestrator: ['websearch'],
+  designer: [],
+  oracle: [],
+  librarian: ['websearch', 'context7', 'grep_app'],
+  explorer: [],
+  fixer: [],
 };
 
 /**
  * Parse a list with wildcard and exclusion syntax.
  */
 export function parseList(items: string[], allAvailable: string[]): string[] {
-    if (!items || items.length === 0) {
-        return [];
-    }
+  if (!items || items.length === 0) {
+    return [];
+  }
 
-    const allow = items.filter((i) => !i.startsWith('!'));
-    const deny = items.filter((i) => i.startsWith('!')).map((i) => i.slice(1));
+  const allow = items.filter((i) => !i.startsWith('!'));
+  const deny = items.filter((i) => i.startsWith('!')).map((i) => i.slice(1));
 
-    if (deny.includes('*')) {
-        return [];
-    }
+  if (deny.includes('*')) {
+    return [];
+  }
 
-    if (allow.includes('*')) {
-        return allAvailable.filter((item) => !deny.includes(item));
-    }
+  if (allow.includes('*')) {
+    return allAvailable.filter((item) => !deny.includes(item));
+  }
 
-    return allow.filter((item) => !deny.includes(item));
+  return allow.filter((item) => !deny.includes(item));
 }
 
 /**
  * Get available MCP names from schema and config.
  */
 export function getAvailableMcpNames(config?: PluginConfig): string[] {
-    const builtinMcps = McpNameSchema.options;
-    const disabled = new Set(config?.disabled_mcps ?? []);
-    return builtinMcps.filter((name) => !disabled.has(name));
+  const builtinMcps = McpNameSchema.options;
+  const disabled = new Set(config?.disabled_mcps ?? []);
+  return builtinMcps.filter((name) => !disabled.has(name));
 }
 
 /**
  * Get the MCP list for an agent (from config or defaults).
  */
 export function getAgentMcpList(
-    agentName: string,
-    config?: PluginConfig,
+  agentName: string,
+  config?: PluginConfig,
 ): string[] {
-    const agentConfig = getAgentOverride(config, agentName);
-    if (agentConfig?.mcps !== undefined) {
-        return agentConfig.mcps;
-    }
+  const agentConfig = getAgentOverride(config, agentName);
+  if (agentConfig?.mcps !== undefined) {
+    return agentConfig.mcps;
+  }
 
-    const defaultMcps = DEFAULT_AGENT_MCPS[agentName as AgentName];
-    return defaultMcps ?? [];
+  const defaultMcps = DEFAULT_AGENT_MCPS[agentName as AgentName];
+  return defaultMcps ?? [];
 }

+ 10 - 10
src/config/utils.ts

@@ -4,20 +4,20 @@ import type { AgentOverrideConfig, PluginConfig } from './schema';
 /**
  * Get agent override config by name, supporting backward-compatible aliases.
  * Checks both the current name and any legacy alias names.
- * 
+ *
  * @param config - The plugin configuration
  * @param name - The current agent name
  * @returns The agent-specific override configuration if found
  */
 export function getAgentOverride(
-    config: PluginConfig | undefined,
-    name: string,
+  config: PluginConfig | undefined,
+  name: string,
 ): AgentOverrideConfig | undefined {
-    const overrides = config?.agents ?? {};
-    return (
-        overrides[name] ??
-        overrides[
-        Object.keys(AGENT_ALIASES).find((k) => AGENT_ALIASES[k] === name) ?? ''
-        ]
-    );
+  const overrides = config?.agents ?? {};
+  return (
+    overrides[name] ??
+    overrides[
+      Object.keys(AGENT_ALIASES).find((k) => AGENT_ALIASES[k] === name) ?? ''
+    ]
+  );
 }

+ 1 - 1
src/hooks/post-read-nudge/index.ts

@@ -4,7 +4,7 @@
  */
 
 const NUDGE =
-  "\n\n---\nReminder to follow the workflow instructions, consider delegation to specialist(s)";
+  '\n\n---\nReminder to follow the workflow instructions, consider delegation to specialist(s)';
 
 interface ToolExecuteAfterInput {
   tool: string;