Skip to content

[Feature] Add Process Handling Commands (list, search, kill) #22

@arhuman

Description

@arhuman

Overview

Add comprehensive process management commands to Minexus for listing, searching, and terminating processes on remote minions. This is a good first issue designed for beginners to learn the Minexus command system architecture.

Category: process
Commands to implement: process:list, process:search, process:kill
Estimated time: 4-8 hours
Difficulty: Beginner-friendly

Task Description

Implement three process-related commands that will allow users to manage processes on remote minions:

  1. process:list - List all running processes
  2. process:search <pattern> - Search for processes by name/pattern
  3. process:kill <pid> - Terminate a process by PID

Learning Objectives

By completing this issue, you will learn:

  • ✅ Minexus command system architecture
  • ✅ Command registration and metadata definition
  • ✅ Parameter validation and error handling
  • ✅ Cross-platform process management
  • ✅ Unit testing best practices
  • ✅ Documentation updates

Required Reading

MUST READ before starting:

  1. documentation/ADDING_COMMANDS.md - Complete command development guide
  2. documentation/CONTRIBUTING.md - Project contribution guidelines
  3. documentation/TESTING.md - Testing requirements

Reference existing commands:

  • internal/command/file_commands.go - File command examples
  • internal/command/file_commands_unix.go - File command examples (Unix specific for multi-arch)
  • internal/command/file_commands_windows.go - File command examples (Windows specific for multi-arch)
  • internal/command/system_commands.go - System command patterns

Implementation Requirements

Step 1: Create Command File

Create a new file: internal/command/process_commands.go

Step 2: Implement Commands

2.1 ProcessListCommand

// ProcessListCommand lists all running processes
type ProcessListCommand struct {
    *BaseCommand
}

func NewProcessListCommand() *ProcessListCommand {
    base := NewBaseCommand(
        "process:list",
        "process", 
        "List all running processes with PID, name, and resource usage",
        "process:list [--format=<table|json>]",
    ).WithParameters(
        Param{
            Name: "format", 
            Type: "string", 
            Required: false, 
            Default: "table",
            Description: "Output format: table or json",
        },
    ).WithExamples(
        Example{
            Description: "List all processes in table format",
            Command:     "command-send all process:list",
            Expected:    "Returns formatted table of running processes",
        },
        Example{
            Description: "List processes in JSON format", 
            Command:     "command-send all process:list --format=json",
            Expected:    "Returns JSON array of process information",
        },
    ).WithNotes(
        "Shows PID, process name, CPU usage, memory usage, and command line",
        "Table format provides human-readable output",
        "JSON format suitable for programmatic processing",
        "Process information refreshed at command execution time",
    )
    
    return &ProcessListCommand{BaseCommand: base}
}

2.2 ProcessSearchCommand

// ProcessSearchCommand searches for processes by name/pattern
type ProcessSearchCommand struct {
    *BaseCommand
}

func NewProcessSearchCommand() *ProcessSearchCommand {
    base := NewBaseCommand(
        "process:search",
        "process",
        "Search for processes by name or pattern",
        "process:search <pattern> [--format=<table|json>]",
    ).WithParameters(
        Param{
            Name: "pattern", 
            Type: "string", 
            Required: true,
            Description: "Process name or pattern to search for",
        },
        Param{
            Name: "format", 
            Type: "string", 
            Required: false, 
            Default: "table",
            Description: "Output format: table or json",
        },
    ).WithExamples(
        Example{
            Description: "Search for processes containing 'nginx'",
            Command:     "command-send all process:search nginx",
            Expected:    "Returns all processes with 'nginx' in the name",
        },
        Example{
            Description: "Search with JSON output",
            Command:     "command-send all process:search python --format=json", 
            Expected:    "Returns JSON array of matching processes",
        },
    ).WithNotes(
        "Pattern matching is case-insensitive",
        "Searches both process name and command line arguments",
        "Returns empty result if no matches found",
    )
    
    return &ProcessSearchCommand{BaseCommand: base}
}

2.3 ProcessKillCommand

// ProcessKillCommand terminates a process by PID
type ProcessKillCommand struct {
    *BaseCommand
}

