segment

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package segment computes, writes and tags the versions of the segments in a Claude Code plugin marketplace monorepo.

The distinguishing choice is the baseline: a segment's previous version is found in the git history of its own plugin.json, not in a git tag. Tags are therefore decoration here rather than state, which is what lets the tool stay correct on a repository that has never been tagged — and stops a forgotten tag silently corrupting the next computation.

Index

Constants

View Source
const ManifestPath = ".claude-plugin/plugin.json"

ManifestPath is where a segment's plugin.json lives, relative to the segment.

Variables

View Source
var (
	// ErrNoVersionKey reports a manifest with no version member.
	ErrNoVersionKey = errors.NewSentinel("skillup.no_version_key", `manifest has no "version" key`)
	// ErrAmbiguousVersion reports a manifest with more than one version member,
	// where guessing which one is meant would be worse than refusing.
	ErrAmbiguousVersion = errors.NewSentinel("skillup.ambiguous_version", `manifest has more than one "version" key`)
)

Sentinels for the two ways a manifest can be unusable. Static, so a caller can match on them with errors.Is rather than on a formatted string.

View Source
var ErrNoBaseline = errors.NewSentinel("skillup.no_baseline", "no baseline commit for segment")

ErrNoBaseline reports a segment whose manifest has no history yet.

View Source
var ErrNotSemver = errors.NewSentinel("skillup.not_semver", "not a semver core")

ErrNotSemver reports a version string that is not a plain semver core.

Functions

func TagName

func TagName(segment string, v Version) string

TagName is the tag skillup creates for a segment version. It matches the format `claude plugin tag` produces, so the two interoperate rather than owning rival namespaces.

Types

type Author

type Author struct {
	Name  string
	Email string
}

Author identifies who made a commit.

Required explicitly rather than left to go-git: go-git reads no git config, so on a machine that happens to have user.name set a commit succeeds, and in a CI container it fails with "author field is required". Taking it as an argument makes the behaviour the same in both places.

type Bump

type Bump int

Bump is the size of change a set of commits warrants.

const (
	BumpNone Bump = iota
	BumpPatch
	BumpMinor
	BumpMajor
)

func HighestBump

func HighestBump(commits []Commit) Bump

HighestBump reduces a set of commits to the single bump they collectively warrant.

func (Bump) String

func (b Bump) String() string

type Commit

type Commit struct {
	SHA     string
	Subject string
	Body    string
}

Commit is the part of a git commit this tool reasons about.

func (Commit) Bump

func (c Commit) Bump() Bump

Bump reports what this commit alone warrants.

The scope is deliberately ignored. Which segment a commit affects is decided by the paths it touched, not by what the author typed in parentheses — a commit messaged feat(forge) that edits no forge file changes nothing about forge.

type Git

type Git struct {
	// contains filtered or unexported fields
}

Git answers the history questions skillup asks, and performs the writes it makes, over go-git via go/repo.

Nothing here shells out. Handing a path or a ref to /bin/git means quoting an external value into a command line every time, and a version tool is not important enough to be the place that rule gets bent.

func NewGitFrom

func NewGitFrom(r Repository) *Git

NewGitFrom wraps an already-open repository, for tests and callers that hold one.

func Open

func Open(ctx context.Context, dir string) (*Git, error)

Open opens an existing checkout.

func (*Git) BaselineCommit

func (g *Git) BaselineCommit(_ context.Context, segmentDir string) (string, error)

BaselineCommit returns the commit at which a segment's version last changed.

This is the whole point of the tool. Deriving the baseline from the manifest's own history — rather than from a git tag, as cocogitto does — means a tag is never load-bearing, so a forgotten one costs visibility instead of silently corrupting the next version.

An empty string means the manifest has no history yet: treat every commit that touched the segment as in scope.

func (*Git) CommitsTouching

func (g *Git) CommitsTouching(_ context.Context, dir, since string) ([]Commit, error)

CommitsTouching returns the commits after since (exclusive) that changed anything under dir, newest first. An empty since means all history.

func (*Git) CreateTag

func (g *Git) CreateTag(_ context.Context, tag, message, at string) error

CreateTag makes an annotated tag at a commit ("" means HEAD).

func (*Git) PushBranch

func (g *Git) PushBranch(ctx context.Context, remoteURL, branch, username, token string) error

PushBranch pushes HEAD to a branch on a remote URL, authenticating with a token. The credential never reaches a command line, because there is no command line.

func (*Git) PushTags

func (g *Git) PushTags(ctx context.Context, remoteURL, username, token string, tags []string) error

PushTags pushes the named tags to a remote URL.

func (*Git) Repo

func (g *Git) Repo() Repository

Repo exposes the underlying repository for the commands that write.

func (*Git) StageAndCommit

func (g *Git) StageAndCommit(ctx context.Context, message string, paths []string, author Author) (string, error)

