Skip to content

feat: add skills support for all AI agents and languages #47

@markcallen

Description

@markcallen

Overview

Add skills support to ballast alongside the existing rules system. Skills are invocable behaviors (triggered on demand) rather than always-on context rules. This issue tracks the full implementation across all supported AI agents (Cursor, Claude Code, OpenCode, Codex) and all supported languages (TypeScript, Python, Go).

The first skill to ship is owasp-security-scan — a cross-language OWASP Top 10 security audit that covers Go, TypeScript, and Python, making it a perfect fit for ballast's own monorepo users.


Background: Rules vs Skills

Rules Skills
Behavior Always-on context guidance Invoked on demand (e.g. /owasp-security-scan)
Install path (Claude) .claude/rules/*.md .claude/skills/*.skill
Install path (Cursor) .cursor/rules/*.mdc .cursor/rules/*.mdc with alwaysApply: false
Triggered by Every conversation Explicit user invocation

Target Formats Per Agent

Agent Install path Format
Claude Code .claude/skills/<name>.skill Zip archive (SKILL.md + references/)
Cursor .cursor/rules/<name>.mdc .mdc with alwaysApply: false frontmatter
OpenCode .opencode/skills/<name>.md Flat markdown (body only)
Codex .codex/rules/<name>.md Flat markdown rule file

Implementation Phases

Phase 1 — Repository Structure

  • Extract owasp-security-scan.skill zip into skills/common/owasp-security-scan/
  • Establish skills/ directory layout mirroring agents/:
    skills/
    └── common/
        └── owasp-security-scan/
            ├── SKILL.md
            └── references/
                ├── owasp-mapping.md
                ├── remediation-guide.md
                ├── ci-workflow.md
                └── tool-config.md
    
  • Delete the top-level owasp-security-scan.skill zip artifact

No per-target template files are needed — Cursor frontmatter is generated programmatically from the description field in SKILL.md.

Phase 2 — Config File Extension

Extend .rulesrc.json with an optional skills array (fully backward-compatible):

{
  "target": "claude",
  "agents": ["linting", "cicd"],
  "skills": ["owasp-security-scan"]
}
  • TypeScript: add skills?: string[] to RulesConfig in config.ts
  • Python: update load_config/save_config to read/write skills
  • Go: add Skills []string \json:"skills,omitempty"`torulesConfig` struct

Phase 3 — Skill Discovery (all 3 CLIs)

Add parallel skill discovery alongside existing agent discovery:

  • COMMON_SKILL_IDS, listSkills(), isValidSkill(), getSkillDir() in TypeScript (agents.ts)
  • Same in Python (cli.py)
  • Same in Go (main.go)

Language-specific skills (skills/typescript/, skills/python/, skills/go/) are scaffolded but empty for now.

Phase 4 — Skill Build Functions (all 3 CLIs)

Each CLI needs 4 functions:

Function Output
buildClaudeSkill(id) In-memory zip (SKILL.md + references/)
buildCursorSkillFormat(id) alwaysApply: false frontmatter + SKILL.md body
buildSkillMarkdown(id) SKILL.md body only (no frontmatter)
getSkillDestination(id, target) Correct install path per target

Zip implementation: Python and Go use stdlib (zipfile, archive/zip) — no new deps. TypeScript needs jszip (or similar) added as a prod dependency.

  • TypeScript: implement in build.ts, add jszip dep
  • Python: implement in cli.py using zipfile stdlib
  • Go: implement in main.go using archive/zip stdlib

Phase 5 — Install Logic & CLI Flags

Add --skill <id> and --all-skills flags to the existing install subcommand (consistent with --agent/--all):

ballast-typescript install --target claude --skill owasp-security-scan
ballast-python install --target cursor --all-skills
ballast-go install --target claude --skill owasp-security-scan --agent linting

Install flow per skill:

  1. Validate skill ID
  2. Compute destination path
  3. Skip if exists and --force not set
  4. Build target-specific output
  5. Write file (create parent dirs)
  6. Persist skill IDs to .rulesrc.json
  7. Append ## Installed skills section to CLAUDE.md / AGENTS.md index files
  • TypeScript: installSkills() in install.ts, wire flags in cli.ts
  • Python: install_skills() in cli.py, wire --skill/--all-skills args
  • Go: installSkills() in main.go, wire flags
  • CLI wrapper (cli/ballast): forward --skill to backend CLIs

Phase 6 — Sync & Embed Updates

CLI Required change
TypeScript New sync:skills script; add "skills" to package.json files
Python Add skills/**/* to pyproject.toml package-data
Go Add //go:embed skills/** + var embeddedSkillsFS embed.FS; sync skills/ into Go package dir
  • TypeScript sync script
  • Python package-data update
  • Go embed + sync

Phase 7 — Tests

Each CLI gets skill-specific tests covering:

  • buildClaudeSkill → valid zip with correct internal paths (<id>/SKILL.md, <id>/references/*.md)

  • buildCursorSkillFormatalwaysApply: false in frontmatter, description from SKILL.md

  • getSkillDestination → correct path for all 4 targets

  • Install integration: install to tmpdir, assert file at expected path

  • Skip/force behavior

  • Config persistence (.rulesrc.json updated with skills array)

  • TypeScript tests (build.test.ts, install.test.ts)

  • Python tests (test_cli.py)

  • Go tests (main_test.go)

Phase 8 — Index File Updates

Update CLAUDE.md and AGENTS.md generation to include a separate ## Installed skills section when skills are installed (distinct from ## Installed agent rules).

  • TypeScript index builder
  • Python index builder
  • Go index builder

Key Design Decisions

  • Zip at install time (not pre-built): keeps source as plain markdown, easy to audit, no stale binary artifacts in the repo
  • No template files for skills: Cursor frontmatter is generated from SKILL.md description field programmatically — no templates/cursor-frontmatter.yaml needed
  • --skill flag, not a new subcommand: consistent with --agent, minimal CLI surface change
  • No language prefix in skill filenames: owasp-security-scan.skill regardless of language — skill names must be stable for invocation
  • Common skills only initially: skills/typescript/, skills/python/, skills/go/ directories scaffolded but empty until language-specific skills are identified

Files Changed (expected)

  • skills/common/owasp-security-scan/SKILL.md (new)
  • skills/common/owasp-security-scan/references/*.md (new)
  • packages/ballast-typescript/src/agents.ts — skill discovery
  • packages/ballast-typescript/src/build.ts — skill build functions
  • packages/ballast-typescript/src/install.tsinstallSkills()
  • packages/ballast-typescript/src/config.tsskills? field
  • packages/ballast-typescript/src/cli.ts--skill/--all-skills flags
  • packages/ballast-python/ballast/cli.py — all Python skill logic
  • packages/ballast-go/cmd/ballast-go/main.go — all Go skill logic + embed
  • cli/ballast/main.go — forward --skill to backends
  • Test files for all three CLIs

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions