agentCLAWHUBUnverified

golang-spf13-viper

Golang configuration library using spf13/viper — layered precedence (flag > env > file > KV > default), BindPFlag/BindPFlags, SetEnvPrefix + SetEnvKeyReplacer + AutomaticEnv, ReadInConfig + ConfigFileNotFoundError, Unmarshal + mapstructure struct tags, Sub for sub-trees, WatchConfig + OnConfigChange for hot reload, viper.New() for test isolation, and remote KV integration. Apply when using or adopting spf13/viper, or when the codebase imports `github.com/spf13/viper`. For CLI command structure alongside viper, see the `samber/cc-skills-golang@golang-spf13-cobra` skill. For general CLI architecture, see `samber/cc-skills-golang@golang-cli`.

OpenClaw

Rank

62

Safety

84

Downloads

1.1k

Updated

Oct 11, 2026

Version

1.1.0

Source

CLAWHUB

About

What it does, and when to use it.

Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.

Avoid when

  • Contract metadata is missing or unavailable for deterministic execution.

Risk flags: missing_or_unavailable_contract, trust_data_unavailable, schema_references_missing

Public facts

Every fact links back to the source it came from.

Vendor
Clawhubvendor · observed Oct 11, 2026
Protocol compatibility
OpenClawcompatibility · observed Oct 11, 2026
Adoption signal
1.1K downloadsadoption · observed Oct 11, 2026
Latest release
1.1.0release · observed Aug 23, 2026
Handshake status
UNKNOWNsecurity

Install and run

Setup complexity: medium.

clawhub skill install s173arkhs3131fq5jf769qq75583hdgt:golang-spf13-viper
  1. Install using `clawhub skill install s173arkhs3131fq5jf769qq75583hdgt:golang-spf13-viper` in an isolated environment before connecting it to live workloads.
  2. No published capability contract is available yet, so validate auth and request/response behavior manually.
  3. Review the upstream CLAWHUB listing at https://clawhub.ai/samber/golang-spf13-viper before using production credentials.

Contract: missing

curl -s "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/snapshot"

Documentation

CLAWHUB

144,483 characters of source documentation, loaded on request.

Extracted files

5 files captured from the source.

SKILL.md

---
name: golang-spf13-viper
description: "Golang configuration library using spf13/viper — layered precedence (flag > env > file > KV > default), BindPFlag/BindPFlags, SetEnvPrefix + SetEnvKeyReplacer + AutomaticEnv, ReadInConfig + ConfigFileNotFoundError, Unmarshal + mapstructure struct tags, Sub for sub-trees, WatchConfig + OnConfigChange for hot reload, viper.New() for test isolation, and remote KV integration. Apply when using or adopting spf13/viper, or when the codebase imports `github.com/spf13/viper`. For CLI command structure alongside viper, see the `samber/cc-skills-golang@golang-spf13-cobra` skill. For general CLI architecture, see `samber/cc-skills-golang@golang-cli`."
user-invocable: true
license: MIT
compatibility: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
metadata:
  author: samber
  version: "1.1.0"
  openclaw:
    emoji: "🔧"
    homepage: https://github.com/samber/cc-skills-golang
    requires:
      bins:
        - go
    install: []
    skill-library-version: "1.21.0"
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent WebFetch mcp__context7__resolve-library-id mcp__context7__query-docs Bash(godig:*) Bash(gopls:*) LSP mcp__gopls__*
paths:
  - "**/*.go"
---

**Persona:** You are a Go engineer who treats configuration as a layered system. Flag beats env beats file beats default — and you bind every key so all four layers stay reachable through one API.

# Using spf13/viper for layered configuration in Go

Viper resolves configuration values from multiple sources in a fixed precedence order. It has no user-facing surface — it doesn't define commands or flags. Its job is to answer "what is the value of key X right now?" by walking its source layers from highest to lowest priority.

**Official Resources:**

