anomalyco / anomalyco/opencode
[FEATURE]: Remove or truncate <location> path in system prompt available_skills — wastes ~3.3K tokens/turn
@jlongster is already working on this.
Since Jul 28, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
The system prompt's <available_skills> block includes a <location> field with the full absolute file path for every skill. This field is:
- Unused by the LLM — skills are loaded via
skill(name="foo"), not by path. The tool's internal registry already mapsname→locationduring discovery, so the LLM never needs to know the path. - Extremely verbose — macOS paths like
<location>/Users/yejq/Library/Application Support/com.xxx.zzz/mmmm/.opencode-config/skills/art-requirement/SKILL.md</location>average ~136 characters each. - Made worse by
pathToFileURL— spaces become%20,#becomes%23, and afile://prefix adds 7 chars on top (as documented in #33786).
Token waste measurement (real-world data)
Measured from an actual system prompt dump with 84 skills:
Total <location> blocks: 84
Total characters in locations: 11,382
Average per location: ~136 chars
Estimated tokens wasted: ~3,252 tokens per turn
3.2K tokens burned on every API call on paths the LLM never reads, never uses, and doesn't need.
Single sample entry:
<location>/Users/yejq/Library/Application Support/com.xxx.zzz/mmmm/.opencode-config/skills/art-requirement/SKILL.md</location>
= 141 characters, for a skill whose name (art-requirement) is the only thing the LLM needs to invoke it.
Why <location> is unnecessary
The skill loading flow:
- System prompt tells the LLM: "these skills exist, here are their names + descriptions"
- LLM decides to use a skill → calls
skill(name="ppt-master") - The tool internally maps
name→location(already known from discovery) - The tool reads SKILL.md and returns the content + a base directory hint
At no point does the LLM need the file path. The tool's internal registry already knows where every skill lives. The <location> field in the system prompt is pure context pollution.
For skills that reference relative resources (scripts/, references/), the base directory is already provided in the tool output when the skill is loaded — again, no need for the LLM to pre-know it.
Where the waste happens
In packages/opencode/src/tool/skill.ts (as documented in #13188):
...accessibleSkills.flatMap((skill) => [
` <skill>`,
` <name>${skill.name}</name>`,
` <description>${skill.description}</description>`,
` <location>${pathToFileURL(skill.location).href}</location>`, // ← waste
` </skill>`,
])
And in the system prompt via Skill.fmt(list, { verbose: true }) (as documented in #22236):
| Injection point | Format | Includes <location>? |
|---|---|---|
System prompt body (verbose: true) |
XML | Yes |
Skill tool description (verbose: false) |
Compact markdown | No |
The verbose: false path already excludes <location>, proving it's not essential for the tool description. The system prompt just inherited it.
Proposed solutions
Option A (minimal, safe): Strip <location> from the system prompt only
- Keep the
verbose: trueXML format but omit the<location>tag - The tool description already omits it (
verbose: false) - The full location is still returned in the tool output when a skill is actually loaded (where it's genuinely useful for resolving relative resource paths)
Option B (configurable): Add an includeLocation flag to Skill.fmt()
Skill.fmt(list, { verbose: true, includeLocation: false })
Option C (root cause): Use the skill name as the identifier instead of the full path
<location>art-requirement</location>
This preserves the field for any consumer that depends on its presence, without the token bloat.
Related issues
- #13188 — Lazy-load agent/skill lists (identifies location as part of the waste but focuses on broader lazy-loading)
- #20647 — 600KB system prompt bloat from skill descriptions (includes
<location>in the bloat total) - #22236 — Skill list injected twice per request (notes
verbose: trueincludes<location>) - #33786 — Location URL-encoding bug via
pathToFileURL(confirms the encoding problem) - anthropics/claude-code#59921 — Claude Code silently drops descriptions above a threshold (downstream symptom of the same budget pressure)
Impact
For users with many skills (common in plugin-heavy setups), this is pure waste — 3K+ tokens that could be used for actual conversation context, tool results, or just cost savings. The fix is effectively a one-line change with zero functional impact, since the skill tool resolves name → location internally regardless of what's in the system prompt.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.