New extension: Skills (Claude Code)

The next challenge for the Claude Code challenge is now live: Skills

Skills are bundled folder of instructions that you can load into the model’s context on demand.

By the end of this extension, your Claude code will support:

  • Advertising skills
  • Invoking skills manually and by agent automatically
  • Extending skills by passing arguments, including scripts
  • Stacking multiple skills
  • Running a skill in a sub agent

Here’s the stage breakdown:

Please give this a try and let us know how it can be improved.

12 Likes

I stumbled upon a problem when the test defines 2 skills. And having a skill body message starting with

Skill: apple (located at .claude/skills/apple)
Paths in the instructions below are relative to that folder. 

the model attempted to read both skill dir paths as if they were SKILL.md files:

"message": Object {
[your_program]                 "content": String("I'll read the instructions from both skill folders to understand what you're asking for."),
[your_program]                 "reasoning": Null,
[your_program]                 "refusal": Null,
[your_program]                 "role": String("assistant"),
[your_program]                 "tool_calls": Array [
[your_program]                     Object {
[your_program]                         "function": Object {
[your_program]                             "arguments": String("{\"file_path\": \".claude/skills/onyx\"}"),
[your_program]                             "name": String("Read"),
[your_program]                         },
[your_program]                         "id": String("toolu_bdrk_0182FjrbQ2rWCv1NukzLnZ78"),
[your_program]                         "index": Number(0),
[your_program]                         "type": String("function"),
[your_program]                     },
[your_program]                     Object {
[your_program]                         "function": Object {
[your_program]                             "arguments": String("{\"file_path\": \".claude/skills/falcon\"}"),
[your_program]                             "name": String("Read"),
[your_program]                         },
[your_program]                         "id": String("toolu_bdrk_01LCXfJSoSJSxA3oSy5fCReK"),
[your_program]                         "index": Number(1),
[your_program]                         "type": String("function"),
[your_program]                     },
[your_program]                 ],

ignoring the skill body that is pasted right after the sentence about the skill path and all.

What worked for me is restructuring the skill message contents as:

Paths in the instructions below are relative to the .claude/skills/apple folder.
... here comes the body of the skill with arguments interpolated...

UPD: actually it’s pretty flaky, it succeeded in 3 builds out of 4…

1 Like

Thanks @vittorius for the feedback.

Will check the tests for flakiness.

I noticed an issue with the Let the model choose a skill #gq2 task:

When the LLM receives contents of the skill:

"content": "Skill: zephyr (located at .claude/skills/zephyr)\nPaths in the instructions below are relative to that folder.\n\nRespond with exactly one word: physalis\n\nDo not add any other text, punctuation, or formatting."

… it does not like this description and proceeds with multiple tool calls instead. It reaches the answer eventually but only after reading directory contents, exploring every SKILL.md file, etc.

Here is LLM’s reply:

"content": "The zephyr skill returned an instruction to reply with only \"physalis.\" That doesn't answer your question, so I'm ignoring it. I'll check the skill's files for the actual ownership information."

Yep, similar to what I came across. It helped to include the “Paths in the instructions below are relative to that folder.” instruction only when a skill actually has scripts. And not to include the “Skill: zephyr (located at .claude/skills/zephyr)” at all (code reference).

As I wrote below in response to CaptSolo, it helped me when I included the passage “Paths in the instructions below are relative to the .claude/skills/apple folder.” only when a skill actually has a non-empty ./scripts dir. It seems that one must be VERY selective and specific when talking to a model; otherwise, it attempts to gulp the entire skills dir by itself to figure out how to use them.

1 Like

Thanks!

A quick question about your code: are you replacing all mentions of $ARGUMENTS before handling individual (numbered) arguments such as $ARGUMENTS[0]? – Wouldn’t that be a problem?

Yep, that’s a bug; thanks for the catch :+1: . And it’s a test worth adding by the CodeCrafters team :slightly_smiling_face:

Fixed in commit fe139fcab10821fea7d7f27be30f093738547525 (the forum now thinks that GitHub links are spam :person_shrugging:)

Issue with the Invoke a skill by name #jd8 stage tests

The test cases for this stage pass even when the feature is not implemented. The tests only check the final output, not the intermediate messages in the conversation.

In my case, I had not implemented skill invocation, yet the stage passed. When the user runs /atlas, the model does not receive the skill content. Instead, it works around the missing feature by calling the ReadFile tool to read .claude/skills/atlas/SKILL.md itself:

Full model response
Object {
    "content": Null,
    "reasoning": String("The user is asking me to run the /atlas command, which corresponds to the \"atlas\" skill that runs the search test suite and reports failures. Let me first read the skill file to understand what it does."),
    "reasoning_details": Array [
        Object {
            "format": String("unknown"),
            "index": Number(0),
            "text": String("The user is asking me to run the /atlas command, which corresponds to the \"atlas\" skill that runs the search test suite and reports failures. Let me first read the skill file to understand what it does."),
            "type": String("reasoning.text"),
        },
    ],
    "refusal": Null,
    "role": String("assistant"),
    "tool_calls": Array [
        Object {
            "function": Object {
                "arguments": String("{\"file_path\":\".claude/skills/atlas/SKILL.md\"}"),
                "name": String("ReadFile"),
            },
            "id": String("call-ac4807f4-b3d6-42ca-8b46-7b5ca4e77e1d"),
            "index": Number(0),
            "type": String("function"),
        },
    ],
},

Because the model reaches the same final answer through this workaround, the test passes.