Summary
The orchestrator currently installs five app-managed skills into every agent's user skills home on startup (server/lib/managed-skills.ts):
browser
integrations
controller-scripts (already partial prefix)
search-skills
skill-creator
These skills are agent-facing: they tell the running agent how to drive the preview browser, use the integrations gateway, generate per-worktree setup/run scripts, search the unified skill catalog, and create new unified skills. They are not intended to be invoked by the user via /<name>.
We want to:
- Rename them with a consistent
controller- prefix so it is obvious from the name alone that they are app-managed, and so they are grouped together in the / picker and the filesystem.
- Hide controller-managed skills from the
/ slash picker so they are not surfaced as user-invokable commands. The agent continues to discover them through the same filesystem location (so the body is still prepended when relevant), but the user does not see them in the autocomplete.
Motivation
- Discoverability for users. Right now a user types
/ and sees /browser, /integrations, /controller-scripts, /search-skills, /skill-creator mixed in with their own per-agent and unified skills. They have no visual cue that these are app-managed, and there is nothing meaningful the user would do by typing them — the agent invokes them via read_body at runtime, not via the slash picker.
- Grouping for the agent. A consistent prefix lets the agent (and the orchestrator) tell at a glance which skills come from the app and which come from the user, without parsing the
MANAGED_MARKER HTML comment.
- Cleaner picker. The
/ picker is a small UI surface. Today, on a fresh install, half the visible items are controller-managed. Hiding them keeps the picker focused on skills the user actually wants to invoke.
Current state
server/lib/managed-skills.ts writes each skill into ~/.anita/skills/<name>/SKILL.md, ~/.codex/skills/<name>/SKILL.md, and ~/.claude/skills/<name>/SKILL.md on startup, tagged with MANAGED_MARKER (<!-- managed-by: coding-orchestrator (issue #109) -->).
server/lib/skills.ts mergeSkillMetadata already surfaces unified skills first and dedupes per-agent skills by name, but it does not distinguish controller-managed per-agent skills from user-authored ones — both render in the picker with scope === "user".
client/src/pages/SessionView.tsx fetchAgentSkills returns the merged list and renders every entry under the / autocomplete, with a scope badge but no managed-vs-user distinction.
server/lib/skills.ts extractSkillInvocation accepts any leading /<name> token and server/routes/sessions.ts will activate it server-side. There is currently no filtering of managed skills from the slash path; a user can type /browser today and it will activate the body. We want to keep the agent-facing body available (the body is what tells the agent how to use the integration/browser/catalog), but stop the user from triggering it themselves via the picker.
Proposed design
1. Rename to a consistent controller- prefix
In server/lib/managed-skills.ts, rename the installed skill directories to:
browser → controller-browser
integrations → controller-integrations
controller-scripts → controller-controller-scripts (or drop the redundant controller- and use controller-scripts, but that breaks the renaming. Recommend: controller-controller-scripts to keep the rule mechanical — "all controller-managed skills start with controller-". Alternatively, accept controller-scripts as the canonical name and document it as the only exception. See open questions.)
search-skills → controller-search-skills
skill-creator → controller-skill-creator
Update the name field inside each SKILL.md body to match the new directory name so extractSkillInvocation and the per-agent skill loader (server/lib/skills.ts createDiskProvider) see consistent names.
Update each skill body to reference the new name where it documents itself ("name", "How to use it", examples that mention /name).
2. Add a managed flag (server) and use it to filter the picker
Extend SkillMetadata (in server/lib/skills.ts) with an optional managed?: boolean field, or extend SkillScope with "managed". (See open question — prefer extending Scope so the existing picker rendering and dedupe logic does not need a parallel path.)
When the disk provider reads a SKILL.md whose body contains MANAGED_MARKER, mark the resulting SkillMetadata with scope: "managed" instead of "user".
mergeSkillMetadata keeps managed skills discoverable for body lookup (the agent still needs to be able to readBody them), but the client filters them out of the / picker:
client/src/pages/SessionView.tsx filteredSkills excludes entries with scope === "managed" before rendering the popover.
3. Hardening on the slash path
Even though the picker is the primary surface, a user can still type /controller-browser manually and submit. Decide on one of:
- Allow it. The body is harmless guidance for the agent;
server/routes/sessions.ts continues to prepend it. This is the simplest path and matches how all per-agent skills work today.
- Reject it server-side.
server/routes/sessions.ts resolveSkillActivation returns a 400 for any skillName whose metadata has scope === "managed" on the active provider.
Recommend allowing it for v1 (matches the "the agent can still read the body" invariant) and revisit only if a managed body turns out to be confusing as a slash-command prefix.
4. Update the managed MANAGED_MARKER constant
The current marker comment references "issue #109", but the managed-skill installer has been extended multiple times since (#134, #146, #157, etc.). Update the marker string to reference this issue so future renames and unowned files can be detected accurately.
Open questions
- Naming for
controller-scripts. Two options:
controller-controller-scripts — mechanically consistent with the controller- rule.
controller-scripts is grandfathered because it was the first one shipped and the name is already stable.
Lean: option 2 keeps the public name stable but option 1 keeps the rule simple. Need user input.
SkillScope extension vs. new managed boolean. The boolean is additive and doesn't disturb existing consumers; the new scope value is cleaner but forces an update everywhere scope is matched (skills-section.tsx, the picker badge, dedupe). Lean: new scope value, but check all consumers in the PR.
- Hide on all surfaces, or just the picker?
skills-section.tsx in Settings only lists unified skills today, so managed skills aren't visible there regardless. Confirm the picker is the only surface to touch.
Acceptance criteria
Related
Summary
The orchestrator currently installs five app-managed skills into every agent's user skills home on startup (
server/lib/managed-skills.ts):browserintegrationscontroller-scripts(already partial prefix)search-skillsskill-creatorThese skills are agent-facing: they tell the running agent how to drive the preview browser, use the integrations gateway, generate per-worktree setup/run scripts, search the unified skill catalog, and create new unified skills. They are not intended to be invoked by the user via
/<name>.We want to:
controller-prefix so it is obvious from the name alone that they are app-managed, and so they are grouped together in the/picker and the filesystem./slash picker so they are not surfaced as user-invokable commands. The agent continues to discover them through the same filesystem location (so the body is still prepended when relevant), but the user does not see them in the autocomplete.Motivation
/and sees/browser,/integrations,/controller-scripts,/search-skills,/skill-creatormixed in with their own per-agent and unified skills. They have no visual cue that these are app-managed, and there is nothing meaningful the user would do by typing them — the agent invokes them viaread_bodyat runtime, not via the slash picker.MANAGED_MARKERHTML comment./picker is a small UI surface. Today, on a fresh install, half the visible items are controller-managed. Hiding them keeps the picker focused on skills the user actually wants to invoke.Current state
server/lib/managed-skills.tswrites each skill into~/.anita/skills/<name>/SKILL.md,~/.codex/skills/<name>/SKILL.md, and~/.claude/skills/<name>/SKILL.mdon startup, tagged withMANAGED_MARKER(<!-- managed-by: coding-orchestrator (issue #109) -->).server/lib/skills.tsmergeSkillMetadataalready surfaces unified skills first and dedupes per-agent skills by name, but it does not distinguish controller-managed per-agent skills from user-authored ones — both render in the picker withscope === "user".client/src/pages/SessionView.tsxfetchAgentSkillsreturns the merged list and renders every entry under the/autocomplete, with a scope badge but no managed-vs-user distinction.server/lib/skills.tsextractSkillInvocationaccepts any leading/<name>token andserver/routes/sessions.tswill activate it server-side. There is currently no filtering of managed skills from the slash path; a user can type/browsertoday and it will activate the body. We want to keep the agent-facing body available (the body is what tells the agent how to use the integration/browser/catalog), but stop the user from triggering it themselves via the picker.Proposed design
1. Rename to a consistent
controller-prefixIn
server/lib/managed-skills.ts, rename the installed skill directories to:browser→controller-browserintegrations→controller-integrationscontroller-scripts→controller-controller-scripts(or drop the redundantcontroller-and usecontroller-scripts, but that breaks the renaming. Recommend:controller-controller-scriptsto keep the rule mechanical — "all controller-managed skills start withcontroller-". Alternatively, acceptcontroller-scriptsas the canonical name and document it as the only exception. See open questions.)search-skills→controller-search-skillsskill-creator→controller-skill-creatorUpdate the
namefield inside eachSKILL.mdbody to match the new directory name soextractSkillInvocationand the per-agent skill loader (server/lib/skills.tscreateDiskProvider) see consistent names.Update each skill body to reference the new name where it documents itself ("name", "How to use it", examples that mention
/name).2. Add a
managedflag (server) and use it to filter the pickerExtend
SkillMetadata(inserver/lib/skills.ts) with an optionalmanaged?: booleanfield, or extendSkillScopewith"managed". (See open question — prefer extendingScopeso the existing picker rendering and dedupe logic does not need a parallel path.)When the disk provider reads a
SKILL.mdwhose body containsMANAGED_MARKER, mark the resultingSkillMetadatawithscope: "managed"instead of "user".mergeSkillMetadatakeepsmanagedskills discoverable for body lookup (the agent still needs to be able toreadBodythem), but the client filters them out of the/picker:client/src/pages/SessionView.tsxfilteredSkillsexcludes entries withscope === "managed"before rendering the popover.3. Hardening on the slash path
Even though the picker is the primary surface, a user can still type
/controller-browsermanually and submit. Decide on one of:server/routes/sessions.tscontinues to prepend it. This is the simplest path and matches how all per-agent skills work today.server/routes/sessions.tsresolveSkillActivationreturns a 400 for anyskillNamewhose metadata hasscope === "managed"on the active provider.Recommend allowing it for v1 (matches the "the agent can still read the body" invariant) and revisit only if a managed body turns out to be confusing as a slash-command prefix.
4. Update the managed
MANAGED_MARKERconstantThe current marker comment references "issue #109", but the managed-skill installer has been extended multiple times since (#134, #146, #157, etc.). Update the marker string to reference this issue so future renames and unowned files can be detected accurately.
Open questions
controller-scripts. Two options:controller-controller-scripts— mechanically consistent with thecontroller-rule.controller-scriptsis grandfathered because it was the first one shipped and the name is already stable.Lean: option 2 keeps the public name stable but option 1 keeps the rule simple. Need user input.
SkillScopeextension vs. newmanagedboolean. The boolean is additive and doesn't disturb existing consumers; the new scope value is cleaner but forces an update everywherescopeis matched (skills-section.tsx, the picker badge, dedupe). Lean: new scope value, but check all consumers in the PR.skills-section.tsxin Settings only lists unified skills today, so managed skills aren't visible there regardless. Confirm the picker is the only surface to touch.Acceptance criteria
controller--prefixed directory in each provider home (~/.anita/skills,~/.codex/skills,~/.claude/skills).SKILL.mdbody'sname:frontmatter matches the directory name./picker in the chat composer no longer lists controller-managed skills.MANAGED_MARKERconstant is updated to reference this issue.controller-browser,controller-integrations); filesystem discovery is unchanged.mergeSkillMetadataincludes managed entries for body lookup.fetchAgentSkillspayloads exposescope: "managed"for the renamed skills.skills.test.tsor add a new picker test).extractSkillInvocationstill matches the new names.[Unreleased]describes the rename and the picker change./<skill-name>(if any) is updated; existing per-agent and unified skills are unaffected.Related
browsermanaged skill andMANAGED_MARKER.controller-scripts.skill-creatormanaged skill.