Developer-Dashboard

 view release on metacpan or  search on metacpan

bin/dashboard  view on Meta::CPAN

    pod2usage(
        -exitval => 0,
        -verbose => 99,
    );
}

if ( $cmd eq 'version' ) {
    require Developer::Dashboard;
    print $Developer::Dashboard::VERSION, "\n";
    exit 0;
}

if ( my $helper_path = _builtin_helper_path($cmd) ) {
    _exec_switchboard_command( $helper_path, @ARGV );
}

if ( my $custom_path = _custom_command_path($cmd) ) {
    _exec_switchboard_command( $custom_path, @ARGV );
}

if ( my ( $skill_name, $skill_command ) = _skill_dotted_command_parts($cmd) ) {
    my $helper_path = _builtin_helper_path('skills');
    _exec_switchboard_command( $helper_path, '_exec', $skill_name, $skill_command, @ARGV );
}

if ( $cmd ne '' ) {
    print STDERR Developer::Dashboard::CLI::Suggest->new()->unknown_command_message($cmd);
}

pod2usage(
    -exitval  => 1,
    -verbose  => 99,
    -sections => [qw(NAME SYNOPSIS)],
);

# _prime_command_result_env($cmd, @argv)
# Runs executable per-command hook files from every DD-OOP-LAYERS CLI root and
# exposes their captured output through RESULT JSON for later hooks and the
# final command target.
# Input: top-level command name plus the remaining argv list.
# Output: true value after hook execution completes.
sub _prime_command_result_env {
    my ( $cmd, @argv ) = @_;
    Developer::Dashboard::Runtime::Result::clear_current();
    Developer::Dashboard::Runtime::Result::clear_last_result();
    $ENV{DEVELOPER_DASHBOARD_COMMAND} = $cmd if defined $cmd && $cmd ne '';
    return 1 if !defined $cmd || $cmd eq '';

    my @hook_roots = _command_hook_roots($cmd);
    return 1 if !@hook_roots;
    my $stream = $cmd eq 'doctor' ? 0 : 1;

    my %results;
    for my $hook_root (@hook_roots) {
        opendir my $dh, $hook_root or next;
        for my $entry ( sort grep { $_ ne '.' && $_ ne '..' } readdir $dh ) {
            my $path = File::Spec->catfile( $hook_root, $entry );
            next if !is_runnable_file($path);
            next if $entry eq 'run';

            my $hook_result = _run_command_hook_streaming(
                $path,
                stream => $stream,
                argv   => \@argv,
            );
            my $result_key = exists $results{$entry} ? _command_hook_result_key($path) : $entry;
            $results{$result_key} = {
                stdout => $hook_result->{stdout},
                stderr => $hook_result->{stderr},
            };
            $results{$result_key}{exit_code} = $hook_result->{exit_code} if defined $hook_result->{exit_code};
            Developer::Dashboard::Runtime::Result::set_current( \%results );
            Developer::Dashboard::Runtime::Result::set_last_result(
                {
                    file   => $path,
                    exit   => $hook_result->{exit_code},
                    STDOUT => $hook_result->{stdout},
                    STDERR => $hook_result->{stderr},
                }
            );
            return 1 if Developer::Dashboard::Runtime::Result::stop_requested( $hook_result->{stderr} );
        }
        closedir $dh;
    }

    if ( !%results ) {
        Developer::Dashboard::Runtime::Result::clear_current();
        Developer::Dashboard::Runtime::Result::clear_last_result();
    }
    return 1;
}

# _load_runtime_env()
# Loads the participating DD-OOP-LAYERS plain-directory and runtime-layer env
# files into the current process before command resolution and helper exec.
# Input: none.
# Output: true value.
sub _load_runtime_env {
    require Developer::Dashboard::PathRegistry;
    Developer::Dashboard::EnvAudit->clear();
    my $paths = Developer::Dashboard::PathRegistry->new(
        home            => $ENV{HOME},
        workspace_roots => [],
        project_roots   => [],
    );
    Developer::Dashboard::EnvLoader->load_runtime_layers( paths => $paths );
    return 1;
}

# _run_command_hook_streaming($path, %args)
# Executes one hook file, streams its output live, and captures stdout/stderr so
# later hooks and the final command can inspect RESULT JSON.
# Input: executable hook path plus an optional stream flag and argv array ref.
# Output: hash reference containing stdout, stderr, and exit_code.
sub _run_command_hook_streaming {
    my ( $path, %args ) = @_;
    my $stream = exists $args{stream} ? $args{stream} : 1;
    my @argv = @{ $args{argv} || [] };
    open my $stdin, '<', File::Spec->devnull() or die "Unable to open " . File::Spec->devnull() . " for hook stdin: $!";
    my $stderr = gensym();
    my $stdout;
    my @command = command_argv_for_path($path);
    my $pid = open3( $stdin, $stdout, $stderr, @command, @argv );
    close $stdin;

    my $selector  = IO::Select->new( $stdout, $stderr );
    my $stdout_fd = fileno($stdout);
    my $stderr_fd = fileno($stderr);
    my $stdout_text = '';
    my $stderr_text = '';
    local $| = 1;
    STDOUT->autoflush(1);
    STDERR->autoflush(1);

    while ( my @ready = $selector->can_read ) {
        for my $fh (@ready) {
            my $buffer = '';
            my $read = sysread( $fh, $buffer, 8192 );
            if ( !defined $read || $read == 0 ) {
                $selector->remove($fh);
                close $fh;
                next;
            }

            if ( fileno($fh) == $stdout_fd ) {
                print STDOUT $buffer if $stream;
                $stdout_text .= $buffer;
                next;
            }

            if ( fileno($fh) == $stderr_fd ) {
                print STDERR $buffer if $stream;
                $stderr_text .= $buffer;
                next;
            }
        }
    }

    waitpid( $pid, 0 );
    return {
        stdout    => $stdout_text,
        stderr    => $stderr_text,
        exit_code => $? >> 8,
    };
}

# _command_hook_roots($cmd)
# Resolves every per-command hook directory across DD-OOP-LAYERS from home to
# the deepest participating layer.
# Input: top-level command name string.
# Output: ordered list of hook directory paths.
sub _command_hook_roots {
    my ($cmd) = @_;
    return () if !defined $cmd || $cmd eq '';
    my @roots;



( run in 0.628 second using v1.01-cache-2.11-cpan-39bf76dae61 )