This document provides guidance for developing CLI commands in GitHub Agentic Workflows. It covers command structure, naming conventions, flag patterns, error handling, testing requirements, and help text standards.
- Command Structure
- Naming Conventions
- Flag Patterns
- Error Handling
- Console Output
- Testing Requirements
- Help Text Standards
- Anti-Patterns
- Examples
Every command should follow this standard structure:
package cli
import (
// Standard library imports
"fmt"
"os"
// Internal imports
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/logger"
// External imports
"github.com/spf13/cobra"
)
// Logger instance with namespace following cli:command_name pattern
var commandLog = logger.New("cli:command_name")
// NewCommandNameCommand creates the command-name command
func NewCommandNameCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "command-name [args]",
Short: "Brief one-line description under 80 chars",
Long: `Detailed description with examples...`,
Args: cobra.ExactArgs(1), // or other validator
RunE: func(cmd *cobra.Command, args []string) error {
// Parse flags
flagValue, _ := cmd.Flags().GetString("flag-name")
verbose, _ := cmd.Flags().GetBool("verbose")
// Call main function
return RunCommandName(args[0], flagValue, verbose)
},
}
// Add flags
cmd.Flags().StringP("flag-name", "f", "default", "Flag description")
addVerboseFlag(cmd)
// Register completions
RegisterDirFlagCompletion(cmd, "output")
return cmd
}
// RunCommandName executes the command logic
func RunCommandName(arg string, flagValue string, verbose bool) error {
commandLog.Printf("Starting command: arg=%s, flagValue=%s", arg, flagValue)
// Validate inputs early
if err := validateInputs(arg, flagValue); err != nil {
return err
}
// Execute command logic
result, err := executeCommand(arg, flagValue)
if err != nil {
return fmt.Errorf("failed to execute command: %w", err)
}
// Output results
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(result))
commandLog.Print("Command completed successfully")
return nil
}
// Internal implementation functions
func validateInputs(arg string, flagValue string) error {
if arg == "" {
return fmt.Errorf("argument cannot be empty")
}
return nil
}
func executeCommand(arg string, flagValue string) (string, error) {
// Implementation
return "Success", nil
}- Logger Namespace: Always create a logger with format
cli:command_name - Public API: Two exported functions:
NewXCommand() *cobra.Command- Creates the commandRunX(...) error- Executes command logic (testable)
- Internal Functions: Private helper functions for implementation
- Flag Parsing: Parse flags in
RunEfunction - Early Validation: Validate all inputs before processing
- Error Wrapping: Use
fmt.Errorfwith%wfor context
Commands can be organized in multiple ways depending on complexity:
For commands with < 500 lines:
command_name.go- All command logiccommand_name_test.go- All tests
Example: status_command.go (368 lines)
For commands with > 500 lines, split into focused files:
command_name_command.go- Command definitioncommand_name_config.go- Configuration typescommand_name_helpers.go- Utility functionscommand_name_validation.go- Validation logiccommand_name_orchestrator.go- Main orchestration
Example: Compile command split into multiple files:
compile_command.go- Command definition (minimal)compile_config.go- Configuration typescompile_helpers.go- Utility functionscompile_validation.go- Validation logiccompile_orchestrator.go- Main orchestration
| File Type | Pattern | Example |
|---|---|---|
| Command file | *_command.go |
audit_command.go |
| Test file | *_command_test.go |
audit_command_test.go |
| Integration test | *_integration_test.go |
compile_integration_test.go |
| Helper functions | *_helpers.go |
compile_helpers.go |
| Configuration | *_config.go |
compile_config.go |
Format: cli:command_name
// ✅ CORRECT
var auditLog = logger.New("cli:audit")
var compileLog = logger.New("cli:compile_command")
var statusLog = logger.New("cli:status_command")
// ❌ INCORRECT
var log = logger.New("audit") // Missing cli: prefix
var logger = logger.New("compile") // Missing cli: prefix, conflicts with package| Function Type | Pattern | Example |
|---|---|---|
| Command creator | NewXCommand() |
NewAuditCommand() |
| Command runner | RunX(...) |
RunAuditWorkflowRun(...) |
| Helper functions | Descriptive names | validateInputs(), fetchWorkflows() |
Configuration struct names should end with Config:
// ✅ CORRECT
type CompileConfig struct {
WorkflowFile string
OutputDir string
Verbose bool
}
type AuditConfig struct {
RunID int64
Verbose bool
}
// ❌ INCORRECT
type CompileOptions struct { ... } // Use Config suffix
type AuditParams struct { ... } // Use Config suffixGlobal flags are available to all commands:
// Defined in commands.go
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose output")
rootCmd.PersistentFlags().BoolVar(&noBanner, "no-banner", false, "Disable banner messages")Common flags with helper functions (defined in flags.go):
// Engine flag - Override AI engine
addEngineFlag(cmd) // --engine/-e
addEngineFilterFlag(cmd) // --engine/-e for filtering
// Repository flag
addRepoFlag(cmd) // --repo/-r
// Output directory flag
addOutputFlag(cmd, defaultDir) // --output/-o
// JSON output flag
addJSONFlag(cmd) // --json/-jReserve these short flags for consistent meanings:
| Short Flag | Meaning | Long Flag |
|---|---|---|
-v |
Verbose output | --verbose |
-e |
Engine selection/filter | --engine |
-r |
Repository | --repo |
-o |
Output directory | --output |
-j |
JSON output | --json |
-f |
Force/file | --force/--file |
-w |
Watch mode | --watch |
- Use kebab-case for flag names:
--output-dir,--run-id - Use descriptive names that indicate purpose with noun phrases and conventional prefixes
- Avoid abbreviations unless universally understood
Validate flags early in the RunE function:
RunE: func(cmd *cobra.Command, args []string) error {
// Parse flags
engine, _ := cmd.Flags().GetString("engine")
runID, _ := cmd.Flags().GetInt64("run-id")
// Validate flags
if engine != "" && !isValidEngine(engine) {
return fmt.Errorf("invalid engine: %s (must be copilot, claude, codex, or custom)", engine)
}
if runID <= 0 {
return fmt.Errorf("run-id must be positive")
}
// Continue with validated values
return RunCommand(engine, runID)
},Register flag completions for better UX:
// Directory completion
RegisterDirFlagCompletion(cmd, "output")
// File completion
RegisterFileFlagCompletion(cmd, "file", "*.md")
// Custom completion
cmd.RegisterFlagCompletionFunc("engine", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"copilot", "claude", "codex", "custom"}, cobra.ShellCompDirectiveNoFileComp
})Always use console.FormatErrorMessage() for user-facing errors:
// ✅ CORRECT - Formatted error message
if err != nil {
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
return err
}
// ❌ INCORRECT - Plain error message
if err != nil {
fmt.Fprintln(os.Stderr, err)
return err
}
// ❌ INCORRECT - Writing to stdout
if err != nil {
fmt.Println(err) // Should use stderr
return err
}Use fmt.Errorf with %w to provide context:
// ✅ CORRECT - Error wrapping with context
result, err := fetchData(url)
if err != nil {
return fmt.Errorf("failed to fetch data from %s: %w", url, err)
}
// ❌ INCORRECT - No context
result, err := fetchData(url)
if err != nil {
return err
}Provide clear, actionable error messages:
// ✅ CORRECT - Actionable error with suggestion
if !workflowExists {
return fmt.Errorf("workflow '%s' not found. Use 'gh aw list' to see available workflows", name)
}
// ✅ CORRECT - Validation error with expected format
if !strings.Contains(input, "=") {
return fmt.Errorf("invalid input format '%s': expected key=value", input)
}
// ❌ INCORRECT - Vague error message
if !workflowExists {
return fmt.Errorf("not found")
}Log errors with context before returning:
func RunCommand(arg string) error {
commandLog.Printf("Starting command with arg: %s", arg)
result, err := processArg(arg)
if err != nil {
commandLog.Printf("Failed to process arg: %v", err)
return fmt.Errorf("failed to process argument: %w", err)
}
commandLog.Print("Command completed successfully")
return nil
}- ALL output goes to stderr:
fmt.Fprintln(os.Stderr, ...) - Use console formatting: Never use plain
fmt.Println() - Consistent styling: Use appropriate message types
import "github.com/github/gh-aw/pkg/console"
// Success messages
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Workflow compiled successfully"))
// Info messages
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Fetching workflow status..."))
// Warning messages
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Workflow has unstaged changes"))
// Error messages
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
// Command messages (CLI commands being executed)
fmt.Fprintln(os.Stderr, console.FormatCommandMessage("gh workflow run workflow.yml"))
// Progress messages (operations in progress)
fmt.Fprintln(os.Stderr, console.FormatProgressMessage("Downloading artifacts..."))
// Prompt messages (user prompts)
fmt.Fprintln(os.Stderr, console.FormatPromptMessage("Select a workflow:"))
// Count messages (numeric counts)
fmt.Fprintln(os.Stderr, console.FormatCountMessage(fmt.Sprintf("Found %d workflows", count)))
// Verbose messages (debug info, only shown with --verbose)
if verbose {
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage("Detailed debug information"))
}
// Location messages (file paths, URLs)
fmt.Fprintln(os.Stderr, console.FormatLocationMessage(filepath))For structured data display, use console.RenderStruct():
type WorkflowStatus struct {
Workflow string `json:"workflow" console:"header:Workflow"`
Engine string `json:"engine" console:"header:Engine"`
Compiled string `json:"compiled" console:"header:Compiled"`
Status string `json:"status" console:"header:Status"`
}
statuses := []WorkflowStatus{...}
// Render table
fmt.Print(console.RenderStruct(statuses))Support JSON output with --json flag:
if jsonOutput {
jsonBytes, err := json.MarshalIndent(data, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal JSON: %w", err)
}
fmt.Println(string(jsonBytes)) // JSON goes to stdout
return nil
}
// Normal output goes to stderr
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Operation complete"))Verbose messages should provide additional detail:
if verbose {
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(
fmt.Sprintf("Processing workflow: %s", workflowName)))
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(
fmt.Sprintf("Engine: %s", engine)))
}Every command must have corresponding test files:
command_name_command.go
command_name_command_test.go # Unit tests
command_name_integration_test.go # Integration tests (optional)
Use table-driven tests for testing multiple scenarios:
func TestRunCommand(t *testing.T) {
tests := []struct {
name string
input string
expected string
shouldErr bool
}{
{
name: "valid input",
input: "test-workflow",
expected: "Success",
shouldErr: false,
},
{
name: "empty input",
input: "",
expected: "",
shouldErr: true,
},
{
name: "invalid format",
input: "invalid@workflow",
expected: "",
shouldErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := RunCommand(tt.input)
if tt.shouldErr {
assert.Error(t, err, "Expected error for input: %s", tt.input)
} else {
assert.NoError(t, err, "Should not error for input: %s", tt.input)
assert.Equal(t, tt.expected, result, "Incorrect result for input: %s", tt.input)
}
})
}
}Use testify assertions appropriately:
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWorkflowCompilation(t *testing.T) {
// Use require for critical setup (stops test if fails)
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.md")
err := os.WriteFile(testFile, []byte(content), 0644)
require.NoError(t, err, "Failed to write test file")
// Use assert for test validations (continues checking)
result, err := CompileWorkflow(testFile)
assert.NoError(t, err, "Should compile valid workflow")
assert.NotNil(t, result, "Result should not be nil")
assert.Equal(t, "expected", result.Field, "Field value incorrect")
}Tests should cover:
- Valid inputs: Normal operation with valid data
- Invalid inputs: Error handling with invalid data
- Edge cases: Empty strings, nil values, boundary conditions
- Flag handling: All flag combinations
- Error paths: All error conditions
Use descriptive test names:
// ✅ CORRECT - Descriptive test names
func TestAuditCommand_ValidRunID(t *testing.T) { ... }
func TestAuditCommand_InvalidRunID(t *testing.T) { ... }
func TestAuditCommand_MissingArtifacts(t *testing.T) { ... }
// ❌ INCORRECT - Vague test names
func TestAudit1(t *testing.T) { ... }
func TestAudit2(t *testing.T) { ... }- Under 80 characters
- Action-oriented (start with verb)
- No period at the end
- Clear and concise
// ✅ CORRECT
Short: "Investigate an agentic workflow run and generate a detailed report"
Short: "Show status of agentic workflow files"
Short: "Compile Markdown workflows to GitHub Actions YAML"
// ❌ INCORRECT
Short: "This command investigates workflows." // Too wordy, has period
Short: "Status" // Not descriptive enough
Short: "A very long description that goes well over the eighty character limit for short descriptions" // Too longStructure long descriptions with:
- Overview: What the command does (1-2 sentences)
- Context: When to use it, what it's for
- Details: Specific behavior, options, features
- Examples: Practical usage examples (minimum 3)
Long: `Audit a single workflow run by downloading artifacts and logs, detecting errors,
analyzing MCP tool usage, and generating a concise Markdown report suitable for AI agents.
This command accepts:
- A numeric run ID (e.g., 1234567890)
- A GitHub Actions run URL (e.g., https://github.com/owner/repo/actions/runs/1234567890)
- A GitHub Actions job URL (e.g., https://github.com/owner/repo/actions/runs/1234567890/job/9876543210)
This command:
- Downloads artifacts and logs for the specified run ID
- Detects errors and warnings in the logs
- Analyzes MCP tool usage statistics
- Generates a concise Markdown report
Examples:
gh aw audit 1234567890 # Audit run with ID 1234567890
gh aw audit https://... # Audit from run URL
gh aw audit 1234567890 -o ./reports # Custom output directory
gh aw audit 1234567890 -v # Verbose output
gh aw audit 1234567890 --parse # Parse agent and firewall logs`,Provide at least 3 practical examples:
- Basic usage: Simplest form of the command
- Common options: With frequently used flags
- Advanced usage: Complex scenarios
Examples:
gh aw compile workflow.md # Compile single workflow
gh aw compile workflow.md -v # Verbose output
gh aw compile -d .github/workflows # Compile directory
gh aw compile workflow.md --watch # Watch mode
gh aw compile --all # Compile all workflowsFor commands that accept workflow IDs, include the standard explanation:
import "github.com/github/gh-aw/pkg/cli"
Long: `Description of command...
` + cli.WorkflowIDExplanation + `
Examples:
...`,// ❌ INCORRECT
fmt.Println("Success")
fmt.Printf("Status: %s\n", status)
// ✅ CORRECT
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Success"))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Status: %s", status)))// ❌ INCORRECT
fmt.Fprintln(os.Stderr, err)
// ✅ CORRECT
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))// ❌ INCORRECT
if err != nil {
return err
}
// ✅ CORRECT
if err != nil {
return fmt.Errorf("failed to parse workflow: %w", err)
}// ❌ INCORRECT - Single file with 2000+ lines
compile_command.go // 2000 lines
// ✅ CORRECT - Split into focused files
compile_command.go // Command definition (50 lines)
compile_config.go // Configuration (100 lines)
compile_validation.go // Validation (300 lines)
compile_orchestrator.go // Main logic (500 lines)// ❌ INCORRECT
var log = logger.New("audit")
var logger = logger.New("compile")
// ✅ CORRECT
var auditLog = logger.New("cli:audit")
var compileLog = logger.New("cli:compile_command")// ❌ INCORRECT
func RunCommand(arg string) error {
// Directly use arg without validation
result := processArg(arg)
return nil
}
// ✅ CORRECT
func RunCommand(arg string) error {
if arg == "" {
return fmt.Errorf("argument cannot be empty")
}
if !isValid(arg) {
return fmt.Errorf("invalid argument format: %s", arg)
}
result := processArg(arg)
return nil
}// ❌ INCORRECT - No completions
cmd.Flags().StringP("output", "o", "", "Output directory")
// ✅ CORRECT - With completions
cmd.Flags().StringP("output", "o", "", "Output directory")
RegisterDirFlagCompletion(cmd, "output")// ❌ INCORRECT
Short: "Compile files"
Long: `This compiles files.`
// ✅ CORRECT
Short: "Compile Markdown workflows to GitHub Actions YAML"
Long: `Compile agentic workflow Markdown files to GitHub Actions YAML with validation.
This command:
- Parses Markdown frontmatter and content
- Validates workflow configuration
- Generates GitHub Actions YAML
- Supports watch mode for automatic recompilation
Examples:
gh aw compile workflow.md
gh aw compile --all
gh aw compile workflow.md --watch`,Here's a complete example following all patterns:
package cli
import (
"fmt"
"os"
"path/filepath"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/logger"
"github.com/spf13/cobra"
)
var validateLog = logger.New("cli:validate_command")
// NewValidateCommand creates the validate command
func NewValidateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "validate <workflow-file>",
Short: "Validate agentic workflow Markdown files",
Long: `Validate agentic workflow Markdown files for correctness and compatibility.
This command:
- Validates frontmatter syntax and required fields
- Checks GitHub Actions compatibility
- Validates tool configurations
- Reports errors and warnings with actionable suggestions
` + WorkflowIDExplanation + `
Examples:
gh aw validate workflow.md # Validate single workflow
gh aw validate workflow.md -v # Verbose output
gh aw validate --all # Validate all workflows
gh aw validate workflow.md --strict # Strict mode validation`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
verbose, _ := cmd.Flags().GetBool("verbose")
strict, _ := cmd.Flags().GetBool("strict")
all, _ := cmd.Flags().GetBool("all")
var workflowFile string
if len(args) > 0 {
workflowFile = args[0]
}
return RunValidate(workflowFile, all, strict, verbose)
},
}
cmd.Flags().Bool("all", false, "Validate all workflow files")
cmd.Flags().Bool("strict", false, "Enable strict mode validation")
return cmd
}
// RunValidate validates workflow files
func RunValidate(workflowFile string, all bool, strict bool, verbose bool) error {
validateLog.Printf("Starting validation: file=%s, all=%v, strict=%v", workflowFile, all, strict)
// Validate inputs
if !all && workflowFile == "" {
return fmt.Errorf("workflow file required (or use --all to validate all workflows)")
}
if all && workflowFile != "" {
return fmt.Errorf("cannot specify both workflow file and --all flag")
}
// Get files to validate
var files []string
if all {
var err error
files, err = findAllWorkflowFiles()
if err != nil {
validateLog.Printf("Failed to find workflow files: %v", err)
return fmt.Errorf("failed to find workflow files: %w", err)
}
} else {
files = []string{workflowFile}
}
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(
fmt.Sprintf("Validating %d workflow file(s)...", len(files))))
}
// Validate each file
var hasErrors bool
for _, file := range files {
if err := validateWorkflowFile(file, strict, verbose); err != nil {
hasErrors = true
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(
fmt.Sprintf("%s: %s", filepath.Base(file), err.Error())))
} else {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(
fmt.Sprintf("%s: Valid", filepath.Base(file))))
}
}
}
if hasErrors {
return fmt.Errorf("validation failed for one or more workflows")
}
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(
fmt.Sprintf("All %d workflow(s) validated successfully", len(files))))
validateLog.Print("Validation completed successfully")
return nil
}
func findAllWorkflowFiles() ([]string, error) {
pattern := filepath.Join(".github", "workflows", "*.md")
return filepath.Glob(pattern)
}
func validateWorkflowFile(file string, strict bool, verbose bool) error {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(
fmt.Sprintf("Validating %s...", file)))
}
// Validation logic here
return nil
}package cli
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRunValidate(t *testing.T) {
tests := []struct {
name string
workflowFile string
all bool
strict bool
shouldErr bool
errorMsg string
}{
{
name: "valid workflow file",
workflowFile: "test-workflow.md",
all: false,
strict: false,
shouldErr: false,
},
{
name: "no file and no all flag",
workflowFile: "",
all: false,
strict: false,
shouldErr: true,
errorMsg: "workflow file required",
},
{
name: "both file and all flag",
workflowFile: "test-workflow.md",
all: true,
strict: false,
shouldErr: true,
errorMsg: "cannot specify both",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup test environment
tmpDir := t.TempDir()
originalDir, err := os.Getwd()
require.NoError(t, err)
defer os.Chdir(originalDir)
err = os.Chdir(tmpDir)
require.NoError(t, err)
// Create test workflow file if specified
if tt.workflowFile != "" {
workflowsDir := filepath.Join(".github", "workflows")
err := os.MkdirAll(workflowsDir, 0755)
require.NoError(t, err)
testFile := filepath.Join(workflowsDir, tt.workflowFile)
err = os.WriteFile(testFile, []byte("---\nengine: copilot\n---\n# Test"), 0644)
require.NoError(t, err)
}
// Run validation
err = RunValidate(tt.workflowFile, tt.all, tt.strict, false)
// Assert results
if tt.shouldErr {
assert.Error(t, err, "Expected error for test: %s", tt.name)
if tt.errorMsg != "" {
assert.Contains(t, err.Error(), tt.errorMsg,
"Error message should contain expected text")
}
} else {
assert.NoError(t, err, "Should not error for test: %s", tt.name)
}
})
}
}
func TestValidateCommand_FlagParsing(t *testing.T) {
cmd := NewValidateCommand()
// Test flag parsing
cmd.SetArgs([]string{"workflow.md", "--strict", "--verbose"})
err := cmd.ParseFlags([]string{"workflow.md", "--strict", "--verbose"})
assert.NoError(t, err, "Should parse flags successfully")
strict, _ := cmd.Flags().GetBool("strict")
assert.True(t, strict, "Strict flag should be true")
verbose, _ := cmd.Flags().GetBool("verbose")
assert.True(t, verbose, "Verbose flag should be true")
}Use this checklist when developing a new command:
- File created with
*_command.gosuffix - Logger created with
cli:command_namenamespace -
NewXCommand()function defined -
RunX()function defined (testable) - Short description under 80 chars
- Long description with context and examples
- Minimum 3 usage examples provided
- Flags added with appropriate short flags
- Flag completions registered
- Input validation implemented
- Error messages use
console.FormatErrorMessage() - Success messages use
console.FormatSuccessMessage() - All output goes to stderr (except JSON)
- JSON output supported if applicable
- Test file created (
*_command_test.go) - Table-driven tests implemented
- Valid input tests added
- Invalid input tests added
- Edge case tests added
- Flag handling tests added
- Documentation updated if needed
- Testing Framework: See
scratchpad/testing.mdfor testing guidelines - Console Rendering: See
skills/console-rendering/SKILL.mdfor console output details - Error Messages: See
skills/error-messages/SKILL.mdfor error message style guide - Code Organization: See
scratchpad/code-organization.mdfor file organization patterns - Breaking Changes: See
scratchpad/breaking-cli-rules.mdfor breaking change guidelines
Last Updated: 2026-01-01