Open the settings for any AI coding assistant that supports commands, skills, and subagents, and you’ll find yourself reaching for a mental shortcut almost immediately: commands are the basic one, skills are the smart one, subagents are the advanced one. A ladder, rung by rung, from simple to sophisticated.
That shortcut is wrong, and it’s wrong in a way that matters the first time you install something you didn’t write.
What Actually Changes When You Type a Slash Command?
Say you type /review src/auth into opencode - an AI coding assistant (one of several, alongside tools like Claude Code, that lets a team define custom commands, skills, and agents as files in the repo). A file called review.md gets matched, your argument gets substituted into it, and the model receives a fully-formed prompt: check for missing input validation, flag hardcoded secrets, confirm test coverage on the changed lines.
Here’s the question worth sitting with: could you have typed all of that yourself, in plain English, without the command existing at all? Yes. Nothing about /review unlocks a capability the model didn’t already have. So what is the command actually doing?
The answer is that it moves a decision from run-time to design-time, and it moves who makes that decision from the model to a human. The opencode client parses /review, matches the file, does the substitution - all of this happens before any inference call is made. The model has no vote in whether this template fires. It’s not being asked to interpret your intent and produce something review-shaped; it’s being handed an exact, pre-written prompt that a human wrote once and now everyone on the team reuses identically.
That’s the whole value proposition, and it survives contact with a harder question: why would a team bother formalizing this into a versioned file instead of just telling engineers “paste the review checklist from the wiki”? Because the moment ten different engineers are each retyping the compliance checklist from memory, you don’t have one review process, you have ten slightly different ones. A command pushes all the variance in the process down into just the model’s generation - the prompt, the context, the guardrails are now fixed and identical every time. The only thing left to chance is what the model says back.
That certainty has a cost, though, and the cost shows up the moment something needs to happen that nobody thought to ask for. Imagine you’re mid-conversation about an unrelated feature, and partway through, it becomes clear a release needs to be cut - tag a version, write a changelog, bump the semantic version. Nobody typed /release. Nobody could have, because nobody knew in advance this would come up. A command can’t fire here, for a very literal reason: there is no tool the model can call that says “invoke command X.” The only entry point into a command is a human typing its name. If nothing gets typed, nothing happens.
What Happens When the Model Reaches for Something on Its Own?
This is exactly the gap a skill exists to close. A skill is also a markdown file with instructions in it, but it carries two extra pieces of metadata a command doesn’t: a name and a description that live in the model’s context at all times, cheaply, whether or not the skill ever gets used.
Here’s the mechanism worth being precise about, because it’s the same one you’ve probably already met in a completely different context. If a team has sixty skills installed, you don’t want all sixty files’ full content sitting in the prompt on every single turn - that’s an enormous, mostly-wasted token cost. So only the cheap part stays resident: the name and a one-line description of when it applies. The full instructions, scripts, and reference files load only after the model decides, mid-task, “this one is relevant right now.” That’s called progressive disclosure, and it’s structurally identical to how a vector search system works: documents get compressed into cheap numerical fingerprints (embeddings) that live in an index at all times, and the system only pulls the full document off disk after one of those fingerprints scores as a likely match. Pay for relevance-matching up front, pay for content only on demand.
The release-cutting scenario that stumped the command works fine here. A git-release skill’s description says something like “use when cutting a new version, tagging, or writing a changelog.” The model, mid-conversation, recognizes the moment and pulls the skill in on its own. Nobody typed anything.
But notice what you traded to get that. With a command, you know with certainty the checklist ran, because a human’s keystroke is the proof. With a skill, even a perfectly-described one, invocation itself is now a probabilistic decision the model makes - not just the wording of its response, but whether it reaches for the skill at all. It can fail to notice a moment it should have caught. It can grab the wrong skill when two descriptions overlap. The command’s whole selling point - push all the uncertainty down into just the generation step - quietly breaks, because now the invocation step is uncertain too.
Commands trade adaptability for certainty. Skills trade certainty for adaptability and a lower standing token cost. Neither is the upgraded version of the other; they’re solving for opposite failure modes.
So Are Commands Just Restricted Skills?
It’s tempting to collapse the two into one idea - “a command is just a skill with the model-facing switch turned off” - and the temptation gets stronger once you learn that a skill can, by default, also be exposed as a typed slash command. Flip one metadata flag and /git-release shows up in the exact same menu a command would, triggered by the exact same human keystroke.
So from the human’s seat, a command and a slash-exposed skill can become genuinely indistinguishable. Same trigger, same menu, both able to redirect execution to a different agent with different permissions. If you strip away the machinery and just watch what a person experiences, the “human-invoked” line that seemed to define commands turns out to be a default posture, not a hard boundary.
But check the other side of that same coin: can a command, configured any way at all, ever be invoked by the model on its own, mid-task, with no human keystroke? It can’t, for the same reason as before - there’s no generic mechanism for it. A skill can always be reached two ways (a human typing its name, or the model discovering it); a command can only ever be reached one way. So the honest version of the claim is narrow: a skill is a superset of a command only on the axis of who can invoke it. On everything else - a command’s ability to pin a specific model or agent, a skill’s portability metadata for sharing across teams and tools - they’re not nested at all. They’re two different tools that happen to share one door.
Why Isn’t a Skill Enough for the Big, Messy Task?
There’s a third construct that hasn’t shown up yet: the subagent. Skills already let the model pull in new instructions mid-task without asking anyone. So what’s left for a subagent to do that a skill can’t already handle?
Picture a task like “figure out where authentication is handled across an unfamiliar fifty-file codebase.” If a skill drives that investigation, everything it touches - every file read, every grep, every dead end - accumulates in the same conversation you’re having about something else entirely. By the time the investigation is done, your context window is full of exploration debris that has nothing to do with what you actually came to talk about, and it sits there for the rest of the session whether it’s useful or not.
A subagent solves this by drawing a hard line around the work. It starts with a completely fresh context - none of your prior messages, none of the files already read - does the exploratory digging in that disposable space, and only the distilled answer travels back to the main conversation. The search process itself is never stored in your primary context at all; it isn’t filtered out afterward, it’s simply never let in. A subagent can also run under different permissions and even a different underlying model entirely, chosen for that one narrow task.
That isolation is also exactly where things go wrong. A subagent has no access to anything you and the primary agent discussed to get to this point - none of your prior corrections, none of the constraints you already agreed on. All it has is whatever instruction it was handed. If that handoff is underspecified - say, “update the production config” when the actual agreement was to touch only three named settings - the subagent doesn’t fail loudly. It does something plausible and wrong, confidently, because it genuinely doesn’t know it wasn’t supposed to. The same isolation that makes subagents safe from context pollution is what makes a sloppy handoff so easy to miss: the exploration trace gets discarded, so there’s often nothing left to review even if you wanted to check its work.
Is “Skills Stay Light, Subagents Go Heavy” Actually Enforced?
Here’s the uncomfortable finding underneath all of this: nothing in the architecture stops a skill from doing exactly what a subagent is supposed to exist to prevent. A SKILL.md file is just instructions. There’s nothing structural that caps how much exploration it can ask the model to do in-place. A skill author can absolutely write “now read every file in the repo and summarize the last fifty commits” directly into a skill’s workflow, and the model will happily pollute the primary context exactly the way subagents were invented to avoid.
The boundary between “this belongs in a skill” and “this belongs in a subagent” is a convention good authors follow, not a rule the system checks for anyone. The nearest thing to a checkable version of that convention: does the skill’s workflow have a fixed, small, enumerable sequence of steps - read this one file, run this one script - or does it contain open-ended verbs like “explore,” “investigate,” “search until you find”? Those verbs imply a number of tool calls nobody can predict in advance, and that’s the tell that the work should have been handed to a subagent instead of run in place.
So Who’s Actually Guarding the Door?
Put all three side by side and a pattern falls out that the “ladder of sophistication” framing completely hides.
invoked by context carried in isolation
Command human only full session history none
Skill human OR model full session history none
Subagent any of the above none (fresh start) full
Every row in that table is about the same two questions: who decided to walk through the door, and how much of what you already know did they bring with them. Not one row says anything about what happens once they’re inside. That’s the part worth stopping on, because it’s the part people quietly assume is baked into the choice of construct - “I made this a command instead of a skill, so it’s controlled.”
It isn’t. Think about a helpdesk call instead of a codebase for a second. Picking “reset my password” from an approved dropdown is a command: scripted, identical every time, and the agent on the line has heard your whole call. That same agent, mid-call about something unrelated, noticing you mentioned a locked account and pulling up the recovery playbook without you asking, is a skill: still your call, still full context, just their judgment on when to reach for it. Escalating to a specialist team that receives a summary ticket instead of a transcript, investigates on its own, and hands back a resolution with no memory of your original call afterward - that’s the subagent. All three of them answer to the exact same company security policy about what any team is actually allowed to touch on your account. The policy was never attached to which door the request came through. It’s attached to what the team behind the door is cleared to do, full stop.
That’s the whole answer to “is a command safer than a skill.” It’s a non-sequitur, in the same way “the agent picked up the phone instead of getting escalated to, so they must not be able to change my billing” is a non-sequitur. The wall doesn’t know which door you used. It only knows who’s standing in front of it now, and what that person or process has been cleared to do.
This is the part that stops being theoretical the moment you install a skill from a marketplace you’ve never opened - the equivalent of a repo pulling in a third-party package nobody on the team has read line by line. Nobody manually reviews every dependency in an npm install either; the control that actually holds isn’t “someone read it first,” it’s the sandbox the untrusted code runs inside regardless of whether anyone read it. The same move applies here: edit: deny, bash: deny on an agent’s permission config isn’t a suggestion the skill has to respect - it’s a wall the skill physically cannot get through, no matter how the instructions inside it are written. The softer option, setting everything to ask so a human approves each action, sounds safer on paper, but a skill that touches forty files and runs fifteen greps turns into forty-five approval prompts in a row - and the moment a human starts reflexively clicking “approve” to get through them, the gate is still technically there and functionally gone.
Where This Leaves You
Commands, skills, and subagents are three different answers to “who gets to start this, and how much do they know when they do.” That’s a real and useful distinction, worth keeping straight. But it’s a completely separate question from “what is anyone, human or model, actually allowed to do once they’ve started” - and that second question is answered once, at the level of the agent and the permissions it’s been configured with, not by which of the three doors got used to walk in.
The next time you’re deciding whether a new piece of automated capability should be a command, a skill, or a subagent, that’s a real design decision worth making carefully. But it isn’t the safety decision. If you haven’t separately checked what the underlying agent is actually permitted to touch, you haven’t secured anything yet - you’ve just picked a doorway. What does your permission configuration actually deny, versus merely ask about, and would it survive the first marketplace skill nobody on your team reads before installing?
What’s your take? Drop a comment below.
Comments