feat: gbot skills list/add/remove for the shared skill library - #91
Conversation
Grok Bot keeps skills per bot behind the gateway, not in a plugin directory. These commands drive getAgentWorkflows, importAgentWorkflowText, and deleteAgentWorkflow so a skill lands on exactly one bot id.
🦋 Changeset detectedLatest commit: f6f6ca6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b95dba0ba6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| source: skill.source || "user", | ||
| ...(skill.sourceRef ? { sourceRef: String(skill.sourceRef) } : {}), | ||
| ...(skill.pluginId ? { pluginId: String(skill.pluginId) } : {}), |
There was a problem hiding this comment.
Fail closed when skill ownership metadata is absent
When a gateway response omits source but still identifies a managed workflow through pluginId or sourceRef, this fallback classifies it as user; removeAgentSkill then passes its ownership guard and calls deleteAgentWorkflow. Since the command promises to refuse plugin and team-managed skills, preserve an unknown source or infer managed ownership from those fields so deletion fails closed.
Useful? React with 👍 / 👎.
| export default async function skillsList({ input }: CliRouteProps<typeof inputSchema>) { | ||
| const backend = await openBackendFromInput(input); | ||
| const skills = resultSchema.parse(await backend.skills(input.ref)); | ||
| const lines = skills.map((s) => `${s.id} ${s.name} [${s.source}]${s.description ? ` ${s.description}` : ''}`); |
There was a problem hiding this comment.
Sanitize skill metadata before terminal rendering
A skill name, source, or description containing terminal control sequences is written directly to the terminal here. These fields can originate in imported SKILL.md frontmatter or marketplace/team skills, so listing such a skill can execute ANSI/OSC effects rather than display literal metadata; strip terminal controls before constructing the text output, as other externally supplied text renderers in this repository do.
Useful? React with 👍 / 👎.
The harness keeps one GlobalSkillLibrary per box and reports library skills as source "workflow". Drop the bot positional, accept "workflow" on remove, refuse bodies over 100k before posting, and say the import may have landed when the echoed list exceeds the response cap.
Why
Plugin Library "Apply to bot…" returns
missing_attach_apibecausegbothad no way to put a skill into Grok Bot at all (ScriptedAlchemy/agent-bundle#804, #803). The harness keeps one skill library per box (GlobalSkillLibrary(<sandRoot>/workflows),conversation-outline.ts:774); every bot reads the same list and there is no per-bot attach. The gateway already routesgetAgentWorkflows,importAgentWorkflowText, anddeleteAgentWorkflowatPOST /api/<method>, the transportgbot bots updateuses. The workflow RPCs take a bot id only to say whose automations ride along, so the CLI resolves any bot internally.Scope
gbot skills list: the library with id, name, and wiresource(workflow= yours,managed,plugin,automation).gbot skills add <SKILL.md|dir>: posts the markdown. Refuses empty or over-100k bodies before posting (the harness truncates silently). A gateway skip surfaces with its reason. If the echoed list exceeds the 2 MiB response cap, the error says the import may have landed and to re-list.gbot skills remove <id|name>: deletes aworkflowskill for every bot. Refusesmanaged,plugin,automation, unknown, and ambiguous names before any delete.skills requires the live gateway, likesendandthread.Review history
The first revision claimed per-bot scoping and gated
removeonsource === "user". Independent review against the harness source found both wrong (library is global; the wire value isworkflow), so this revision drops the bot positional and the--nameflag (dead: the harness derives names from frontmatter or the first heading).Verification
npm run checkgreen on top ofmainat e5b5c87: validate, build, artifact validate, typecheck, 405 unit tests (12 new intest/gateway-skills.test.js, fixtures use the harness's wire shapes) and route tests. Smoke through the builtdist/bin/gbot.mjsagainst a fake gateway with a group first in the roster: add routed through the bot, list showed[workflow]/[plugin], remove refused the plugin skill and deleted the library skill. No live gateway on this machine (app not running).