- [pkg.go.dev/github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper)
- [github.com/spf13/viper](https://github.com/spf13/viper)

This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See `samber/cc-skills-golang@golang-pkg-go-dev` skill (`godig`) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See `samber/cc-skills-golang@golang-gopls` skill (`gopls`). Context7 remains a fallback for docs not indexed on pkg.go.dev.

```bash
go get github.com/spf13/viper@latest
```

## Viper vs. cobra

Cobra owns the command tree — subcommands, flags, arg validation, completions. Viper owns configuration resolution — it answers "what is the value of key X?" by walking its source layers. Viper has no user-facing surface; it is purely a key-value resolver. Use cobra alone for flag-only CLIs; viper alone for config-file daemons; both when you need both, binding flags at `PersistentPreRunE` via `BindPFlag`.

→ See `samber/cc-skills-gol

_meta.json

{
  "ownerId": "kn72rhnkwjfeex9wr1n7y24qa983cjn3",
  "slug": "golang-spf13-viper",
  "version": "1.1.0",
  "publishedAt": 1787452433233
}

references/binding-and-env.md

# Viper Env Binding and Flag Binding

## The binding interaction model

Three settings control how viper maps environment variables to keys. They must be set together:

```go
viper.SetEnvPrefix("MYAPP")                           // adds MYAPP_ prefix
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // database.host → MYAPP_DATABASE_HOST
viper.AutomaticEnv()                                   // activates auto-binding
```

Call these before any `ReadInConfig` or `viper.Get*` call — typically in a root command's `PersistentPreRunE` or in `init()`.

## AutomaticEnv vs BindEnv

| Method | Behavior |
| --- | --- |
| `AutomaticEnv()` | Every key is automatically mapped to its env equivalent (with prefix and replacer applied) |
| `BindEnv(key, envVars...)` | Only the specified key is bound, to the specified env var name(s) |

Use `AutomaticEnv` for the common case. Use `BindEnv` when you need to bind to an env var with a name that doesn't follow your prefix/replacer convention (e.g., third-party env vars like `GOOGLE_APPLICATION_CREDENTIALS`).

```go
// Bind a specific non-prefixed env var
viper.BindEnv("google.credentials", "GOOGLE_APPLICATION_CREDENTIALS")
```

## SetEnvKeyReplacer in depth

Viper keys use `.` as separator for nested values. Env vars cannot contain dots. The replacer maps between them.

```go
// Config file:
// database:
//   host: localhost
//   max_conn: 25

// Without replacer:
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
viper.GetString("database.host")  // looks for MYAPP_DATABASE.HOST — no match

// With replacer:
viper.SetEnvPrefix("MYAPP")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
viper.GetString("database.host")  // looks for MYAPP_DATABASE_HOST — matches
```

The replacer operates on the viper key **before** prepending the prefix, so the lookup chain is: `database.host` → replace `.` with `_` → `database_host` → prepend prefix → `MYAPP_DATABASE_HOST`.

## AllowEmptyEnv

By default, viper ignores env vars set to the empty string — the empty string is treated as "not set" and viper continues down the precedence stack. Override this behavior:

```go
viper.AllowEmptyEnv(true)
// now MYAPP_PORT="" → viper.GetInt("port") == 0, not the default
```

## Flag binding

Bind a pflag after defining it:

```go
func init() {
    rootCmd.PersistentFlags().Int("port", 8080, "listen port")
    viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port"))
}
```

Bind an entire flag set:

```go
viper.BindPFlags(rootCmd.PersistentFlags())
```

**Timing rule:** Bind flags in `init()` or in `PersistentPreRunE`. The binding call must happen before `Execute()` parses flags — specifically, before any `viper.Get*` call on a flag-backed key. Binding after `Execute()` causes the flag's `Changed` state to be unknown, so viper may not promote the flag value to the correct precedence layer.

## How pflag binding interacts with precedence

Viper checks `flag.Changed` (whether the user explicitly passed the flag

references/sources-and-formats.md

# Viper Config Sources and File Formats

## Supported file formats

Viper detects format from file extension. Supported extensions:

| Format     | Extensions      |
| ---------- | --------------- |
| YAML       | `.yaml`, `.yml` |
| TOML       | `.toml`         |
| JSON       | `.json`         |
| HCL        | `.hcl`          |
| INI        | `.ini`          |
| Properties | `.properties`   |
| dotenv     | `.env`          |

Force a format when there is no extension:

```go
viper.SetConfigType("yaml")
viper.SetConfigFile("/etc/myapp/config")  // no extension — type required
```

## Config file search

```go
viper.SetConfigName("config")            // file name without extension
viper.SetConfigType("yaml")              // required when no extension
viper.AddConfigPath("$HOME/.myapp")      // search path 1 (highest priority when multiple)
viper.AddConfigPath("/etc/myapp/")       // search path 2
viper.AddConfigPath(".")                 // search path 3 (lowest priority)

// viper searches paths in order, stops at the first match
if err := viper.ReadInConfig(); err != nil {
    var notFound *viper.ConfigFileNotFoundError
    if !errors.As(err, &notFound) {
        return err  // real error (permission denied, malformed YAML, etc.)
    }
    // not found — continue with flags/env/defaults
}

// After reading, this returns the resolved path:
fmt.Println("Using config:", viper.ConfigFileUsed())
```

## Merging multiple config files

`MergeInConfig` merges a second config file into the current state. Later values override earlier ones for the same key.

```go
viper.SetConfigFile("base.yaml")
viper.ReadInConfig()

viper.SetConfigFile("override.yaml")
viper.MergeInConfig()  // keys from override.yaml win on collision
```

Pattern: ship a base config with the binary, let users drop an override in `~/.myapp/override.yaml`.

## Multiple config files via SetConfigFile

For environment-based config loading:

```go
env := os.Getenv("APP_ENV")
if env == "" {
    env = "development"
}
viper.SetConfigFile(fmt.Sprintf("config.%s.yaml", env))
viper.ReadInConfig()
```

## Remote KV stores (etcd, Consul)

Viper supports remote KV stores via the `viper/remote` sub-package. This keeps remote config behind an opt-in import:

```go
import _ "github.com/spf13/viper/remote"

// etcd
viper.AddRemoteProvider("etcd3", "http://127.0.0.1:2379", "/config/myapp.yaml")
viper.SetConfigType("yaml")
viper.ReadRemoteConfig()

// Consul
viper.AddRemoteProvider("consul", "localhost:8500", "myapp/config")
viper.SetConfigType("json")
viper.ReadRemoteConfig()
```

**Caution:** Remote config adds network latency to startup and a runtime dependency. Use it only when you need centralized config across many service instances. For most applications, files + env vars are sufficient.

Watch for remote changes:

```go
go func() {
    for {
        time.Sleep(5 * time.Second)
        viper.WatchRemoteConfig()
        // re-read values after watching
    }
}()
```

## Embedding config with go:embed

references/testing-and-isolation.md

# Viper Test Isolation

## The global state problem

The top-level `viper.*` functions operate on a global `*viper.Viper` instance shared across all tests in the same process. Tests that call `viper.SetConfigFile`, `viper.Set`, or `viper.ReadInConfig` pollute this global state, causing flaky test ordering.

```go
// ✗ Bad — sets global state that affects later tests
func TestPortConfig(t *testing.T) {
    viper.SetDefault("port", 8080)
    viper.Set("port", 9090)
    assert.Equal(t, 9090, viper.GetInt("port"))
    // global viper now has port=9090 for all subsequent tests
}
```

## viper.New() per test (correct approach)

```go
func TestPortConfig(t *testing.T) {
    v := viper.New()
    v.SetDefault("port", 8080)
    v.Set("port", 9090)
    assert.Equal(t, 9090, v.GetInt("port"))
}

func TestDefaultPort(t *testing.T) {
    v := viper.New()
    v.SetDefault("port", 8080)
    assert.Equal(t, 8080, v.GetInt("port"))  // clean — not affected by TestPortConfig
}
```

## Injecting viper into your app

For test isolation to work, your application code must accept a `*viper.Viper` instead of calling the global functions directly:

```go
// ✓ Good — accepts a viper instance
type Server struct {
    cfg *viper.Viper
}

func NewServer(v *viper.Viper) *Server {
    return &Server{cfg: v}
}

func (s *Server) Port() int {
    return s.cfg.GetInt("port")
}

// In tests:
func TestServer(t *testing.T) {
    v := viper.New()
    v.Set("port", 9090)
    s := NewServer(v)
    assert.Equal(t, 9090, s.Port())
}

// In main:
func main() {
    // viper setup...
    s := NewServer(viper.GetViper())  // pass the global instance in production
}
```

## Reading config files in tests

```go
func TestReadConfig(t *testing.T) {
    v := viper.New()
    v.SetConfigFile("testdata/config.yaml")
    require.NoError(t, v.ReadInConfig())
    assert.Equal(t, "localhost", v.GetString("host"))
}
```

Use `testdata/` for config files. Go test tooling sets the working directory to the package directory, so relative paths work reliably.

## t.Setenv interactions

`t.Setenv` sets an env var for the duration of a test and restores it on cleanup. Combined with `viper.New()` + `AutomaticEnv`, this lets you test env var binding without global pollution:

```go
func TestEnvBinding(t *testing.T) {
    t.Setenv("MYAPP_PORT", "9090")

    v := viper.New()
    v.SetEnvPrefix("MYAPP")
    v.AutomaticEnv()

    assert.Equal(t, 9090, v.GetInt("port"))
    // t.Setenv restores original MYAPP_PORT (or unsets it) after this test
}
```

## viper.Reset() — use with caution

`viper.Reset()` resets the global viper instance to its zero state. It is rarely the right solution:

- It affects all code running concurrently that also uses the global viper.
- It does not stop any active `WatchConfig` goroutines.
- Using it in `TestMain` or `t.Cleanup` makes tests order-dependent.

Prefer `viper.New()` per test. Reserve `Reset()` for tools that call into viper-based libraries and must restore state between runs.

#
Github ReposUpdated 2d agoRank 70

AionUi

Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!

MCPOPENCLAW
Github ReposUpdated 6mo agoRank 70

activepieces

AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents

OPENCLAW
Github ReposUpdated 6mo agoRank 70

cherry-studio

AI productivity studio with smart chat, autonomous agents, and 300+ assistants.

MCPOPENCLAW
Github ReposUpdated 7mo agoRank 70

CopilotKit

The Frontend for Agents & Generative UI. React + Angular

OPENCLAW

Machine-readable data

The same record, as JSON, for agents and crawlers.

{
  "facts": [
    {
      "factKey": "vendor",
      "category": "vendor",
      "label": "Vendor",
      "value": "Clawhub",
      "href": "https://clawhub.ai/samber/skills/golang-spf13-viper",
      "sourceUrl": "https://clawhub.ai/samber/skills/golang-spf13-viper",
      "sourceType": "profile",
      "confidence": "medium",
      "observedAt": "2026-10-11T08:59:02.243Z",
      "isPublic": true
    },
    {
      "factKey": "protocols",
      "category": "compatibility",
      "label": "Protocol compatibility",
      "value": "OpenClaw",
      "href": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/contract",
      "sourceUrl": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/contract",
      "sourceType": "contract",
      "confidence": "medium",
      "observedAt": "2026-10-11T08:59:02.243Z",
      "isPublic": true
    },
    {
      "factKey": "traction",
      "category": "adoption",
      "label": "Adoption signal",
      "value": "1.1K downloads",
      "href": "https://clawhub.ai/samber/golang-spf13-viper",
      "sourceUrl": "https://clawhub.ai/samber/golang-spf13-viper",
      "sourceType": "profile",
      "confidence": "medium",
      "observedAt": "2026-10-11T08:59:02.243Z",
      "isPublic": true
    },
    {
      "factKey": "latest_release",
      "category": "release",
      "label": "Latest release",
      "value": "1.1.0",
      "href": "https://clawhub.ai/samber/golang-spf13-viper",
      "sourceUrl": "https://clawhub.ai/samber/golang-spf13-viper",
      "sourceType": "release",
      "confidence": "medium",
      "observedAt": "2026-08-23T02:33:53.233Z",
      "isPublic": true
    },
    {
      "factKey": "handshake_status",
      "category": "security",
      "label": "Handshake status",
      "value": "UNKNOWN",
      "href": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/trust",
      "sourceUrl": "https://www.xpersona.co/api/v1/agents/clawhub-samber-golang-spf13-viper/trust",
      "sourceType": "trust",
      "confidence": "medium",
      "observedAt": null,
      "isPublic": true
    }
  ],
  "events": [
    {
      "eventType": "release",
      "title": "Release 1.1.0",
      "description": "golang-spf13-viper 1.1.0 - Added compatibility with Claude Code, Codex, and similar harnesses. - Expanded metadata: new allowed tools (including godig, gopls, LSP), and specified explicit file patterns. - Guidance improved: Points to `golang-pkg-go-dev` skill for package docs/symbols/vulnerabilities and `golang-gopls` for code navigation. - Removed obsolete `skill-card.md` file. - Minor copy clarifications and rewording for tips on documentation lookup, making best practices and advisory links more visible.",
      "href": "https://clawhub.ai/samber/golang-spf13-viper",
      "sourceUrl": "https://clawhub.ai/samber/golang-spf13-viper",
      "sourceType": "release",
      "confidence": "medium",
      "observedAt": "2026-08-23T02:33:53.233Z",
      "isPublic": true
    }
  ]
}

Record generated Oct 11, 2026.

Sponsored

Ads related to golang-spf13-viper and adjacent AI workflows.