Skip to content

fix(sandbox): install skills natively per harness (AI-1034) - #240

Open
seanoliver wants to merge 3 commits into
mainfrom
sean/ai-1034-native-skills-install
Open

fix(sandbox): install skills natively per harness (AI-1034)#240
seanoliver wants to merge 3 commits into
mainfrom
sean/ai-1034-native-skills-install

Conversation

@seanoliver

@seanoliver seanoliver commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

What's inside

Bottom of a 2-PR stack under #241, which removes the system prompt we write for the CLI agents.

Claude Tag wrote the first pass at this (previously #180, now closed).

Problem

Installing a skill means copying its folder somewhere an agent will look, and each agent reads a different directory.

  • skills add takes a flag naming which agents to install for, but we weren't not passing it.
  • Without that flag, the CLI tries to detect an installed agent, and when it cannot, it installs for all 71 it knows about.
  • So every sandbox got ~52 folders it had no use for: .aider-desk, .factory, .kilocode, .windsurf, .zencoder, and non-dotted data/ and skills/ among them. Each holds a copy of the same two Supabase skills.
  • It also didn't install Codex skills into the native .agents/skills folder, which required us to include an addendum to the Codex prompt to get it to look in the Claude skills folder (.claude/skills).

Changes

Install

Name the three agents we run in this repo:

skills add '<dir>' --agent claude-code codex opencode --skill '*' --copy --yes

That writes two directories, .claude/skills and .agents/skills, instead of 52.

--copy is required so that .claude/skills gets created in an empty eval workspace. Without it only .agents/skills shows up.

After installing, installSkills confirms every skill is actually present in both directories (.claude/skills and .agents/skills). It used to check .claude/skills only.

Plumbing for the next PR

The prompt builders now take the agent as an argument and ignore it. This is used by #241 to skip the prompt text for the CLI agents.

How to review

Confirm the new install works:

pnpm --filter @supabase-evals/sandbox test:docker

If interested, run both install strategies side by side in a temp folder to see the difference:

mkdir -p /tmp/probe/stage/skills/demo /tmp/probe/old /tmp/probe/new
printf -- '---\nname: demo\ndescription: Demo.\n---\n\nBody.\n' > /tmp/probe/stage/skills/demo/SKILL.md

# old, no --agent
cd /tmp/probe/old
env -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINT npx -y skills@1.5.11 add /tmp/probe/stage --skill '*' --copy --yes
ls -A

# new, three agents named
cd /tmp/probe/new
env -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINT npx -y skills@1.5.11 add /tmp/probe/stage --agent claude-code codex opencode --skill '*' --copy --yes
ls -A

You should see 53 entries (old) vs. 3 (new).

Follow up tasks

  • Remove skills-lock.json from the workspace after installing. The CLI drops it in the root and it ends up in the exported workspace, same as before this change.
  • Assert the full set of workspace directories in the docker test. It currently samples five of the ~52 the old fallback created, so it catches that fallback firing but does not prove nothing else was written.
  • Add a check that each agent actually surfaces the skills it finds. Both this PR and fix(framework): remove the harness system prompt for CLI agents (AI-1034) #241 assume it, and nothing tests it.

Ref AI-1034

Skills were installed with a flagless `skills add`, which — finding no
agent CLI installed yet — falls back to every one of the 71 agents the CLI
knows. That scattered ~53 stray roots across the workspace (`.adal`,
`.factory`, and non-dotted `data/` and `skills/` among them), and the
workspace is exported into run artifacts and scored. It is also
order-dependent: had an agent CLI been installed first, the fallback would
have quietly stopped producing `.claude/skills` altogether.

`skills add` now names the three CLI harnesses explicitly, which installs
into exactly the two project scopes they discover natively:

- `.claude/skills/` — Claude Code
- `.agents/skills/` — Codex and OpenCode

This is the actual fix for Codex, which does not read `.claude/skills` at
all and therefore saw no skills in any eval. All three are installed
unconditionally: the ids collapse to two directories, an unused copy costs
a few kilobytes, and no agent id has to be threaded through
`createAgentEnvironment` for correctness. Argument order matters —
`--agent` is variadic, so the source directory must precede it and
`--skill` terminates the list. `--copy` stays: symlink mode skips agents
whose top-level directory does not already exist.

The post-install check now verifies every agent scope, so a skill missing
from one of them fails loudly instead of leaving that harness silently
skill-less.

The session's harness id is threaded through to the two prompt-addendum
builders (`buildSkillsPrompt`, `buildToolSurfaceAddendum`, the latter
lifted out of `startSession` so it is testable), but neither branches on
it yet: every harness still gets the same injected text as before. What
each one should actually be told is a separate change.

Refs AI-1034, #164
Review fixes on the explicit-agent install:

- The no-`--agent` fallback does reach both `.claude/skills` and
  `.agents/skills`, so it was never the reason skills failed to arrive.
  It is a pollution and determinism problem: ~52 stray roots in the
  scored workspace, and detection that depends on the environment. The
  order-dependence the comment claimed does not reproduce on 1.5.11.
- `LocalStackSessionArgs.agent` said the addendum builders tailor their
  text to it. They take it and ignore it here.
- Quote the staging dir in the install command.
- The docker test checks a sample of the fallback's stray roots, not the
  whole set. Say so, and include the non-dotted ones.
- README described the injected-listing path as though it were the
  native one.
The README claimed the harness does not describe installed skills. It
still does, for every harness, and this change's own unit test pins that.
@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
evals Ignored Ignored Aug 25, 2026 10:17pm

Request Review

* Detection depends on the surrounding environment, so naming the agents is
* what makes the install predictable.
*/
export const SKILLS_INSTALL_AGENTS = [

@mattrossman mattrossman Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When someone tries to add a new harness elsewhere in the repo, can we make this fail loudly if they forget to update it, instead of silently running evals w/o skills?

An exhaustive Record<AgentHarnessId, string | null> could do it, since the runner relies on the same id. SKILLS_INSTALL_AGENTS and SKILLS_INSTALL_DIRS could then be derived from it.

E.g.

const SKILLS_PATH_BY_AGENT: Record<AgentHarnessId, string | null> = {
  'ai-sdk': null,
  'claude-code': '.claude/skills',
  codex: '.agents/skills',
  opencode: '.agents/skills',
};

export const SKILLS_INSTALL_AGENTS: AgentHarnessId[] = [];
export const SKILLS_INSTALL_DIRS: string[] = [];
for (const id of agentHarnessIdSchema.options) {
  const path = SKILLS_PATH_BY_AGENT[id];
  if (path === null) continue;
  SKILLS_INSTALL_AGENTS.push(id);
  if (!SKILLS_INSTALL_DIRS.includes(path)) SKILLS_INSTALL_DIRS.push(path);
}

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.

2 participants