Skip to content

User-global custom skills/commands (with override precedence, like schemas) #757

Closed

Description

@askpatrickw

Feature: User-global custom skills/commands (override mechanism)

Summary

OpenSpec currently lets users customize project artifact workflows via schemas, but does not provide a way to add or override tool-specific commands/skills (such as /openspec-team-apply-change) without patching project code or package templates.

This feature would introduce OS-specific global directories (e.g. ~/.config/openspec/commands, ~/.config/openspec/skills), enabling a user to define custom commands/skills that OpenSpec overlays onto generated tool integrations. If a global version and a built-in version share a name, the user's version takes precedence. These global skills/commands would be placed in the appropriate tool-specific folders (.claude/commands, .claude/skills, etc.) when running openspec init or openspec update.

Motivation

  • Teams and individuals often need new commands or workflow skills (e.g., a new team orchestration action) without modifying package code or copying project files.
  • Built-in OpenSpec templates are not extensible, so custom commands/skills must be managed separately.
  • A global override mechanism—mirroring schemas—lets users safely customize, share, and shadow built-in commands.
  • This would improve portability, shareability, and maintainability for advanced workflows and integrations.

Acceptance Criteria

  • User can define custom skills/commands in OS-specific global directories such as ~/.config/openspec/skills or ~/.config/openspec/commands.
  • Global custom skills/commands are automatically copied and overlaid on top of generated built-in commands during openspec init/openspec update.
  • If a global skill/command matches the name of a built-in, the global version wins (shadowing).
  • Skills/commands are validated before installation to avoid breaking tool integrations.
  • CLI tooling supports listing, discovering, and editing global vs project vs package skills.

References & Design Details

  • Precedence: project-local overrides (future), then user-global, then package built-in. Same name = override.
  • Possible directory structure:
    • ~/.config/openspec/skills/<name>
    • ~/.config/openspec/commands/<name>
  • Would support use cases like /openspec-team-apply-change and user-specific customizations.
  • Background discussion (search for related issues)

Questions

  • How should deletion/masking work? (Explicit tombstone file? DisabledSkills config?)
  • Should future project-local overrides also exist, for even more granular control?

Request

Please consider implementing a user-global custom skills/commands mechanism for OpenSpec that overlays generated tool integrations, enabling extensible, maintainable customization for multi-tool teams.

