Skip to content

Conversation

@IgorWarzocha
Copy link
Owner

Summary

Adds a foldable "Skills" section to the sidebar (both web and TUI) that displays available skills filtered by the currently selected agent's permissions.

Changes

  • Web Sidebar: Added foldable skills section in packages/app/src/pages/layout.tsx
  • TUI Sidebar: Added skills section in packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
  • Agent-based Filtering: Skills automatically reload when agent selection changes
  • Skill API: New /skill endpoint in packages/opencode/src/server/skill.ts with permission-based filtering
  • Permission System: Fixed specificity ordering in PermissionNext.evaluate() for correct skill filtering

Technical Details

  • Skills are filtered using agent permissions via PermissionNext.evaluate()
  • Skills reload when the current agent changes via globalSync.skill.setSelectedAgent()
  • TUI sync includes skills in the data model
  • Fixed permission evaluation to prioritize exact matches over glob patterns

Files Modified

  • packages/app/src/pages/layout.tsx - Skills section UI
  • packages/app/src/context/global-sync.tsx - Skill loading API
  • packages/app/src/context/local.tsx - Agent change detection
  • packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx - TUI skills section
  • packages/opencode/src/server/skill.ts - New skill endpoint
  • packages/opencode/src/permission/next.ts - Specificity fix
  • packages/opencode/src/cli/cmd/tui/context/sync.tsx - Skill sync support
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx - Agent-based skill loading

@gemini-code-assist
Copy link

Summary of Changes

Hello @IgorWarzocha, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the application's user experience by integrating a new "Skills" section into the sidebar across both web and TUI interfaces. This feature allows users to easily discover and access skills relevant to their current context, driven by an intelligent agent-based filtering system. The backend now includes a dedicated "/skill" API for managing and serving these filtered skills, supported by a crucial fix to the permission evaluation system that ensures accurate and specific access control.

Highlights

  • Skills Section UI: Introduced a new, foldable "Skills" section in both the web and TUI sidebars, allowing users to view available skills.
  • Agent-Based Skill Filtering: Implemented dynamic filtering of displayed skills based on the currently selected agent's permissions, ensuring only relevant skills are shown.
  • New Skill API Endpoint: Added a "/skill" API endpoint to the backend, responsible for fetching and filtering skills according to agent and session permissions.
  • Permission Specificity Fix: Corrected the PermissionNext.evaluate() logic to properly prioritize permission rules, ensuring that exact matches take precedence over glob patterns and wildcards.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new "Skills" section to both the web and TUI sidebars, which displays skills filtered by the current agent's permissions. This is supported by a new /skill API endpoint and an important fix to the permission evaluation logic. The changes are well-implemented across the frontend and backend. I have a couple of suggestions to improve efficiency and code readability.

Comment on lines 152 to 155
function setSelectedAgent(directory: string, agent: string) {
setGlobalStore("selected_agent", directory, agent)
loadSkills(directory, undefined, agent)
}

Choose a reason for hiding this comment

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

medium

The loadSkills call within setSelectedAgent is redundant. The only caller of this function, in packages/app/src/context/local.tsx, immediately calls globalSync.skill.load again with more specific parameters (including sessionID). This results in two network requests to load skills, where the second one's result overwrites the first. Removing this call will improve efficiency and make the function's behavior align better with its name (i.e., only setting the agent, without the side effect of loading data).

  function setSelectedAgent(directory: string, agent: string) {
    setGlobalStore("selected_agent", directory, agent)
  }

Comment on lines 33 to 40
const agentName =
(agentParam && agentParam !== "undefined" && agentParam !== "" ? agentParam : undefined) ??
(await (async () => {
if (!sessionID) return undefined
const messages = await Session.messages({ sessionID, limit: 10 })
return messages.findLast((m) => m.info.role === "user")?.info.agent
})()) ??
(await Agent.defaultAgent())

Choose a reason for hiding this comment

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

medium

This logic for determining agentName is a bit complex and hard to read due to the chained nullish coalescing operators (??) and an immediately-invoked async function expression (IIAFE). Refactoring this into a more straightforward async IIAFE would greatly improve readability and maintainability.

Suggested change
const agentName =
(agentParam && agentParam !== "undefined" && agentParam !== "" ? agentParam : undefined) ??
(await (async () => {
if (!sessionID) return undefined
const messages = await Session.messages({ sessionID, limit: 10 })
return messages.findLast((m) => m.info.role === "user")?.info.agent
})()) ??
(await Agent.defaultAgent())
const agentName = await (async () => {
if (agentParam && agentParam !== "undefined" && agentParam !== "") {
return agentParam;
}
if (sessionID) {
const messages = await Session.messages({ sessionID, limit: 10 });
const agentFromSession = messages.findLast((m) => m.info.role === "user")?.info.agent;
if (agentFromSession) {
return agentFromSession;
}
}
return Agent.defaultAgent();
})();

OpenCode Agent added 2 commits January 4, 2026 15:14
Remove redundant loadSkills call and simplify agentName resolution logic.
@IgorWarzocha
Copy link
Owner Author

/oc verify this is all sound

@opencode-agent
Copy link

opencode-agent bot commented Jan 4, 2026

Based on my review of the code, the implementation is sound. Here's my analysis:

✅ Correct Implementation

Web UI (packages/app/src/pages/layout.tsx:970-998)

  • Skills section displays correctly with foldable behavior
  • Uses globalSync.data.skill for data source
  • Proper conditional rendering when skills exist

TUI UI (packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx:161-185)

  • Consistent with web implementation
  • Skills loaded from sync.data.skill
  • Foldable design matching other sidebar sections

TUI Skill Loading (packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:250-257)

  • Reactively loads skills when agent changes
  • Passes sessionID and agent to skill endpoint

Skill Endpoint (packages/opencode/src/server/skill.ts)

  • Correctly determines agent from param, session, or default
  • Merges agent and session permissions
  • Filters skills using PermissionNext.evaluate()
  • Returns non-denied skills

Permission Fix (packages/opencode/src/permission/next.ts:220-242)

  • Specificity ordering is correct: * (0) < glob (1) < exact (2)
  • Uses stable sort pattern via score comparison
  • Returns most specific match

Web Integration (packages/app/src/context/local.tsx:77-83)

  • Agent change triggers both setSelectedAgent and loadSkills
  • This is necessary because loadSkills requires sessionID from route params

Note on Reviewer Comments

The reviewer mentioned a redundant loadSkills call in setSelectedAgent, but the current code at global-sync.tsx:152-154 shows:

function setSelectedAgent(directory: string, agent: string) {
  setGlobalStore("selected_agent", directory, agent)
}

There is no redundant call - the reviewer was likely looking at an earlier version or there's been a fix.

The reviewer also suggested refactoring the IIAFE in skill.ts:33-45, but the current implementation is clear and functional. This is a style preference, not a correctness issue.

The PR is ready to merge.

New%20session%20-%202026-01-04T15%3A19%3A43.287Z
opencode session  |  github run

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