Skills
Skills are modular extensions to the AI agent. A skill can provide:
- Domain knowledge: instructions the agent receives for a task
- Runnable scripts — tools the agent can call during a task (shell scripts, Python scripts, TypeScript, executables)
- Reference files — documentation the agent can query mid-task
Skills allow you to give the agent specialised expertise for specific frameworks, internal APIs, company conventions, or complex workflows without modifying Tarsk itself.
How Skills Work
Section titled “How Skills Work”Tarsk lists each skill’s name, description, and when-to-use text in the system prompt under an # Available Skills section before the agent processes your request. Full SKILL.md instructions load only when you invoke the skill explicitly with /skill-name or //skill-name, or when the thread’s enabledSkills list enables it. In every other case, the agent calls the read_skill tool to load the skill’s full instructions on demand when they look relevant to the task.
Set disable-model-invocation: true in the skill frontmatter to keep a skill out of the catalog. Invoke such skills manually with /skill-name (see below).
Skill Structure
Section titled “Skill Structure”A skill is a directory containing a SKILL.md file. The minimum structure is:
my-skill/ SKILL.mdWrite YAML frontmatter followed by the skill’s instructions in SKILL.md. Tarsk includes these instructions with your message when you invoke or enable the skill:
---name: my-skilldescription: Provides guidance for working with internal payments API.license: MITcompatibility: Node.js 18+, Python 3.9+---
## Payments API
Always use `PaymentService` class from `src/services/payment.ts`.Never call Stripe API directly — use abstraction layer....Frontmatter Fields
Section titled “Frontmatter Fields”| Field | Required | Description |
|---|---|---|
name | Yes | Display name for skill (must match directory name, 1-64 chars, lowercase alphanumeric, hyphens only) |
description | Yes | Shown in the Available Skills catalog; helps the agent decide when to load the skill (1-1024 chars) |
license | No | License identifier (e.g. MIT) |
compatibility | No | System requirements or dependencies |
allowed-tools | No | Filter deferred tools and control shell interpolation (see Tool Allowlists) |
when_to_use | No | Shown to the agent as guidance for when the skill applies |
disable-model-invocation | No | When true, the skill stays out of the Available Skills catalog; invoke it manually with /skill-name (see below) |
Tool Allowlists
Section titled “Tool Allowlists”Tarsk combines non-empty allowed-tools lists from skills invoked with /skill-name or //skill-name and from the thread’s enabledSkills for the current turn. It uses a union: if one skill lists browser and another lists find_images, the agent can use both. Tarsk ignores absent or empty lists; if no non-empty list remains, it applies no skill-based tool filter. An entry of * or all in any of these skills removes the skill-based tool filter.
The Available Skills catalog does not contribute to this union. Calling read_skill loads and preprocesses instructions without changing the turn’s tool allowlist.
Tarsk applies this filter to deferred tools, including optional and MCP tools behind tool_search. It leaves eager tools, such as read, write, edit, and bash, outside the filter. Optional or MCP tools configured for eager loading also bypass it. Project tool settings and mode restrictions still apply; use allowed-tools to select deferred tools, not to enforce read-only access.
Tarsk keeps the management tools ask_user, todo, tasks, tool_search, and read_skill in the resolved allowlist. A Bash or Bash(pattern) entry also includes execute_skill_script; a Read entry includes read_skill_reference. These entries do not enable tools that project settings or the current mode exclude.
For shell tool approvals, Tarsk adds the current turn’s Bash(pattern) entries to the project’s allowed command patterns. A matching bash command or script launch can run without an approval prompt. Bare Bash does not grant blanket approval for these tool calls. See Script execution and permissions for script checks and Shell command security for template commands.
Prompt Interpolation
Section titled “Prompt Interpolation”When a skill is activated, its instructions are preprocessed before injection. You can embed dynamic content:
| Syntax | Description |
|---|---|
!`command` | Run a shell command and substitute the output inline |
```! ... ``` | Run a multi-line shell command block |
!`tarsk:models` | Inject a list of enabled models with pricing and coding intelligence |
$ARGUMENTS, $0, $name | Substitute skill invocation arguments |
${TARSK_SKILL_DIR} | Absolute path to the skill directory |
Shell command security
Section titled “Shell command security”Tarsk checks each template command against the containing skill’s own allowed-tools, including when the agent loads it with read_skill. It does not use the combined tool allowlist for this check:
Bash(pattern)permits commands that match the pattern. For example,Bash(git *)matchesgit diff HEAD.- Bare
Bashpermits any shell command in that skill’s template. - Without a matching Bash entry, Tarsk substitutes an error message for the command. Tool entries such as
*oralldo not grant shell interpolation permission.
Pattern matching does not validate shell syntax or constrain each command in a pipeline or command chain. A trailing * matches a token prefix, so Bash(git *) can also match text that starts with git and contains shell operators. Tarsk substitutes invocation arguments before this check without shell escaping.
Template commands run through the host shell without Tarsk’s shell-approval gate or Seatbelt sandbox wrapper. Project shell permissions do not govern this path, and the script tool’s timeout does not apply. Review a skill’s template commands and argument handling before invoking it or making it available to the agent.
Model listing (tarsk:models)
Section titled “Model listing (tarsk:models)”!`tarsk:models` expands to enabled models sorted cheapest-first, one per line:
- anthropic/claude-haiku-4.5 at $0.80 in, $4.00 out, intelligence at 35- google/gemini-2.0-flash at $0.10 in, $0.40 out, intelligence at 28Prices are USD per million tokens (rounded to the cent). Intelligence is the model’s coding index. This builtin does not require Bash in allowed-tools.
Example: cheapest subagent
Section titled “Example: cheapest subagent”---name: cheap-subagentdescription: Delegate simple tasks to a cheap subagentallowed-tools: Bash agent---
Available models (cheapest first):!`tarsk:models`
For simple subtasks, use the agent tool with model and provider from the cheapest entry above.Optional Subfolders
Section titled “Optional Subfolders”my-skill/ SKILL.md scripts/ ← runnable tools exposed to agent generate.sh validate.py scaffold.ts references/ ← documentation agent can read on demand api-spec.md schema.json assets/ ← static files, images, data templates/ output.jsonscripts/: The agent runs files here through execute_skill_script, with optional arguments. Supported types:
.sh,.bash→bash.py→python3.js→node.ts→bun run- Other extensions → Direct execution if executable
references/ — Files agent can read via read_skill_reference. Useful for large specifications or schemas you don’t want injected into every prompt automatically.
assets/ — Static files the skill can reference, such as templates, configuration files, or data files.
Script execution and permissions
Section titled “Script execution and permissions”The agent calls execute_skill_script to run a file from a skill’s scripts/ directory. Tarsk checks the interpreter, script path, and arguments as a launch command through the shell-permission gate when a project gate exists. With shell approval enabled, an unmatched launch requires your approval. This check covers the launch command, not each command inside the script; the executor does not recheck the script’s own skill-specific Bash(pattern) entries.
With Seatbelt enabled on macOS, Tarsk wraps the script process in the sandbox and adds the skill directory to its readable paths. The tool uses a 30-second timeout by default and accepts a timeout override. Template shell interpolation uses a separate execution path, as described under Shell command security.
Skill Discovery Locations
Section titled “Skill Discovery Locations”Tarsk loads skills from three directories plus its bundled skills:
| Location | Scope |
|---|---|
~/.agents/skills/ | Global, available to all projects |
<projectPath>/.agents/skills/ | Project, only available within that project |
<threadPath>/.agents/skills/ | Thread, only available within that thread |
Project skills override global skills with the same name, and thread skills override both. Tarsk also ships app-bundled skills that are available everywhere; a project or global skill with the same name overrides the bundled one.
Installing from the Marketplace
Section titled “Installing from the Marketplace”Open Settings → Plugins → Skills to browse and install community skills with one click. Installed skills are saved to the project’s .agents/skills/ directory.
Creating a Skill
Section titled “Creating a Skill”- Create a directory in
~/.agents/skills/(global) or<threadPath>/.agents/skills/(project-level):Terminal window mkdir -p ~/.agents/skills/my-skill - Create
SKILL.mdwith the frontmatter and instructions. - Optionally add
scripts/andreferences/subdirectories. - Start a new conversation — the skill is available immediately.
Example: Framework Conventions Skill
Section titled “Example: Framework Conventions Skill”---name: react-query-conventionsdescription: Conventions for using React Query and TanStack Query in this project.---
## Query Keys
Always define query keys as constants in `src/query-keys.ts`.Never inline query key strings.
## Mutations
Use `useMutation` hook from `@tanstack/react-query`:
```typescriptconst createUser = useMutation({ mutationFn: (userData: CreateUserInput) => api.post("/users", userData), onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.users }); toast.success("User created successfully"); }, onError: (error) => { toast.error(`Failed to create user: ${error.message}`); },});```
Always invalidate related queries in `onSuccess`.
### Queries
Use `useQuery` with proper error handling:
```typescriptconst { data: users, isLoading, error } = useQuery({ queryKey: queryKeys.users, queryFn: () => api.get('/users'), staleTime: 5 * 60 * 1000, // 5 minutes retry: 3});
if (isLoading) return <LoadingSpinner />;if (error) return <ErrorMessage error={error} />;return <UserList users={users} />;```Invoke /react-query-conventions to load this skill’s instructions. For other prompts, the agent can choose to load it with read_skill based on its description.
Script Examples
Section titled “Script Examples”PDF Text Extractor Script
Section titled “PDF Text Extractor Script”File: ~/.agents/skills/pdf-processing/scripts/extract-text.py
#!/usr/bin/env python3import sysimport jsonimport PyPDF2
def main(): if len(sys.argv) < 2: print(json.dumps({"error": "No PDF path provided"}), file=sys.stderr) sys.exit(1)
pdf_path = sys.argv[1]
try: with open(pdf_path, 'rb') as file: reader = PyPDF2.PdfReader(file) text = "" for page in reader.pages: text += page.extract_text()
print(json.dumps({ "success": True, "text": text, "pages": len(reader.pages) })) except Exception as e: print(json.dumps({"error": str(e)}), file=sys.stderr) sys.exit(1)
if __name__ == "__main__": main()Make it executable:
chmod +x ~/.agents/skills/pdf-processing/scripts/extract-text.pyDatabase Query Script
Section titled “Database Query Script”File: database-queries/scripts/explain-query.sh
#!/bin/bashQUERY="$1"DB_NAME="${2:-postgres}"
if [ -z "$QUERY" ]; then echo "Error: No query provided" >&2 exit 1fi
echo "Analyzing query execution plan..."psql -d "$DB_NAME" -c "EXPLAIN ANALYZE $QUERY"Script Best Practices
Section titled “Script Best Practices”- Accept arguments: Use command line arguments for input
- Output JSON: Return structured data for easy parsing
- Error handling: Send errors to stderr, exit with non-zero status
- Dependencies: Check for required tools/libraries
- Timeouts: Keep operations under 30 seconds
# Good: Accept input as argumentsINPUT_FILE = sys.argv[1] if len(sys.argv) > 1 else None
# Good: Output structured dataprint(json.dumps({"result": data, "success": True}))
# Good: Errors to stderrprint("Error processing file", file=sys.stderr)Reference Files
Section titled “Reference Files”API Documentation
Section titled “API Documentation”File: stripe-api/references/api-reference.md
# Stripe API Reference
## Authentication
All API requests require authentication using your secret key:
```bashcurl https://api.stripe.com/v1/charges \ -u sk_test_your_key:```
## Create a Payment Intent
**Endpoint:** `POST /v1/payment_intents`
**Parameters:**
- `amount` (required): Amount in cents- `currency` (required): Three-letter ISO code- `payment_method_types` (required): Array of payment methods
**Example:**
```bashcurl https://api.stripe.com/v1/payment_intents \ -u sk_test_your_key: \ -d amount=1000 \ -d currency=usd \ -d "payment_method_types[]"=card```Referencing in Skills
Section titled “Referencing in Skills”Tell the agent about reference files:
---name: stripe-apidescription: Work with Stripe payment APIs---
## Reference Documentation
For detailed API documentation, use the `read_skill_reference` tool with:
- skillName: "stripe-api"- referencePath: "api-reference.md"
For authentication examples, use:
- referencePath: "guides/authentication.md"Naming and Validation
Section titled “Naming and Validation”Valid Skill Names
Section titled “Valid Skill Names”✅ Good names:
pdf-processingweb-scrapingdatabase-toolsapi-testingcode-review
❌ Invalid names:
PDF-Processing(uppercase not allowed)pdf_processing(underscores not allowed)pdf processing(spaces not allowed)-pdf(cannot start with hyphen)pdf-(cannot end with hyphen)pdf--processing(consecutive hyphens not allowed)
Rules:
- 1-64 characters
- Lowercase letters, numbers, and hyphens only
- Must not start or end with hyphen
- No consecutive hyphens
Skill Activation
Section titled “Skill Activation”Auto-Discovery
Section titled “Auto-Discovery”Tarsk lists every eligible skill’s name, description, and when-to-use text in the system prompt under an # Available Skills section. The agent reads that catalog, decides which skills look relevant to the current task, and calls the read_skill tool to load the full SKILL.md instructions on demand. Full instructions are injected directly only for explicit /skill-name or //skill-name invocations and for skills listed in the thread’s enabledSkills.
Manual-Only Skills (disable-model-invocation)
Section titled “Manual-Only Skills (disable-model-invocation)”Set disable-model-invocation: true when a skill should not appear in the Available Skills catalog. The agent will only receive the skill when you invoke it explicitly:
---name: deploydescription: Deploy the application to staging or productiondisable-model-invocation: true---Invoke with a slash command in your message:
/deploy stagingBundled skills use a double slash: //skill-creator.
In Settings → Plugins → Skills, each installed skill shows a puzzle icon to the left of its name: click it to toggle disable-model-invocation without editing the file, and hover it to see roughly how many tokens the skill’s description adds to the context window.
The Available Skills catalog and thread enabledSkills both skip skills with this flag. Only an explicit /skill-name (or //skill-name) in the message activates them.
Thread records can store enabledSkills and disabledSkills overrides in the database, but the app does not expose a UI for these fields yet.
Advanced Features
Section titled “Advanced Features”Progressive Disclosure
Section titled “Progressive Disclosure”Skills use progressive disclosure to minimize context usage:
1. Startup (always loaded):
- Skill name and description
- ~100 tokens per skill
2. Activation (explicit invocation or on-demand read_skill):
- Full SKILL.md instructions
- <5000 tokens per skill
3. On-demand (when agent requests):
- Reference files via
read_skill_reference - Loaded only when needed
Script Timeouts
Section titled “Script Timeouts”Scripts have a 30-second timeout by default. Set the timeout argument on execute_skill_script to allow more time. Printing progress does not extend the timeout.
Progress output
import sys
for i in range(100): process_chunk(i) print(f"Progress: {i}%", file=sys.stderr) sys.stderr.flush()Background processing
#!/bin/bashnohup long-running-process.sh > output.log 2>&1 &PID=$!echo "Started process: $PID"echo "Check output: tail -f output.log"Troubleshooting
Section titled “Troubleshooting”Skill Isn’t Activating
Section titled “Skill Isn’t Activating”Check the skill name:
- Must match directory name exactly
- Lowercase, alphanumeric, hyphens only
Check frontmatter:
# ❌ Missing closing delimitername: my-skilldescription: My skill
# ❌ Name mismatchDirectory: my-skill/Frontmatter: name: different-name
# ✅ Correct---name: my-skilldescription: My skill---Write a clear description:
The agent reads the description and when_to_use text to decide when the skill helps. State what the skill does and when to use it.
Script Isn’t Executing
Section titled “Script Isn’t Executing”Check permissions:
chmod +x ~/.agents/skills/my-skill/scripts/my-script.shCheck dependencies:
try: import PyPDF2except ImportError: print("Error: PyPDF2 not installed. Run: pip install PyPDF2", file=sys.stderr) sys.exit(1)Getting Started Checklist
Section titled “Getting Started Checklist”- Create skills folder:
mkdir -p ~/.agents/skills - Create your first skill (e.g.,
code-review) - Write clear instructions in SKILL.md
- Test activation with a relevant task
- Add scripts for specialized tools (optional)
- Add reference docs for detailed info (optional)
- Create project-specific skills in
.agents/skills/ - Commit project skills to git for your team
Skills transform Tarsk from a general-purpose AI assistant into a specialized tool tailored to your specific technologies, workflows, and requirements.