From 8da000524e50610076d02c32ab97d57e8e01f0bb Mon Sep 17 00:00:00 2001 From: diogo464 Date: Mon, 23 Jun 2025 10:37:37 +0100 Subject: Remove implementation plans and goal files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up project by removing internal planning documents 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- GOAL.md | 35 ------ IMPLEMENTATION_PLAN.md | 313 ------------------------------------------------- IMPROVEMENT_PLAN.md | 166 -------------------------- IMPROVEMENT_PLAN_V2.md | 264 ----------------------------------------- 4 files changed, 778 deletions(-) delete mode 100644 GOAL.md delete mode 100644 IMPLEMENTATION_PLAN.md delete mode 100644 IMPROVEMENT_PLAN.md delete mode 100644 IMPROVEMENT_PLAN_V2.md diff --git a/GOAL.md b/GOAL.md deleted file mode 100644 index acee5b2..0000000 --- a/GOAL.md +++ /dev/null @@ -1,35 +0,0 @@ -# project goals -the goal of this project is to create a cli tool named `demon` that should be used to spawn background processes and redirect their stdout and stderr to files. -here is an overview of the subcommands the tool should provide and their usage: - -## demon run -``` -# the identifier is a required argument -# all remaining arguments will be used to spawn the process with the first one being the executable name -# three files should be created `.pid`, `.stdout`, `.stderr` -# the cli tool should exit immediatly but the spawned process should be left running the background -demon run --id - -# example usage -demon run --id npm-dev npm run dev -# this should create the files `npm-dev.pid`, `npm-dev.stdout` and `npm-dev.stderr` -# if the pid file already exists and the process is still running you should fail with a descriptive error message -``` - -## demon stop -``` -# this should kill the process if it is running, otherwise do nothing -demon stop --id -``` - -## demon tail -``` -# this should tail both .stderr and .stdout files by default, or just the selected ones -demon tail [--stdout] [--stderr] --id -``` - -## demon cat -``` -# this should cat both files or just the selected ones -demon cat [--stdout] [--stderr] --id -``` 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 @@ -# Demon CLI Implementation Plan - -## Project Overview -A CLI tool named `demon` for spawning and managing background processes with stdout/stderr redirection. - -## Requirements Summary -- **Files created**: `.pid`, `.stdout`, `.stderr` in working directory -- **Platform**: Linux only (maybe macOS later) -- **File location**: Working directory (add to .gitignore) -- **Signal handling**: SIGTERM then SIGKILL with configurable timeout -- **Logging**: Tool logs to stderr, process output to stdout -- **Concurrency**: Single process tail for now - -## CLI Structure -``` -demon run --id -demon stop --id [--timeout ] -demon tail [--stdout] [--stderr] --id -demon cat [--stdout] [--stderr] --id -demon list -demon status --id -demon clean -``` - -## Implementation Progress - -### ✅ Phase 1: Project Setup -- [x] Add dependencies: clap, tracing, tracing-subscriber, notify -- [x] Create CLI structure with Commands enum and Args structs - -### 🔄 Phase 2: Core Process Management -- [ ] **CURRENT**: Implement `demon run` - - Check if process already running via PID file - - Spawn process with stdout/stderr redirection to files - - Write PID to `.pid` file - - Truncate log files when starting new process - - Detach process so parent can exit -- [ ] Implement `demon stop` - - Read PID from file - - Send SIGTERM first - - Wait for timeout, then send SIGKILL - - Clean up PID file - - Handle already-dead processes gracefully - -### 📋 Phase 3: File Operations -- [ ] Implement `demon cat` - - Read and display `.stdout` and/or `.stderr` files - - Handle file selection flags properly - - Error handling for missing files -- [ ] Implement `demon tail` - - Use `notify` crate for file watching - - Support both stdout and stderr simultaneously - - Handle file rotation/truncation - - Clean shutdown on Ctrl+C - -### 📋 Phase 4: Additional Commands -- [ ] Implement `demon list` - - Scan working directory for `.pid` files - - Check which processes are actually running - - Display process info -- [ ] Implement `demon status` - - Check if specific process is running - - Display process info -- [ ] Implement `demon clean` - - Find orphaned files (PID exists but process dead) - - Remove orphaned `.pid`, `.stdout`, `.stderr` files - -### 📋 Phase 5: Error Handling & Polish -- [ ] Robust error handling throughout -- [ ] Proper cleanup on failures -- [ ] Input validation -- [ ] Help text and documentation - -## Technical Implementation Details - -### Process Spawning (demon run) -```rust -// 1. Check if .pid exists and process is running -// 2. Truncate/create .stdout and .stderr files -// 3. Spawn process with: -// - stdout redirected to .stdout -// - stderr redirected to .stderr -// - stdin redirected to /dev/null -// 4. Write PID to .pid file -// 5. Don't call .wait() - let process run detached -``` - -### Process Stopping (demon stop) -```rust -// 1. Read PID from .pid file -// 2. Send SIGTERM to process -// 3. Wait for timeout (default 10s) -// 4. If still running, send SIGKILL -// 5. Remove .pid file -// 6. Handle process already dead gracefully -``` - -### File Tailing (demon tail) -```rust -// 1. Use notify crate to watch file changes -// 2. When files change, read new content and print -// 3. Handle both stdout and stderr based on flags -// 4. Default: show both if neither flag specified -// 5. Graceful shutdown on Ctrl+C -``` - -### File Listing (demon list) -```rust -// 1. Glob for *.pid files in current directory -// 2. For each PID file, check if process is running -// 3. Display: ID, PID, Status, Command (if available) -``` - -## Dependencies Used -- `clap` (derive feature) - CLI argument parsing -- `tracing` + `tracing-subscriber` - Structured logging -- `notify` - File system notifications for tail -- Standard library for process management - -## File Naming Convention -- PID file: `.pid` -- Stdout log: `.stdout` -- Stderr log: `.stderr` - -## Error Handling Strategy -- Use `Result<(), Box>` for main functions -- Log errors using `tracing::error!` -- Exit with code 1 on errors -- Provide descriptive error messages - -## Testing Strategy -- Manual testing with simple commands (sleep, echo, etc.) -- Test edge cases: process crashes, missing files, etc. -- Test signal handling and cleanup - -## Current Status -- ✅ All core functionality implemented and tested -- ✅ CLI structure with proper subcommands and arguments -- ✅ Process spawning and management working correctly -- ✅ File watching and real-time tailing functional -- ✅ Error handling and edge cases covered -- ✅ Clean up functionality for orphaned files - -## Implementation Complete! - -All planned features have been successfully implemented: - -1. **`demon run`** - ✅ Spawns background processes with file redirection -2. **`demon stop`** - ✅ Graceful termination with SIGTERM/SIGKILL timeout -3. **`demon tail`** - ✅ Real-time file watching with notify crate -4. **`demon cat`** - ✅ Display log file contents -5. **`demon list`** - ✅ Show all managed processes with status -6. **`demon status`** - ✅ Detailed status of specific process -7. **`demon clean`** - ✅ Remove orphaned files from dead processes - -## Testing Summary - -All commands have been tested and work correctly: -- Process spawning and detachment -- Signal handling (SIGTERM → SIGKILL) -- File redirection (stdout/stderr) -- Duplicate process detection -- File watching and real-time updates -- Orphan cleanup -- Error handling for edge cases - -## Final Architecture - -The implementation follows the planned modular structure: -- **CLI Interface**: Uses clap with enum-based subcommands ✅ -- **Process Manager**: Handles spawning, tracking, and termination ✅ -- **File Operations**: Manages PID files and log redirection ✅ -- **Output Display**: Implements both cat and tail functionality ✅ - ---- - -# Wait Subcommand Implementation Plan - -## Overview -Add a `wait` subcommand to the demon CLI that blocks until a specified process terminates, with configurable timeout and polling interval. - -## Requirements Summary -- **Default timeout**: 30 seconds -- **Infinite timeout**: Use `--timeout 0` -- **Exit codes**: 0 for success, 1 for failure -- **PID file**: Leave untouched (don't clean up) -- **Output**: Quiet operation, only show error messages -- **Polling interval**: 1 second default, configurable with `--interval` flag - -## Implementation Details - -### 1. Command Structure -```rust -/// Wait for a daemon process to terminate -Wait(WaitArgs), -``` - -### 2. Arguments Structure -```rust -#[derive(Args)] -struct WaitArgs { - /// Process identifier - id: String, - - /// Timeout in seconds (0 = infinite) - #[arg(long, default_value = "30")] - timeout: u64, - - /// Polling interval in seconds - #[arg(long, default_value = "1")] - interval: u64, -} -``` - -### 3. Core Function Implementation -```rust -fn wait_daemon(id: &str, timeout: u64, interval: u64) -> Result<()> { - // 1. Check if PID file exists - // 2. Read PID from file - // 3. Check if process exists initially - // 4. If timeout == 0, loop indefinitely - // 5. Otherwise, loop with timeout tracking - // 6. Poll every `interval` seconds - // 7. Return appropriate exit codes -} -``` - -### 4. Logic Flow -1. **Initial validation**: - - Check if PID file exists → error if not - - Read PID from file → error if invalid - - Check if process is running → error if already dead - -2. **Waiting loop**: - - If timeout = 0: infinite loop - - Otherwise: track elapsed time - - Poll every `interval` seconds using `is_process_running_by_pid()` - - Break when process terminates or timeout reached - -3. **Exit conditions**: - - Process terminates → exit 0 - - Timeout reached → error message + exit 1 - - Initial errors → error message + exit 1 - -### 5. Error Messages -- "Process '{id}' not found (no PID file)" -- "Process '{id}' is not running" -- "Timeout reached waiting for process '{id}' to terminate" - -## Testing Strategy - -### New Tests -1. **test_wait_nonexistent_process**: Should fail with appropriate error -2. **test_wait_already_dead_process**: Should fail when process already terminated -3. **test_wait_process_terminates**: Should succeed when process terminates normally -4. **test_wait_timeout**: Should fail when timeout is reached -5. **test_wait_infinite_timeout**: Test with timeout=0 (use short-lived process) -6. **test_wait_custom_interval**: Test with different polling intervals - -### Updated Tests -Replace `std::thread::sleep(Duration::from_millis(100))` with `demon wait` in: -- `test_run_creates_files` → `demon wait test --timeout 5` -- `test_run_with_complex_command` → `demon wait complex --timeout 5` -- Similar tests that wait for process completion - -## Files to Modify - -### 1. src/main.rs -- Add `Wait(WaitArgs)` to `Commands` enum (around line 146) -- Add `WaitArgs` struct after other Args structs (around line 206) -- Add `Commands::Wait(args) => wait_daemon(&args.id, args.timeout, args.interval)` to match statement (around line 246) -- Implement `wait_daemon()` function (add after other daemon functions) - -### 2. tests/cli.rs -- Add new test functions for wait subcommand -- Update existing tests to use wait instead of sleep where appropriate - -### 3. README.md -- Add wait command to command reference section -- Add examples showing wait usage - -### 4. LLM Guide (print_llm_guide function) -- Add wait command documentation -- Add to available commands list -- Add usage examples - -## Command Usage Examples - -```bash -# Wait with default 30s timeout -demon wait my-process - -# Wait indefinitely -demon wait my-process --timeout 0 - -# Wait with custom timeout and interval -demon wait my-process --timeout 60 --interval 2 -``` - -## Implementation Order -1. Implement core functionality in main.rs -2. Add comprehensive tests -3. Update existing tests to use wait -4. Update documentation (README + LLM guide) -5. Test full integration - -## Key Implementation Notes -- Use existing `is_process_running_by_pid()` function for consistency -- Use existing `PidFile::read_from_file()` for PID file handling -- Follow existing error handling patterns with anyhow -- Use `std::thread::sleep(Duration::from_secs(interval))` for polling -- Track elapsed time for timeout implementation -- Maintain quiet operation - no progress messages \ No newline at end of file diff --git a/IMPROVEMENT_PLAN.md b/IMPROVEMENT_PLAN.md deleted file mode 100644 index a553884..0000000 --- a/IMPROVEMENT_PLAN.md +++ /dev/null @@ -1,166 +0,0 @@ -# Demon CLI Improvement Plan - -## Overview -This document outlines the planned improvements to the demon CLI tool based on feedback and best practices. - -## Improvement Tasks - -### 1. Switch to `anyhow` for Error Handling -**Priority**: High -**Status**: Pending - -**Goal**: Replace `Box` with `anyhow::Result` throughout the codebase for better error handling. - -**Tasks**: -- Replace all `Result<(), Box>` with `anyhow::Result<()>` -- Use `anyhow::Context` for better error context -- Simplify error handling code -- Update imports and error propagation - -**Benefits**: -- Better error messages with context -- Simpler error handling -- More idiomatic Rust error handling - -### 2. Implement CLI Testing with `assert_cmd` -**Priority**: High -**Status**: Pending - -**Goal**: Create comprehensive integration tests for all CLI commands using the `assert_cmd` crate. - -**Prerequisites**: -- Research and document `assert_cmd` usage in CLAUDE.md -- Add `assert_cmd` dependency -- Create test infrastructure - -**Test Coverage Required**: -- `demon run`: Process spawning, file creation, duplicate detection -- `demon stop`: Process termination, timeout handling, cleanup -- `demon tail`: File watching behavior (basic scenarios) -- `demon cat`: File content display, flag handling -- `demon list`: Process listing, status detection -- `demon status`: Individual process status checks -- `demon clean`: Orphaned file cleanup -- Error scenarios: missing files, invalid PIDs, etc. - -**Test Structure**: -``` -tests/ -├── cli.rs # Main CLI integration tests -├── fixtures/ # Test data and helper files -└── common/ # Shared test utilities -``` - -### 3. Add Quiet Flag to List Command -**Priority**: Medium -**Status**: Pending - -**Goal**: Add `-q/--quiet` flag to the `demon list` command for machine-readable output. - -**Requirements**: -- Add `quiet` field to `ListArgs` struct (if needed, since `List` currently has no args) -- Convert `List` command to use `ListArgs` struct -- When quiet flag is used: - - No headers - - One line per process: `id:pid:status` - - No "No daemon processes found" message when empty - -**Example Output**: -```bash -# Normal mode -$ demon list -ID PID STATUS COMMAND --------------------------------------------------- -my-app 12345 RUNNING N/A - -# Quiet mode -$ demon list -q -my-app:12345:RUNNING -``` - -### 4. Add LLM Command -**Priority**: Medium -**Status**: Pending - -**Goal**: Add a `demon llm` command that outputs a comprehensive usage guide for other LLMs. - -**Requirements**: -- Add `Llm` variant to `Commands` enum -- No arguments needed -- Output to stdout (not stderr like other messages) -- Include all commands with examples -- Assume the reader is an LLM that needs to understand how to use the tool - -**Content Structure**: -- Tool overview and purpose -- All available commands with syntax -- Practical examples for each command -- Common workflows -- File structure explanation -- Error handling tips - -### 5. Remove `glob` Dependency -**Priority**: Low -**Status**: Pending - -**Goal**: Replace the `glob` crate with standard library `std::fs` functionality. - -**Implementation**: -- Remove `glob` from Cargo.toml -- Replace `glob("*.pid")` with `std::fs::read_dir()` + filtering -- Update imports -- Ensure same functionality is maintained - -**Functions to Update**: -- `list_daemons()`: Find all .pid files -- `clean_orphaned_files()`: Find all .pid files - -**Implementation Pattern**: -```rust -// Replace glob("*.pid") with: -std::fs::read_dir(".")? - .filter_map(|entry| entry.ok()) - .filter(|entry| { - entry.path().extension() - .and_then(|ext| ext.to_str()) - .map(|ext| ext == "pid") - .unwrap_or(false) - }) -``` - -## Implementation Order - -1. **Document assert_cmd** - Add understanding to CLAUDE.md -2. **Switch to anyhow** - Foundation for better error handling -3. **Implement tests** - Ensure current functionality works correctly -4. **Add quiet flag** - Small feature addition -5. **Add LLM command** - Documentation feature -6. **Remove glob** - Cleanup and reduce dependencies - -## Success Criteria - -- [ ] All existing functionality remains intact -- [ ] Comprehensive test coverage (>80% of CLI scenarios) -- [ ] Better error messages with context -- [ ] Machine-readable list output option -- [ ] LLM-friendly documentation command -- [ ] Reduced dependency footprint -- [ ] All changes committed with proper messages - -## Risk Assessment - -**Low Risk**: -- anyhow migration (straightforward replacement) -- quiet flag addition (additive change) -- LLM command (new, isolated feature) - -**Medium Risk**: -- glob removal (need to ensure exact same behavior) -- CLI testing (need to handle file system interactions carefully) - -## Notes - -- Each improvement should be implemented, tested, and committed separately -- Maintain backward compatibility for all existing commands -- Update IMPLEMENTATION_PLAN.md as work progresses -- Consider adding integration tests that verify the actual daemon functionality \ No newline at end of file diff --git a/IMPROVEMENT_PLAN_V2.md b/IMPROVEMENT_PLAN_V2.md deleted file mode 100644 index 3bac936..0000000 --- a/IMPROVEMENT_PLAN_V2.md +++ /dev/null @@ -1,264 +0,0 @@ -# Demon CLI Improvement Plan v2 - -## Overview -This plan outlines four major improvements to enhance the demon CLI tool's usability, error handling, and documentation. - -## Task 1: Rename PidFileData to PidFile - -### Rationale -- Shorter, cleaner name -- More intuitive - it represents a PID file, not just data about one -- Follows Rust naming conventions better - -### Implementation Steps -1. Rename struct `PidFileData` to `PidFile` -2. Update all references throughout the codebase -3. Update comments and documentation -4. Verify compilation and tests pass - -### Files to modify -- `src/main.rs` - struct definition and all usages - -### Risk Assessment -- **Low risk** - Simple rename refactor -- No functional changes -- All tests should continue to pass - -## Task 2: Implement PidFileReadError Enum - -### Rationale -- Better error handling with specific error types -- Eliminates redundant file existence checks -- More idiomatic Rust error handling -- Cleaner code in error handling paths - -### Design -```rust -#[derive(Debug)] -pub enum PidFileReadError { - /// The PID file does not exist - FileNotFound, - /// The PID file exists but has invalid content - FileInvalid(String), // Include reason for invalidity - /// IO error occurred while reading - IoError(std::io::Error), -} -``` - -### Implementation Steps -1. Define `PidFileReadError` enum with appropriate variants -2. Implement `Display` and `Error` traits for the enum -3. Update `PidFile::read_from_file()` to return `Result` -4. Update all call sites to handle the specific error types: - - `is_process_running()` - handle FileNotFound and FileInvalid as "not running" - - `stop_daemon()` - handle FileNotFound as "not running", FileInvalid as "cleanup needed" - - `list_daemons()` - handle FileInvalid as "INVALID" entry - - `status_daemon()` - handle FileNotFound as "NOT FOUND", FileInvalid as "ERROR" - - `clean_orphaned_files()` - handle FileInvalid as "needs cleanup" -5. Remove redundant `Path::new().exists()` checks where the error type provides this info -6. Test all error scenarios - -### Files to modify -- `src/main.rs` - enum definition, read_from_file method, all usage sites - -### Risk Assessment -- **Medium risk** - Changes error handling logic -- Need thorough testing of error scenarios -- Must ensure all edge cases are handled properly - -## Task 3: Make --id a Positional Argument - -### Analysis - -#### Current CLI Pattern -```bash -demon run --id web-server python -m http.server 8080 -demon stop --id web-server -demon status --id web-server -``` - -#### Proposed CLI Pattern -```bash -demon run web-server python -m http.server 8080 -demon stop web-server -demon status web-server -``` - -#### Pros -- **Better UX**: More natural and concise -- **Consistent with common tools**: Similar to git, docker, etc. -- **Faster to type**: No --id flag needed -- **More intuitive**: ID naturally comes first before the command - -#### Cons -- **Breaking change**: Existing scripts/users need to update -- **Potential ambiguity**: ID could be confused with command in some cases -- **Parsing complexity**: Need careful handling of edge cases - -#### Design Decisions -1. **Make ID positional for all commands that currently use --id** -2. **Keep -- separator support** for complex commands -3. **Update help text** to reflect new usage -4. **Maintain backward compatibility** by supporting both patterns initially (with deprecation warning) - -#### Commands to Update -- `run ` - ID becomes first positional arg -- `stop ` - ID becomes positional arg, remove timeout flag positioning issues -- `tail ` - ID becomes positional arg -- `cat ` - ID becomes positional arg -- `status ` - ID becomes positional arg - -#### Implementation Strategy -1. **Phase 1**: Support both patterns with deprecation warnings -2. **Phase 2**: Remove old pattern support (future version) - -### Implementation Steps -1. Define new argument structures with positional ID fields -2. Update clap derive macros to make ID positional -3. Update help text and documentation strings -4. Add deprecation warnings for --id usage (optional) -5. Update all internal function calls -6. Update tests to use new CLI pattern -7. Update LLM guide output - -### Files to modify -- `src/main.rs` - argument structures, help text -- `tests/cli.rs` - all test commands -- LLM guide text - -### Risk Assessment -- **High risk** - Breaking change for users -- Need to update all tests -- Must carefully verify argument parsing edge cases -- Consider gradual migration strategy - -## Task 4: Write Comprehensive README.md - -### Target Audience -- Developers who need background process management -- LLM agents and their operators -- DevOps engineers running long-term tasks -- Anyone working with npm run dev, build processes, etc. - -### Content Structure -```markdown -# Demon - Background Process Manager - -## Overview -Brief description focusing on core value proposition - -## Installation -- `cargo install demon` (when published) -- Building from source -- System requirements - -## Quick Start -- Basic examples -- Common workflows - -## Use Cases -- Development servers (npm run dev) -- Background tasks and scripts -- LLM agent process management -- CI/CD pipeline tasks -- Long-running computations - -## Command Reference -- Complete command documentation -- Examples for each command -- Common flags and options - -## Integration with LLM Agents -- How agents can use demon -- Machine-readable output formats -- Best practices for automation - -## Advanced Usage -- File management -- Process lifecycle -- Troubleshooting -- Performance considerations - -## Contributing -- Development setup -- Testing -- Contribution guidelines -``` - -### Key Messages -1. **Simplicity**: Easy background process management -2. **Visibility**: Always know what's running and its status -3. **Integration**: Built for automation and LLM agents -4. **Reliability**: Robust process lifecycle management - -### Implementation Steps -1. Research similar tools for README inspiration -2. Write comprehensive content covering all sections -3. Include practical examples and screenshots/command outputs -4. Add badges for build status, crates.io, etc. (when applicable) -5. Review and refine for clarity and completeness - -### Files to create -- `README.md` - comprehensive documentation - -### Risk Assessment -- **Low risk** - Documentation only -- No functional changes -- Easy to iterate and improve - -## Execution Status - -✅ **Task 1**: Rename PidFileData to PidFile - COMPLETED -✅ **Task 2**: Implement PidFileReadError enum - COMPLETED -✅ **Task 3**: Make --id positional - COMPLETED -✅ **Task 4**: Write README.md - COMPLETED - -## Summary of Completed Work - -All planned improvements have been successfully implemented: - -1. **Cleaner Naming**: PidFileData renamed to PidFile for better clarity -2. **Better Error Handling**: PidFileReadError enum provides specific error types and eliminates redundant checks -3. **Improved CLI UX**: Positional arguments make the interface more natural and consistent with common tools -4. **Comprehensive Documentation**: README.md provides complete project documentation optimized for developers and LLM agents - -The demon CLI tool now has: -- Better usability with positional arguments -- More robust error handling -- Comprehensive documentation -- All tests passing -- Clean, maintainable codebase - -## Testing Strategy - -After each task: -1. Run `cargo build` to ensure compilation -2. Run `cargo test` to ensure all tests pass -3. Manual testing of affected functionality -4. Format code with `cargo fmt` -5. Commit changes with descriptive message - -## Success Criteria - -- All tests pass after each change -- No regressions in functionality -- Improved error messages and handling -- Better CLI usability -- Comprehensive documentation -- Clean, maintainable code - -## Rollback Plan - -Each task will be committed separately, allowing for easy rollback if issues arise: -1. Git commit after each successful task -2. If issues found, can revert specific commits -3. Tests provide safety net for functionality - -## Timeline Estimate - -- Task 1: 15-20 minutes (straightforward refactor) -- Task 2: 30-45 minutes (error handling logic) -- Task 3: 45-60 minutes (CLI argument changes + tests) -- Task 4: 30-45 minutes (documentation writing) - -Total: ~2-3 hours \ No newline at end of file -- cgit