Nous Research Adds /learn to Hermes Agent’s Skills System, Capturing Workflows as Slash Commands Without Hand-Writing SKILL.md
Nous Research has added /learn to the Hermes Agent Skills System. The command authors a standards-compliant SKILL.md from a local directory, a doc URL, a past conversation, or pasted notes. The live agent sources the material with its own tools, then writes the skill — no hand-writing and no separate ingestion engine. Here is how it works, where it fits, and what to review before trusting the output. The post Nous Research Adds /learn to Hermes Agent’s Skills System, Capturing Workflows as S...
Nous Research has expanded the Skills System inside Hermes Agent, its open-source self-improving agent. The new addition is /learn, a command that writes a reusable skill for you. Point it at a document page, a local SDK, a past conversation, or pasted notes. The live agent gathers the material, then authors a SKILL.md on your behalf.
Hermes Agent can now /learn from anything: feed it directories of any source material (code, API docs, manuals, PDFs, configs) and it distills a verifiable reusable skill pic.twitter.com/oRznwCRF3E
— Nous Research (@NousResearch) June 23, 2026
Hermes Skills System
Skills are on-demand knowledge documents the agent loads when needed. Each one is a folder containing a SKILL.md file with instructions. They follow a progressive disclosure pattern to keep token usage low. The format is compatible with the agentskills.io open standard.
All skills live in ~/.hermes/skills/, the single source of truth. On a fresh install, bundled skills are copied from the repo. Hub-installed and agent-created skills land there too. Every installed skill becomes a slash command automatically. Running /plan or /axolotl loads that skill’s instructions into the turn.
Think of a skill as a reference document the agent reads only when relevant. Memory, by contrast, holds small durable facts that should always stay in context.
How /learn Works
/learn removes the hand-writing step. You describe a source, and the agent does the sourcing with tools it already has. It reads local directories with read_file and search_files. It fetches online docs with web_extract. It can also capture a workflow you just walked it through.
Copy CodeCopiedUse a different Browser
# A local SDK or doc directory
/learn the REST client in ~/projects/acme-sdk, focus on auth + pagination
# An online doc page
/learn https://docs.example.com/api/quickstart
# The workflow you just completed in this conversation
/learn how I just deployed the staging server
# Pasted notes or a described procedure
/learn filing an expense: open the portal, New > Expense, attach receipt, submit
The agent then authors a skill that follows the house authoring standards. That means a description under 60 characters, the standard section order, and Hermes-tool framing. It does not invent commands that do not exist.
There is no separate ingestion engine. /learn builds a standards-guided prompt and hands it to the agent as a normal turn. So it works the same in the CLI, the messaging gateway, the TUI, and the dashboard. It also works on any terminal backend, whether local, Docker, or remote. The dashboard adds a Learn a skill button with a directory field, a URL field, and a text box.
The agent saves the result with the skill_manage tool. If you have the write-approval gate on, that approval step still applies.
Why Skills Stay Cheap
Skills load in three levels, so the agent pays only for what it uses.
LevelCallReturnsApprox. cost0skills_list()Names, descriptions, categories~3k tokens1skill_view(name)Full content plus metadataVaries2skill_view(name, path)A specific reference fileVaries
The agent sees a compact index at all times. It loads full skill content only when a task needs it. This keeps a large skill library from flooding the context window.
Four Ways to Create a Skill
/learn is one path among several. The right choice depends on who authors the skill and where the source lives.
MethodWho authorsSource inputReview gateBest forHand-write SKILL.mdYouYour own knowledgeNoneFull control over wording/learnThe live agentDir, URL, conversation, notesskill_manage gateTurning existing material into a skill fastskill_manage (auto)The agent itselfA workflow it just solvedwrite_approval gateCapturing procedural memory after hard tasksSkills Hub installA third partyRegistry or GitHub repoSecurity scannerReusing community or vendor skills
Agent-created skills are the agent’s procedural memory. The agent may save an approach after a complex task of five or more tool calls. It also saves when it hit a dead end and found the working path. By default, write_approval is false, so the agent writes freely. Set it to true to stage every write for review under ~/.hermes/pending/skills/.
Use Cases With Examples
• Onboarding an internal API: Run /learn on your private docs URL. The agent produces a skill covering auth, pagination, and common calls. New teammates then invoke it as a slash command.
• Capturing a deploy runbook: Walk the agent through one staging deploy. Then run /learn how I just deployed the staging server. The procedure becomes repeatable across the CLI and chat platforms.
• Grouping a recurring task: Use a skill bundle to load several skills at once. One slash command then pulls in review, test, and PR skills together.
Copy CodeCopiedUse a different Browser
# ~/.hermes/skill-bundles/backend-dev.yaml
name: backend-dev
description: Backend feature work — review, test, PR workflow.
- github-code-review
- test-driven-development
- github-pr-workflow
instruction: |
Always start by writing failing tests, then implement.
A Look at the SKILL.md Format
A skill is mostly a markdown file with YAML frontmatter. The body follows a fixed section order. /learn targets this exact shape so output stays consistent.
Copy CodeCopiedUse a different Browser
name: my-skill
description: Brief description of what this skill does
version: 1.0.0
platforms: [macos, linux] # Optional — restrict to specific OS
tags: [python, automation]
category: devops
# Skill Title
## When to Use
Trigger conditions for this skill.
## Procedure
1. Step one
2. Step two
## Pitfalls
- Known failure modes and fixes
## Verification
How to confirm it worked.
The platforms field can hide a skill on incompatible operating systems. Conditional fields can also show a skill only when certain toolsets are present or absent.
Interactive Explainer
Local directory
Progressive disclosure · agentskills.io standard
Demo by Marktechpost
(function(){
var root=document.getElementById('hl-learn-demo');
var src='dir';
var tabs=root.querySelectorAll('.tab');
var srcLabel=root.querySelector('#hl-srclabel');
var srcIn=root.querySelector('#hl-src');
var focLabel=root.querySelector('#hl-foclabel');
var focIn=root.querySelector('#hl-focus');
var nameIn=root.querySelector('#hl-name');
var catIn=root.querySelector('#hl-cat');
var descIn=root.querySelector('#hl-desc');
var ct=root.querySelector('#hl-ct');
var cmdPrev=root.querySelector('#hl-cmdprev');
var runBtn=root.querySelector('#hl-run');
var out=root.querySelector('#hl-out');
var stage=root.querySelector('#hl-stage');
var skillPre=root.querySelector('#hl-skill');
var copyBtn=root.querySelector('#hl-copy');
var presets={
dir:{label:'Directory path',foc:'Focus / instruction (optional)',ph:'~/projects/acme-sdk',
val:'~/projects/acme-sdk',focv:'focus on auth + pagination',tool:'read_file + search_files',
verb:'Reading directory'},
url:{label:'Doc URL',foc:'Focus / instruction (optional)',ph:'https://docs.example.com/api/quickstart',
val:'https://docs.example.com/api/quickstart',focv:'',tool:'web_extract',verb:'Fetching page'},
conv:{label:'What you just did',foc:'Extra detail (optional)',ph:'how I just deployed the staging server',
val:'how I just deployed the staging server',focv:'',tool:'conversation history',verb:'Reviewing this conversation'},
notes:{label:'Pasted procedure',foc:'Extra detail (optional)',ph:'open the portal, New > Expense, attach receipt, submit',
val:'open the portal, New > Expense, attach receipt, submit',focv:'',tool:'pasted text',verb:'Parsing pasted notes'}
function slug(s){return (s||'').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'')||'my-skill';}
function esc(s){return (s||'').replace(/&/g,'&').replace(//g,'>');}
function updateCt(){
var n=descIn.value.length;
ct.textContent=n+' / 60';
ct.className='ct'+(n>60?' over':'');
function updateCmd(){
var p=presets[src];
var arg=srcIn.value.trim();
var foc=focIn.value.trim();
var tail=foc?(', '+foc):'';
if(src==='dir') tail = foc?(' '+foc):'';
if(src==='dir') body=arg+(foc?(', '+foc):'');
else if(src==='url') body=arg;
else body=arg+(foc?(' — '+foc):'');
cmdPrev.innerHTML='$ /learn '+esc(body);
function setTab(s){
tabs.forEach(function(t){t.classList.toggle('on',t.dataset.src===s);});
var p=presets[s];
srcLabel.textContent=p.label;
focLabel.textContent=p.foc;
srcIn.value=p.val;srcIn.placeholder=p.ph;
focIn.value=p.focv;
updateCmd();
tabs.forEach(function(t){t.addEventListener('click',function(){setTab(t.dataset.src);});});
[srcIn,focIn].forEach(function(el){el.addEventListener('input',updateCmd);});
descIn.addEventListener('input',updateCt);
function buildSkill(){
var name=slug(nameIn.value);
var cat=slug(catIn.value)||'general';
var desc=descIn.value.trim().slice(0,60);
var p=presets[src];
var arg=srcIn.value.trim();
var foc=focIn.value.trim();
var tagBase=name.split('-').slice(0,2);
var tags='['+tagBase.join(', ')+']';
var whenLine, procLines, verifyLine, sourceNote;
if(src==='dir'){
whenLine='Use when working with the code in '+arg+'.';
sourceNote='Authored from local source: '+arg+(foc?(' ('+foc+').'):'.');
procLines=['1. Inspect the relevant module before calling it.',
'2. Apply the documented auth and request pattern.',
'3. Handle pagination and errors as the source specifies.'];
verifyLine='Run a sample call and confirm a valid response.';
} else if(src==='url'){
whenLine='Use when integrating the API documented at '+arg+'.';
sourceNote='Authored from doc page: '+arg+'.';
procLines=['1. Read the quickstart steps in order.',
'2. Set credentials and the base URL.',
'3. Make the first request and inspect the payload.'];
verifyLine='Confirm the response matches the documented schema.';
} else if(src==='conv'){
whenLine='Use to repeat this procedure: '+arg+'.';
sourceNote='Captured from a completed workflow in conversation.';
procLines=['1. Reproduce the steps in the order performed.',
'2. Substitute environment-specific values where noted.',
'3. Stop and report if any step fails.'];
verifyLine='Confirm the end state matches the original run.';
whenLine='Use to follow this procedure: '+arg+'.';
sourceNote='Authored from pasted notes.';
procLines=['1. '+arg.split(',')[0].trim()+'.',
'2. Continue through each listed step in order.',
'3. Confirm completion before closing the task.'];
verifyLine='Confirm the final step completed successfully.';
var L='\n';
s+='---'+L;
s+='name: '+esc(name)+L;
s+='description: '+esc(desc)+L;
s+='version: 1.0.0'+L;
s+='metadata:'+L;
s+=' hermes:'+L;
s+=' tags: '+esc(tags)+L;
s+=' category: '+esc(cat)+L;
s+='---'+L+L;
s+='# '+esc(name.replace(/-/g,' ').replace(/\b\w/g,function(c){return c.toUpperCase();}))+''+L+L;
s+='## When to Use'+L+esc(whenLine)+L+L;
s+='## Procedure'+L+procLines.map(esc).join(L)+L+L;
s+='## Pitfalls'+L+'- Do not invent calls the source does not define.'+L+'- Re-check the source if behavior changes.'+L+L;
s+='## Verification'+L+esc(verifyLine)+L+L;
s+='<!-- '+esc(sourceNote)+' -->';
return {html:s, name:name, cat:cat};
function plainSkill(name,cat,html){
var tmp=html.replace(/<[^>]+>/g,'');
var d=document.createElement('textarea');d.innerHTML=tmp;return d.value;
function typeStage(lines,done){
stage.innerHTML='';
lines.forEach(function(t,i){
var d=document.createElement('div');
d.className='ln';d.style.animationDelay=(i*0.32)+'s';
d.innerHTML=t;stage.appendChild(d);
setTimeout(done, lines.length*320+150);
function run(){
var p=presets[src];
runBtn.disabled=true;runBtn.textContent='Running…';
out.classList.add('show');
var built=buildSkill();
var lines=[
'→ '+p.verb+' via '+p.tool+'',
'→ Building standards-guided prompt (≤60-char description, fixed section order)',
'→ Agent authoring SKILL.md as a normal turn',
'✓ skill_manage(create) → ~/.hermes/skills/'+built.cat+'/'+built.name+'/',
'✓ Slash command ready: /'+built.name
typeStage(lines,function(){
skillPre.innerHTML=built.html;
runBtn.disabled=false;runBtn.textContent='Run /learn ▸';
copyBtn.addEventListener('click',function(){
var txt=skillPre.innerText;
if(navigator.clipboard){navigator.clipboard.writeText(txt);}
copyBtn.textContent='Copied ✓';
setTimeout(function(){copyBtn.textContent='Copy';},1400);
runBtn.addEventListener('click',run);
// auto-resize for WordPress iframe embedding
function report(){
var h=document.body.offsetHeight+40;
if(window.parent){window.parent.postMessage({hlLearnHeight:h},'*');}
window.addEventListener('load',report);
window.addEventListener('resize',report);
new MutationObserver(report).observe(root,{subtree:true,childList:true,attributes:true});
updateCt();setTab('dir');
setTimeout(report,200);