Skip to content

fix: render extension skills for target agent, not just active agent …#3599

Open
Nimraakram22 wants to merge 1 commit into
github:mainfrom
Nimraakram22:fix/2948-agent-aware-skill-rendering
Open

fix: render extension skills for target agent, not just active agent …#3599
Nimraakram22 wants to merge 1 commit into
github:mainfrom
Nimraakram22:fix/2948-agent-aware-skill-rendering

Conversation

@Nimraakram22

Copy link
Copy Markdown
Contributor

Fixes #2948.

Problem

Extension skill rendering was scoped to the active agent (init-options.json's ai field) even when registering extensions for a different, non-active agent — e.g. after specify integration install copilot --integration-options "--skills" while Claude remained active, or specify integration upgrade copilot. The target agent received command files instead of SKILL.md files.

Root cause

ExtensionManager._get_skills_dir() and _register_extension_skills() always resolved the skills directory/settings from the active agent in init-options.json, ignoring the agent_name argument passed to register_enabled_extensions_for_agent().

Fix

  • Added is_agent_skills_enabled() to resolve per-agent skills-mode from .specify/integration.json's integration_settings[agent].parsed_options.skills, falling back to the legacy global ai_skills flag for the active agent.
  • Threaded agent_name through _get_skills_dir() and _register_extension_skills().
  • register_enabled_extensions_for_agent() now computes skills_mode_active per the target agent, and always attempts skill registration for that agent (matching prior best-effort error handling).

Testing

  • Manually verified: specify integration upgrade copilot --force with a non-active, skills-mode Copilot integration now renders .github/skills/speckit-git-*/SKILL.md files instead of command files, with no regression to the active agent's own skills.
  • pytest tests/ -k extension: 616/616 pass.
  • Full suite: 44 pre-existing failures unrelated to this change (Windows symlink-privilege and PowerShell path-resolution issues — confirmed present on main without this change).

…ithub#2948)

Extension skill rendering was previously scoped only to the active
agent (init-options.json's 'ai' field), even when
register_enabled_extensions_for_agent was called for a different,
non-active agent (e.g. after 'integration install <agent>
--skills' or 'integration upgrade <agent>' for a secondary
integration). This meant a skills-mode agent that wasn't the
active one received command files instead of skills.

Changes:
- Add is_agent_skills_enabled() in _init_options.py: resolves
  per-agent skills-mode from .specify/integration.json's
  integration_settings[agent].parsed_options.skills, falling back
  to the legacy global init-options ai_skills flag for the active
  agent.
- ExtensionManager._get_skills_dir() now accepts an optional
  agent_name and resolves the skills directory for that specific
  agent instead of always using the active agent.
- ExtensionManager._register_extension_skills() now accepts and
  threads through agent_name.
- register_enabled_extensions_for_agent() now computes
  skills_mode_active per the target agent_name (not the active
  agent), and always attempts skill registration for that agent
  (matching the prior best-effort error handling), while still
  skipping duplicate command-file registration when the target is
  in skills mode.

Verified manually: 'specify integration upgrade copilot --force'
with a non-active, skills-mode Copilot integration now renders
.github/skills/speckit-git-*/SKILL.md files instead of command
files, with no change to the active agent's own skills.

Test suite: 616/616 extension-related tests pass. Full suite has
44 pre-existing failures unrelated to this change (Windows
symlink-privilege and PowerShell path-resolution issues,
confirmed present on main via git stash).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Makes extension skill rendering aware of the target integration rather than only the active agent.

Changes:

  • Resolves per-agent skills mode from integration state.
  • Threads target agent through skill-directory resolution and rendering.
  • Updates extension re-registration behavior and documentation.
Show a summary per file
File Description
src/specify_cli/_init_options.py Adds per-agent skills-mode resolution.
src/specify_cli/extensions/__init__.py Targets extension skill generation by agent.
src/specify_cli/integrations/_helpers.py Updates registration behavior documentation.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (2)

src/specify_cli/extensions/init.py:980

  • The target-agent branch bypasses the repository's safe directory creation. resolve_agent_skills_dir only constructs a path, and _ensure_usable follows symlinked parents with mkdir; _register_extension_skills then writes SKILL.md beneath it. A project with (for example) a symlinked .github/skills can therefore make registration write outside the project. Route this path through _ensure_safe_shared_directory as resolve_active_skills_dir does (see shared_infra.py:163-196) before writing.
                skills_dir = resolve_agent_skills_dir(self.project_root, agent_name)

src/specify_cli/extensions/init.py:1856

  • registered_skills is still a single global list of names, so merging a second agent's identically named skills loses which directories own them. For example, after Claude and non-active Copilot both render speckit-git-feature, uninstalling either agent removes its copy and clears the list in unregister_agent_artifacts; the other copy remains but is no longer tracked, so later extension removal leaves it orphaned. Track generated skills per agent (with legacy-list compatibility) before enabling multi-agent skill rendering.
                        existing_skills = self._valid_name_list(
                            metadata.get("registered_skills", [])
                        )
                        merged_skills = list(
                            dict.fromkeys(existing_skills + registered_skills)
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment on lines +919 to +921
works correctly for non-active agents (#2948). When omitted,
falls back to the previous behaviour of using the active agent
from init-options.
return isinstance(opts, Mapping) and opts.get("ai_skills") is True


def is_agent_skills_enabled(project_path: Path, agent_name: str, opts: Mapping[str, Any] | None) -> bool:

@mnriem mnriem left a comment

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.

Please address Copilot feedback and address test & lint errors

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.

Extension commands aren't rendered as skills for non-active skills-mode agents on add/install/upgrade

4 participants