[BUG] Conditional (paths:) skills can never activate in auto mode — the trigger is wired only to Read/Edit/Write, which bashFirst tells the model not to use
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Conditional skills (those declaring paths: in SKILL.md frontmatter) are activated only by the Read, Edit and Write tools. Auto mode's bashFirst system prompt (#88041) instructs the model to do all file I/O through Bash instead of those three tools. The two features are individually reasonable and mutually exclusive in practice: in an auto-mode session, a paths:-scoped skill can never activate, no matter how many matching files the model reads and writes.
This is a root cause for the "trigger never fires" symptom of #62049 (closed as stale, never diagnosed).
I hit this on a Ruby TUI project whose CLAUDE.md says, unconditionally, "read the tuile skill before designing components". The skill declares paths: ["**/*.rb"]. I spent a whole session editing .rb files — the skill never appeared, and Skill(tuile) returned Unknown skill: tuile. Every file access had gone through cat / sed / heredocs, as auto mode directs.
What Should Happen?
Either:
- Bash file access should also feed the conditional-skill matcher. Paths are already extracted from Bash commands elsewhere in the product (permission checks, file-write detection), so the signal exists.
- Or
bashFirstshould not be injected when the session has conditional skills loaded — the cheap fix, at the cost of leaving conditional skills dependent on tool choice.
Option 1 is the real fix: as long as activation is tied to three specific tool names, any future prompt or model that prefers a different path to the filesystem will silently switch these skills off again.
Steps to Reproduce
- Create
~/.claude/skills/zebra/SKILL.md:
```yaml
---
name: zebra
description: Test skill. Says ZEBRA.
paths:
- "**/*.rb"
---
Say ZEBRA.
```
- Start a session in a repo containing
.rbfiles, with auto mode enabled. - Ask for a change to a
.rbfile. Auto mode steers the edit through Bash (sed, heredoc,python3). - The skill never appears in the available-skills listing;
/zebraandSkill(zebra)returnUnknown skill: zebra. - Now do one
Readof the same.rbfile. The skill activates and appears in the listing.
Step 5 is the control: the mechanism works, it is just unreachable through the tool auto mode tells the model to use.
Evidence
From the 2.1.247 native binary.
The loader parses paths: into a gitignore-style matcher, keeps such skills in a conditionalSkills map (not in the listing), and promotes them to dynamicSkills on a path match:
for (let [r, o] of Pv().conditionalSkills) {
if (o.type !== "prompt" || !o.paths || o.paths.length === 0) continue;
let s = Rjr.default().add(D9(o.paths, "skill_paths")); // the `ignore` package
for (let i of e) {
let a = o1t(i) ? Ajr(t, i) : i; // absolute -> relative to cwd
if (!a || a.startsWith("..") || o1t(a)) continue;
if (s.ignores(a)) {
Pv().dynamicSkills.set(m9e(o), o),
Pv().conditionalSkills.delete(r),
Pv().activatedConditionalSkillNames.add(r),
n.push(r),
_(`[skills] Activated conditional skill '${r}' (matched path: ${a})`);
break
}
}
}
Reached through one entry point:
async function bJ(e, t, n, r) {
let o = ze(), s = await gLs(e, [t], o, r);
if (s.length > 0) { ...; w9e(s).then(() => { Sjr([t], o) }).catch(() => {}) }
Sjr([t], o)
}
bJ has exactly three call sites, all guarded by !CLAUDE_CODE_SIMPLE && remoteCall === undefined:
- the Edit handler —
await bJ(t.session.host, g, c, t.storageV5) - the Write handler —
await bJ(n.session.host, d, u, n.storageV5) - the Read handler —
await bJ(o.session.host, p, o.dynamicSkillDirTriggers, o.storageV5)
No Bash path reaches it. Relevant telemetry: tengu_dynamic_skills_changed with source: "conditional_paths".
Meanwhile the bashFirst auto-mode injection (quoted in full in #88041) says:
Do your work through the Bash tool wherever it can accomplish the job: read files with cat, head, or sed -n, search with grep and find, and make file changes with sed, heredocs, or short scripts, rather than using the dedicated Read, Edit, or Write tools.
Those are precisely the three tools activation is hooked to.
Correlation check on this install: of 10 personal skills, the 5 declaring paths: were all absent from the session's available-skills listing; the 5 without it were all present.
Notes
- Because an unactivated conditional skill is also unreachable by name (#49835), a project
CLAUDE.mdthat instructs "read skill X" cannot be obeyed — the model getsUnknown skill, with nothing to indicate the skill exists but is dormant. A dormant conditional skill arguably deserves a distinct error, or a listing entry marked as such. - Suggested labels:
area:skills,area:tools,area:core.
Is this a regression?
I don't know — bashFirst is the newer half of the pair.
Claude Code Version
2.1.247 (Claude Code), native build
Platform
Anthropic API
Operating System
Linux (7.0.0-29-generic)
Terminal/Shell
fish
Additional Information
Related:
- #88041 — the
bashFirstauto-mode instruction itself (this is a further consequence of it) - #62049 — "
paths:never triggers skill auto-loading", closed as stale; this is a root cause for it - #49835 —
paths:makes a skill undiscoverable and unreachable by name