Nous Research Adds /learn to Hermes Agent’s Skills System, Capturing Workflows as Slash Commands Without Hand-Writing SKILL.md
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 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.
# 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.
| Level | Call | Returns | Approx. cost |
|---|---|---|---|
| 0 | skills_list() |
Names, descriptions, categories | ~3k tokens |
| 1 | skill_view(name) |
Full content plus metadata | Varies |
| 2 | skill_view(name, path) |
A specific reference file | Varies |
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.
| Method | Who authors | Source input | Review gate | Best for |
|---|---|---|---|---|
Hand-write SKILL.md |
You | Your own knowledge | None | Full control over wording |
/learn |
The live agent | Dir, URL, conversation, notes | skill_manage gate |
Turning existing material into a skill fast |
skill_manage (auto) |
The agent itself | A workflow it just solved | write_approval gate |
Capturing procedural memory after hard tasks |
| Skills Hub install | A third party | Registry or GitHub repo | Security scanner | Reusing 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
/learnon 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.
# ~/.hermes/skill-bundles/backend-dev.yaml
name: backend-dev
description: Backend feature work — review, test, PR workflow.
skills:
- 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.
---
name: my-skill
description: Brief description of what this skill does
version: 1.0.0
platforms: [macos, linux] # Optional — restrict to specific OS
metadata:
hermes:
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
<div class=”tab” data-src=”url”>
Doc URL</div><div class=”tab” data-src=”conv”>
Conversation</div><div class=”tab” data-src=”notes”>
Pasted notes</div></div>
<div class=”row”>
<div>
<label>Skill name (slug)</label>
<input id=”hl-name” value=”acme-api” maxlength=”40″ placeholder=”lowercase-with-hyphens”>
</div>
<div>
<label>Category</label>
<input id=”hl-cat” value=”devops” maxlength=”24″ placeholder=”e.g. devops”>
</div>
</div>
<label>Description (house standard: ≤ 60 characters)</label>
<input id=”hl-desc” value=”Acme REST API: auth, pagination, common calls” maxlength=”90″>
<div class=”ct” id=”hl-ct”>45 / 60</div>
<label id=”hl-srclabel”>Directory path</label>
<input id=”hl-src” value=”~/projects/acme-sdk”>
<label id=”hl-foclabel”>Focus / instruction (optional)</label>
<input id=”hl-focus” value=”focus on auth + pagination”>
<div class=”cmdprev cmd” id=”hl-cmdprev”></div>
<button class=”run” id=”hl-run”>Run /learn ▸</button>
<div class=”out” id=”hl-out”>
<div class=”pd”>
<div class=”lv”><b>Level 0</b><span>skills_list() · ~3k tokens</span></div>
<div class=”lv”><b>Level 1</b><span>skill_view(name) · full</span></div>
<div class=”lv”><b>Level 2</b><span>skill_view(name, path)</span></div>
</div>
<div class=”stage” id=”hl-stage”></div>
<div class=”skillwrap”>
<div class=”cap”><b>Generated ~/.hermes/skills/<category>/<name>/SKILL.md</b>
<button class=”copy” id=”hl-copy”>Copy</button></div>
<pre id=”hl-skill”></pre>
</div>
<div class=”note”>Saved via the <span class=”cmd”>skill_manage</span> tool. With <span class=”cmd”>skills.write_approval: true</span>, this write is staged under <span class=”cmd”>~/.hermes/pending/skills/</span> for review.</div>
</div>
</div>
<div class=”ft”>
<span>Progressive disclosure · agentskills.io standard</span>
<span>Demo by <b>Marktechpost</b></span>
</div>
<script>
(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,’<’).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):”;
var body;
if(src===’dir’) body=arg+(foc?(‘, ‘+foc):”);
else if(src===’url’) body=arg;
else body=arg+(foc?(‘ — ‘+foc):”);
cmdPrev.innerHTML='<span class=”pl”>$ </span>/learn ‘+esc(body);
}
function setTab(s){
src=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.’;
} else {
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’;
var s=”;
s+='<span class=”fm”>—</span>’+L;
s+='<span class=”key”>name:</span> ‘+esc(name)+L;
s+='<span class=”key”>description:</span> ‘+esc(desc)+L;
s+='<span class=”key”>version:</span> 1.0.0’+L;
s+='<span class=”key”>metadata:</span>’+L;
s+=’ <span class=”key”>hermes:</span>’+L;
s+=’ <span class=”key”>tags:</span> ‘+esc(tags)+L;
s+=’ <span class=”key”>category:</span> ‘+esc(cat)+L;
s+='<span class=”fm”>—</span>’+L+L;
s+='<span class=”h1″># ‘+esc(name.replace(/-/g,’ ‘).replace(/bw/g,function(c){return c.toUpperCase();}))+'</span>’+L+L;
s+='<span class=”h2″>## When to Use</span>’+L+esc(whenLine)+L+L;
s+='<span class=”h2″>## Procedure</span>’+L+procLines.map(esc).join(L)+L+L;
s+='<span class=”h2″>## Pitfalls</span>’+L+’- Do not invent calls the source does not define.’+L+’- Re-check the source if behavior changes.’+L+L;
s+='<span class=”h2″>## Verification</span>’+L+esc(verifyLine)+L+L;
s+='<span class=”fm”><!– ‘+esc(sourceNote)+’ –></span>’;
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=[
‘<span class=”tool”>→ ‘+p.verb+'</span> via <span class=”ok”>’+p.tool+'</span>’,
‘→ Building standards-guided prompt (≤60-char description, fixed section order)’,
‘→ Agent authoring <span class=”ok”>SKILL.md</span> as a normal turn’,
‘<span class=”ok”>✓ skill_manage(create)</span> → ~/.hermes/skills/’+built.cat+’/’+built.name+’/’,
‘<span class=”ok”>✓ Slash command ready:</span> /’+built.name
];
typeStage(lines,function(){
skillPre.innerHTML=built.html;
runBtn.disabled=false;runBtn.textContent=’Run /learn ▸’;
report();
});
report();
}
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});
// init
updateCt();setTab(‘dir’);
setTimeout(report,200);
})();
</script>
</div>
</body></html>”>
Key Takeaways
- Hermes Agent’s new
/learncommand authors a reusable skill from a directory, URL, conversation, or pasted notes — no hand-writing needed. - The live agent sources material with its own tools (
read_file,search_files,web_extract), then writes a standards-compliantSKILL.md. - There is no separate ingestion engine, so
/learnworks the same across the CLI, TUI, messaging gateway, and dashboard. - Progressive disclosure keeps skills cheap: a ~3k-token index loads first, and full content loads only when a task needs it.
- Skills save via
skill_manage, so thewrite_approvalgate can stage every write for review before it lands.
Sources
- Skills System — Hermes Agent documentation
- Learning a skill from sources (/learn)
- Hermes Agent documentation home
- NousResearch/hermes-agent — GitHub
- Nous Research on X
The post Nous Research Adds /learn to Hermes Agent’s Skills System, Capturing Workflows as Slash Commands Without Hand-Writing SKILL.md appeared first on MarkTechPost.