Skip to content

feat(coding-agent): list a large skill catalog one line per skill - #4456

Merged
code-yeongyu merged 7 commits into
mainfrom
feat/compact-skill-catalog-lines
Oct 27, 2026
Merged

code-yeongyu merged 7 commits into
mainfrom
feat/compact-skill-catalog-lines

Conversation

@code-yeongyu

@code-yeongyu code-yeongyu commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Fixes #4449

What

A large skill catalog is listed one line per skill instead of a five-line XML block:

<available_skills>
- smart-rebase [r0]: Rebase the current branch onto the right base branch with local conflict handling…
- gpt-image-gen [r1/bundle/SKILL-3R5ZD4J5.md]: MUST read before generating images…
</available_skills>
  • Location is the root alias alone when the file is <root>/<name>/SKILL.md (100 of 101 skills on a real catalog), else the path under the root, so every skill stays resolvable through the existing <skill_roots> table; one header line states the rule.
  • One-line descriptions are capped at 160 characters and keep the description's when-to-use sentence (Use when/for/whenever ..., MUST USE ..., USE ONLY WHEN, This skill should be used when, Triggers: ..., Korean triggers: ..., Triggers - ...): a cut keeps that sentence whole (or its start, when it alone is longer than the line) and fills the remaining room with the leading what-it-does text, then the text after it, cut at word boundaries. Multi-line descriptions are flattened first, so a trigger on a later line still counts. A catalog whose 160-character lines overflow the 48 KiB cap tries 100-character lines before dropping descriptions.
  • Compact mode also triggers from 16 KiB of descriptions (was 64 KiB); the 100-skill count trigger is unchanged. A ~100-skill personal library no longer falls back to full multi-sentence descriptions when a few skills are hidden.
  • The names-only fallback (over 48 KiB) uses the same line form without descriptions, and its header says - name [location] (no : when to use it). Small catalogs render exactly as before.
  • The <available_skills> wrapper and the catalog marker sentence are kept, so the Claude-subscription prompt extractor still finds the block.

Measured: when-to-use clause survival vs tokens (review FIX_FIRST at 7da8afe)

Real libraries on a dev machine: ~/.agents/skills (71 skills, 65 with a trigger clause) and the same plus the omo plugin skills (97 skills, 89 with a clause). Tokens are an o200k estimate (js-tiktoken) of the whole rendered skills section. "Whole" = the full trigger sentence appears in the line; "start" = its first 40 characters appear.

set version clause whole clause start tokens bytes
~/.agents/skills (71) main 64/65 64/65 10,335 41,211
~/.agents/skills (71) pr-7da8afe7b9 4/65 16/65 2,270 8,938
~/.agents/skills (71) new 39/65 64/65 3,252 13,111
+ omo plugin skills (97) main 19/89 52/89 7,573 28,445
+ omo plugin skills (97) pr-7da8afe7b9 4/89 17/89 3,057 12,112
+ omo plugin skills (97) new 62/89 88/89 4,374 17,735

main renders the 71-skill set in full XML (below its thresholds) and the 97-skill set with a 160-character head cut (over the 48 KiB cap). The previous head of this PR (100-character head cut) kept the whole clause for 4 skills.

Candidate rules, whole-clause survival / tokens (71-skill set · 97-skill set):

rule 100 120 140 160 180
head cut 4/65 / 2,261 · 4/89 / 3,043 6/65 / 2,554 · 7/89 / 3,449 9/65 / 2,877 · 11/89 / 3,874 13/65 / 3,146 · 19/89 / 4,265 17/65 / 3,398 · 23/89 / 4,602
trigger-first 25/65 / 2,288 · 36/89 / 3,096 28/65 / 2,598 · 44/89 / 3,507 33/65 / 2,920 · 56/89 / 3,928 39/65 / 3,219 · 62/89 / 4,332 42/65 / 3,480 · 66/89 / 4,680

The bar was the 160-character baseline: a clause survives whenever it fits in 160 characters (39/65 and 62/89 clauses are that short). Trigger-first at 160 meets it exactly at the lowest cost of any rule that does; 140 falls to 33/65. A head cut never comes close at any cap.

Tests

  • RED -> GREEN test/suite/regressions/4449-compact-skill-catalog-lines.test.ts: on main both original cases fail (expected false to be true for the 16 KiB trigger, expected [] to have a length of 98 for the line form). The new table-driven case keeps the when-to-use clause: $shape covers 11 description shapes (Use when / Use for / Use this whenever / MUST USE first / USE ONLY WHEN / This skill should be used when / Triggers: / Korean triggers / Triggers - Korean list / trigger on a later line / clause longer than the line). Against the previous head 7da8afe, 11 of 13 cases failed (RED); all 13 pass now (GREEN). It also resolves every listed line back to the skill's file through the roots table. The short-trigger case also asserts that the lead fill survives (the line starts with Operates the deployment toolchain); replacing the lead fill with "" makes that case fail.
  • Updated 3460-context-file-size-cap.test.ts for the line form and the new byte threshold. Its 400-skill case also covers the 100-character fallback tier.
  • 3483-prompt-goldens.json: only prompts.compact changed, and only between </skill_roots> and </available_skills>. The fixture's two-line descriptions are now flattened ("... verify behavior. Keep the full description."); every other key is byte-identical.
  • docs/settings.md now states the real thresholds (16 KiB, 160 characters), the one-line format, and the trigger-sentence rule.
  • 8 related files, 164 tests green. Push gates (each its own process): git diff --check origin/main...HEAD, bun run check, those tests, and node scripts/check-pr-changelog.mjs.

Follow-up

@code-yeongyu

Copy link
Copy Markdown
Owner Author

Measured on a real session (omo 5.1.48 install; "after" = the same tree with this PR's skills-prompt module).

run 1 total / cacheRead run 2 total / cacheRead skills block system prompt
before 50,574 / 0 50,799 / 0 8,342 18,585
after 44,109 / 0 44,109 / 44,105 3,485 13,869

-6,465 input tokens per request (run 1 totals) on a 101-skill catalog.

Correction to the issue text: the "cliff" claim does not hold for this library. Measured with 98 skills, the catalog stays compact because its full render exceeds the existing 48 KiB cap. The 16 KiB description trigger only changes catalogs whose full render fits under 48 KiB (roughly 50-100 skills with ~300-character descriptions), which would otherwise list every full description.

Method: same fixed empty cwd, omo -p "." --model anthropic/claude-opus-5-5 run twice per build, before vs after build; numbers are the first assistant message's usage from the session jsonl (input total = input + cacheRead + cacheWrite). Block sizes are o200k estimates from the captured first payload.

…atalog

A cut now keeps the description's trigger sentence (Use when/for, MUST USE,
Triggers:, Korean triggers) and fills the remaining room with the text around
it, at 160 characters; a catalog that still overflows the 48 KiB cap tries
100-character lines before dropping descriptions. The names-only header no
longer promises a when-to-use part, and settings.md documents the real
thresholds and format. Refs #4449, follow-up #4518.
Removing the lead fill makes the 'what it does, then Use when' case fail. Refs #4449.
@code-yeongyu
code-yeongyu marked this pull request as ready for review October 27, 2026 13:01
…talog-lines

# Conflicts:
#	packages/coding-agent/test/suite/regressions/3483-prompt-goldens.json
@code-yeongyu
code-yeongyu merged commit 4179b9d into main Oct 27, 2026
10 of 36 checks passed
@code-yeongyu
code-yeongyu deleted the feat/compact-skill-catalog-lines branch October 27, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Large skill catalog: list one line per skill (name, shortest location, 100-char line) and compact from 16 KiB of descriptions

1 participant