Python library implementing Anthropic's Agent Skills functionality for LLM-powered agents
Project description
faskill
Fork of maxvaega/skillkit
Extensively refactored with bug fixes, security hardening, and new abstractions.
Maintained by AmritaConstant.
faskill is a Python library that brings Anthropic's Agent Skills to any LLM-powered agent. It discovers, loads, and invokes packaged expertise — defined in standard SKILL.md files — with progressive disclosure for token efficiency.
Features
- SKILL.md compatible — works with any existing skill, drop-in ready
- Framework-agnostic — use standalone or with LangChain (more integrations planned)
- Model-agnostic — works with any LLM
- Multi-source discovery — custom directories, plugins with priority-based conflict resolution
- Progressive disclosure — metadata-first loading, 80% memory reduction, LRU caching; scripts loaded on demand
- Script execution — Python, Shell, JavaScript, Ruby, Perl with security validation and timeout enforcement
- Pluggable runner —
Runnerabstraction withHostRunnerdefault; swap in Docker, Firecracker, etc. - Plugin ecosystem — supports plugin manifests (
.claude-plugin/plugin.json) with namespaced skill access - Comprehensive error hierarchy — 20+ typed exceptions for precise error handling
Notable Improvements Over Upstream (skillkit)
This fork contains significant refactoring and bug fixes beyond the original:
| Area | Improvement |
|---|---|
| Bug fix | PRIORITY_CUSTOM no longer decrements — ≥5 sources won't trigger ValueError: Priority must be positive |
| Bug fix | create_langchain_tools() no longer eagerly loads all scripts — respects progressive disclosure (L1→L2→L3) |
| New | InvalidSkillNameError — names with spaces are rejected (bypass with NO_FAIL_ON_SPACE=1) |
| New | Runner abstraction — HostRunner (default, warns once about bare host security) with swappable backends |
| New | ArgumentSerializationError / ArgumentSizeError / ToolIDValidationError — fine-grained script errors |
| New | ConfigurationError / AsyncStateError / PluginError — better error reporting |
| Refactor | SkillManager → SkillContext with create_context() factory; modular architecture (discovery, parser, registry, invoker, processors, scripts, runner) |
| Refactor | Complete test suite (425 tests), 70%+ coverage, comprehensive fixtures |
Installation
pip install faskill # Core library
pip install faskill[langchain] # With LangChain integration
pip install faskill[all] # All extras
Quick Start
1. Create a skill
.claude/skills/code-reviewer/SKILL.md
---
name: code-reviewer
description: Review code for best practices and potential issues
allowed-tools: Read, Grep
---
# Code Reviewer
Analyze the provided code for:
- Best practices violations
- Potential bugs
- Security vulnerabilities
Use $ARGUMENTS to access user input.
2. Use standalone
from faskill import create_context
ctx = create_context(skill_dirs=["./.claude/skills"])
ctx.discover()
# List available skills
for skill in ctx.list_skills():
print(f"{skill.name}: {skill.description}")
# Invoke a skill
result = ctx.invoke_skill("code-reviewer", "Review function calculate_total()")
print(result)
3. Use with LangChain
from faskill import create_context
from faskill.integrations.langchain import create_langchain_tools
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
ctx = create_context(skill_dirs=["./.claude/skills"])
ctx.discover()
tools = create_langchain_tools(ctx)
llm = ChatOpenAI(model="gpt-4o")
agent = create_agent(llm, tools, system_prompt="You are a helpful assistant.")
result = agent.invoke({"messages": [{"role": "user", "content": "Review my code"}]})
SKILL.md Format
---
name: my-skill # Required: unique identifier
description: ... # Required: human-readable description
allowed-tools: Bash, Read # Optional: tool allowlist
version: "1.0" # Optional: semantic version
---
# Skill content with $ARGUMENTS placeholder
- Argument substitution:
$ARGUMENTS→ user input;$$ARGUMENTS→ literal$ARGUMENTS - No placeholder: arguments are appended to the end
Script Execution
Skills can bundle executable scripts for deterministic operations:
my-skill/
├── SKILL.md
└── scripts/
└── extract.py
result = ctx.execute_skill_script(
skill_name="my-skill",
script_name="extract",
arguments={"file": "doc.pdf", "pages": "all"},
timeout=30,
)
if result.success:
print(result.stdout)
else:
print(f"Error ({result.exit_code}): {result.stderr}")
Supported types: .py, .sh, .js, .rb, .pl, .bat, .cmd, .ps1.
Script runner (pluggable)
ScriptExecutor accepts a runner parameter — swap in sandboxed backends:
from faskill.core.scripts import ScriptExecutor
from faskill.core.runner import HostRunner # default (warns once about security)
# Default: bare host
executor = ScriptExecutor(runner=HostRunner())
# Future: Docker, Firecracker, gVisor...
# executor = ScriptExecutor(runner=DockerRunner(image="python:3.12"))
API Reference
create_context()
from faskill import create_context
ctx = create_context(
skill_dirs=["./skills", "./plugins"], # Directory list; directories with
# .claude-plugin/plugin.json are
# detected as plugins
default_script_timeout=30, # seconds (1-600)
max_cache_size=100, # LRU cache entries
)
SkillContext
| Method | Description |
|---|---|
discover() |
Sync skill discovery |
adiscover() |
Async skill discovery |
list_skills() |
List all discovered skill metadata |
list_skills(include_qualified=True) |
List names including plugin:skill |
get_skill(name) |
Get metadata by name; raises SkillNotFoundError |
invoke_skill(name, args) |
Sync invocation with caching |
ainvoke_skill(name, args) |
Async invocation with caching |
execute_skill_script(...) |
Execute a bundled script |
get_cache_stats() |
Cache hit/miss statistics |
clear_cache(name?) |
Clear cache entries |
add_source(path) |
Add a skill directory after construction |
Key types
| Type | Description |
|---|---|
SkillMetadata |
Name, description, path, allowed tools, priority |
Skill |
Full skill: metadata + content + scripts |
SkillSource |
Source directory with type and priority |
ScriptMetadata |
Detected script name, path, language, description |
ScriptExecutionResult |
exit_code, stdout, stderr, execution_time_ms, etc. |
CacheStats |
size, max_size, hits, misses, hit_rate |
Runner |
Abstract base for script execution backends |
HostRunner |
Default runner — bare host subprocess (warns once) |
Exception hierarchy
SkillsUseError
├── SkillParsingError
│ ├── InvalidYAMLError
│ ├── MissingRequiredFieldError
│ ├── InvalidSkillNameError # name contains spaces (bypass: NO_FAIL_ON_SPACE=1)
│ └── InvalidFrontmatterError
├── SkillNotFoundError
├── SkillInvocationError
│ ├── ArgumentProcessingError
│ ├── ArgumentSerializationError
│ ├── ArgumentSizeError
│ └── ContentLoadError
├── ConfigurationError
├── AsyncStateError
├── PluginError
│ ├── ManifestNotFoundError
│ ├── ManifestParseError
│ └── ManifestValidationError
├── ScriptError
│ ├── InterpreterNotFoundError
│ ├── ScriptNotFoundError
│ ├── ScriptPermissionError
│ ├── ArgumentSerializationError
│ ├── ArgumentSizeError
│ └── ToolIDValidationError
├── SkillSecurityError
│ ├── SuspiciousInputError
│ ├── SizeLimitExceededError
│ └── PathSecurityError
Examples
See examples/ directory:
| File | What it demonstrates |
|---|---|
basic_usage.py |
Sync and async standalone usage |
async_usage.py |
Async usage with FastAPI |
langchain_agent.py |
LangChain agent integration |
multi_source.py |
Multi-source discovery and conflict resolution |
file_references.py |
Secure file path resolution |
caching_demo.py |
Cache performance demonstration |
script_execution.py |
Script execution with error handling |
Documentation
- Core Features — multi-source discovery, caching, scripts, patterns
- API Reference — SKILL.md spec, system requirements, development
- LangChain Integration — sync/async, script tools, tool ID format
Where to find skills
Contributing
- Fork → branch → make changes → add tests
- Ensure
uv run pytestpasses (≥70% coverage) - Ensure
uv run ruff check src/anduv run pyright src/pass - Submit a pull request
See CONTRIBUTING.md for detailed guidelines.
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file faskill-0.1.0.tar.gz.
File metadata
- Download URL: faskill-0.1.0.tar.gz
- Upload date:
- Size: 112.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af96ca379ff8d3aafb71dca7b5f6a9f7888ddb5e083c631f809239f149e44664
|
|
| MD5 |
88545a3851e86c438c39a6a4908f447b
|
|
| BLAKE2b-256 |
0a1bb7753f6808003bae4df3756de6373cb090f6d9cae01c965e20f0f576f379
|
File details
Details for the file faskill-0.1.0-py3-none-any.whl.
File metadata
- Download URL: faskill-0.1.0-py3-none-any.whl
- Upload date:
- Size: 56.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28839329ff0bf4e3bb9abeceaebfbb6f6dd7923e618b8f09f9a8b99610c113ec
|
|
| MD5 |
4451b8de1cda7bdcbe3642d01e02a001
|
|
| BLAKE2b-256 |
e6689eec361a7d22c6744ea937d9f744a2955b7aabdd563fa15d86e4fbd2b674
|