func NewProcessKillCommand() *ProcessKillCommand {
    base := NewBaseCommand(
        "process:kill",
        "process",
        "Terminate a process by PID",
        "process:kill <pid> [--signal=<signal>]",
    ).WithParameters(
        Param{
            Name: "pid", 
            Type: "int", 
            Required: true,
            Description: "Process ID to terminate",
        },
        Param{
            Name: "signal", 
            Type: "string", 
            Required: false, 
            Default: "TERM",
            Description: "Signal to send (TERM, KILL, INT, etc.)",
        },
    ).WithExamples(
        Example{
            Description: "Gracefully terminate process",
            Command:     "command-send minion abc123 process:kill 1234",
            Expected:    "Sends SIGTERM to process 1234",
        },
        Example{
            Description: "Force kill process",
            Command:     "command-send minion abc123 process:kill 1234 --signal=KILL",
            Expected:    "Sends SIGKILL to process 1234",
        },
    ).WithNotes(
        "Default signal is TERM (graceful termination)",
        "Use KILL signal only when TERM fails",
        "Requires appropriate permissions to terminate target process",
        "Returns error if process doesn't exist or cannot be terminated",
    )
    
    return &ProcessKillCommand{BaseCommand: base}
}

Step 3: Registration

Add commands to internal/command/setup.go:

func SetupCommands() *Registry {
    registry := NewRegistry()
    
    // ... existing commands ...
    
    // Process commands
    registry.Register(NewProcessListCommand())
    registry.Register(NewProcessSearchCommand()) 
    registry.Register(NewProcessKillCommand())
    
    return registry
}

Implementation Details

Cross-platform Process Management

Handle different operating systems appropriately:

Unix/Linux/macOS:

  • Use ps command for listing: ps aux or ps -eo pid,ppid,user,%cpu,%mem,comm,args
  • Use pgrep for searching: pgrep -f <pattern>
  • Use kill command: kill -<signal> <pid>

Windows:

  • Use tasklist for listing: tasklist /FO CSV
  • Use tasklist with filters for searching: tasklist /FI "IMAGENAME eq <pattern>"
  • Use taskkill for termination: taskkill /PID <pid> /F

Error Handling Requirements

  • ✅ Validate PID is a positive integer
  • ✅ Handle non-existent processes gracefully
  • ✅ Handle permission errors (cannot kill process)
  • ✅ Provide clear error messages for invalid signals
  • ✅ Handle platform-specific command failures

Output Format Examples

Table format (default):

PID     NAME            CPU%   MEM%   COMMAND
1234    nginx           0.1    2.3    nginx: master process
5678    python3         15.2   8.7    python3 /app/server.py

JSON format:

[
  {
    "pid": 1234,
    "name": "nginx", 
    "cpu_percent": 0.1,
    "memory_percent": 2.3,
    "command": "nginx: master process"
  },
  {
    "pid": 5678,
    "name": "python3",
    "cpu_percent": 15.2, 
    "memory_percent": 8.7,
    "command": "python3 /app/server.py"
  }
]

Testing Requirements

Unit Tests

Create internal/command/process_commands_test.go:

Required test cases:

  • ✅ Test command metadata (name, category, description)
  • ✅ Test parameter validation
  • ✅ Test invalid PID handling (non-numeric, negative, zero)
  • ✅ Test invalid signal names
  • ✅ Test format parameter validation
  • ✅ Test cross-platform compatibility
  • ✅ Mock process execution results

Test structure:

func TestProcessListCommand(t *testing.T) {
    cmd := NewProcessListCommand()
    
    // Test metadata
    metadata := cmd.Metadata()
    assert.Equal(t, "process:list", metadata.Name)
    assert.Equal(t, "process", metadata.Category)
    
    // Test execution with mocked data
    // ... implement test cases
}

func TestProcessKillValidation(t *testing.T) {
    // Test PID validation
    testCases := []struct{
        input       string
        shouldError bool
        errorMsg    string
    }{
        {"process:kill 1234", false, ""},
        {"process:kill -1", true, "PID must be positive"},
        {"process:kill abc", true, "PID must be numeric"},
        {"process:kill", true, "missing PID parameter"},
    }
    
    // ... implement validation tests
}

Integration Tests

Ensure commands work in the full integration test suite:

  • ✅ Commands execute without errors
  • ✅ Results are stored in database correctly
  • ✅ Cross-platform compatibility verified

Documentation Updates

1. Update COMMANDS.md

Add process commands section to documentation/COMMANDS.md:

## Process Commands

### process:list
List all running processes with resource usage information.

**Usage**: `process:list [--format=<table|json>]`

**Examples**:
```bash
# List all processes in table format
command-send all process:list

# Get process list in JSON format
command-send all process:list --format=json

process:search

Search for processes by name or pattern.

Usage: process:search <pattern> [--format=<table|json>]

Examples:

# Find all nginx processes
command-send all process:search nginx

# Search for Python processes with JSON output
command-send all process:search python --format=json

process:kill

Terminate a process by PID.

Usage: process:kill <pid> [--signal=<signal>]

Examples:

# Gracefully terminate process
command-send minion abc123 process:kill 1234

# Force kill process
command-send minion abc123 process:kill 1234 --signal=KILL

### 2. Update README.md
Add to command examples in main README.md.

## Validation Checklist

Before submitting your PR, ensure:

### Development
- [ ] Created `internal/command/process_commands.go` with all three commands
- [ ] Commands registered in `internal/command/setup.go`
- [ ] All commands follow BaseCommand pattern from ADDING_COMMANDS.md
- [ ] Cross-platform compatibility implemented
- [ ] Error handling covers all edge cases
- [ ] Parameter validation implemented

### Testing  
- [ ] Unit tests created: `internal/command/process_commands_test.go`
- [ ] All test cases pass: `make test`
- [ ] Integration tests pass: `SLOW_TESTS=1 make test`
- [ ] Commands tested manually with console
- [ ] Cross-platform testing completed (if possible)

### Documentation
- [ ] Commands documented in `documentation/COMMANDS.md`
- [ ] README.md updated with examples
- [ ] Code includes comprehensive comments
- [ ] Notes added to commands with usage tips

### Code Quality
- [ ] Code follows Go best practices
- [ ] All functions have proper error handling
- [ ] Commands use fluent API correctly
- [ ] No legacy/compatibility code added
- [ ] Code is reversible/removable

## Development Commands

```bash
# Run unit tests during development
make test

# Run comprehensive tests before PR
SLOW_TESTS=1 make test

# Build and test manually
make build
./console

# Test commands interactively
minexus> command-send all process:list
minexus> command-send all process:search nginx
minexus> command-send minion abc123 process:kill 1234

Implementation Tips

For Beginners

  1. Start small: Implement process:list first, then process:search, finally process:kill
  2. Follow patterns: Copy structure from existing commands in system_commands.go
  3. Test frequently: Run make test after each small change
  4. Read existing code: Study how ShellExecutor works in shell_commands.go
  5. Ask questions: Use GitHub discussions for help

Cross-platform Considerations

  • Use runtime.GOOS to detect operating system
  • Implement OS-specific command execution
  • Test on multiple platforms if available
  • Provide clear error messages for unsupported platforms

Performance Notes

  • Process listing can be expensive on systems with many processes
  • Consider adding limits or pagination for large process lists
  • Cache results appropriately if needed

Success Criteria

Your implementation is complete when:

  • ✅ All three commands execute successfully
  • ✅ Commands appear in help output
  • ✅ Integration tests pass
  • ✅ Commands work cross-platform
  • ✅ Documentation is comprehensive
  • ✅ Code follows project patterns
  • ✅ Error handling is robust

Getting Help

  • Documentation: Start with ADDING_COMMANDS.md
  • Existing Code: Reference system_commands.go and shell_commands.go
  • Testing: Follow patterns in existing test files
  • Pull Request: For questions about implementation

Good luck! This issue is designed to be beginner-friendly while teaching you the core Minexus architecture. Take your time, read the documentation, and don't hesitate to ask questions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions