Documentation
¶
Overview ¶
Copyright (c) 2025 Guilherme Silva Sousa Licensed under the MIT License See LICENSE file in the project root for full license information. Package installer provides the complete command installation process for ccmd. It handles the entire installation workflow from cloning repositories to updating lock files, with proper error handling and rollback capabilities.
Copyright (c) 2025 Guilherme Silva Sousa Licensed under the MIT License See LICENSE file in the project root for full license information.
Copyright (c) 2025 Guilherme Silva Sousa Licensed under the MIT License See LICENSE file in the project root for full license information.
Copyright (c) 2025 Guilherme Silva Sousa Licensed under the MIT License See LICENSE file in the project root for full license information.
Copyright (c) 2025 Guilherme Silva Sousa Licensed under the MIT License See LICENSE file in the project root for full license information.
Example ¶
Example demonstrates basic command installation
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
// Create installer with options
opts := installer.Options{
Repository: "https://github.com/example/mycmd.git",
Version: "v1.0.0",
InstallDir: ".claude/commands",
}
inst, err := installer.New(opts)
if err != nil {
log.Fatal(err)
}
// Install the command
ctx := context.Background()
if err := inst.Install(ctx); err != nil {
log.Fatal(err)
}
fmt.Println("Command installed successfully")
}
Output:
Example (CommandManager) ¶
Example_commandManager demonstrates using the CommandManager
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
// Create a command manager for the current project
cm := installer.NewCommandManager(".")
ctx := context.Background()
// Install a specific command
if err := cm.Install(ctx, "example/tool", "v1.0.0", "", false); err != nil {
log.Fatal(err)
}
// List installed commands
commands, err := cm.GetInstalledCommands()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Installed commands:\n")
for _, cmd := range commands {
fmt.Printf("- %s (v%s): %s\n", cmd.Name, cmd.Version, cmd.Description)
}
}
Output:
Example (ErrorHandling) ¶
Example_errorHandling demonstrates error handling during installation
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
opts := installer.Options{
Repository: "https://github.com/nonexistent/repo.git",
InstallDir: ".claude/commands",
}
inst, err := installer.New(opts)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
if err := inst.Install(ctx); err != nil {
// Check if error is retryable
if installer.IsRetryableError(err) {
fmt.Println("Installation failed but can be retried")
} else {
fmt.Println("Installation failed permanently")
}
// Get installation phase where error occurred
phase := installer.GetInstallationPhase(err)
if phase != "" {
fmt.Printf("Error occurred during: %s\n", phase)
}
return
}
fmt.Println("Command installed successfully")
}
Output:
Example (ExtractRepoPath) ¶
Example_extractRepoPath demonstrates extracting repository paths
package main
import (
"fmt"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
urls := []string{
"https://github.com/user/repo.git",
"git@github.com:user/repo.git",
"https://gitlab.com/org/project.git",
}
for _, url := range urls {
path := installer.ExtractRepoPath(url)
fmt.Printf("%s -> %s\n", url, path)
}
}
Output: https://github.com/user/repo.git -> user/repo git@github.com:user/repo.git -> user/repo https://gitlab.com/org/project.git -> org/project
Example (ForceReinstall) ¶
Example_forceReinstall demonstrates force reinstallation
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
opts := installer.Options{
Repository: "https://github.com/example/mycmd.git",
Version: "v2.0.0",
Force: true, // Overwrite existing installation
InstallDir: ".claude/commands",
}
inst, err := installer.New(opts)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
if err := inst.Install(ctx); err != nil {
log.Fatal(err)
}
fmt.Println("Command reinstalled with version v2.0.0")
}
Output:
Example (InstallFromConfig) ¶
Example_installFromConfig demonstrates installing all commands from ccmd.yaml
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
ctx := context.Background()
projectPath := "." // Current directory
// Install all commands defined in ccmd.yaml
if err := installer.InstallFromConfig(ctx, projectPath, false); err != nil {
log.Fatal(err)
}
fmt.Println("All commands from ccmd.yaml installed")
}
Output:
Example (InstallFromShorthand) ¶
Example_installFromShorthand demonstrates GitHub shorthand installation
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
// Use the integration function for shorthand support
ctx := context.Background()
opts := installer.IntegrationOptions{
Repository: "user/repo", // GitHub shorthand
Version: "latest",
}
if err := installer.InstallCommand(ctx, opts, true); err != nil {
log.Fatal(err)
}
fmt.Println("Command installed from user/repo")
}
Output:
Example (NormalizeRepositoryURL) ¶
Example_normalizeRepositoryURL demonstrates URL normalization
package main
import (
"fmt"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
urls := []string{
"user/repo",
"github.com/user/repo",
"https://github.com/user/repo",
"git@github.com:user/repo.git",
}
for _, url := range urls {
normalized := installer.NormalizeRepositoryURL(url)
fmt.Printf("%s -> %s\n", url, normalized)
}
}
Output: user/repo -> https://github.com/user/repo.git github.com/user/repo -> https://github.com/user/repo.git https://github.com/user/repo -> https://github.com/user/repo.git git@github.com:user/repo.git -> git@github.com:user/repo.git
Example (ParseRepositorySpec) ¶
Example_parseRepositorySpec demonstrates parsing repository specifications
package main
import (
"fmt"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
specs := []string{
"user/repo",
"user/repo@v1.0.0",
"https://github.com/user/repo.git@feature/branch",
"git@github.com:user/repo.git",
}
for _, spec := range specs {
repo, version := installer.ParseRepositorySpec(spec)
if version == "" {
fmt.Printf("%s -> repo: %s, version:\n", spec, repo)
} else {
fmt.Printf("%s -> repo: %s, version: %s\n", spec, repo, version)
}
}
}
Output: user/repo -> repo: user/repo, version: user/repo@v1.0.0 -> repo: user/repo, version: v1.0.0 https://github.com/user/repo.git@feature/branch -> repo: https://github.com/user/repo.git, version: feature/branch git@github.com:user/repo.git -> repo: git@github.com:user/repo.git, version:
Example (WithCustomName) ¶
Example_withCustomName demonstrates installation with a custom command name
package main
import (
"context"
"fmt"
"log"
"github.com/gifflet/ccmd/internal/installer"
)
func main() {
opts := installer.Options{
Repository: "https://github.com/example/long-command-name.git",
Name: "lcn", // Short alias
InstallDir: ".claude/commands",
}
inst, err := installer.New(opts)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
if err := inst.Install(ctx); err != nil {
log.Fatal(err)
}
fmt.Println("Command installed as 'lcn'")
}
Output:
Index ¶
- Constants
- func ExtractRepoPath(gitURL string) string
- func GetInstallationPhase(err error) string
- func InstallCommand(ctx context.Context, opts IntegrationOptions, addToConfig bool) error
- func InstallFromConfig(ctx context.Context, projectPath string, force bool) error
- func IsRetryableError(err error) bool
- func NormalizeRepositoryURL(url string) string
- func ParseRepositorySpec(spec string) (repository, version string)
- func WrapInstallationError(err error, code errors.ErrorCode, repository, version, phase string) error
- type CommandManager
- type GitClient
- type InstallationError
- type InstalledCommand
- type Installer
- type IntegrationOptions
- type MultiValidationError
- type Options
- type ValidationError
Examples ¶
Constants ¶
const ( ErrMsgRepositoryNotFound = "repository not found or inaccessible" ErrMsgInvalidRepository = "invalid repository structure" ErrMsgMissingMetadata = "ccmd.yaml not found in repository" ErrMsgInvalidMetadata = "invalid ccmd.yaml format" ErrMsgCommandExists = "command already exists" ErrMsgInstallationFailed = "installation failed" ErrMsgRollbackFailed = "failed to rollback installation" ErrMsgVersionNotFound = "specified version not found" ErrMsgNoVersionAvailable = "no version information available" ErrMsgPermissionDenied = "permission denied" ErrMsgDiskFull = "insufficient disk space" ErrMsgNetworkError = "network error during download" ErrMsgInvalidCommandName = "invalid command name" ErrMsgLockFileCorrupted = "lock file is corrupted" ErrMsgConcurrentInstall = "another installation is in progress" )
Common installation error messages
const ( PhaseValidation = "validation" PhaseClone = "clone" PhaseVerification = "verification" PhaseInstall = "install" PhaseMetadata = "metadata" PhaseLockFile = "lock_file" PhaseCleanup = "cleanup" PhaseRollback = "rollback" )
Installation phases
Variables ¶
This section is empty.
Functions ¶
func ExtractRepoPath ¶
ExtractRepoPath extracts owner/repo from a Git URL
func GetInstallationPhase ¶
GetInstallationPhase extracts the installation phase from an error
func InstallCommand ¶
func InstallCommand(ctx context.Context, opts IntegrationOptions, addToConfig bool) error
InstallCommand performs an integrated installation with project management
func InstallFromConfig ¶
InstallFromConfig installs all commands defined in a project's ccmd.yaml
func IsRetryableError ¶
IsRetryableError checks if an installation error is retryable
func NormalizeRepositoryURL ¶
NormalizeRepositoryURL normalizes various repository formats to a full URL
func ParseRepositorySpec ¶
ParseRepositorySpec parses a repository specification (URL[@version])
Types ¶
type CommandManager ¶
type CommandManager struct {
// contains filtered or unexported fields
}
CommandManager provides high-level command management operations
func NewCommandManager ¶
func NewCommandManager(projectPath string) *CommandManager
NewCommandManager creates a new command manager
func (*CommandManager) GetInstalledCommands ¶
func (cm *CommandManager) GetInstalledCommands() ([]InstalledCommand, error)
GetInstalledCommands returns a list of installed commands
func (*CommandManager) Install ¶
func (cm *CommandManager) Install(ctx context.Context, repository, version, name string, force bool) error
Install installs a command with the given options
func (*CommandManager) InstallFromProject ¶
func (cm *CommandManager) InstallFromProject(ctx context.Context, force bool) error
InstallFromProject installs all commands from the project's ccmd.yaml
type GitClient ¶
type GitClient interface {
Clone(opts git.CloneOptions) error
ValidateRemoteRepository(url string) error
GetLatestTag(repoPath string) (string, error)
GetCurrentCommit(repoPath string) (string, error)
IsGitRepository(path string) bool
}
GitClient defines the interface for git operations
type InstallationError ¶
type InstallationError struct {
// contains filtered or unexported fields
}
InstallationError represents an error that occurred during installation
func NewInstallationError ¶
func NewInstallationError(code errors.ErrorCode, message, repository, version, phase string) *InstallationError
NewInstallationError creates a new installation error
func (*InstallationError) Error ¶
func (e *InstallationError) Error() string
Error implements the error interface
func (*InstallationError) GetCode ¶
func (e *InstallationError) GetCode() errors.ErrorCode
GetCode returns the error code
func (*InstallationError) Phase ¶
func (e *InstallationError) Phase() string
Phase returns the installation phase where the error occurred
func (*InstallationError) Repository ¶
func (e *InstallationError) Repository() string
Repository returns the repository that was being installed
func (*InstallationError) Unwrap ¶
func (e *InstallationError) Unwrap() error
Unwrap returns the underlying error
func (*InstallationError) Version ¶
func (e *InstallationError) Version() string
Version returns the version that was being installed
type InstalledCommand ¶
type InstalledCommand struct {
Name string
Version string
Description string
Author string
Repository string
Path string
}
InstalledCommand represents an installed command
type Installer ¶
type Installer struct {
// contains filtered or unexported fields
}
Installer manages the command installation process
type IntegrationOptions ¶
type IntegrationOptions struct {
Repository string // Git repository URL or shorthand (e.g., "user/repo")
Version string // Version/tag to install
Name string // Override command name
Force bool // Force reinstall
ProjectPath string // Path to project (for ccmd.yaml updates)
GlobalInstall bool // Install globally vs project-local
}
IntegrationOptions provides options for integrated installation
type MultiValidationError ¶
type MultiValidationError struct {
Errors []ValidationError
}
MultiValidationError represents multiple validation errors
func (*MultiValidationError) Add ¶
func (e *MultiValidationError) Add(field, message string)
Add adds a validation error
func (*MultiValidationError) Error ¶
func (e *MultiValidationError) Error() string
Error implements the error interface
func (*MultiValidationError) HasErrors ¶
func (e *MultiValidationError) HasErrors() bool
HasErrors returns true if there are any validation errors
type Options ¶
type Options struct {
Repository string // Git repository URL
Version string // Version/tag to install (optional)
Name string // Override command name (optional)
Force bool // Force reinstall if already exists
InstallDir string // Directory to install commands (default: .claude/commands)
FileSystem fs.FileSystem // File system interface (for testing)
GitClient GitClient // Git client interface (for testing)
TempDirPrefix string // Prefix for temporary directories
ProjectPath string // Path to project root (where ccmd.yaml lives)
}
Options represents configuration for the installer
type ValidationError ¶
ValidationError represents a validation error during installation
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
Error implements the error interface