fix(sandbox): install skills natively per harness (AI-1034) - #240
fix(sandbox): install skills natively per harness (AI-1034)#240seanoliver wants to merge 5 commits into
Conversation
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
| * Detection depends on the surrounding environment, so naming the agents is | ||
| * what makes the install predictable. | ||
| */ | ||
| export const SKILLS_INSTALL_AGENTS = [ |
There was a problem hiding this comment.
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);
}There was a problem hiding this comment.
Done: SKILLS_PATH_BY_AGENT is the single source and both lists derive from it. A new harness id won't compile until its scope is declared, and the error names the missing harness.
SKILLS_INSTALL_DIR reads from the same map, so the prompt listing can't drift from the install.
…kills-install # Conflicts: # apps/framework/harness/run-eval.ts
`SKILLS_INSTALL_AGENTS` and `SKILLS_INSTALL_DIRS` were two hand-maintained lists. Adding a harness meant remembering both; forgetting one left that harness running evals with no skills installed, silently. Both are now derived from `SKILLS_PATH_BY_AGENT`, a `Record<AgentHarnessId, string | null>`. The record is exhaustive by type, so adding an id to `agentHarnessIdSchema` fails to compile until its project scope is declared. Verified: adding a fifth id errors with TS2741 naming the missing harness. `SKILLS_INSTALL_DIR` now reads from the same map rather than repeating `.claude/skills`, so the prompt listing cannot drift from the install either. Refs AI-1034
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 addtakes a flag naming which agents to install for, but we weren't not passing it..aider-desk,.factory,.kilocode,.windsurf,.zencoder, and non-dotteddata/andskills/among them. Each holds a copy of the same two Supabase skills..agents/skillsfolder, 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:
That writes two directories,
.claude/skillsand.agents/skills, instead of 52.--copyis required so that.claude/skillsgets created in an empty eval workspace. Without it only.agents/skillsshows up.After installing,
installSkillsconfirms every skill is actually present in both directories (.claude/skillsand.agents/skills). It used to check.claude/skillsonly.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:
If interested, run both install strategies side by side in a temp folder to see the difference:
You should see 53 entries (old) vs. 3 (new).
Follow up tasks
skills-lock.jsonfrom the workspace after installing. The CLI drops it in the root and it ends up in the exported workspace, same as before this change.Ref AI-1034