Skip to content

feat: per-target agent frontmatter transformation during sync #241

Description

@thinhngotony

Problem

Skillshare syncs the same agent .md file verbatim to all targets (OpenCode, Copilot, Cursor), but each target now enforces incompatible frontmatter schemas for agent tool permissions. This causes hard crashes or silent breakage.

Target Key Type Example
OpenCode ≥1.17 permission: object permission:\n read: allow\n bash: allow
Copilot tools: array tools: [read, bash, agent]
Cursor tools: array tools: [read, bash, agent]

OpenCode rejects the array format outright:

Configuration is invalid at manager.agent.md
↳ Expected object | undefined, got ["read","search","execute","agent","mcp-atlassian/*"] tools

Copilot has no permission: field in its schema — only tools: as an array is recognized. The Copilot changelog (v1.0.69) confirms: "Support comma-separated tools in custom agent frontmatter" — non-negotiable array format.

Fixing the source to permission: {object} silently breaks Copilot and Cursor. There is currently no single format that works across all three targets.

Evidence

  • OpenCode v1.17.13: validation rejects array under tools: key — expects object or absent
  • Copilot API schema (AgentInfo.tools): "type": "array", "items": { "type": "string" } — no permission field exists
  • MCP server tool naming also diverges: Copilot uses mcp-atlassian while OpenCode uses glob patterns like mcp-atlassian/*

Proposed Solution

Add per-target frontmatter transformation during skillshare sync agents. The canonical source stays in one format, and skillshare generates the correct format per target.

Option A: Config-driven field mapping

targets:
  copilot:
    agents:
      path: ~/.copilot/agents
      frontmatter:
        map:
          permission: tools          # rename field
          permission.*: flatten      # object → array (keys become values)
  cursor:
    agents:
      path: ~/.cursor/agents
      frontmatter:
        map:
          permission: tools
          permission.*: flatten
  opencode:
    agents:
      path: ~/.config/opencode/agents
      # no transformation — use source format as-is

Option B: Pre-sync hook script

targets:
  copilot:
    agents:
      path: ~/.copilot/agents
      frontmatter:
        transform: ~/.config/skillshare/transforms/opencode-to-copilot.py

Option C: Extension-based (reuse existing extras mechanism)

Move agents to extras: config and use per-target extensions (already supported for commands/rules). Requires agents to be managed as extras rather than native agent targets.

Alternatives Considered

  1. Maintain separate agent files per target — works but defeats single-source-of-truth; every edit requires updating N copies.
  2. Pick lowest-common-denominator format — Copilot's tools: [array] ignores OpenCode's richer permission model (read: allow vs read: full).
  3. Wait for CLI standardization — unlikely; each tool ships its own schema independently.

Scope

  • Small (1-3 files, < 200 lines)
  • Medium (3-10 files, 200-500 lines)
  • Large (10+ files, 500+ lines)

Likely touches: agent sync pipeline, config parsing, frontmatter transformer.

Open Questions

  • Which option (A/B/C) fits skillshare's architecture best?
  • Should transformations be composable (field rename + flatten + strip unsupported keys)?
  • Should the transform run at sync time (A/B) or at collect time (C)?
  • How to handle MCP tool naming divergence (mcp-atlassian vs mcp-atlassian/*)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions