Skip to content

Commit f70da2f

Browse files
ostermanClaude (via Conductor)claudeaknysh
authored
feat: Remove deep exits from version command (#1783)
* feat: Remove deep exits from version command and allow execution with invalid config The version command is a diagnostic tool that must always work, even when atmos.yaml is invalid, missing, or malformed. This change: 1. Removes the log.Fatal() deep exit from internal/exec/version.go, replacing it with proper error returns 2. Adds version command detection in cmd/root.go PersistentPreRunE to suppress config errors for version 3. Moves InitializeMarkdown() calls after error checking to prevent deep exits with invalid configs 4. Adds comprehensive integration tests ensuring version works with invalid YAML, aliases, and schema 5. Documents the version command implementation and avoiding deep exits pattern in PRDs 6. Excludes test fixtures from YAML validation in pre-commit hooks This establishes the pattern for refactoring all commands to remove deep exits, enabling proper error handling, better testability, and command composability through the registry pattern. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Enable Forbidigo enforcement for viper.BindEnv/BindPFlag and update flag-handler agent The pkg/flags/ infrastructure has been fully implemented but documentation and linter rules were outdated, leading to incorrect advice about flag handling. **Linter Changes (.golangci.yml):** - Uncommented Forbidigo rules banning viper.BindEnv() outside pkg/flags/ - Uncommented Forbidigo rules banning viper.BindPFlag() outside pkg/flags/ - Updated comments from "FUTURE ENFORCEMENT" to "ENFORCED" - Linter now catches any direct Viper binding calls and directs to flag-handler agent **Documentation Changes (CLAUDE.md):** - Removed outdated "pkg/flags/ does not exist yet" warnings - Added comprehensive flag handling documentation showing correct StandardParser pattern - Documented that viper.BindEnv/BindPFlag are BANNED with Forbidigo enforcement - Added example code showing correct usage with flags.NewStandardParser() - References cmd/version/version.go as the canonical example **Agent Updates (.claude/agents/flag-handler.md):** - Updated description to reflect pkg/flags/ is FULLY IMPLEMENTED - Expanded AUTO-INVOKE triggers to catch all flag-related discussions - Removed all "future architecture" warnings - Updated mission to enforce correct patterns, not describe hypothetical ones - Agent now correctly states that viper.BindEnv/BindPFlag are banned This prevents future incidents where incorrect flag handling advice is given. The flag-handler agent should now be automatically invoked whenever flags are discussed, and Forbidigo will catch any violations in code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Skip markdown initialization when config invalid and add missing periods Two refinements to configuration error handling and documentation compliance: **Configuration Error Handling (cmd/root.go):** - Wrap InitializeMarkdown calls in guard checking if initErr == nil - Prevents calling markdown renderers when config is invalid - Previously moved after error check but still called unconditionally - Now only initializes markdown when config loaded successfully **Documentation Compliance (CLAUDE.md):** - Add missing terminal periods per godot linter rules - Line 386: "...patterns" → "...patterns." - Line 435: "...implementation" → "...implementation." - All comments and documentation must end with periods These changes ensure version command can run even when markdown initialization would fail due to invalid config, and maintain documentation linter compliance. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * docs: Update version command PRD with final implementation and diagnostic rationale Update the atmos-version-command.md PRD to reflect the final working state of the version command implementation and strengthen the diagnostic command rationale. **Implementation Updates:** - Updated Execute() code snippet to show the `if initErr == nil` guard around InitializeMarkdown calls - Updated line number reference from 568-581 to 571-586 (current state) - Expanded Key Design Decision explanation with double-guard rationale: 1. Version command check happens first (allows version with invalid config) 2. Markdown renderers skipped when config invalid (prevents deep exit) - Clarified that without the initErr guard, version would still fail despite bypass logic **Diagnostic Command Rationale:** - Added prominent statement: "Version is a diagnostic command. Users run it first when troubleshooting. Therefore, it must work even when everything else is broken." - Expanded list of scenarios when users run version (debugging, upgrades, installation verification) - Added explicit list of config failures version must tolerate (invalid YAML, aliases, schema, missing files, corrupted state) - Emphasized that version is the foundation of diagnostic workflow - Highlighted the bootstrapping problem: can't fix config without knowing version The PRD now accurately documents the final implementation with the markdown initialization guard and provides comprehensive rationale for why version must be exceptionally resilient. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Fix pre-commit hook configuration issues Fixed two pre-commit hook failures: 1. **golangci-lint config validation**: - Removed invalid `exclude` properties from forbidigo pattern objects - The forbidigo linter schema doesn't allow `exclude` as an additional property within pattern objects - Moved exclusions to proper `issues.exclusions.rules` section - Added exclusion rules for `viper.BindEnv` and `viper.BindPFlag` in `pkg/flags/` and test files 2. **YAML validation**: - Added `tests/fixtures/` to check-yaml exclude pattern - The `tests/fixtures/scenarios/invalid-config-yaml/atmos.yaml` file is intentionally invalid (test fixture) - Pre-commit was incorrectly trying to validate this test fixture The forbidigo configuration now follows the correct schema: - Pattern objects only contain `pattern`, `msg`, and optionally `pkg` fields - Exclusions are handled via `issues.exclusions.rules` with `path` and `text` matching 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * feat: Leverage atmos error infrastructure in version command Enhanced error handling in version command to use the new error builder infrastructure from PR #1763: **Error Sentinels Added (errors/errors.go):** - ErrVersionDisplayFailed - Failed to display version information - ErrVersionCheckFailed - Failed to check for version updates - ErrVersionFormatInvalid - Invalid version output format - ErrVersionCacheLoadFailed - Failed to load version check cache - ErrVersionGitHubAPIFailed - Failed to query GitHub API for releases **Config Error Enrichment (cmd/root.go):** - Version command config errors now include helpful hints - Other commands' config errors provide actionable guidance - Exit code 2 for configuration/usage errors **Version Command Error Enrichment (internal/exec/version.go):** - Invalid format errors include examples and hints - GitHub API errors explain rate limiting and connectivity issues - Cache loading errors provide helpful context - All errors include structured debugging context **Example File Added:** - internal/exec/examples/version_format_example.md - Shows correct usage for JSON and YAML formats - Demonstrates piping to jq and version checking **Benefits:** - Improved user experience with actionable error messages - Better debugging with structured context - Consistent error handling across the codebase - Maintains version command resilience (exits 0 for warnings) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Correct version format example to match Atmos conventions Fixed the version format example file to follow the terse, concise format used throughout Atmos cmd/markdown usage files. **Changes:** - Rewrote version_format_example.md to use dash-prefixed descriptions - Removed verbose headers (###), sample outputs, and explanations - Kept only essential usage patterns matching cmd/markdown/* format - Location remains internal/exec/examples/ (correct for go:embed) **Updated atmos-errors agent:** - Documented correct format for usage examples in error messages - Clarified difference between error examples and CLI help files - Added guidelines based on existing cmd/markdown/ patterns **Format now matches:** ``` - Brief description \`\`\` $ command example \`\`\` ``` This aligns with all existing usage files in cmd/markdown/ while keeping the file in the correct location for embedding from internal/exec/. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Move version format example inline and follow Atmos conventions Removed the `internal/exec/examples/` directory pattern and converted the version format example to an inline constant, which is cleaner and follows Go best practices. **Changes:** - Converted `version_format_example.md` to inline const in version.go - Removed `_ "embed"` import (no longer needed) - Changed from `WithExampleFile()` to `WithExample()` - Deleted `internal/exec/examples/` directory - Added reference copy to `cmd/version/markdown/` for consistency **Rationale:** - Go embed doesn't allow `../` paths across module boundaries - `internal/exec/` cannot embed from `cmd/version/markdown/` - Inline constants are simpler and don't create new directory patterns - Reference file in `cmd/version/markdown/` maintains consistency with other version subcommand usage files This aligns with Atmos patterns while avoiding the creation of a new `internal/exec/examples/` convention. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Enrich invalid log level errors with markdown formatting This commit fixes a regression where invalid log level errors were displayed as plain text instead of markdown-formatted errors with proper sections (# Error, ## Explanation). Changes: - Updated pkg/logger/utils.go to return properly wrapped errors - Enhanced cmd/root.go Execute() to enrich log level errors before returning them, initializing markdown renderer when needed - Modified pkg/config/utils.go checkConfig() to preserve error format - Fixed internal/exec/version_test.go to use errors.Is() instead of exact equality check for enriched errors - Refactored error handling into handleConfigInitError() to reduce complexity and improve maintainability The fix ensures that when an invalid log level is detected during config validation, the error is enriched with explanation text and properly formatted as markdown for user-friendly output. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Update version test to use correct error sentinel and fix JSON regex This commit fixes two test failures: 1. TestDisplayVersionInFormat/Invalid_format: Updated to expect errUtils.ErrVersionFormatInvalid instead of ErrInvalidFormat, and added errUtils import to version_test.go 2. TestCLICommands/version_--format_json_with_invalid_config: Fixed regex pattern to handle multi-line JSON output by adding (?s) flag to make . match newlines These failures occurred because the version command returns enriched errors with exit codes, and the JSON output is formatted with newlines for readability. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * test: Fix version test failures and add error context testing helpers Fixed two failing tests in version_additional_test.go: 1. TestVersionExec_Execute_PrintStyledTextError - Added missing mock expectation for PrintMessage("") call that occurs before PrintStyledText 2. TestDisplayVersionInFormat_ErrorContexts - Updated to use errors.GetAllSafeDetails to traverse error chain for context validation, replacing simple string contains check that failed with error builder Added new testing helpers in errors/testing.go: - HasContext(err, key, value) - Check if error has specific context - GetContext(err, key) - Retrieve context value by key These helpers simplify error context assertions from 13 lines of boilerplate to a single line, making tests cleaner and more maintainable. Example usage: assert.True(t, errUtils.HasContext(err, "format", "xml")) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * refactor: Remove pointless error enrichment from debug log Removed error enrichment that added user-facing hints to an error that was only logged at DEBUG level and never shown to users. The version command continues successfully regardless of config errors, so enriching the error with hints like "The version command works even with invalid configuration" is contradictory and wasteful. Before: - Built enriched error with hints and context - Logged to DEBUG (never shown to user) - Version command continued anyway After: - Log the original error to DEBUG - No wasted enrichment for invisible logs - Same behavior, clearer code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * test: Delete tautological tests that don't test production code Removed 7 useless tests (204 lines, 42% of the file) that were either: 1. Testing Go language features (struct initialization, field assignment) 2. Testing mock behavior instead of production logic 3. Testing trivial if statements (if disabled { return false }) Deleted tests: - TestNewVersionExec - tests that constructor returns non-nil (duh) - TestVersionStruct - tests that v.Field = "x" makes v.Field == "x" (wtf) - TestGetLatestVersion_DisabledCheck - tests if !enabled { return false } - TestIsCheckVersionEnabled_CacheLoadError - tests if err != nil { return false } - TestIsCheckVersionEnabled_FrequencyCheck - just returns what the mock returns - TestGetLatestVersion_EmptyReleaseTag - tests if str == "" { return false } - TestExecute_WithFormatFlag - mock expectations that prove nothing Kept tests that actually verify business logic: - Error handling and enrichment - Version string comparison with "v" prefix trimming - Format validation and output generation Before: 486 lines After: 282 lines Savings: 42% reduction in test bloat 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: Fix false positive matching in HasContext error helper Fixed critical bug where HasContext("format", "xml") would match "format=xml2" or "myformat=xml" due to substring matching. Before (broken): expectedPair := key + "=" + value if strings.Contains(detailStr, expectedPair) { return true } After (correct): parts := strings.SplitN(pair, "=", 2) if parts[0] == key && parts[1] == value { return true } Now properly parses "key1=value1 key2=value2" format by: 1. Splitting on space to get individual pairs 2. Splitting each pair on first '=' (handles values with '=') 3. Exact key and value equality checks Added tests for false positive cases: - Similar key prefix: "myformat=xml" doesn't match "format" - Similar value prefix: "format=xml2" doesn't match "xml" - Value contains search: "format=xml_extended" doesn't match "xml" Uses same parsing logic as GetContext for consistency. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * docs: Fix atmos-errors agent to use succinct example format Updated agent instructions to match the actual production format we use: Before (verbose, with output): - Showed full command + output examples - Used markdown headings and sections - Included JSON/YAML output displays After (succinct, commands only): - Bullet points with single commands - NO output shown - Just `$ command` in code blocks - Matches version.go format exactly Key changes: - Examples are inline const strings (not separate files) - No go:embed directives needed - Format: `- Description\n\n` + "```" + `\n$ command\n` + "```" - Clear rules: NO output sections, keep it succinct This ensures the agent generates examples matching our production code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * refactor: Replace ugly string concatenation with go:embed Fixed terrible advice in agent instructions and production code. Before (horrible): const example = - Example + backticks + command + backticks After (clean): //go:embed examples/version_format.md var versionFormatExample string Changes: 1. Created internal/exec/examples/version_format.md with clean markdown 2. Updated version.go to use go:embed instead of string hell 3. Fixed agent instructions to recommend go:embed for multi-line content Why this matters: - No more escaped backticks and string concatenation - Proper syntax highlighting in editors - Easy to edit markdown without Go string escaping - Actually readable and maintainable The agent now correctly instructs to use go:embed for examples. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude (via Conductor) <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: Andriy Knysh <[email protected]>
1 parent d2cf477 commit f70da2f

File tree

24 files changed

+3884
-43
lines changed

24 files changed

+3884
-43
lines changed

.claude/agents/atmos-errors.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,106 @@ return errUtils.Build(errUtils.ErrComponentNotFound).
414414
Err()
415415
```
416416

417+
## Usage Examples for Error Messages
418+
419+
When creating errors with complex usage examples (especially for format or syntax errors), you can embed example content using `WithExampleFile()` or `WithExample()`.
420+
421+
### When to Include Examples
422+
423+
Include usage examples when:
424+
- Error is about invalid format/syntax (e.g., `--format json` vs `--format xml`)
425+
- Error is about missing or malformed configuration
426+
- Multiple correct usage patterns exist
427+
- User needs to see concrete examples to understand the fix
428+
429+
### Example Format Guidelines
430+
431+
**IMPORTANT: Examples should be succinct commands only, NO output.**
432+
433+
Examples should be stored in markdown files and embedded using go:embed:
434+
435+
```markdown
436+
# examples/version_format.md
437+
438+
- Display version in JSON format
439+
440+
```bash
441+
$ atmos version --format json
442+
```
443+
444+
- Display version in YAML format
445+
446+
```bash
447+
$ atmos version --format yaml
448+
```
449+
450+
- Pipe JSON output to jq
451+
452+
```bash
453+
$ atmos version --format json | jq -r .version
454+
```
455+
```
456+
457+
**Format rules:**
458+
- Each example starts with `- Description` (bullet point)
459+
- Followed by blank line
460+
- Command in triple backticks with `bash` language hint
461+
- Commands use `$` prompt
462+
- NO "Output:" sections
463+
- NO example output shown
464+
- Keep it succinct - just show the commands
465+
466+
### Where to Store Examples
467+
468+
**IMPORTANT: Use go:embed for multi-line examples.**
469+
470+
Create markdown files and embed them:
471+
472+
```go
473+
// internal/exec/version.go
474+
475+
//go:embed examples/version_format.md
476+
var versionFormatExample string
477+
478+
// Use in error:
479+
return errUtils.Build(errUtils.ErrVersionFormatInvalid).
480+
WithExplanationf("The format '%s' is not supported for version output", format).
481+
WithExample(versionFormatExample).
482+
WithHint("Use --format json for JSON output").
483+
WithHint("Use --format yaml for YAML output").
484+
WithContext("format", format).
485+
WithExitCode(2).
486+
Err()
487+
```
488+
489+
**File structure:**
490+
```
491+
internal/exec/
492+
├── version.go
493+
└── examples/
494+
├── version_format.md
495+
└── other_examples.md
496+
```
497+
498+
**Why go:embed:**
499+
- Clean, readable markdown files (not escaped strings)
500+
- Proper syntax highlighting in editors
501+
- Easy to edit without string concatenation hell
502+
- No escaped backticks or quotes
503+
504+
**Note:** This is different from CLI command usage files which go in `cmd/markdown/atmos_*_usage.md` and are displayed via `--help`.
505+
506+
### Inline Examples for Simple Cases
507+
508+
For simple examples, use `WithExample()` directly:
509+
510+
```go
511+
return errUtils.Build(errUtils.ErrInvalidFormat).
512+
WithExample("```yaml\nworkflows:\n deploy:\n steps:\n - command: terraform apply\n```").
513+
WithHint("Check your workflow syntax").
514+
Err()
515+
```
516+
417517
## Testing & Sentry
418518

419519
Test with `errors.Is()` and `errUtils.GetExitCode()`. Format with `errUtils.Format(err, config)`.

.claude/agents/flag-handler.md

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
---
22
name: flag-handler
33
description: >-
4-
Use this agent for implementing new Atmos CLI commands using unified flag parsing and command registry pattern. Expert in CommandProvider interface, StandardParser, and Cobra integration.
5-
6-
**Invoke when:**
7-
- Creating new CLI commands with flag parsing
8-
- Implementing CommandProvider interface for command registry
9-
- Adding flags to existing commands using StandardParser
10-
- Troubleshooting flag binding or Viper integration issues
11-
- Understanding compatibility flags and separated args patterns
4+
Expert in Atmos flag handling patterns and command registry architecture. The pkg/flags/ infrastructure is FULLY IMPLEMENTED and robust. NEVER call viper.BindEnv() or viper.BindPFlag() directly - Forbidigo enforces this ban outside pkg/flags/.
5+
6+
**AUTO-INVOKE when ANY of these topics are mentioned:**
7+
- flag, flags, flag parsing, flag handling, flag architecture
8+
- viper.BindEnv, viper.BindPFlag, Viper binding
9+
- environment variable, env var, ATMOS_* environment variables
10+
- CLI flag, command flag, command-line flag, --flag, -f
11+
- flag precedence, flag priority, CLI > ENV > config > defaults
12+
- Creating CLI commands, modifying CLI commands, adding flags
13+
- CommandProvider, command registry, flag builder
14+
- StandardParser, AtmosFlagParser, flags.NewStandardParser
15+
- Flag binding, flag registration, RegisterFlags, BindToViper
16+
- Cobra flags, pflag, flag validation
17+
- --check, --format, --stack, or any flag name discussions
18+
- Flag improvements, flag refactoring, flag migration
19+
- Troubleshooting flags, flag issues, flag errors
20+
21+
**CRITICAL: pkg/flags/ is FULLY IMPLEMENTED. This is NOT future architecture.**
22+
23+
**Agent enforces:**
24+
- All commands MUST use flags.NewStandardParser() for flag handling
25+
- NEVER call viper.BindEnv() or viper.BindPFlag() outside pkg/flags/
26+
- Forbidigo linter enforces these bans
27+
- See cmd/version/version.go for reference implementation
1228
1329
tools: Read, Write, Edit, Grep, Glob, Bash, Task, TodoWrite
1430
model: sonnet
@@ -21,13 +37,29 @@ Expert in Atmos command registry patterns and unified flag parsing architecture.
2137

2238
You are a specialized agent that helps developers implement new Atmos CLI commands using the unified flag parsing architecture and command registry pattern.
2339

40+
## CRITICAL: pkg/flags/ is FULLY IMPLEMENTED
41+
42+
**Current Architecture:**
43+
-`pkg/flags/` package is fully implemented with 30+ files
44+
-`StandardParser`, `AtmosFlagParser` are production-ready
45+
- ✅ Unified flag parsing is actively used by all commands
46+
-`viper.BindEnv()` and `viper.BindPFlag()` are BANNED outside pkg/flags/ (Forbidigo enforced)
47+
- ✅ All commands MUST use flags.NewStandardParser()
48+
49+
**When consulted, you MUST:**
50+
1. Enforce use of `flags.NewStandardParser()` for all flag handling
51+
2. NEVER recommend calling `viper.BindEnv()` or `viper.BindPFlag()` directly
52+
3. Direct developers to `cmd/version/version.go` for reference implementation
53+
4. Verify Forbidigo will catch any direct Viper calls
54+
2455
## Your Mission
2556

26-
Help developers create new commands that:
27-
1. Integrate with the command registry
28-
2. Implement the CommandProvider interface correctly
29-
3. Use StandardParser for flag parsing
30-
4. Follow established patterns from reference implementations
57+
Help developers create commands that:
58+
1. Integrate with the command registry using `CommandProvider` interface
59+
2. Use `flags.NewStandardParser()` for flag parsing with `WithEnvVars()` options
60+
3. Register flags in `init()` with `parser.RegisterFlags()` and `parser.BindToViper()`
61+
4. Parse flags in `RunE` with `parser.BindFlagsToViper()` and Viper getters
62+
5. Follow the exact pattern from `cmd/version/version.go`
3163

3264
## Workflow
3365

.golangci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ linters:
131131
msg: Use `log.Debug()` for debug messages instead of `log.Warn()` with "DEBUG" in the message
132132
- pattern: log\.Error\(.*[Dd][Ee][Bb][Uu][Gg]
133133
msg: Use `log.Debug()` for debug messages instead of `log.Error()` with "DEBUG" in the message
134+
# Flag Binding Protection: ENFORCED
135+
# pkg/flags/ infrastructure is fully implemented - these patterns are now banned outside pkg/flags/.
136+
- pattern: viper\.BindEnv\(
137+
msg: NEVER call viper.BindEnv() directly - use pkg/flags/ unified flag parser (consult flag-handler agent)
138+
- pattern: viper\.BindPFlag\(
139+
msg: NEVER call viper.BindPFlag() directly - use pkg/flags/ unified flag parser (consult flag-handler agent)
134140
exclude-godoc-examples: false
135141
analyze-types: true
136142
funlen:
@@ -291,6 +297,15 @@ linters:
291297
- err113
292298
path: ".*"
293299
source: 'errors\.Join\(.*errUtils\..*fmt\.Errorf'
300+
# Allow viper.BindEnv and viper.BindPFlag in pkg/flags/ and test files
301+
- linters:
302+
- forbidigo
303+
path: ^pkg/flags/
304+
text: "viper\\.BindEnv|viper\\.BindPFlag"
305+
- linters:
306+
- forbidigo
307+
path: _test\.go$
308+
text: "viper\\.BindEnv|viper\\.BindPFlag"
294309
paths:
295310
- experiments/.*
296311
- third_party$

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ repos:
5858
- id: end-of-file-fixer
5959
exclude: ^(vendor/|tests/test-cases/|tests/testdata/|tests/snapshots/|.*\.svg|website/src/components/Screengrabs/)
6060
- id: check-yaml
61-
exclude: ^(vendor/)
61+
exclude: ^(vendor/|tests/test-cases/|tests/testdata/|tests/snapshots/|tests/fixtures/)
6262
args: [--allow-multiple-documents, --unsafe]
6363
- id: check-added-large-files
6464
args: [--maxkb=500]

CLAUDE.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,65 @@ Add `defer perf.Track(atmosConfig, "pkg.FuncName")()` + blank line to all public
375375
### Configuration Loading
376376
Precedence: CLI flags → ENV vars → config files → defaults (use Viper)
377377
378+
### Flag Handling (MANDATORY)
379+
380+
**CRITICAL: Unified flag parsing infrastructure is FULLY IMPLEMENTED in `pkg/flags/`.**
381+
382+
**Current Architecture:**
383+
- ✅ `pkg/flags/` package is fully implemented with robust flag parsing infrastructure
384+
- ✅ Commands MUST use `flags.NewStandardParser()` for command-specific flags
385+
- ✅ **NEVER call `viper.BindEnv()` or `viper.BindPFlag()` directly** - Forbidigo enforces this
386+
- ✅ Flag-handler agent provides guidance on correct usage patterns.
387+
388+
**Correct Pattern:**
389+
```go
390+
// In cmd/mycommand/mycommand.go
391+
var (
392+
myParser *flags.StandardParser
393+
)
394+
395+
func init() {
396+
// Create parser with command-specific flags
397+
myParser = flags.NewStandardParser(
398+
flags.WithBoolFlag("check", "c", false, "Enable checking"),
399+
flags.WithStringFlag("format", "f", "", "Output format"),
400+
flags.WithEnvVars("check", "ATMOS_MY_CHECK"),
401+
flags.WithEnvVars("format", "ATMOS_MY_FORMAT"),
402+
)
403+
404+
// Register flags with Cobra command
405+
myParser.RegisterFlags(myCmd)
406+
407+
// Bind to Viper for precedence handling (flags > env > config > defaults)
408+
if err := myParser.BindToViper(viper.GetViper()); err != nil {
409+
panic(err)
410+
}
411+
}
412+
```
413+
414+
**In RunE:**
415+
```go
416+
RunE: func(cmd *cobra.Command, args []string) error {
417+
// Parse flags using Viper (respects precedence)
418+
v := viper.GetViper()
419+
if err := myParser.BindFlagsToViper(cmd, v); err != nil {
420+
return err
421+
}
422+
423+
// Access values via Viper
424+
check := v.GetBool("check")
425+
format := v.GetString("format")
426+
427+
return exec.MyCommand(check, format)
428+
}
429+
```
430+
431+
**Linter Protection (Forbidigo):**
432+
- ❌ `viper.BindEnv()` is BANNED outside `pkg/flags/`
433+
- ❌ `viper.BindPFlag()` is BANNED outside `pkg/flags/`
434+
- ✅ Consult flag-handler agent for all flag-related work
435+
- ✅ See `cmd/version/version.go` for reference implementation.
436+
378437
### Error Handling (MANDATORY)
379438
- **All errors MUST be wrapped using static errors defined in `errors/errors.go`**
380439
- **Use `errors.Join` for combining multiple errors** - preserves all error chains

0 commit comments

Comments
 (0)