diff options
| author | diogo464 <[email protected]> | 2025-06-23 10:37:37 +0100 |
|---|---|---|
| committer | diogo464 <[email protected]> | 2025-06-23 10:37:37 +0100 |
| commit | 8da000524e50610076d02c32ab97d57e8e01f0bb (patch) | |
| tree | 3b430492f6eeeecdc216818aa68301c7dfd8cc37 /IMPLEMENTATION_PLAN.md | |
| parent | b257b2259dc3ad7d0aa4df86ef241b66932204a9 (diff) | |
Remove implementation plans and goal files
Clean up project by removing internal planning documents
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
Diffstat (limited to 'IMPLEMENTATION_PLAN.md')
| -rw-r--r-- | IMPLEMENTATION_PLAN.md | 313 |
1 files changed, 0 insertions, 313 deletions
diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md deleted file mode 100644 index 56c3caf..0000000 --- a/IMPLEMENTATION_PLAN.md +++ /dev/null | |||
| @@ -1,313 +0,0 @@ | |||
| 1 | # Demon CLI Implementation Plan | ||
| 2 | |||
| 3 | ## Project Overview | ||
| 4 | A CLI tool named `demon` for spawning and managing background processes with stdout/stderr redirection. | ||
| 5 | |||
| 6 | ## Requirements Summary | ||
| 7 | - **Files created**: `<id>.pid`, `<id>.stdout`, `<id>.stderr` in working directory | ||
| 8 | - **Platform**: Linux only (maybe macOS later) | ||
| 9 | - **File location**: Working directory (add to .gitignore) | ||
| 10 | - **Signal handling**: SIGTERM then SIGKILL with configurable timeout | ||
| 11 | - **Logging**: Tool logs to stderr, process output to stdout | ||
| 12 | - **Concurrency**: Single process tail for now | ||
| 13 | |||
| 14 | ## CLI Structure | ||
| 15 | ``` | ||
| 16 | demon run --id <identifier> <command...> | ||
| 17 | demon stop --id <id> [--timeout <seconds>] | ||
| 18 | demon tail [--stdout] [--stderr] --id <id> | ||
| 19 | demon cat [--stdout] [--stderr] --id <id> | ||
| 20 | demon list | ||
| 21 | demon status --id <id> | ||
| 22 | demon clean | ||
| 23 | ``` | ||
| 24 | |||
| 25 | ## Implementation Progress | ||
| 26 | |||
| 27 | ### ✅ Phase 1: Project Setup | ||
| 28 | - [x] Add dependencies: clap, tracing, tracing-subscriber, notify | ||
| 29 | - [x] Create CLI structure with Commands enum and Args structs | ||
| 30 | |||
| 31 | ### 🔄 Phase 2: Core Process Management | ||
| 32 | - [ ] **CURRENT**: Implement `demon run` | ||
| 33 | - Check if process already running via PID file | ||
| 34 | - Spawn process with stdout/stderr redirection to files | ||
| 35 | - Write PID to `.pid` file | ||
| 36 | - Truncate log files when starting new process | ||
| 37 | - Detach process so parent can exit | ||
| 38 | - [ ] Implement `demon stop` | ||
| 39 | - Read PID from file | ||
| 40 | - Send SIGTERM first | ||
| 41 | - Wait for timeout, then send SIGKILL | ||
| 42 | - Clean up PID file | ||
| 43 | - Handle already-dead processes gracefully | ||
| 44 | |||
| 45 | ### 📋 Phase 3: File Operations | ||
| 46 | - [ ] Implement `demon cat` | ||
| 47 | - Read and display `.stdout` and/or `.stderr` files | ||
| 48 | - Handle file selection flags properly | ||
| 49 | - Error handling for missing files | ||
| 50 | - [ ] Implement `demon tail` | ||
| 51 | - Use `notify` crate for file watching | ||
| 52 | - Support both stdout and stderr simultaneously | ||
| 53 | - Handle file rotation/truncation | ||
| 54 | - Clean shutdown on Ctrl+C | ||
| 55 | |||
| 56 | ### 📋 Phase 4: Additional Commands | ||
| 57 | - [ ] Implement `demon list` | ||
| 58 | - Scan working directory for `.pid` files | ||
| 59 | - Check which processes are actually running | ||
| 60 | - Display process info | ||
| 61 | - [ ] Implement `demon status` | ||
| 62 | - Check if specific process is running | ||
| 63 | - Display process info | ||
| 64 | - [ ] Implement `demon clean` | ||
| 65 | - Find orphaned files (PID exists but process dead) | ||
| 66 | - Remove orphaned `.pid`, `.stdout`, `.stderr` files | ||
| 67 | |||
| 68 | ### 📋 Phase 5: Error Handling & Polish | ||
| 69 | - [ ] Robust error handling throughout | ||
| 70 | - [ ] Proper cleanup on failures | ||
| 71 | - [ ] Input validation | ||
| 72 | - [ ] Help text and documentation | ||
| 73 | |||
| 74 | ## Technical Implementation Details | ||
| 75 | |||
| 76 | ### Process Spawning (demon run) | ||
| 77 | ```rust | ||
| 78 | // 1. Check if <id>.pid exists and process is running | ||
| 79 | // 2. Truncate/create <id>.stdout and <id>.stderr files | ||
| 80 | // 3. Spawn process with: | ||
| 81 | // - stdout redirected to <id>.stdout | ||
| 82 | // - stderr redirected to <id>.stderr | ||
| 83 | // - stdin redirected to /dev/null | ||
| 84 | // 4. Write PID to <id>.pid file | ||
| 85 | // 5. Don't call .wait() - let process run detached | ||
| 86 | ``` | ||
| 87 | |||
| 88 | ### Process Stopping (demon stop) | ||
| 89 | ```rust | ||
| 90 | // 1. Read PID from <id>.pid file | ||
| 91 | // 2. Send SIGTERM to process | ||
| 92 | // 3. Wait for timeout (default 10s) | ||
| 93 | // 4. If still running, send SIGKILL | ||
| 94 | // 5. Remove <id>.pid file | ||
| 95 | // 6. Handle process already dead gracefully | ||
| 96 | ``` | ||
| 97 | |||
| 98 | ### File Tailing (demon tail) | ||
| 99 | ```rust | ||
| 100 | // 1. Use notify crate to watch file changes | ||
| 101 | // 2. When files change, read new content and print | ||
| 102 | // 3. Handle both stdout and stderr based on flags | ||
| 103 | // 4. Default: show both if neither flag specified | ||
| 104 | // 5. Graceful shutdown on Ctrl+C | ||
| 105 | ``` | ||
| 106 | |||
| 107 | ### File Listing (demon list) | ||
| 108 | ```rust | ||
| 109 | // 1. Glob for *.pid files in current directory | ||
| 110 | // 2. For each PID file, check if process is running | ||
| 111 | // 3. Display: ID, PID, Status, Command (if available) | ||
| 112 | ``` | ||
| 113 | |||
| 114 | ## Dependencies Used | ||
| 115 | - `clap` (derive feature) - CLI argument parsing | ||
| 116 | - `tracing` + `tracing-subscriber` - Structured logging | ||
| 117 | - `notify` - File system notifications for tail | ||
| 118 | - Standard library for process management | ||
| 119 | |||
| 120 | ## File Naming Convention | ||
| 121 | - PID file: `<id>.pid` | ||
| 122 | - Stdout log: `<id>.stdout` | ||
| 123 | - Stderr log: `<id>.stderr` | ||
| 124 | |||
| 125 | ## Error Handling Strategy | ||
| 126 | - Use `Result<(), Box<dyn std::error::Error>>` for main functions | ||
| 127 | - Log errors using `tracing::error!` | ||
| 128 | - Exit with code 1 on errors | ||
| 129 | - Provide descriptive error messages | ||
| 130 | |||
| 131 | ## Testing Strategy | ||
| 132 | - Manual testing with simple commands (sleep, echo, etc.) | ||
| 133 | - Test edge cases: process crashes, missing files, etc. | ||
| 134 | - Test signal handling and cleanup | ||
| 135 | |||
| 136 | ## Current Status | ||
| 137 | - ✅ All core functionality implemented and tested | ||
| 138 | - ✅ CLI structure with proper subcommands and arguments | ||
| 139 | - ✅ Process spawning and management working correctly | ||
| 140 | - ✅ File watching and real-time tailing functional | ||
| 141 | - ✅ Error handling and edge cases covered | ||
| 142 | - ✅ Clean up functionality for orphaned files | ||
| 143 | |||
| 144 | ## Implementation Complete! | ||
| 145 | |||
| 146 | All planned features have been successfully implemented: | ||
| 147 | |||
| 148 | 1. **`demon run`** - ✅ Spawns background processes with file redirection | ||
| 149 | 2. **`demon stop`** - ✅ Graceful termination with SIGTERM/SIGKILL timeout | ||
| 150 | 3. **`demon tail`** - ✅ Real-time file watching with notify crate | ||
| 151 | 4. **`demon cat`** - ✅ Display log file contents | ||
| 152 | 5. **`demon list`** - ✅ Show all managed processes with status | ||
| 153 | 6. **`demon status`** - ✅ Detailed status of specific process | ||
| 154 | 7. **`demon clean`** - ✅ Remove orphaned files from dead processes | ||
| 155 | |||
| 156 | ## Testing Summary | ||
| 157 | |||
| 158 | All commands have been tested and work correctly: | ||
| 159 | - Process spawning and detachment | ||
| 160 | - Signal handling (SIGTERM → SIGKILL) | ||
| 161 | - File redirection (stdout/stderr) | ||
| 162 | - Duplicate process detection | ||
| 163 | - File watching and real-time updates | ||
| 164 | - Orphan cleanup | ||
| 165 | - Error handling for edge cases | ||
| 166 | |||
| 167 | ## Final Architecture | ||
| 168 | |||
| 169 | The implementation follows the planned modular structure: | ||
| 170 | - **CLI Interface**: Uses clap with enum-based subcommands ✅ | ||
| 171 | - **Process Manager**: Handles spawning, tracking, and termination ✅ | ||
| 172 | - **File Operations**: Manages PID files and log redirection ✅ | ||
| 173 | - **Output Display**: Implements both cat and tail functionality ✅ | ||
| 174 | |||
| 175 | --- | ||
| 176 | |||
| 177 | # Wait Subcommand Implementation Plan | ||
| 178 | |||
| 179 | ## Overview | ||
| 180 | Add a `wait` subcommand to the demon CLI that blocks until a specified process terminates, with configurable timeout and polling interval. | ||
| 181 | |||
| 182 | ## Requirements Summary | ||
| 183 | - **Default timeout**: 30 seconds | ||
| 184 | - **Infinite timeout**: Use `--timeout 0` | ||
| 185 | - **Exit codes**: 0 for success, 1 for failure | ||
| 186 | - **PID file**: Leave untouched (don't clean up) | ||
| 187 | - **Output**: Quiet operation, only show error messages | ||
| 188 | - **Polling interval**: 1 second default, configurable with `--interval` flag | ||
| 189 | |||
| 190 | ## Implementation Details | ||
| 191 | |||
| 192 | ### 1. Command Structure | ||
| 193 | ```rust | ||
| 194 | /// Wait for a daemon process to terminate | ||
| 195 | Wait(WaitArgs), | ||
| 196 | ``` | ||
| 197 | |||
| 198 | ### 2. Arguments Structure | ||
| 199 | ```rust | ||
| 200 | #[derive(Args)] | ||
| 201 | struct WaitArgs { | ||
| 202 | /// Process identifier | ||
| 203 | id: String, | ||
| 204 | |||
| 205 | /// Timeout in seconds (0 = infinite) | ||
| 206 | #[arg(long, default_value = "30")] | ||
| 207 | timeout: u64, | ||
| 208 | |||
| 209 | /// Polling interval in seconds | ||
| 210 | #[arg(long, default_value = "1")] | ||
| 211 | interval: u64, | ||
| 212 | } | ||
| 213 | ``` | ||
| 214 | |||
| 215 | ### 3. Core Function Implementation | ||
| 216 | ```rust | ||
| 217 | fn wait_daemon(id: &str, timeout: u64, interval: u64) -> Result<()> { | ||
| 218 | // 1. Check if PID file exists | ||
| 219 | // 2. Read PID from file | ||
| 220 | // 3. Check if process exists initially | ||
| 221 | // 4. If timeout == 0, loop indefinitely | ||
| 222 | // 5. Otherwise, loop with timeout tracking | ||
| 223 | // 6. Poll every `interval` seconds | ||
| 224 | // 7. Return appropriate exit codes | ||
| 225 | } | ||
| 226 | ``` | ||
| 227 | |||
| 228 | ### 4. Logic Flow | ||
| 229 | 1. **Initial validation**: | ||
| 230 | - Check if PID file exists → error if not | ||
| 231 | - Read PID from file → error if invalid | ||
| 232 | - Check if process is running → error if already dead | ||
| 233 | |||
| 234 | 2. **Waiting loop**: | ||
| 235 | - If timeout = 0: infinite loop | ||
| 236 | - Otherwise: track elapsed time | ||
| 237 | - Poll every `interval` seconds using `is_process_running_by_pid()` | ||
| 238 | - Break when process terminates or timeout reached | ||
| 239 | |||
| 240 | 3. **Exit conditions**: | ||
| 241 | - Process terminates → exit 0 | ||
| 242 | - Timeout reached → error message + exit 1 | ||
| 243 | - Initial errors → error message + exit 1 | ||
| 244 | |||
| 245 | ### 5. Error Messages | ||
| 246 | - "Process '{id}' not found (no PID file)" | ||
| 247 | - "Process '{id}' is not running" | ||
| 248 | - "Timeout reached waiting for process '{id}' to terminate" | ||
| 249 | |||
| 250 | ## Testing Strategy | ||
| 251 | |||
| 252 | ### New Tests | ||
| 253 | 1. **test_wait_nonexistent_process**: Should fail with appropriate error | ||
| 254 | 2. **test_wait_already_dead_process**: Should fail when process already terminated | ||
| 255 | 3. **test_wait_process_terminates**: Should succeed when process terminates normally | ||
| 256 | 4. **test_wait_timeout**: Should fail when timeout is reached | ||
| 257 | 5. **test_wait_infinite_timeout**: Test with timeout=0 (use short-lived process) | ||
| 258 | 6. **test_wait_custom_interval**: Test with different polling intervals | ||
| 259 | |||
| 260 | ### Updated Tests | ||
| 261 | Replace `std::thread::sleep(Duration::from_millis(100))` with `demon wait` in: | ||
| 262 | - `test_run_creates_files` → `demon wait test --timeout 5` | ||
| 263 | - `test_run_with_complex_command` → `demon wait complex --timeout 5` | ||
| 264 | - Similar tests that wait for process completion | ||
| 265 | |||
| 266 | ## Files to Modify | ||
| 267 | |||
| 268 | ### 1. src/main.rs | ||
| 269 | - Add `Wait(WaitArgs)` to `Commands` enum (around line 146) | ||
| 270 | - Add `WaitArgs` struct after other Args structs (around line 206) | ||
| 271 | - Add `Commands::Wait(args) => wait_daemon(&args.id, args.timeout, args.interval)` to match statement (around line 246) | ||
| 272 | - Implement `wait_daemon()` function (add after other daemon functions) | ||
| 273 | |||
| 274 | ### 2. tests/cli.rs | ||
| 275 | - Add new test functions for wait subcommand | ||
| 276 | - Update existing tests to use wait instead of sleep where appropriate | ||
| 277 | |||
| 278 | ### 3. README.md | ||
| 279 | - Add wait command to command reference section | ||
| 280 | - Add examples showing wait usage | ||
| 281 | |||
| 282 | ### 4. LLM Guide (print_llm_guide function) | ||
| 283 | - Add wait command documentation | ||
| 284 | - Add to available commands list | ||
| 285 | - Add usage examples | ||
| 286 | |||
| 287 | ## Command Usage Examples | ||
| 288 | |||
| 289 | ```bash | ||
| 290 | # Wait with default 30s timeout | ||
| 291 | demon wait my-process | ||
| 292 | |||
| 293 | # Wait indefinitely | ||
| 294 | demon wait my-process --timeout 0 | ||
| 295 | |||
| 296 | # Wait with custom timeout and interval | ||
| 297 | demon wait my-process --timeout 60 --interval 2 | ||
| 298 | ``` | ||
| 299 | |||
| 300 | ## Implementation Order | ||
| 301 | 1. Implement core functionality in main.rs | ||
| 302 | 2. Add comprehensive tests | ||
| 303 | 3. Update existing tests to use wait | ||
| 304 | 4. Update documentation (README + LLM guide) | ||
| 305 | 5. Test full integration | ||
| 306 | |||
| 307 | ## Key Implementation Notes | ||
| 308 | - Use existing `is_process_running_by_pid()` function for consistency | ||
| 309 | - Use existing `PidFile::read_from_file()` for PID file handling | ||
| 310 | - Follow existing error handling patterns with anyhow | ||
| 311 | - Use `std::thread::sleep(Duration::from_secs(interval))` for polling | ||
| 312 | - Track elapsed time for timeout implementation | ||
| 313 | - Maintain quiet operation - no progress messages \ No newline at end of file | ||
