Chapter 2: Skill Format and Loader Architecture

April 13, 2026 ยท View on GitHub

Welcome to Chapter 2: Skill Format and Loader Architecture. In this part of OpenSkills Tutorial: Universal Skill Loading for Coding Agents, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.

OpenSkills uses Claude-style SKILL.md and generates an agent-readable skills registry in AGENTS.md.

Architecture Highlights

LayerRole
skill packageinstruction + resources
loaderinstall/read/update lifecycle
sync enginewrites <available_skills> manifest

Summary

You now understand how OpenSkills maps skill files into runtime-usable metadata.

Next: Chapter 3: Installation Sources and Trust Model

Source Code Walkthrough

src/types.ts

The SkillMetadata interface in src/types.ts handles a key part of this chapter's functionality:

}

export interface SkillMetadata {
  name: string;
  description: string;
  context?: string;
}

This interface is important because it defines how OpenSkills Tutorial: Universal Skill Loading for Coding Agents implements the patterns covered in this chapter.

src/commands/update.ts

The updateSkills function in src/commands/update.ts handles a key part of this chapter's functionality:

 * Update installed skills from their recorded source metadata.
 */
export async function updateSkills(skillNames: string[] | string | undefined): Promise<void> {
  const requested = normalizeSkillNames(skillNames);
  const skills = findAllSkills();

  if (skills.length === 0) {
    console.log('No skills installed.\n');
    console.log('Install skills:');
    console.log(`  ${chalk.cyan('npx openskills install anthropics/skills')}         ${chalk.dim('# Project (default)')}`);
    console.log(`  ${chalk.cyan('npx openskills install owner/skill --global')}     ${chalk.dim('# Global (advanced)')}`);
    return;
  }

  let targets = skills;

  if (requested.length > 0) {
    const requestedSet = new Set(requested);
    targets = skills.filter((skill) => requestedSet.has(skill.name));

    const missing = requested.filter((name) => !skills.some((skill) => skill.name === name));
    if (missing.length > 0) {
      console.log(chalk.yellow(`Skipping missing skills: ${missing.join(', ')}`));
    }
  } else {
    // Default to updating all installed skills
    targets = skills;
  }

  if (targets.length === 0) {
    console.log(chalk.yellow('No matching skills to update.'));
    return;

This function is important because it defines how OpenSkills Tutorial: Universal Skill Loading for Coding Agents implements the patterns covered in this chapter.

src/commands/update.ts

The updateSkillFromDir function in src/commands/update.ts handles a key part of this chapter's functionality:

        continue;
      }
      updateSkillFromDir(skill.path, localPath);
      writeSkillMetadata(skill.path, { ...metadata, installedAt: new Date().toISOString() });
      console.log(chalk.green(`โœ… Updated: ${skill.name}`));
      updated++;
      continue;
    }

    if (!metadata.repoUrl) {
      console.log(chalk.yellow(`Skipped: ${skill.name} (missing repo URL metadata)`));
      missingRepoUrl.push(skill.name);
      skipped++;
      continue;
    }

    const tempDir = join(homedir(), `.openskills-temp-${Date.now()}`);
    mkdirSync(tempDir, { recursive: true });

    const spinner = ora(`Updating ${skill.name}...`).start();
    try {
      execSync(`git clone --depth 1 --quiet "${metadata.repoUrl}" "${tempDir}/repo"`, { stdio: 'pipe' });
      const repoDir = join(tempDir, 'repo');
      const subpath = metadata.subpath && metadata.subpath !== '.' ? metadata.subpath : '';
      const sourceDir = subpath ? join(repoDir, subpath) : repoDir;

      if (!existsSync(join(sourceDir, 'SKILL.md'))) {
        spinner.fail(`SKILL.md missing for ${skill.name}`);
        console.log(chalk.yellow(`Skipped: ${skill.name} (SKILL.md not found in repo at ${subpath || '.'})`));
        missingRepoSkillFile.push({ name: skill.name, subpath: subpath || '.' });
        skipped++;
        continue;

This function is important because it defines how OpenSkills Tutorial: Universal Skill Loading for Coding Agents implements the patterns covered in this chapter.

src/commands/update.ts

The isPathInside function in src/commands/update.ts handles a key part of this chapter's functionality:

  mkdirSync(targetDir, { recursive: true });

  if (!isPathInside(targetPath, targetDir)) {
    console.error(chalk.red('Security error: Installation path outside target directory'));
    process.exit(1);
  }

  rmSync(targetPath, { recursive: true, force: true });
  cpSync(sourceDir, targetPath, { recursive: true, dereference: true });
}

function isPathInside(targetPath: string, targetDir: string): boolean {
  const resolvedTargetPath = resolve(targetPath);
  const resolvedTargetDir = resolve(targetDir);
  const resolvedTargetDirWithSep = resolvedTargetDir.endsWith(sep)
    ? resolvedTargetDir
    : resolvedTargetDir + sep;
  return resolvedTargetPath.startsWith(resolvedTargetDirWithSep);
}

This function is important because it defines how OpenSkills Tutorial: Universal Skill Loading for Coding Agents implements the patterns covered in this chapter.

How These Components Connect

flowchart TD
    A[SkillMetadata]
    B[updateSkills]
    C[updateSkillFromDir]
    D[isPathInside]
    E[isLocalPath]
    A --> B
    B --> C
    C --> D
    D --> E