Skills
indusagi can create skills. Ask it to build one for your use case.
Skills are self-contained instruction packages the agent loads on-demand. A skill
is a markdown document (SKILL.md, or a root-level *.md) with frontmatter the
loader validates, plus any helper scripts and references it points at. Only each
skill's name and description sit in the system prompt; the full instructions are
loaded when a task matches.
The implementation is indus-code-rebuild/src/briefing/skills.ts (the loader and
validator) and src/briefing/compose.ts (the system-prompt block). Skills follow
the Agent Skills format; the policy over the frontmatter is the rebuild's own.
Table of Contents
Locations
Security: Skills can instruct the model to perform any action and may include executable code the model invokes. Review skill content before use.
Skills are discovered from two roots, project before user (so a project-local
card shadows a user-global one of the same name), defined in
src/console/startup.ts and src/console/slash/builtins.ts:
- Project:
<cwd>/.indusagi/skills/ - User:
<home>/.indusagi/skills/
Discovery rules (gatherSkillCards over each root):
- A direct
*.mdfile at the root of a skills directory is a single-file skill (its name defaults to the enclosing directory). - A
SKILL.mdfile in any subdirectory is a packaged skill.
Names are deduped across roots — the first card found wins.
Note: Unlike older docs, there is no
--skill/--no-skillsCLI flag and noskillssettings array. The two roots above are the only sources. Adding other harnesses' skill directories (e.g.~/.claude/skills) is not supported by the current loader.
How Skills Work
- At startup, indusagi scans the skill roots (
gatherSkillCards) and lists each discovered card — name plus shortened location — in the startup banner panel (gatherSkillsinsrc/console/startup.ts). - The system prompt's skills section (
SKILLS_SECTIONinsrc/briefing/compose.ts) renders the model-invocable cards into an<available_skills>block —name,description, andlocationper skill. - When a task matches a skill's description, the agent loads the full
SKILL.mdatlocationwith thereadtool and follows it, using relative paths for scripts and assets.
This is progressive disclosure: only descriptions are in context; full
instructions load on demand. A skill whose frontmatter sets
disable-model-invocation: true is excluded by modelInvocableCards from the set
the <available_skills> block would render, and is reachable only via its command.
Status: Step 1 (the startup scan and banner) is live. Step 2 is the compose-layer behavior, and it is fully implemented — but the session runner currently composes the system prompt with only
{ tools }(composeBriefing({ tools })insrc/boot/runners/session.ts), so it never passesskillsinto the briefing context. The<available_skills>block is therefore built by the composer but not yet emitted into the live system prompt. The/skill:<name>commands (below), which inject the invocation block directly as a turn, are wired and work today. SeeFEATURE_GAP_ROADMAP.md.
Skill Commands
Each discovered card is registered as a /skill:<name> slash command
(buildDynamicCommands in src/console/slash/commands/dynamic.ts, spliced into
the catalog by src/console/slash/builtins.ts):
/skill:brave-search # run the skill with no body
/skill:pdf-tools extract # run the skill, "extract" becomes the body
Invoking the command submits an Agent-Skills invocation block as a normal turn:
<skill name="pdf-tools" location="/abs/path/SKILL.md">
extract
</skill>
The conductor parses that leading block (parseSkillInvocation in
src/conductor/skill-parse/parse.ts) and submits the body text as the prompt. The
trailing argument string after the command becomes the body.
An enableSkillCommands preference exists in settings.json (default true,
declared in src/settings/contract.ts and exposed as a toggle in the settings
picker):
{
"enableSkillCommands": true
}
Note: This preference is defined and surfaced in the settings UI, but the current
builtins.tsbuilds the/skill:<name>rows unconditionally at module load — nothing readsenableSkillCommandsto suppress them yet. Treat the toggle as not-yet-wired (seeFEATURE_GAP_ROADMAP.md).
Skill Structure
A packaged skill is a directory with a SKILL.md file; everything else is freeform.
my-skill/
├── SKILL.md # required: frontmatter + instructions
├── scripts/
│ └── process.sh
├── references/
│ └── api-reference.md
└── assets/
└── template.json
SKILL.md Format
---
name: my-skill
description: What this skill does and when to use it. Be specific.
---
# My Skill
## Setup
Run once before first use:
`cd /path/to/skill && npm install`
## Usage
`./scripts/process.sh <input>`
Reference bundled files with paths relative to the skill's own directory.
Frontmatter
Parsed and projected by projectFrontmatter in src/briefing/skills.ts. The
recognised keys:
| Field | Required | Description |
|---|---|---|
name |
(see below) | ≤ 64 chars, lowercase words joined by single hyphens. For a packaged SKILL.md it must match the parent directory. |
description |
Yes | ≤ 1024 chars. What the skill does and when to use it. |
license |
No | License tag. |
compatibility |
No | Free-form compatibility note. |
metadata |
No | Open key/value mapping. |
allowed-tools |
No | Comma/whitespace-separated tool list; projected to allowedTools. |
disable-model-invocation |
No | When true, hide from the prompt; expose only as /skill:<name>. |
The length limits are the exported constants SKILL_NAME_LIMIT (64) and
SKILL_DESCRIPTION_LIMIT (1024). Any frontmatter key not in this set is an error
(checkFrontmatterKeys).
Name Rules
The name must match ^[a-z0-9]+(?:-[a-z0-9]+)*$ — lowercase letters/digits joined
by single internal hyphens, no leading/trailing or consecutive hyphens. When the
frontmatter omits name, the loader infers it from the directory and skips the
directory-match check.
Valid: pdf-processing, data-analysis, code-review
Invalid: PDF-Processing, -pdf, pdf--processing
Validation
The loader is non-throwing: each candidate file produces a SkillDiagnostic with a
kind of loaded, skipped, invalid, or collision. A file fails to load
(invalid) when:
- it cannot be read,
- it carries an unrecognised frontmatter key,
- the name is empty, too long, malformed, or (for a packaged
SKILL.md) does not match its directory, - the description is missing or exceeds the limit.
A second card with a name already taken is dropped as a collision (first wins).
Example
brave-search/
├── SKILL.md
├── search.js
└── content.js
SKILL.md:
---
name: brave-search
description: Web search and content extraction via Brave Search API. Use for searching documentation, facts, or any web content.
---
# Brave Search
## Setup
`cd /path/to/brave-search && npm install`
## Search
`./search.js "query"` # basic search
`./search.js "query" --content` # include page content
Related
- Prompt Templates —
/namemacros that expand into prompts - Extensions — addons that contribute tools and commands
