fix: include bundled skills/ dir in skills status and sync discovery (#461)

This commit is contained in:
Cameron
2026-03-02 17:06:08 -08:00
committed by GitHub
parent 0dd462b60d
commit 00b6f00446
3 changed files with 45 additions and 7 deletions

View File

@@ -335,11 +335,18 @@ async function main() {
break;
case 'skills': {
const { showStatus, runSkillsSync } = await import('./skills/index.js');
const { showStatus, runSkillsSync, enableSkill } = await import('./skills/index.js');
switch (subCommand) {
case 'status':
await showStatus();
break;
case 'enable':
if (!args[2]) {
console.error('Usage: lettabot skills enable <name>');
process.exit(1);
}
enableSkill(args[2]);
break;
default:
await runSkillsSync();
}

View File

@@ -5,7 +5,7 @@
import { existsSync, readdirSync, cpSync, mkdirSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import * as p from '@clack/prompts';
import { PROJECT_SKILLS_DIR, GLOBAL_SKILLS_DIR, SKILLS_SH_DIR, parseSkillFile } from './loader.js';
import { PROJECT_SKILLS_DIR, BUNDLED_SKILLS_DIR, GLOBAL_SKILLS_DIR, SKILLS_SH_DIR, parseSkillFile } from './loader.js';
const HOME = process.env.HOME || process.env.USERPROFILE || '';
const WORKING_DIR = process.env.WORKING_DIR || '/tmp/lettabot';
@@ -68,10 +68,12 @@ function discoverSkills(): SkillInfo[] {
}
};
// Discover from all sources (order matters - first source wins for duplicates)
// Discover from all sources (order matters - first source wins for duplicates).
// Priority matches the loader hierarchy: project (.skills/) > bundled (skills/) > external.
addFromDir(PROJECT_SKILLS_DIR, 'builtin'); // .skills/ project overrides
addFromDir(BUNDLED_SKILLS_DIR, 'builtin'); // skills/ bundled with repo
addFromDir(CLAWDHUB_DIR, 'clawdhub');
addFromDir(VERCEL_DIR, 'vercel');
addFromDir(PROJECT_SKILLS_DIR, 'builtin');
return skills.sort((a, b) => a.name.localeCompare(b.name));
}
@@ -218,3 +220,32 @@ export async function runSkillsSync(): Promise<void> {
p.log.info(`Skills directory: ${TARGET_DIR}`);
p.outro(`✨ Added ${toAdd.length}, removed ${toRemove.length} skill(s)`);
}
/**
* Non-interactively enable a single skill by name.
* Searches BUNDLED_SKILLS_DIR, then GLOBAL_SKILLS_DIR, then SKILLS_SH_DIR.
*/
export function enableSkill(name: string): void {
// Search order: highest priority first (project local > global > bundled > skills.sh)
const sourceDirs = [PROJECT_SKILLS_DIR, GLOBAL_SKILLS_DIR, BUNDLED_SKILLS_DIR, SKILLS_SH_DIR];
mkdirSync(TARGET_DIR, { recursive: true });
const dest = join(TARGET_DIR, name);
if (existsSync(dest)) {
console.log(`Skill '${name}' is already enabled.`);
return;
}
for (const dir of sourceDirs) {
const src = join(dir, name);
if (existsSync(src) && existsSync(join(src, 'SKILL.md'))) {
cpSync(src, dest, { recursive: true });
console.log(`Enabled skill '${name}' from ${dir}`);
return;
}
}
console.error(`Skill '${name}' not found. Run 'lettabot skills status' to see available skills.`);
process.exit(1);
}

View File

@@ -6,7 +6,7 @@ import * as p from '@clack/prompts';
import { join } from 'node:path';
import { getSkillsSummary, type SkillsSummary } from './status.js';
import { installSkillDeps } from './install.js';
import { hasBinary, GLOBAL_SKILLS_DIR, SKILLS_SH_DIR } from './loader.js';
import { hasBinary, BUNDLED_SKILLS_DIR, GLOBAL_SKILLS_DIR, SKILLS_SH_DIR } from './loader.js';
import type { NodeManager, SkillStatus } from './types.js';
import { createLogger } from '../logger.js';
@@ -261,7 +261,7 @@ export async function listSkills(): Promise<void> {
*/
export async function showStatus(): Promise<void> {
const enabledSummary = getSkillsSummary([WORKING_SKILLS_DIR]);
const availableSummary = getSkillsSummary([GLOBAL_SKILLS_DIR, SKILLS_SH_DIR]);
const availableSummary = getSkillsSummary([BUNDLED_SKILLS_DIR, GLOBAL_SKILLS_DIR, SKILLS_SH_DIR]);
// Get names of enabled skills to filter available
const enabledNames = new Set(enabledSummary.skills.map(s => s.skill.name));
@@ -297,6 +297,6 @@ export async function showStatus(): Promise<void> {
}
log.info('');
log.info(` To enable: lettabot skills enable <name>`);
log.info(` To enable: lettabot skills enable <name> (or run: lettabot skills)`);
log.info(` Skills dir: ${WORKING_SKILLS_DIR}`);
}