go-codemod
$
npx mdskill add github/gh-aw/go-codemodUse this skill when adding or updating codemods used by `gh aw fix`.
SKILL.md
.github/skills/go-codemodView on GitHub ↗
---
name: go-codemod
description: Implement and test Go codemods for the gh aw fix command.
---
# Go Codemod Implementation Guide
Use this skill when adding or updating codemods used by `gh aw fix`.
## Understand the fix pipeline first
1. Review `pkg/cli/fix_command.go` to understand execution flow:
- codemods are loaded once via `GetAllCodemods()`
- each codemod is applied in registry order
- frontmatter is re-parsed before each codemod
- codemods must return `(newContent, applied, error)` and be safe for no-op input
2. Review the codemod registry in `pkg/cli/fix_codemods.go`.
3. Review helper utilities in `pkg/cli/yaml_frontmatter_utils.go` and reusable codemod helper constructors in `pkg/cli/codemod_factory.go`.
## Implementation steps
1. Create a new codemod file in `pkg/cli/` named `codemod_<feature>.go`.
2. Add a package logger with `logger.New("cli:codemod_<feature>")`.
3. Implement `get<Feature>Codemod() Codemod` and populate all metadata fields:
- `ID` (stable, unique)
- `Name` (human-readable)
- `Description` (clear migration behavior)
- `IntroducedIn` (release version)
4. In `Apply`:
- check frontmatter preconditions first
- return unchanged content and `applied=false` when migration is not needed
- transform only frontmatter using `applyFrontmatterLineTransform` from `pkg/cli/yaml_frontmatter_utils.go`
- preserve comments/formatting/markdown body
- avoid lossy rewrites and avoid touching unrelated keys
5. Prefer existing helpers before writing custom parsing logic:
- `findAndReplaceInLine`
- `removeFieldFromBlock`
- `removeParentBlockIfTrulyEmpty`
- `newFieldRemovalCodemod`
- `newMoveTopLevelKeyToOnBlockCodemod`
6. If the codemod depends on external data or side effects, inject dependencies through a `...WithDeps` constructor so tests can mock behavior.
7. Register the codemod in `pkg/cli/fix_codemods.go` within `GetAllCodemods()`.
## Testing requirements
Create `pkg/cli/codemod_<feature>_test.go` and cover:
1. Metadata correctness (`ID`, `Name`, `Description`, `IntroducedIn`, `Apply != nil`).
2. Happy path migration with expected output.
3. No-op behavior when deprecated input is absent.
4. Idempotence behavior (already-migrated input remains unchanged).
5. Preservation guarantees:
- inline comments
- indentation
- markdown body after frontmatter
6. Edge cases specific to the codemod (nested fields, mixed forms, ordering constraints, strict-mode checks, etc.).
7. Dependency-injection behavior if `...WithDeps` exists (success and fallback/error paths).
When complexity is high (expression rewriting, parser-like behavior), add fuzz tests in `codemod_<feature>_fuzz_test.go` using the pattern in `pkg/cli/codemod_steps_run_secrets_env_fuzz_test.go`.
## Registry/order tests
After adding a codemod, update `pkg/cli/fix_codemods_test.go`:
1. Expected codemod IDs list.
2. Expected codemod order list.
Order matters because codemods run sequentially and later codemods observe prior transformations.
## Validation commands
Run targeted checks first:
1. `go test -v ./pkg/cli -run Codemod -count=1`
2. `go test -v ./pkg/cli -run Fix -count=1`
Then run repository standards:
3. `make build`
4. `make test-unit`
5. `make lint`
## Quality bar
A codemod is ready only when it is:
- deterministic
- safe on repeated runs
- conservative (no unrelated rewrites)
- fully covered by tests for migration and no-op paths
- registered and order-verified in fix codemod registry tests