Skip to content

fix(presets): create skill overrides when skills_source is marketplace#2905

Open
echarrod wants to merge 2 commits into
github:mainfrom
echarrod:fix/preset-skills-marketplace-source
Open

fix(presets): create skill overrides when skills_source is marketplace#2905
echarrod wants to merge 2 commits into
github:mainfrom
echarrod:fix/preset-skills-marketplace-source

Conversation

@echarrod

@echarrod echarrod commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Bug: When a project configured "skills_source": "marketplace" in .specify/integration.json and "ai_skills": false in init-options.json, running specify preset add <preset> appeared to succeed (registry updated, files placed in .specify/presets/) but silently skipped writing the local .claude/skills/<skill-name>/SKILL.md override file.
  • Root cause: Two guards in PresetManager both keyed on ai_skills_enabled:
    1. _get_skills_dir() returned None early for non-kimi agents when ai_skills was false, so _register_skills() bailed before reaching the create_missing_skills check.
    2. create_missing_skills was itself gated on ai_skills_enabled, so even when a skills dir existed the new skill file would not be created.
  • Fix: Both checks now read skills_source from integration.json and treat ai_skills as effectively enabled when skills_source == "marketplace". This enables the local SKILL.md to shadow the marketplace plugin version, which is the whole point of a preset override in a marketplace-integrated project.

Fixes #2906

Test plan

  • New test TestPresetSkills::test_skill_created_when_ai_skills_false_but_marketplace_source covers the exact failure scenario: ai_skills: false + skills_source: marketplace + preset with a type: command entry → SKILL.md IS created.
  • All existing TestPresetSkills tests (19 total) continue to pass - ai_skills: false without skills_source: marketplace is unchanged.
  • test_skill_not_updated_when_ai_skills_disabled confirms the non-marketplace ai_skills: false behaviour is preserved.

🤖 Generated with Claude Code

echarrod and others added 2 commits June 9, 2026 13:15
/analyze is explicitly read-only and produces a compact analysis
report from heavy artefact reads (spec.md, plan.md, tasks.md). It
matches the canonical use case for context: fork — bulk inputs that
collapse to a short summary, no need for conversation history.

Forking keeps the artefact contents out of the main conversation
context, which is the concern raised in github#752.

Done as a per-command opt-in via FORK_CONTEXT_COMMANDS so other
spec-kit commands (which are interactive or have side effects) are
unaffected.

Refs github#752
When a project had `"skills_source": "marketplace"` in integration.json
alongside `"ai_skills": false` in init-options.json, `_register_skills()`
silently skipped writing the local SKILL.md override.  Two guards both
needed relaxing:

1. `_get_skills_dir()` returned None early for non-kimi agents when
   ai_skills was false, so _register_skills never reached the
   create_missing_skills check at all.
2. `create_missing_skills` was gated on `ai_skills_enabled`, so even
   if the skills dir existed the new skill file would not be created.

For marketplace-sourced projects a local SKILL.md is required to shadow
the marketplace plugin version.  Both checks now treat ai_skills as
effectively enabled when skills_source == "marketplace".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@echarrod echarrod force-pushed the fix/preset-skills-marketplace-source branch from d21c355 to 9a6557d Compare June 9, 2026 12:19
echarrod added a commit to luno/spec-kit-preset-jira that referenced this pull request Jun 9, 2026
Explains the manual skill override step needed for projects using
skills_source: marketplace, with a clear note to remove the file
once github/spec-kit#2905 is released.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mnriem mnriem requested a review from Copilot June 9, 2026 19:08

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

This PR fixes preset skill override generation for marketplace-integrated projects where .specify/integration.json sets skills_source: "marketplace" but .specify/init-options.json has ai_skills: false, ensuring local SKILL.md overrides are still written when installing presets. It also introduces Claude-specific per-command forked-subagent frontmatter injection (currently for speckit-analyze) and adds/updates tests covering both behaviors.

Changes:

  • Adjust PresetManager skill-dir resolution and missing-skill creation logic to treat ai_skills as effectively enabled when skills_source == "marketplace".
  • Add regression coverage ensuring preset installs still create local SKILL.md overrides in the marketplace + ai_skills: false scenario.
  • Add Claude integration support/tests for injecting context: fork + agent: general-purpose into speckit-analyze skill frontmatter.
Show a summary per file
File Description
src/specify_cli/presets.py Bypass ai_skills guard for marketplace projects so preset installs can still write local skill overrides.
src/specify_cli/integrations/claude/__init__.py Add per-command fork-context frontmatter injection for Claude skills (currently analyze).
tests/test_presets.py Add regression test for marketplace skills_source with ai_skills: false still creating SKILL.md.
tests/integrations/test_integration_claude.py Add tests verifying fork-context frontmatter injection is applied only to configured commands.

Copilot's findings

Tip

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

  • Files reviewed: 4/4 changed files
  • Comments generated: 2

Comment on lines +1120 to +1126
from . import load_init_options, _get_skills_dir as _module_get_skills_dir
opts = load_init_options(self.project_root) or {}
agent = opts.get("ai")
if not isinstance(agent, str) or not agent:
return None
skills_dir = _module_get_skills_dir(self.project_root, agent)
return skills_dir if skills_dir.is_dir() else None
Comment on lines +203 to +207
fork_config = FORK_CONTEXT_COMMANDS.get(stem)
if fork_config:
for key, value in fork_config.items():
updated = self._inject_frontmatter_flag(updated, key, value)

@mnriem

mnriem commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Hey @echarrod — thanks for the detailed write-up! I took a look but have some concerns.

The skills_source: "marketplace" feature doesn't exist. I searched main for skills_source across all Python, JSON, YAML, and Markdown files and got zero hits. There's no code that reads or writes a "skills_source": "marketplace" field in integration.json — not in the state schema, not in _write_integration_json, and not in any init flow.

The repro steps in #2906 also reference specify init --here --integration claude --ai claude, but --ai isn't a valid flag on specify init — only --integration exists.

Could you point me to where skills_source: "marketplace" gets written today? Without the upstream feature that produces this state, the fix guards against a condition that can't occur. If this is intended for a future feature, it should land after that feature ships.

PRs should deliver a single focused change. This PR bundles two unrelated things: a preset/marketplace fix and the context: fork injection for the analyze skill. The fork context change looks fine on its own and belongs in ClaudeIntegration, but it should be its own PR so each change can be reviewed, reverted, and bisected independently.

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.

Bug: preset skill overrides not written when skills_source is marketplace and ai_skills is false

3 participants