mkskill

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 7 Imported by: 0

README

mkskill

Go Reference

Compose a Claude Code SKILL.md and an agent-agnostic AGENTS.md from a project's ai/ source sections — the shared generator behind every ot4go project's docs and skill, so they're produced identically everywhere instead of each repo re-implementing it.

go get github.com/ot4go/mkskill                          # library (each project's own skill)
go install github.com/ot4go/mkskill/cmd/mkskill@latest   # binary (bootstrap a repo)
  • Zero dependencies — standard library only.
  • One source of truth — a Spec (name, trigger description, ordered sections) renders both the skill and the agent docs, so they can't drift apart.
  • -global install — write the skill into ~/.claude/skills/<name>/SKILL.md (available from every project, no elevation).
  • Self-hosted — mkskill's own skill is generated by mkskill.

It encodes the ot4go docs-and-skills convention (__convenciones/AI-and-claude-skills.md).

The model

SKILL.md and AGENTS.md are build artifacts — edit the ai/core/*.md sources, regenerate the rest. A Spec is all mkskill needs:

type Spec struct {
    Name        string   // skill name; also the default skill directory
    Description string   // the trigger text Claude uses to load the skill
    FS          fs.FS    // the embedded ai/ tree (an embed.FS satisfies fs.FS)
    Sections    []string // section file paths within FS, in order
}

func (s Spec) Skill() (string, error)                // frontmatter + "# Name" + sections
func (s Spec) AgentDocs() (string, error)            // the same sections, no frontmatter
func (s Spec) RunGenerateClaudeSkill(args []string) error  // -dst | -global | -force
func (s Spec) RunGenerateAgentDocs(args []string) error    // -dst | -force
func (s Spec) Dispatch(cmd string, args []string) (bool, error)
func (s Spec) CheckParams() (err error, done bool)         // os.Args → run or fall through

Use it as a library (Go projects)

The host embeds its ai/ tree once and lists the section order; mkskill owns the rendering and the commands.

//go:embed ai
var aiFS embed.FS

var Skill = mkskill.Spec{
    Name:        "myproj",
    Description: "What it is. Use when … . Triggers on … .",
    FS:          aiFS,
    Sections:    []string{"ai/core/overview.md", "ai/core/cli.md"}, // just the order
}

// at the top of main():
if err, done := Skill.CheckParams(); done {
    return
} else if err != nil {
    fmt.Fprintf(os.Stderr, "error: %v\n", err)
    os.Exit(1)
}
// …not a mkskill command: the host handles os.Args itself…

CheckParams inspects os.Args: it runs a generate command if the program was invoked as one (done=true), reports err if one was recognised but failed, and returns both zero when it's not a generate command — so the host falls through to its own commands. (Dispatch(cmd, args) is the lower-level primitive when you've already split the command name out.)

Regenerate with go generate

With CheckParams in main, a //go:generate directive lets the project regenerate its own tracked AGENTS.md by invoking itself — no separate driver:

//go:generate go run ./cmd/myproj generate-agent-docs -force
package main

go generate ./... then runs that command and rewrites AGENTS.md. Installing the skill (generate-claude-skill -global) stays a manual per-machine step — it writes to your home, not the repo.

Bootstrap a new project

The mkskill binary scaffolds the source tree and a skill.go wired to the library:

go install github.com/ot4go/mkskill/cmd/mkskill@latest
cd myproject
mkskill init           # ai/core/*.md + skill.go (name = the directory)
mkskill init -i        # …asking for the name and description

It does not generate other projects' skills — each project does that itself with its own skill.go + Skill.CheckParams(). The binary also installs mkskill's own skill (mkskill generate-claude-skill -global).

A non-Go project (a tiny build driver)

A repo that isn't Go can read its ai/ sources from disk with os.DirFS and hand the command line to Dispatch, run with go run:

// gen.go
package main

import ("os"; "github.com/ot4go/mkskill")

func main() {
    spec := mkskill.Spec{
        Name: "myproj", Description: "What it is. Use when … . Triggers on … .",
        FS:       os.DirFS("."),
        Sections: []string{"ai/core/overview.md", "ai/core/rules.md"},
    }
    if ok, err := spec.Dispatch(os.Args[1], os.Args[2:]); !ok || err != nil {
        os.Exit(1)
    }
}
go run gen.go generate-claude-skill -global    # ~/.claude/skills/myproj/SKILL.md
go run gen.go generate-agent-docs              # AGENTS.md

Destinations & flags

Command Flags Destination
generate-claude-skill (none) .claude/skills/<name>/SKILL.md (project-local)
generate-claude-skill -global ~/.claude/skills/<name>/SKILL.md (user home, no elevation)
generate-claude-skill -dst <f> exactly that path
generate-agent-docs -dst <f> AGENTS.md by default

-global and -dst are mutually exclusive; -force overwrites an existing file.

License

MIT

Documentation

Overview

Package mkskill composes a Claude Code SKILL.md and an agent-agnostic AGENTS.md from a project's ai/ source sections, and exposes the two generate commands (generate-claude-skill / generate-agent-docs) so every ot4go CLI handles its docs and skill identically. The shared convention lives in __convenciones/AI-and-claude-skills.md.

A Spec is the single source of truth: a name, a trigger description, and the ordered markdown sections. Skill() renders the skill (YAML frontmatter + "# Name" heading + sections); AgentDocs() renders the same sections without frontmatter. The Run* / Dispatch helpers own the flag parsing, including -global.

A host wires it in at the top of main with CheckParams: it runs a generate command if the program was invoked as one, else falls through so the host handles its own command line:

if err, done := Skill.CheckParams(); done {
	return
} else if err != nil {
	fmt.Fprintf(os.Stderr, "error: %v\n", err)
	os.Exit(1)
}
// …not a mkskill command: the host handles os.Args itself…

(Dispatch is the lower-level primitive CheckParams is built on, when the host has already split out the command name.)

The host embeds its ai/ tree once and lists the section order:

//go:embed ai
var aiFS embed.FS
var Skill = mkskill.Spec{Name: "myproj", Description: "…", FS: aiFS,
	Sections: []string{"ai/core/overview.md", "ai/core/cli.md"}}

The mkskill binary (cmd/mkskill) bootstraps a new project with `mkskill init` and installs mkskill's own skill — it does not generate other projects' skills.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Usage added in v0.1.2

func Usage(detail bool) string

Usage returns mkskill's command help for a host to embed in its own help, so it stays in sync as mkskill gains commands or flags. detail=false is the compact header (one aligned "name flags" line per command); detail=true adds a description line under each. Both have no leading indent or trailing newline — the host positions them.

Types

type Spec

type Spec struct {
	Name        string   // skill name; also the default skill directory
	Description string   // the trigger text Claude uses to decide when to load the skill
	FS          fsys.FS  // the embedded ai/ tree (an embed.FS satisfies fs.FS)
	Sections    []string // section file paths within FS, in order (e.g. "ai/core/overview.md")
}

Spec is everything mkskill needs to render a project's skill and agent docs. The host embeds its ai/ tree once (//go:embed ai → an fs.FS) and lists the section files in order; the same ordered sections feed both outputs, so they can't drift.

func (Spec) AgentDocs

func (s Spec) AgentDocs() (string, error)

AgentDocs renders the agent-agnostic document: an "# Name" heading and the same sections, with no tool-specific frontmatter. Suitable for AGENTS.md, .cursor/rules, CONVENTIONS.md, or any LLM context input.

func (Spec) CheckParams

func (s Spec) CheckParams() (err error, done bool)

CheckParams is the one-call host integration: it inspects os.Args and runs a generate command if the program was invoked as one. It returns done=true when a generate command ran successfully (the host should stop), err!=nil when a generate command was recognised but failed, and both zero when os.Args is not a generate command (so the host falls through to its own argument handling):

if err, done := Skill.CheckParams(); done {
	return
} else if err != nil {
	fmt.Fprintf(os.Stderr, "error: %v\n", err)
	os.Exit(1)
}
// …not a mkskill command: the host handles os.Args itself…

func (Spec) Dispatch

func (s Spec) Dispatch(cmd string, args []string) (handled bool, err error)

Dispatch routes the two generate commands. It returns handled=false for any other command so a host CLI can fall through to its own commands:

if ok, err := spec.Dispatch(cmd, args); ok { return err }

func (Spec) RunGenerateAgentDocs

func (s Spec) RunGenerateAgentDocs(args []string) error

RunGenerateAgentDocs parses [-dst f] [-force] and writes the AGENTS.md (default destination AGENTS.md).

func (Spec) RunGenerateClaudeSkill

func (s Spec) RunGenerateClaudeSkill(args []string) error

RunGenerateClaudeSkill parses [-dst f] [-global] [-force] and writes the SKILL.md. Default destination is the project-local .claude/skills/<name>/SKILL.md; -global writes ~/.claude/skills/<name>/SKILL.md (available from every project, no elevation — it is under the user's own home); -global and -dst are mutually exclusive.

func (Spec) Skill

func (s Spec) Skill() (string, error)

Skill renders the Claude Code SKILL.md: YAML frontmatter (name + description), an "# Name" heading, then the sections separated by blank lines.

Directories

Path Synopsis
cmd
mkskill command
Command mkskill bootstraps a project for the ot4go docs-and-skills convention and installs mkskill's own skill.
Command mkskill bootstraps a project for the ot4go docs-and-skills convention and installs mkskill's own skill.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL