Skip to content
This repository was archived by the owner on Dec 15, 2025. It is now read-only.
This repository was archived by the owner on Dec 15, 2025. It is now read-only.

RFC: Universal output format interfaces for all commands #85

@jordanpartridge

Description

@jordanpartridge

RFC: Universal Output Format Interfaces

This RFC proposes a fundamental architectural change to ensure all Conduit commands support multiple output formats consistently.

Motivation

Currently, commands have inconsistent output formatting. Some are decorative-only, making it difficult to:

  • Process command output programmatically
  • Export data for analysis
  • Build integrations and pipelines
  • Create consistent user experiences

Proposed Architecture

1. Abstract Command Class with Format Interfaces

<?php

namespace Conduit\Commands;

use LaravelZero\Framework\Commands\Command;

abstract class AbstractConduitCommand extends Command
{
    protected $signature = '{command} {--format=terminal : Output format (terminal, json, table, markdown, csv)}';
    
    public function handle(): int
    {
        $data = $this->getData();
        $format = $this->option('format');
        
        return match($format) {
            'json' => $this->outputJson($data),
            'table' => $this->outputTable($data),
            'markdown' => $this->outputMarkdown($data),
            'csv' => $this->outputCsv($data),
            default => $this->outputTerminal($data)
        };
    }
    
    /**
     * Get the data to be formatted
     */
    abstract protected function getData(): array;
    
    /**
     * Format output for terminal (decorative)
     */
    abstract protected function outputTerminal(array $data): int;
    
    /**
     * Format output as JSON
     */
    abstract protected function outputJson(array $data): int;
    
    /**
     * Format output as table
     */
    abstract protected function outputTable(array $data): int;
    
    /**
     * Format output as markdown
     */
    abstract protected function outputMarkdown(array $data): int;
    
    /**
     * Format output as CSV
     */
    abstract protected function outputCsv(array $data): int;
}

2. Component Scaffolding with Stubs

When creating new commands:

conduit make:command spotify:analytics

Generates:

<?php

namespace ConduitSpotify\Commands;

use Conduit\Commands\AbstractConduitCommand;

class AnalyticsCommand extends AbstractConduitCommand
{
    protected $signature = 'spotify:analytics {--format=terminal : Output format}';
    
    protected function getData(): array
    {
        // TODO: Implement data collection
        return [];
    }
    
    protected function outputTerminal(array $data): int
    {
        // TODO: Implement terminal formatting
        $this->info('Analytics data in terminal format');
        return self::SUCCESS;
    }
    
    protected function outputJson(array $data): int
    {
        // TODO: Implement JSON formatting
        $this->line(json_encode($data, JSON_PRETTY_PRINT));
        return self::SUCCESS;
    }
    
    protected function outputTable(array $data): int
    {
        // TODO: Implement table formatting
        $this->table(['Metric', 'Value'], $data);
        return self::SUCCESS;
    }
    
    protected function outputMarkdown(array $data): int
    {
        // TODO: Implement markdown formatting
        $this->line('# Analytics Report');
        return self::SUCCESS;
    }
    
    protected function outputCsv(array $data): int
    {
        // TODO: Implement CSV formatting
        $this->line('metric,value');
        return self::SUCCESS;
    }
}

3. Helper Traits for Common Patterns

trait FormatsAsJson
{
    protected function outputJson(array $data): int
    {
        $this->line(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        return self::SUCCESS;
    }
}

trait FormatsAsTable
{
    protected function outputTable(array $data): int
    {
        if (empty($data)) {
            $this->warn('No data to display');
            return self::SUCCESS;
        }
        
        $headers = array_keys(reset($data));
        $rows = array_map('array_values', $data);
        $this->table($headers, $rows);
        return self::SUCCESS;
    }
}

4. Global Options in Conduit Core

// In AppServiceProvider
$this->app->beforeResolving(AbstractConduitCommand::class, function ($command) {
    // Add global format validation
    // Set default formats based on config
    // Handle piping detection
});

Benefits

  1. Consistency: Every command supports the same output formats
  2. Enforced Implementation: Abstract methods ensure formats aren't forgotten
  3. Developer Experience: Stubs with TODOs guide implementation
  4. User Experience: Users know --format works everywhere
  5. Composability: Easy to pipe and chain commands
  6. Future-Proof: New formats can be added to abstract class

Migration Path

  1. Create AbstractConduitCommand in core
  2. Update existing commands gradually
  3. New commands must extend AbstractConduitCommand
  4. Deprecation warnings for old-style commands
  5. Full migration in next major version

Example Usage

# Human-readable
conduit spotify:current

# For scripts
conduit spotify:current --format=json | jq '.artist'

# For documentation
conduit knowledge:search "error" --format=markdown > errors.md

# For spreadsheets
conduit analytics:report --format=csv > report.csv

# For dashboards
conduit status:all --format=table

Additional Considerations

  • Detect when output is piped and default to json/plain
  • Add --fields option to select specific fields
  • Support --output=file.json for direct file writing
  • Consider --jq option for built-in JSON filtering
  • Add format to config for user preferences

This would make Conduit the most integration-friendly CLI framework!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions