Skip to content

Skills part 4: editing skills in Settings, with the built-ins as examples - #93

Open
shanforge wants to merge 2 commits into
shan/issue-86-skill-invocationfrom
shan/issue-87-skill-editor
Open

Skills part 4: editing skills in Settings, with the built-ins as examples#93
shanforge wants to merge 2 commits into
shan/issue-86-skill-invocationfrom
shan/issue-87-skill-editor

Conversation

@shanforge

Copy link
Copy Markdown
Collaborator

Part of #56. Closes #87 — the last box of #64, which this closes too.

Stacked on #92 — review that first; this branch contains it.

The built-ins are the documentation

#64 asks that they be "readable as examples", so they are listed first, opening one shows
exactly what it says to the model, and each is written the way someone's own skill should be:
a short summary, instructions that say what to do rather than what to be, and a tool list
that is narrow — because narrowing is the normal case, not an advanced option.

Editing one copies it. That is SkillStore's decision rather than this screen's, and the
form says so before you save. "Restore" appears only on the ones that have been changed,
because a button that undoes nothing teaches you not to trust it.

What the form has to get right

  • The invocation is shown as you type the name. What you read and what you type are
    different strings, and this is where that becomes obvious.
  • "Limit which tools" is a switch, not an empty field. Absent and empty mean different
    things, and inferring "narrows to nothing" from an empty text field would take every tool
    from a skill the moment someone cleared it to retype.
  • The tool list splits on commas or whitespace, because someone will use either and a
    list that accepts one silently drops half of what was typed.
  • Validation is the store's. The form shows SkillName.Rejection's message and keeps what
    was typed; a second copy of the name rules is a second copy that can disagree.
  • The three tool states read as words — "Any tool the agent has", "No tools", "Only: …" —
    because they are easy to confuse and the difference matters.

Export and import

SkillTransfer presents a panel rather than inventing a path: this is the user's filesystem,
and a silent write into Documents is how a feature becomes something people cannot find
again. A file that cannot be read costs that file and nothing else — picking six and losing
all of them to one bad encoding would be the worst possible reading of "import".

The result is said out loud including when it is zero: an import that quietly does
nothing is indistinguishable from one that worked. The wording is pure and tested at every
count, because "1 skills" is the kind of thing nobody notices until it ships. The log names
the file, never the path.

What the review found

Instructions past the prompt budget were stored and never sent, with nothing saying so.
Only a bounded amount of a skill reaches the model — it is layered on top of the system
prompt and everything else the turn needs. Paste a long document into a skill and it saves,
looks complete, and the agent quietly acts on the first part. The way you find out is an
answer that ignored half of what you wrote.

The count is now shown while it is still editable, and the two limits are distinguished
because they mean different things: past the prompt budget the extra is saved but never used;
past the file limit it will not save at all — refused rather than silently cut, since losing
what someone typed on save is the one failure here that cannot be undone.

Verification

  • xcodebuild build — succeeds
  • ./scripts/test-no-llm.sh1877 tests in 170 suites pass
  • SwiftFormat 0.62.1 --lint — 0/573 files require formatting
  • SwiftLint 0.65.0 --strict — 0 violations in 732 files

AISettingsTab went one line past the 450-line type-body cap, so the per-tool switches and
their static group list moved to AISettingsTab+Tools.swift — static data rather than logic,
which is what the extension-file rule is for. Three members widened to internal, each
carrying the project's // Extension-visible: +Tools marker so the wider access reads as
deliberate.

The click-through for the whole skills feature

This is where it can be driven end to end, so this is the PR carrying the evidence request.

  1. Settings → AI → Skills — three built-ins, each with its /name and a "built-in" badge.
    Open one: the instructions are readable, and are what the model is actually given.
  2. Invoke one from the main window by picking it in + → Skills. A chip appears saying
    which. Send: the answer follows the skill.
  3. Do the same from the Command Center island. Same menu, same chip, same behaviour —
    that is the point of Ask Logue part 1: one assistant on both surfaces #61 and the reason this box was cheap.
  4. Type /tighten-this and a document name — no chip needed.
  5. Type a name that does not exist — the send is refused and the banner names what you
    typed, on both surfaces.
  6. Make a skill with "Limit which tools" on and one tool listed, then ask it to do
    something needing a different tool. It should decline rather than call the other tool.
  7. Name a tool you have turned off in Settings — it stays off. A skill can take tools
    away and never add one.
  8. Edit a built-in, save, reopen Settings — your version is there. Press Restore: the
    original comes back.
  9. Export a skill, then import it — it arrives, renamed if the name is taken.
  10. Import two files at once where one is malformed — the good one lands and the toast
    says "Imported 1 skill of 2".
  11. Paste something very long into the instructions — the notice says how much of it is
    actually sent.

