custom-skills.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import {
  2. copyFileSync,
  3. existsSync,
  4. mkdirSync,
  5. readdirSync,
  6. statSync,
  7. } from 'node:fs';
  8. import { join, dirname } from 'node:path';
  9. import { homedir } from 'node:os';
  10. /**
  11. * A custom skill bundled in this repository.
  12. * Unlike npx-installed skills, these are copied from src/skills/ to ~/.config/opencode/skills/
  13. */
  14. export interface CustomSkill {
  15. /** Skill name (folder name) */
  16. name: string;
  17. /** Human-readable description */
  18. description: string;
  19. /** List of agents that should auto-allow this skill */
  20. allowedAgents: string[];
  21. /** Source path in this repo (relative to project root) */
  22. sourcePath: string;
  23. }
  24. /**
  25. * Registry of custom skills bundled in this repository.
  26. */
  27. export const CUSTOM_SKILLS: CustomSkill[] = [
  28. {
  29. name: 'cartography',
  30. description: 'Repository understanding and hierarchical codemap generation',
  31. allowedAgents: ['orchestrator'],
  32. sourcePath: 'src/skills/cartography',
  33. },
  34. ];
  35. /**
  36. * Get the target directory for custom skills installation.
  37. */
  38. export function getCustomSkillsDir(): string {
  39. return join(homedir(), '.config', 'opencode', 'skills');
  40. }
  41. /**
  42. * Recursively copy a directory.
  43. */
  44. function copyDirRecursive(src: string, dest: string): void {
  45. if (!existsSync(dest)) {
  46. mkdirSync(dest, { recursive: true });
  47. }
  48. const entries = readdirSync(src);
  49. for (const entry of entries) {
  50. const srcPath = join(src, entry);
  51. const destPath = join(dest, entry);
  52. const stat = statSync(srcPath);
  53. if (stat.isDirectory()) {
  54. copyDirRecursive(srcPath, destPath);
  55. } else {
  56. const destDir = dirname(destPath);
  57. if (!existsSync(destDir)) {
  58. mkdirSync(destDir, { recursive: true });
  59. }
  60. copyFileSync(srcPath, destPath);
  61. }
  62. }
  63. }
  64. /**
  65. * Install a custom skill by copying from src/skills/ to ~/.config/opencode/skills/
  66. * @param skill - The custom skill to install
  67. * @param projectRoot - Root directory of oh-my-opencode-slim project
  68. * @returns True if installation succeeded, false otherwise
  69. */
  70. export function installCustomSkill(
  71. skill: CustomSkill,
  72. projectRoot: string,
  73. ): boolean {
  74. try {
  75. const sourcePath = join(projectRoot, skill.sourcePath);
  76. const targetPath = join(getCustomSkillsDir(), skill.name);
  77. // Validate source exists
  78. if (!existsSync(sourcePath)) {
  79. console.error(`Custom skill source not found: ${sourcePath}`);
  80. return false;
  81. }
  82. // Copy skill directory
  83. copyDirRecursive(sourcePath, targetPath);
  84. return true;
  85. } catch (error) {
  86. console.error(`Failed to install custom skill: ${skill.name}`, error);
  87. return false;
  88. }
  89. }