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
1 change: 1 addition & 0 deletions cmd/gh-aw/command_groups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestCommandGroupAssignments(t *testing.T) {
{name: "validate command in development group", commandName: "validate", expectedGroup: "development", shouldHaveGroup: true},
{name: "mcp command in development group", commandName: "mcp", expectedGroup: "development", shouldHaveGroup: true},
{name: "fix command in development group", commandName: "fix", expectedGroup: "development", shouldHaveGroup: true},
{name: "format command in development group", commandName: "format", expectedGroup: "development", shouldHaveGroup: true},
{name: "domains command in development group", commandName: "domains", expectedGroup: "development", shouldHaveGroup: true},

// Execution Commands
Expand Down
13 changes: 7 additions & 6 deletions cmd/gh-aw/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,10 +557,10 @@ func runCompileCmd(cmd *cobra.Command, args []string) error {
}

type commandSet struct {
addCmd, addWizardCmd, editCmd, updateCmd, deployCmd, trialCmd, initCmd, statusCmd, listCmd *cobra.Command
mcpCmd, logsCmd, auditCmd, viewCmd, healthCmd, outcomesCmd, mcpServerCmd, prCmd, secretsCmd *cobra.Command
fixCmd, upgradeCmd, completionCmd, hashCmd, projectCmd, doctorCmd, checksCmd, validateCmd, lintCmd *cobra.Command
domainsCmd, experimentsCmd, forecastCmd, gradersCmd, modelsCmd, envCmd, jsonSchemaCmd *cobra.Command
addCmd, addWizardCmd, editCmd, updateCmd, deployCmd, trialCmd, initCmd, statusCmd, listCmd *cobra.Command
mcpCmd, logsCmd, auditCmd, viewCmd, healthCmd, outcomesCmd, mcpServerCmd, prCmd, secretsCmd *cobra.Command
fixCmd, formatCmd, upgradeCmd, completionCmd, hashCmd, projectCmd, doctorCmd, checksCmd, validateCmd, lintCmd *cobra.Command
domainsCmd, experimentsCmd, forecastCmd, gradersCmd, modelsCmd, envCmd, jsonSchemaCmd *cobra.Command
}

func fixPathForCommand(s string) string {
Expand Down Expand Up @@ -729,6 +729,7 @@ func createCommandSet() commandSet {
prCmd: cli.NewPRCommand(),
secretsCmd: cli.NewSecretsCommand(),
fixCmd: cli.NewFixCommand(),
formatCmd: cli.NewFormatCommand(),
upgradeCmd: cli.NewUpgradeCommand(validateEngine),
completionCmd: cli.NewCompletionCommand(),
hashCmd: cli.NewHashCommand(),
Expand Down Expand Up @@ -855,7 +856,7 @@ func assignCommandGroups(cmds commandSet) {
removeCmd.GroupID, cmds.editCmd.GroupID, cmds.updateCmd.GroupID, cmds.deployCmd.GroupID, cmds.upgradeCmd.GroupID = "setup", "setup", "setup", "setup", "setup"
cmds.secretsCmd.GroupID, cmds.envCmd.GroupID, cmds.doctorCmd.GroupID = "setup", "setup", "setup"
compileCmd.GroupID, cmds.validateCmd.GroupID, cmds.lintCmd.GroupID = "development", "development", "development"
cmds.mcpCmd.GroupID, cmds.fixCmd.GroupID, cmds.domainsCmd.GroupID = "development", "development", "development"
cmds.mcpCmd.GroupID, cmds.fixCmd.GroupID, cmds.formatCmd.GroupID, cmds.domainsCmd.GroupID = "development", "development", "development", "development"
runCmd.GroupID, enableCmd.GroupID, disableCmd.GroupID, cmds.trialCmd.GroupID = "execution", "execution", "execution", "execution"
cmds.logsCmd.GroupID, cmds.auditCmd.GroupID, cmds.viewCmd.GroupID = "analysis", "analysis", "analysis"
cmds.healthCmd.GroupID, cmds.outcomesCmd.GroupID, cmds.checksCmd.GroupID = "analysis", "analysis", "analysis"
Expand All @@ -870,7 +871,7 @@ func addCommandsToRoot(cmds commandSet) {
compileCmd, cmds.addCmd, cmds.addWizardCmd, cmds.editCmd, cmds.updateCmd, cmds.deployCmd, cmds.upgradeCmd, cmds.trialCmd, newCmd, cmds.initCmd,
runCmd, removeCmd, cmds.statusCmd, cmds.listCmd, enableCmd, disableCmd, cmds.logsCmd, cmds.auditCmd, cmds.viewCmd,
cmds.healthCmd, cmds.outcomesCmd, cmds.checksCmd, cmds.mcpCmd, cmds.mcpServerCmd, cmds.prCmd, versionCmd, cmds.secretsCmd,
cmds.fixCmd, cmds.validateCmd, cmds.lintCmd, cmds.completionCmd, cmds.hashCmd, cmds.projectCmd, cmds.doctorCmd,
cmds.fixCmd, cmds.formatCmd, cmds.validateCmd, cmds.lintCmd, cmds.completionCmd, cmds.hashCmd, cmds.projectCmd, cmds.doctorCmd,
cmds.domainsCmd, cmds.experimentsCmd, cmds.forecastCmd, cmds.gradersCmd, cmds.modelsCmd, cmds.envCmd, cmds.jsonSchemaCmd,
)
}
Expand Down
50 changes: 50 additions & 0 deletions docs/adr/59971-add-frontmatter-format-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# ADR-59971: Add Frontmatter Format Command

**Date**: 2026-09-10
**Status**: Draft
**Deciders**: gh-aw maintainers

---

### Context

`gh-aw` workflows store behavior in YAML frontmatter embedded in Markdown files, and this PR adds a CLI path that rewrites that frontmatter. The diff shows repeated requirements to preserve Markdown body content and comments, while also normalizing indentation, key naming via existing codemods, and deterministic field ordering. It also adds support for formatting individual workflows, all workflows in the default directory, and files under a custom directory. The architectural question is how workflow frontmatter normalization should be implemented without introducing a separate configuration format or breaking user-authored comments.

### Decision

We will add a dedicated `gh aw format` command that applies the existing codemod pipeline and then rewrites workflow frontmatter through a YAML AST-based normalizer while preserving comments and the Markdown body. We decided to integrate formatting into the CLI command set and operate directly on workflow Markdown files because the PR evidence shows the desired behavior is repository-local, repeatable, and aligned with other `gh aw` authoring commands. This approach makes frontmatter canonicalization an explicit authoring workflow instead of an incidental side effect of compile or validation paths.

### Alternatives Considered

#### Alternative 1: Reformat frontmatter implicitly during compile or validation

The project could normalize workflow frontmatter whenever users run compile, validate, or related commands. This was considered because those commands already parse workflows and would have access to the same frontmatter data. It was not chosen because the diff introduces a standalone `format` command and targeted CLI tests, indicating a need for an explicit write operation that users can run on demand without coupling source-file mutation to read-oriented commands.

#### Alternative 2: Implement formatting with text-based string rewriting only

Another option would be to format the frontmatter with line-oriented replacements or regex-style transforms and avoid YAML AST handling. This was considered because it could be simpler for a narrow subset of field migrations. It was not chosen because the implementation and tests emphasize preserving comments, handling nested mappings, and avoiding false delimiters inside block scalars, which are stronger fits for structured YAML node processing than plain text rewriting.

#### Alternative 3: Leave frontmatter style unmanaged and rely only on codemods

The team could keep workflow syntax migrations limited to existing codemods and avoid introducing a canonical formatting pass. This was considered because it minimizes new CLI surface area and avoids file rewrite logic. It was not chosen because the PR explicitly adds deterministic ordering, indentation normalization, and round-trip coverage, showing that consistent formatting is now treated as part of workflow maintainability rather than an optional manual convention.

### Consequences

#### Positive
- Workflow authors get a dedicated command to canonicalize frontmatter structure across one file, many files, or custom workflow directories.
- Existing codemods can be reused before formatting, allowing syntax migrations and canonical layout to happen in a single operation.
- YAML-node-based normalization can preserve comments and Markdown content while still enforcing deterministic ordering and indentation.

#### Negative
- The CLI surface area grows, adding another authoring command that maintainers must document, test, and support.
- Formatting now depends on careful frontmatter parsing and YAML encoding behavior, which can introduce edge cases around delimiters, block scalars, or comment retention.
- Rewriting files in place may create larger diffs for users when canonical ordering changes multiple fields at once.

#### Neutral
- The command is grouped with other development-oriented CLI commands rather than changing execution behavior.
- Test coverage now includes comment preservation, round-tripping, block scalar handling, field ordering, and command registration.
- The formatter works on Markdown workflow files directly and does not introduce a new persisted intermediate representation.

---

*ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.*
Loading
Loading