🤖 Generated with Claude Code

https://claude.ai/code/session_01M3Wpnj9ZmWPKYdFPB1AVY3

shanforge and others added 2 commits September 4, 2026 13:33
… examples

Part of #56. Closes #87 — the last box of #64, which this closes too.

## The built-ins are the documentation

#64 asks that they be "readable as examples", so they are listed first, opening one shows
exactly what it says to the model, and each is written the way someone's own skill should
be: a short summary, instructions that say what to *do* rather than what to be, and a tool
list that is narrow — because narrowing is the normal case, not an advanced option.

**Editing one copies it.** That is `SkillStore`'s decision rather than this screen's, and
the form says so before you save. "Restore" appears only on the ones that have been changed,
because a button that undoes nothing is a button that teaches you not to trust it.

## What the form has to get right

- **The invocation is shown as you type the name**, because what you read and what you type
  are different strings and this is where that becomes obvious.
- **"Limit which tools" is a switch, not an empty field.** Absent and empty mean different
  things — see `AgentSkill.allowedToolNames` — and inferring "narrows to nothing" from an
  empty text field would take every tool from a skill the moment someone cleared it to retype.
- **The tool list splits on commas or whitespace**, because someone will use either and a
  list that accepts one silently drops half of what was typed.
- **Validation is the store's.** The form shows `SkillName.Rejection`'s message and keeps
  what was typed; a second copy of the name rules is a second copy that can disagree.
- The three tool states read as words in the list — "Any tool the agent has", "No tools",
  "Only: …" — because they are easy to confuse and the difference matters.

## Export and import

`SkillTransfer` presents a panel rather than inventing a path: this is the user's
filesystem, and a silent write into Documents is how a feature becomes something people
cannot find again. `MessageActions.exportMarkdown` already makes that argument.

A file that cannot be read costs that file and nothing else — picking six and losing all of
them to one bad encoding would be the worst possible reading of "import". The result is said
out loud **including when it is zero**, because an import that quietly does nothing is
indistinguishable from one that worked. The wording is pure and tested at every count, since
"1 skills" is the kind of thing nobody notices until it ships.

The log names the *file*, never the path — the project rule, and the filename is what the
user recognises anyway.

## Two size caps, split rather than suppressed

`AISettingsTab` went one line past the 450-line type-body cap, so the per-tool switches and
their static group list moved to `AISettingsTab+Tools.swift` — static data rather than
logic, which is exactly what the extension-file rule is for. Three members widened to
internal to make that work, each carrying the project's `// Extension-visible: +Tools`
marker so the wider access reads as deliberate.

Verified: build succeeds, 1877 tests in 170 suites pass, SwiftFormat --lint clean over 573
files, SwiftLint --strict 0 violations in 732 files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3Wpnj9ZmWPKYdFPB1AVY3
**Instructions past the prompt budget were stored and never sent, with nothing saying so.**
Only `AgentSkill.maxInstructionCharacters` of a skill ever reaches the model — it is layered
on top of the system prompt and everything else the turn needs, so it spends context the
conversation would otherwise have. Paste a long document into a skill and it saves, appears
complete, and the agent quietly acts on the first part of it. The way you find out is an
answer that ignored half of what you wrote.

The count is now said while it is still editable, and the two limits are distinguished
because they mean different things: past the prompt budget the extra is saved but never
used; past the file limit it will not save at all — refused rather than silently cut, since
losing what someone typed on save is the one failure here that cannot be undone.

Verified: build succeeds, 1877 tests in 170 suites pass, SwiftFormat --lint clean over 573
files, SwiftLint --strict 0 violations in 732 files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3Wpnj9ZmWPKYdFPB1AVY3
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.

1 participant