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 project provides functionality for managing ccmd project files, including parsing and validating ccmd.yaml configuration files and managing the ccmd-lock.yaml lock file format.
Configuration File (ccmd.yaml) ¶
The ccmd.yaml file declares Claude commands required by a project. It should be placed at the root of your project directory.
Schema Format:
The ccmd.yaml file has a simple structure:
commands: - owner/repository@v1.0.0 - owner/repository # version defaults to latest
Example ccmd.yaml:
commands: - example/claude-command@v1.2.3 - another/command@latest - org/tool # version omitted, defaults to latest
Command Format:
Commands are specified as strings in the format "owner/repository@version" where:
- owner/repository: GitHub repository path
- @version: Optional version specifier (defaults to latest if omitted)
Version Specification:
The version after @ supports:
- Semantic versions: v1.0.0, 1.2.3, v2.0.0-beta.1
- Branch names: main, develop, feature/xyz
- Tag names: release-1.0, stable
- "latest" or omitted: uses the latest release
Repository Format:
The repository part must be in the format "owner/repository" where:
- owner: GitHub username or organization
- repository: Repository name
Both must contain only alphanumeric characters, hyphens, and underscores.
Lock File Format (ccmd-lock.yaml) ¶
The ccmd-lock.yaml file tracks exact versions and metadata for installed commands. It ensures reproducible installations by locking specific commit hashes and providing integrity verification through checksums.
Example ccmd-lock.yaml:
version: "1.0"
updated_at: 2024-01-15T10:30:00Z
commands:
gh:
name: gh
repository: github.com/cli/cli
version: v2.40.0
commit_hash: abc123def456abc123def456abc123def456abc1
installed_at: 2024-01-10T14:22:00Z
updated_at: 2024-01-15T10:30:00Z
file_size: 45678901
checksum: 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
dependencies:
- git
metadata:
arch: amd64
os: darwin
cobra-cli:
name: cobra-cli
repository: github.com/spf13/cobra-cli
version: v1.3.0
commit_hash: def456abc123def456abc123def456abc123def4
installed_at: 2024-01-12T09:15:00Z
updated_at: 2024-01-12T09:15:00Z
file_size: 12345678
checksum: abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
Lock File Fields:
- version: Lock file format version (currently "1.0")
- updated_at: Last modification timestamp of the lock file
- commands: Map of installed commands by name
Command Fields:
- name: Command name (must match the map key)
- repository: Source repository URL
- version: Version specifier used during installation (tag, branch, or commit)
- commit_hash: Exact 40-character git commit SHA
- installed_at: Initial installation timestamp
- updated_at: Last update timestamp
- file_size: Size of the installed binary in bytes
- checksum: SHA256 hash of the installed binary (64 characters)
- dependencies: Optional list of runtime dependencies
- metadata: Optional key-value pairs for additional information
The lock file ensures that:
- Installations can be reproduced exactly using the commit hash
- Binary integrity can be verified using the checksum
- Installation history is tracked with timestamps
- Dependencies and metadata provide context for troubleshooting
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. Package project provides functionality for managing ccmd project files.
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 ¶
package main
import (
"fmt"
"log"
"path/filepath"
"strings"
"time"
"github.com/gifflet/ccmd/internal/fs"
"github.com/gifflet/ccmd/pkg/project"
)
func main() {
// Create a new lock file
lockFile := project.NewLockFile()
// Add a command entry
ghCmd := &project.CommandLockInfo{
Name: "gh",
Source: "github.com/cli/cli",
Version: "v2.40.0",
Commit: strings.Repeat("a", 40), // Example SHA
Resolved: "github.com/cli/cli@v2.40.0",
InstalledAt: time.Now(),
UpdatedAt: time.Now(),
Dependencies: []string{"git"},
Metadata: map[string]string{
"arch": "amd64",
"os": "darwin",
},
}
if err := lockFile.AddCommand(ghCmd); err != nil {
log.Fatal(err)
}
// Save to file
lockFilePath := filepath.Join("/tmp", "ccmd-lock.yaml")
fileSystem := fs.OS{}
if err := lockFile.SaveToFile(lockFilePath, fileSystem); err != nil {
log.Fatal(err)
}
// Load from file
loaded, err := project.LoadFromFile(lockFilePath, fileSystem)
if err != nil {
log.Fatal(err)
}
// Retrieve a command
if cmd, exists := loaded.GetCommand("gh"); exists {
fmt.Printf("Command: %s\n", cmd.Name)
fmt.Printf("Version: %s\n", cmd.Version)
fmt.Printf("Repository: %s\n", cmd.Source)
}
}
Output: Command: gh Version: v2.40.0 Repository: github.com/cli/cli
Index ¶
- Constants
- func CalculateChecksum(filepath string) (string, error)
- func SaveConfig(config *Config, path string, fileSystem fs.FileSystem) error
- func WriteConfig(config *Config, w io.Writer) error
- type Command
- type CommandLockInfo
- type Config
- type ConfigCommand
- type LockFile
- func (lf *LockFile) AddCommand(cmd *Command) error
- func (lf *LockFile) GetCommand(name string) (*Command, bool)
- func (lf *LockFile) ListCommands() ([]*CommandLockInfo, error)
- func (lf *LockFile) RemoveCommand(name string) bool
- func (lf *LockFile) SaveToFile(filepath string, fileSystem fs.FileSystem) error
- func (lf *LockFile) SetCommand(name string, cmd *CommandLockInfo)
- func (lf *LockFile) Validate() error
- type LockManager
- func (m *LockManager) AddCommand(cmd interface{}) error
- func (m *LockManager) GetCommand(name string) (*CommandLockInfo, error)
- func (m *LockManager) HasCommand(name string) bool
- func (m *LockManager) ListCommands() ([]*CommandLockInfo, error)
- func (m *LockManager) Load() error
- func (m *LockManager) RemoveCommand(name string) error
- func (m *LockManager) Save() error
- func (m *LockManager) UpdateCommand(cmd interface{}) error
- type Manager
- func (m *Manager) AddCommand(repo, version string) error
- func (m *Manager) CommandExists(name string) (bool, error)
- func (m *Manager) ConfigExists() bool
- func (m *Manager) ConfigPath() string
- func (m *Manager) InitializeConfig() error
- func (m *Manager) InitializeLock() error
- func (m *Manager) LoadConfig() (*Config, error)
- func (m *Manager) LoadLegacyLockFile() (*LockFile, error)
- func (m *Manager) LoadLockFile() (*LockFile, error)
- func (m *Manager) LockExists() bool
- func (m *Manager) LockPath() string
- func (m *Manager) MigrateLegacyLockFile() error
- func (m *Manager) RemoveCommand(repo string) error
- func (m *Manager) SaveConfig(config *Config) error
- func (m *Manager) SaveLockFile(lockFile *LockFile) error
- func (m *Manager) Sync() error
- func (m *Manager) UpdateCommand(repo, newVersion string) error
- func (m *Manager) UpdateCommandInLockFile(cmd *Command) error
- type ModelCommand
Examples ¶
Constants ¶
const ( // ConfigFileName is the default name for the configuration file ConfigFileName = "ccmd.yaml" // LockFileName is the default name for the lock file LockFileName = "ccmd-lock.yaml" )
const LockFileVersion = "1.0"
LockFileVersion represents the current version of the lock file format
Variables ¶
This section is empty.
Functions ¶
func CalculateChecksum ¶
CalculateChecksum calculates the SHA256 checksum of a file
Example ¶
package main
import (
"fmt"
"github.com/gifflet/ccmd/pkg/project"
)
func main() {
// In practice, this would be the path to an installed binary
binaryPath := "/usr/local/bin/gh"
// Calculate the SHA256 checksum
checksum, err := project.CalculateChecksum(binaryPath)
if err != nil {
// Handle error - file might not exist in this example
fmt.Println("Error calculating checksum")
return
}
fmt.Printf("Checksum length: %d characters\n", len(checksum))
// Output would be: Checksum length: 64 characters
}
Output:
func SaveConfig ¶
func SaveConfig(config *Config, path string, fileSystem fs.FileSystem) error
SaveConfig saves a Config to a ccmd.yaml file
Types ¶
type CommandLockInfo ¶
type CommandLockInfo struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Source string `yaml:"source"`
Resolved string `yaml:"resolved"`
Commit string `yaml:"commit,omitempty"`
InstalledAt time.Time `yaml:"installed_at"`
UpdatedAt time.Time `yaml:"updated_at"`
Dependencies []string `yaml:"dependencies,omitempty"`
Metadata map[string]string `yaml:"metadata,omitempty"`
}
CommandLockInfo represents a locked command entry according to PRD
func (*CommandLockInfo) Validate ¶
func (c *CommandLockInfo) Validate() error
Validate validates a command entry
type Config ¶
type Config struct {
// Project metadata
Name string `yaml:"name,omitempty"`
Version string `yaml:"version,omitempty"`
Description string `yaml:"description,omitempty"`
Author string `yaml:"author,omitempty"`
Repository string `yaml:"repository,omitempty"`
Entry string `yaml:"entry,omitempty"`
Tags []string `yaml:"tags,omitempty"`
// Commands can be either strings or ConfigCommand objects
Commands interface{} `yaml:"commands"`
}
Config represents the ccmd.yaml configuration file structure
func LoadConfig ¶
func LoadConfig(path string, fileSystem fs.FileSystem) (*Config, error)
LoadConfig loads and parses a ccmd.yaml file
func ParseConfig ¶
ParseConfig parses ccmd.yaml content from a reader
func (*Config) GetCommands ¶
func (c *Config) GetCommands() ([]ConfigCommand, error)
GetCommands returns a normalized list of ConfigCommand objects
type ConfigCommand ¶
ConfigCommand represents a single command declaration in ccmd.yaml
func (*ConfigCommand) IsSemanticVersion ¶
func (c *ConfigCommand) IsSemanticVersion() bool
IsSemanticVersion checks if the version is a semantic version
func (*ConfigCommand) ParseOwnerRepo ¶
func (c *ConfigCommand) ParseOwnerRepo() (owner, repo string, err error)
ParseOwnerRepo extracts owner and repo name from the repo field
func (*ConfigCommand) Validate ¶
func (c *ConfigCommand) Validate() error
Validate performs validation on a ConfigCommand
type LockFile ¶
type LockFile struct {
Version string `yaml:"version"`
LockfileVersion int `yaml:"lockfileVersion"`
Commands map[string]*CommandLockInfo `yaml:"commands"`
}
LockFile represents the ccmd-lock.yaml file structure
func LoadFromFile ¶
func LoadFromFile(filepath string, fileSystem fs.FileSystem) (*LockFile, error)
LoadFromFile loads a lock file from disk
func NewLockFile ¶
func NewLockFile() *LockFile
NewLockFile creates a new lock file with the current version
func (*LockFile) AddCommand ¶
AddCommand adds or updates a command in the lock file
Example ¶
package main
import (
"fmt"
"log"
"strings"
"time"
"github.com/gifflet/ccmd/pkg/project"
)
func main() {
lockFile := project.NewLockFile()
// Add multiple commands
commands := []*project.CommandLockInfo{
{
Name: "cobra-cli",
Source: "github.com/spf13/cobra-cli",
Version: "v1.3.0",
Commit: strings.Repeat("c", 40),
Resolved: "github.com/spf13/cobra-cli@v1.3.0",
InstalledAt: time.Now(),
UpdatedAt: time.Now(),
},
{
Name: "golangci-lint",
Source: "github.com/golangci/golangci-lint",
Version: "v1.55.0",
Commit: strings.Repeat("e", 40),
Resolved: "github.com/golangci/golangci-lint@v1.55.0",
InstalledAt: time.Now(),
UpdatedAt: time.Now(),
},
}
for _, cmd := range commands {
if err := lockFile.AddCommand(cmd); err != nil {
log.Printf("Failed to add %s: %v", cmd.Name, err)
}
}
fmt.Printf("Total commands: %d\n", len(lockFile.Commands))
}
Output: Total commands: 2
func (*LockFile) GetCommand ¶
GetCommand retrieves a command by name
func (*LockFile) ListCommands ¶
func (lf *LockFile) ListCommands() ([]*CommandLockInfo, error)
ListCommands returns a list of all commands in the lock file
func (*LockFile) RemoveCommand ¶
RemoveCommand removes a command from the lock file
func (*LockFile) SaveToFile ¶
func (lf *LockFile) SaveToFile(filepath string, fileSystem fs.FileSystem) error
SaveToFile saves the lock file to disk
func (*LockFile) SetCommand ¶
func (lf *LockFile) SetCommand(name string, cmd *CommandLockInfo)
SetCommand sets or updates a command in the lock file (compatibility method)
type LockManager ¶
type LockManager struct {
// contains filtered or unexported fields
}
LockManager provides compatibility wrapper for old internal/lock.Manager
func NewLockManager ¶
func NewLockManager(dir string) *LockManager
NewLockManager creates a new lock manager for the given directory
func NewLockManagerWithFS ¶
func NewLockManagerWithFS(dir string, fileSystem fs.FileSystem) *LockManager
NewLockManagerWithFS creates a new lock manager with a file system (for testing)
func (*LockManager) AddCommand ¶
func (m *LockManager) AddCommand(cmd interface{}) error
AddCommand adds a command to the lock file
func (*LockManager) GetCommand ¶
func (m *LockManager) GetCommand(name string) (*CommandLockInfo, error)
GetCommand retrieves a command by name
func (*LockManager) HasCommand ¶
func (m *LockManager) HasCommand(name string) bool
HasCommand checks if a command exists in the lock file
func (*LockManager) ListCommands ¶
func (m *LockManager) ListCommands() ([]*CommandLockInfo, error)
ListCommands returns all commands in the lock file
func (*LockManager) RemoveCommand ¶
func (m *LockManager) RemoveCommand(name string) error
RemoveCommand removes a command from the lock file
func (*LockManager) UpdateCommand ¶
func (m *LockManager) UpdateCommand(cmd interface{}) error
UpdateCommand updates an existing command
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager provides high-level operations for project file management
func NewManager ¶
NewManager creates a new project manager for the given directory
func NewManagerWithFS ¶
func NewManagerWithFS(rootDir string, fileSystem fs.FileSystem) *Manager
NewManagerWithFS creates a new project manager with a custom filesystem
func (*Manager) AddCommand ¶
AddCommand adds a new command to the configuration
func (*Manager) CommandExists ¶
CommandExists checks if a command is installed in the project
func (*Manager) ConfigExists ¶
ConfigExists checks if the config file exists
func (*Manager) ConfigPath ¶
ConfigPath returns the full path to the config file
func (*Manager) InitializeConfig ¶
InitializeConfig creates a new config file with default values
func (*Manager) InitializeLock ¶
InitializeLock creates a new lock file
func (*Manager) LoadConfig ¶
LoadConfig loads the project configuration file
func (*Manager) LoadLegacyLockFile ¶
LoadLegacyLockFile loads commands.lock JSON file for migration
func (*Manager) LoadLockFile ¶
LoadLockFile loads the project lock file
func (*Manager) LockExists ¶
LockExists checks if the lock file exists
func (*Manager) MigrateLegacyLockFile ¶
MigrateLegacyLockFile migrates from commands.lock to ccmd-lock.yaml
func (*Manager) RemoveCommand ¶
RemoveCommand removes a command from the configuration
func (*Manager) SaveConfig ¶
SaveConfig saves the project configuration file
func (*Manager) SaveLockFile ¶
SaveLockFile saves the project lock file
func (*Manager) Sync ¶
Sync ensures the lock file is in sync with the configuration This is a placeholder for future implementation that would: 1. Check all commands in config are in lock file 2. Remove commands from lock that are not in config 3. Update versions as needed
func (*Manager) UpdateCommand ¶
UpdateCommand updates a command's version in the configuration
func (*Manager) UpdateCommandInLockFile ¶
UpdateCommandInLockFile updates command information in the lock file
type ModelCommand ¶
type ModelCommand struct {
Name string `json:"name"`
Version string `json:"version"`
Source string `json:"source"`
Repository string `json:"repository"`
CommitHash string `json:"commit_hash,omitempty"`
InstalledAt time.Time `json:"installed_at"`
UpdatedAt time.Time `json:"updated_at"`
FileSize int64 `json:"file_size,omitempty"`
Checksum string `json:"checksum,omitempty"`
Dependencies []string `json:"dependencies,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
ModelCommand provides compatibility with internal/models.Command
func FromCommandLockInfo ¶
func FromCommandLockInfo(cmd *CommandLockInfo) *ModelCommand
FromCommandLockInfo creates ModelCommand from CommandLockInfo
func (*ModelCommand) ToCommandLockInfo ¶
func (c *ModelCommand) ToCommandLockInfo() *CommandLockInfo
ToCommandLockInfo converts ModelCommand to CommandLockInfo