Activity

  1. 21st-connector commented on Feb 24, 2026

    @21st-connector
    Contributor

    Task completed.

    The enrichment comment has been posted on issue #757. Here's a summary of what was included:

    Existing precedent: The codebase already has a three-tier schema override system (resolver.ts:63-91) with project-local > user-global > package built-in precedence, using XDG directories (global-config.ts:66-87). This pattern directly informs how skills/commands overrides should work.

    Current architecture: Skills/commands are generated from hardcoded TypeScript templates in src/core/templates/workflows/ with no external discovery mechanism. Key integration points for the feature are in skill-generation.ts, init.ts, and update.ts.

    Suggested approach: Mirror the schema resolver pattern -- add getUserSkillsDir()/getUserCommandsDir() functions, create a merge layer in getSkillTemplates() that discovers user-global overrides, and validate user files against existing interfaces.

    Related issues: Linked 6 related open issues including #752 (global install), #756 (user-level schemas), #689 (standard skills folder), #590 (packaged skills), #611 (context bloat), and #350 (custom paths).

    Design question input: Noted that the existing workflows array in GlobalConfig already provides a disable mechanism for built-in skills, and that the SKILL.md YAML frontmatter format could serve as the canonical format for user-defined skills.


    View full conversation

    Powered by 1Code

  2. 21st-connector commented on Feb 24, 2026

    @21st-connector
    Contributor

    Issue Enrichment: Codebase Analysis

    Thanks for the detailed proposal, @askpatrickw. Here's an analysis of the existing codebase to help inform implementation.


    Existing Precedent: Schema Override System

    OpenSpec already implements a three-tier precedence system for schemas that this feature would mirror. The resolution logic is in src/core/artifact-graph/resolver.ts:63-91:

    1. Project-local:  <projectRoot>/openspec/schemas/<name>/  (highest)
    2. User-global:    ~/.local/share/openspec/schemas/<name>/
    3. Package built-in: <npm-package>/schemas/<name>/          (lowest)
    

    Global directory paths are defined in src/core/global-config.ts:66-87 using XDG Base Directory Specification:

    • Linux/macOS: ~/.local/share/openspec/ (via XDG_DATA_HOME)
    • Windows: %LOCALAPPDATA%/openspec/

    The listSchemasWithInfo() function in resolver.ts:224-302 already tracks source: 'project' | 'user' | 'package' and which schemas shadow others. This pattern can be directly reused for skills/commands.


    Current Skills/Commands Architecture

    Skills and commands are generated from hardcoded TypeScript template functions in src/core/templates/workflows/ (11 workflows). There is no mechanism to discover or load external skill/command definitions.

    Key files involved in generation:

    File Role
    src/core/shared/skill-generation.ts getSkillTemplates(), getCommandContents(), generateSkillContent()
    src/core/command-generation/generator.ts generateCommands() using tool-specific adapters
    src/core/command-generation/registry.ts CommandAdapterRegistry - 24 tool adapters
    src/core/init.ts:499-597 generateSkillsAndCommands() - init flow
    src/core/update.ts:170-244 Update flow (same pattern)

    Generation flow:

    1. Read global config for profile/delivery settings (src/core/global-config.ts)
    2. Filter templates by profile workflows (src/core/profiles.ts)
    3. For each selected tool: write SKILL.md files and tool-specific command files via adapters

    Skills currently use a SkillTemplate interface (src/core/templates/types.ts):

    interface SkillTemplate {
      name: string;
      description: string;
      instructions: string;
      license?: string;
      compatibility?: string;
      metadata?: Record<string, string>;
    }

    Commands use a CommandContent interface (src/core/command-generation/types.ts):

    interface CommandContent {
      id: string;
      name: string;
      description: string;
      category: string;
      tags: string[];
      body: string;
    }

    Suggested Implementation Approach

    Based on the existing architecture, a natural approach would be:

    1. Global directories (following schema pattern from global-config.ts):

    ~/.local/share/openspec/skills/<skill-dir-name>/SKILL.md
    ~/.local/share/openspec/commands/<command-id>.md
    

    2. Resolution functions (mirror resolver.ts pattern):

    • getUserSkillsDir() / getUserCommandsDir() in global-config.ts
    • A getSkillTemplatesWithOverrides() function that:
      1. Loads built-in templates from src/core/templates/workflows/
      2. Scans user-global directory for overrides/additions
      3. Merges with user-global taking precedence on name match

    3. Integration points - The main changes would be in:

    • src/core/shared/skill-generation.ts - Add user-global skill discovery to getSkillTemplates()
    • src/core/init.ts:517-527 - Merge user-global templates before generation
    • src/core/update.ts:170-171 - Same merge in update flow
    • src/commands/schema.ts - Model for a potential openspec skill which / openspec skill list command

    4. Validation - Parse and validate user-provided SKILL.md files against the SkillTemplate interface before overlay (similar to resolveSchema() in resolver.ts:109-153 which validates via Zod).


    Related Issues

    Several open issues are directly related or would benefit from this work:


    Open Design Questions

    Re: Deletion/masking - The schema system doesn't currently support disabling/tombstoning a built-in. Options:

    1. A disabled field in ~/.config/openspec/config.json (extends GlobalConfig interface in global-config.ts:15-20)
    2. A tombstone file convention (e.g., ~/.local/share/openspec/skills/openspec-explore/.disabled)
    3. Leveraging the existing workflows array in GlobalConfig - users already can filter which workflows are active via openspec config profile

    Option 3 is notable because the workflows filter in GlobalConfig already controls which skills get generated. A user wanting to "disable" a built-in skill could simply remove it from their workflow list. Custom skills would need a separate mechanism.

    Re: Project-local overrides - The schema system already supports this tier (getProjectSchemasDir() in resolver.ts:44-46). Adding project-local skills/commands would complete the three-tier model: project > user-global > package.

    Re: Skill file format - User-defined skills would need a parseable format. The existing SKILL.md with YAML frontmatter (as generated by generateSkillContent() in skill-generation.ts:127-149) could serve as the canonical format for user-defined skills too.

  3. askpatrickw commented on Feb 26, 2026

    @askpatrickw
    Author

    I no longer think this is needed. Use a tool like Skillshare to roll out skills globally across all agents and all developers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions