Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.23']
go-version: ['1.26']

steps:
- name: Checkout code
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.26'
cache: true

- name: Download dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.26'
cache: true

- name: golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
version: v1.64.8
version: v2.11.4
args: --timeout=5m
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.26'
cache: true

- name: Download dependencies
Expand All @@ -44,7 +44,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.26'
cache: true

- name: Run GoReleaser
Expand Down
19 changes: 2 additions & 17 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,14 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.26'
cache: true

- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest

- name: Run govulncheck
run: |
set +e
output=$(govulncheck ./... 2>&1)
exit_code=$?
echo "$output"
# Allow GO-2026-4602 (requires Go 1.25.8, not yet released)
if echo "$output" | grep -q "GO-2026-4602"; then
echo "::warning::GO-2026-4602 requires Go 1.25.8 (not yet released)"
fi
# Fail only if there are other vulnerabilities
if [ $exit_code -ne 0 ]; then
other_vulns=$(echo "$output" | grep "Vulnerability #" | grep -v "GO-2026-4602")
if [ -n "$other_vulns" ]; then
exit 1
fi
fi
run: govulncheck ./...

gosec:
name: Gosec
Expand Down
71 changes: 38 additions & 33 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
version: "2"

linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
- misspell
- revive

linters-settings:
errcheck:
check-type-assertions: true
check-blank: true

govet:
enable-all: true
disable:
- fieldalignment

revive:
settings:
errcheck:
check-type-assertions: true
check-blank: true

govet:
enable-all: true
disable:
- fieldalignment

revive:
rules:
- name: exported
arguments:
- disableStutteringCheck
- name: var-naming
- name: package-comments
disabled: true

misspell:
locale: US

exclusions:
rules:
- name: exported
arguments:
- disableStutteringCheck
- name: var-naming
- name: package-comments
disabled: true
- path: _test\.go
linters:
- errcheck

gofmt:
simplify: true
formatters:
enable:
- gofmt
- goimports

goimports:
local-prefixes: opencode-agent-switcher
settings:
gofmt:
simplify: true

misspell:
locale: US
goimports:
local-prefixes:
- opencode-agent-switcher

issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck

max-issues-per-linter: 50
max-same-issues: 3
new: false

run:
timeout: 5m
modules-download-mode: readonly
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This document provides instructions and guidelines for AI agents operating withi

`opencode-agent-switcher` is a Go CLI tool designed to manage and switch AI models and modes for various agents in the Opencode environment. It interacts with the `opencode` CLI and modifies agent configuration files.

- **Language:** Go 1.23+
- **Language:** Go 1.26+
- **Entry Point:** `main.go`
- **Module:** `opencode-agent-switcher`
- **Dependencies:**
Expand Down Expand Up @@ -230,7 +230,7 @@ Templates allow saving and restoring agent configurations (model + mode assignme
### CD Pipeline (GoReleaser)
The project uses GoReleaser for automated binary releases:

**Trigger:** Push a version tag (e.g., `v0.7.0`)
**Trigger:** Push a version tag (e.g., `v0.7.2`)

**Process:**
1. Tag push triggers `.github/workflows/release.yml`
Expand All @@ -243,8 +243,8 @@ The project uses GoReleaser for automated binary releases:
**Release Flow:**
```bash
# After merging to main and updating CHANGELOG.md
git tag -a v0.7.0 -m "Release v0.7.0"
git push origin v0.7.0
git tag -a v0.7.2 -m "Release v0.7.2"
git push origin v0.7.2
# CD pipeline automatically creates the release
```

Expand Down
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.2] - 2026-04-02

### Security
- Updated Go version from 1.23 to 1.26
- Fixes GO-2026-4602: FileInfo can escape from a Root in os
- CVE-2026-27139 (Medium severity)
- Ensures continued support (Go 1.23 ended Feb 11, 2026)
- Removed temporary workaround for GO-2026-4602 in security.yml

### Changed
- Updated CI/CD workflows to use Go 1.26
- Updated golangci-lint from v1.64.8 to v2.11.4 (built with Go 1.26)
- Migrated .golangci.yml to v2 format
- Updated golangci-lint-action from v6 to v7
- Updated documentation to reflect Go 1.26+ requirement

## [0.7.1] - 2026-04-02

### Fixed
Expand Down Expand Up @@ -127,7 +143,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- YAML frontmatter parsing for agent configuration files
- MIT License

[Unreleased]: https://github.com/mario-gc/opencode-agent-switcher/compare/v0.7.1...HEAD
[Unreleased]: https://github.com/mario-gc/opencode-agent-switcher/compare/v0.7.2...HEAD
[0.7.2]: https://github.com/mario-gc/opencode-agent-switcher/compare/v0.7.1...v0.7.2
[0.7.1]: https://github.com/mario-gc/opencode-agent-switcher/compare/v0.7.0...v0.7.1
[0.7.0]: https://github.com/mario-gc/opencode-agent-switcher/compare/v0.6.1...v0.7.0
[0.6.1]: https://github.com/mario-gc/opencode-agent-switcher/compare/v0.6.0...v0.6.1
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Go Report Card](https://goreportcard.com/badge/github.com/mario-gc/opencode-agent-switcher)](https://goreportcard.com/report/github.com/mario-gc/opencode-agent-switcher)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Go Version](https://img.shields.io/badge/Go-1.23+-00ADD8?logo=go)](https://golang.org/)
[![Go Version](https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go)](https://golang.org/)

A CLI tool for managing and switching AI models and modes for Opencode agents.

Expand All @@ -28,7 +28,7 @@ A CLI tool for managing and switching AI models and modes for Opencode agents.

## Prerequisites

- **Go:** Version 1.23 or higher
- **Go:** Version 1.26 or higher
- **Opencode:** The `opencode` CLI tool must be installed and configured

## Installation
Expand All @@ -39,10 +39,10 @@ Download the latest release for your platform from the [Releases page](https://g

```bash
# Linux (amd64)
curl -sL https://github.com/mario-gc/opencode-agent-switcher/releases/latest/download/opencode-agent-switcher_0.7.0_linux_amd64.tar.gz | tar xz
curl -sL https://github.com/mario-gc/opencode-agent-switcher/releases/latest/download/opencode-agent-switcher_0.7.2_linux_amd64.tar.gz | tar xz

# Linux (arm64)
curl -sL https://github.com/mario-gc/opencode-agent-switcher/releases/latest/download/opencode-agent-switcher_0.7.0_linux_arm64.tar.gz | tar xz
curl -sL https://github.com/mario-gc/opencode-agent-switcher/releases/latest/download/opencode-agent-switcher_0.7.2_linux_arm64.tar.gz | tar xz

# Make executable
chmod +x opencode-agent-switcher
Expand Down Expand Up @@ -85,7 +85,7 @@ opencode-agent-switcher

```bash
opencode-agent-switcher --version
# Output: opencode-agent-switcher 0.7.0 (commit: abc1234, built: 2026-04-02)
# Output: opencode-agent-switcher 0.7.2 (commit: abc1234, built: 2026-04-02)
```

### Workflow
Expand Down
17 changes: 17 additions & 0 deletions agents/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const maxFrontmatterSize = 64 * 1024

var validSegment = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_\-\.]*$`)

// ValidateModelID validates a model ID string format.
// Returns an error if the model ID is empty, exceeds 256 characters, or has invalid segments.
func ValidateModelID(modelID string) error {
if modelID == "" {
return fmt.Errorf("model ID cannot be empty")
Expand Down Expand Up @@ -58,6 +60,8 @@ func isPathWithinDir(path, dir string) (bool, error) {
return strings.HasPrefix(absPath, absDir+string(filepath.Separator)), nil
}

// LoadAllAgents loads agents from all available sources.
// Returns a deduplicated list with project configurations taking precedence over global ones.
func LoadAllAgents() ([]models.Agent, error) {
agentMap := make(map[string]models.Agent)

Expand Down Expand Up @@ -114,10 +118,15 @@ func getProjectAgentsDir() string {
return filepath.Join(".", ".opencode", "agents")
}

// LoadAgents loads agents from a directory of markdown files.
// Deprecated: Use LoadAgentsFromDir for more control over source metadata.
func LoadAgents(agentsDir string) ([]models.Agent, error) {
return LoadAgentsFromDir(agentsDir, models.SourceGlobal, models.FormatMarkdown)
}

// LoadAgentsFromDir loads agents from markdown files in a directory.
// Parameters location and format specify the source metadata for each agent.
// Returns a list of agents with valid model fields.
func LoadAgentsFromDir(agentsDir, location, format string) ([]models.Agent, error) {
absAgentsDir, err := filepath.Abs(agentsDir)
if err != nil {
Expand Down Expand Up @@ -185,6 +194,8 @@ func LoadAgentsFromDir(agentsDir, location, format string) ([]models.Agent, erro
return agentList, nil
}

// ParseFrontmatter extracts YAML frontmatter from markdown content.
// Returns a map of frontmatter fields or an error if parsing fails.
func ParseFrontmatter(content string) (map[string]interface{}, error) {
if !strings.HasPrefix(content, "---") {
return nil, fmt.Errorf("no frontmatter found")
Expand All @@ -208,6 +219,9 @@ func ParseFrontmatter(content string) (map[string]interface{}, error) {
return frontmatter, nil
}

// UpdateAgentModel updates the model field for an agent.
// Supports both markdown files and JSON configuration files.
// Returns an error if the model ID is invalid or the file cannot be updated.
func UpdateAgentModel(agentPath, agentName, newModel string) error {
if err := ValidateModelID(newModel); err != nil {
return fmt.Errorf("invalid model ID: %w", err)
Expand All @@ -231,6 +245,9 @@ func UpdateAgentModel(agentPath, agentName, newModel string) error {
return updateAgentFieldInMarkdown(agentPath, "model", newModel)
}

// UpdateAgentMode updates the mode field for an agent.
// Supports both markdown files and JSON configuration files.
// Returns an error if the mode is invalid (must be 'primary', 'subagent', or 'all').
func UpdateAgentMode(agentPath, agentName, newMode string) error {
if !isValidMode(newMode) {
return fmt.Errorf("invalid mode: must be 'primary', 'subagent', or 'all'")
Expand Down
Loading
Loading