StageAndCommit stages the given paths and commits them, returning the new hash. It reports an empty hash and no error when nothing was staged.

func (*Git) TagExists

func (g *Git) TagExists(_ context.Context, tag string) bool

TagExists reports whether a tag is present.

func (*Git) VersionAt

func (g *Git) VersionAt(_ context.Context, sha, segmentDir string) (string, error)

VersionAt reads a segment's version as it was at a commit.

Needed because the working tree and the baseline commit can disagree: after apply writes a bump but before it is committed, the manifest has advanced and history has not. Computing from the working tree in that window bumps twice.

type Inconsistency

type Inconsistency struct {
	Segment string `json:"segment"`
	Problem string `json:"problem"`
}

Inconsistency is a disagreement between the marketplace catalogue and what is on disk.

func CheckConsistency

func CheckConsistency(root string) ([]Inconsistency, error)

CheckConsistency compares the marketplace catalogue against the segments on disk.

Three ways a marketplace drifts, all of which are invisible until a consumer hits them:

  • a catalogue entry pointing at a directory that is not there
  • a segment on disk that the catalogue never lists, so nobody can install it
  • a version declared in both places, disagreeing

The last is the subtle one. A marketplace entry may carry its own version, and where it does it wins for resolution — so a stale entry pins consumers to an old version while plugin.json says something else entirely.

type Marketplace

type Marketplace struct {
	Plugins []struct {
		Name    string `json:"name"`
		Source  string `json:"source"`
		Version string `json:"version,omitempty"`
	} `json:"plugins"`
}

Marketplace is the subset of marketplace.json this tool reads.

type Plan

type Plan struct {
	Segment  Segment  `json:"segment"`
	Current  string   `json:"current"`
	Baseline string   `json:"baseline_commit,omitempty"`
	Commits  int      `json:"commits_considered"`
	Bump     string   `json:"bump"`
	Next     string   `json:"next"`
	Action   string   `json:"action"` // "none", "raise", "manifest-ahead"
	Reasons  []string `json:"reasons,omitempty"`
}

Plan is what skillup would do to one segment.

func PlanAllIn

func PlanAllIn(ctx context.Context, root string) ([]Plan, error)

PlanAllIn opens the repository at root and plans every segment in it. Most callers want this rather than assembling a Planner themselves.

type Planner

type Planner struct {
	Root string
	Git  *Git
}

Planner computes plans.

func NewPlanner

func NewPlanner(ctx context.Context, root string) (*Planner, error)

NewPlanner opens the repository at root and returns a Planner for it.

func (*Planner) PlanAll

func (p *Planner) PlanAll(ctx context.Context) ([]Plan, error)

PlanAll computes a plan for every segment in the marketplace.

func (*Planner) PlanOne

func (p *Planner) PlanOne(ctx context.Context, s Segment) (Plan, error)

PlanOne computes the plan for a single segment.

type Repository

type Repository interface {
	WithRepo(func(*git.Repository) error) error
	WithTree(func(*git.Worktree) error) error
	Commit(context.Context, string, *git.CommitOptions) (plumbing.Hash, error)
	Push(context.Context, *git.PushOptions) error
}

Repository is the slice of gitlab.com/phpboyscout/go/repo that skillup needs.

Declared here rather than taking repo.RepoLike wholesale: this is the estate's own "depend on the narrowest role" rule, and it means a fake in a test implements four methods instead of nine roles.

type Segment

type Segment struct {
	Name string
	Dir  string // relative to the repository root
}

Segment is one plugin in the marketplace.

func Discover

func Discover(root string) ([]Segment, error)

Discover reads the marketplace manifest and returns its segments.

type Version

type Version struct{ Major, Minor, Patch int }

Version is a semver core. Pre-release and build metadata are deliberately unmodelled: a marketplace segment ships from a branch, so there is nowhere for a pre-release to live.

func ParseVersion

func ParseVersion(s string) (Version, error)

ParseVersion reads a plain semver core.

func ReadVersion

func ReadVersion(segmentDir string) (Version, error)

ReadVersion returns the version declared in a segment's manifest.

func (Version) Compare

func (v Version) Compare(o Version) int

Compare returns -1, 0 or 1.

func (Version) Next

func (v Version) Next(b Bump) Version

Next applies a bump.

Below 1.0.0 a breaking change resolves to a MINOR bump, never a major. Reaching 1.0 is a promise a human makes deliberately; it is not an arithmetic consequence of someone typing an exclamation mark.

func (Version) String

func (v Version) String() string

type WriteResult

type WriteResult struct {
	Path     string
	From, To Version
	Written  bool
	Skipped  bool
}

WriteResult records what WriteVersion did, including declining to act.

func WriteVersion

func WriteVersion(segmentDir string, want Version) (WriteResult, error)

WriteVersion sets the version in a segment's manifest, preserving every other byte. It refuses to lower a version: see WriteResult.

Jump to

Keyboard shortcuts

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