├── .mcp.json ├── test ├── test-rules-simple.md ├── test-task-simple.md ├── test-simple.sh ├── test-todo-tasks.md ├── README.md ├── test-todo-rules.md ├── test-immediate-execution.sh ├── test-plugin-comprehensive.sh └── test-functional-realworld.sh ├── .claude-plugin ├── marketplace.json └── plugin.json ├── claude-plugins-marketplace ├── marketplace.json ├── plugins.json └── README.md ├── .gitignore ├── hooks ├── scripts │ ├── log-file-changes.sh │ ├── check-daemon-status.sh │ └── session-end-prompt.sh └── hooks.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── pull_request_template.md ├── LICENSE ├── examples ├── task.example.md └── rules.example.md ├── commands ├── stop.md ├── restart.md ├── bin │ └── nights-watch ├── start.md ├── status.md ├── logs.md └── setup.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── marketplace-example ├── plugins.json └── README.md ├── view-logs.sh ├── setup-nights-watch.sh ├── agents └── task-executor.md ├── claude-nights-watch-manager.sh ├── mcp-server └── nights-watch-server.sh ├── verify-plugin.sh ├── claude-nights-watch-daemon.sh └── README.md /.mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "nights-watch": { 4 | "command": "${CLAUDE_PLUGIN_ROOT}/mcp-server/nights-watch-server.sh", 5 | "env": { 6 | "NIGHTS_WATCH_ROOT": "${CLAUDE_PLUGIN_ROOT}" 7 | } 8 | } 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /test/test-rules-simple.md: -------------------------------------------------------------------------------- 1 | # Simple Test Rules 2 | 3 | ## ALLOWED: 4 | - Create `test-output.txt` in the claude-nights-watch directory 5 | - List directory contents 6 | - Log all actions 7 | 8 | ## FORBIDDEN: 9 | - Do not create any other files 10 | - Do not modify existing files 11 | - Do not access directories outside claude-nights-watch -------------------------------------------------------------------------------- /test/test-task-simple.md: -------------------------------------------------------------------------------- 1 | # Simple Test Task 2 | 3 | Create a file called `test-output.txt` in the current directory with the following content: 4 | 5 | ``` 6 | Claude Nights Watch Test 7 | ======================== 8 | Execution Time: [current timestamp] 9 | Status: Task executed successfully 10 | 11 | This file was created autonomously by Claude Nights Watch. 12 | ``` 13 | 14 | Then list the contents of the current directory to confirm the file was created. -------------------------------------------------------------------------------- /.claude-plugin/marketplace.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-nights-watch-marketplace", 3 | "description": "Official marketplace for Claude Nights Watch plugin", 4 | "owner": { 5 | "name": "Aniket Karne", 6 | "email": "[email protected]", 7 | "url": "https://github.com/aniketkarne/ClaudeNightsWatch" 8 | }, 9 | "plugins": [ 10 | { 11 | "name": "claude-nights-watch", 12 | "source": "./", 13 | "description": "Autonomous task execution daemon for Claude CLI" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /claude-plugins-marketplace/marketplace.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-nights-watch-marketplace", 3 | "description": "Official marketplace for Claude Nights Watch plugin", 4 | "owner": { 5 | "name": "Aniket Karne", 6 | "email": "[email protected]", 7 | "url": "https://github.com/aniketkarne/ClaudeNightsWatch" 8 | }, 9 | "plugins": [ 10 | { 11 | "name": "claude-nights-watch", 12 | "source": ".", 13 | "description": "Autonomous task execution daemon for Claude CLI" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific task files 2 | task.md 3 | rules.md 4 | 5 | # Logs and runtime files 6 | logs/ 7 | *.log 8 | *.pid 9 | 10 | 11 | # OS files 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # Editor files 16 | *.swp 17 | *.swo 18 | *~ 19 | .idea/ 20 | .vscode/ 21 | 22 | # Temporary files 23 | *.tmp 24 | *.bak 25 | *.backup 26 | 27 | # Node modules (if using ccusage locally) 28 | node_modules/ 29 | package-lock.json 30 | 31 | # Python (if tasks involve Python) 32 | __pycache__/ 33 | *.pyc 34 | *.pyo 35 | .pytest_cache/ 36 | .coverage 37 | htmlcov/ 38 | 39 | # Environment files 40 | .env 41 | .env.local 42 | 43 | # Output directories 44 | output/ 45 | results/ 46 | reports/ -------------------------------------------------------------------------------- /hooks/scripts/log-file-changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Hook: Log file changes after Write/Edit operations 4 | # Tracks significant modifications for task execution audit trail 5 | 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | PLUGIN_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" 8 | ACTIVITY_LOG="$PLUGIN_ROOT/logs/nights-watch-activity.log" 9 | 10 | # Ensure log directory exists 11 | mkdir -p "$(dirname "$ACTIVITY_LOG")" 12 | 13 | # Log the file operation with timestamp 14 | # This helps track what was modified during autonomous execution 15 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] File operation: $CLAUDE_TOOL_USE" >> "$ACTIVITY_LOG" 2>/dev/null 16 | 17 | # Silent operation - don't interrupt Claude's workflow 18 | exit 0 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Safety considerations** 20 | How does this feature maintain safety in autonomous execution? 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /hooks/scripts/check-daemon-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Hook: Check daemon status on session start 4 | # Displays a helpful message if daemon is running or suggests starting it 5 | 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | PLUGIN_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" 8 | PID_FILE="$PLUGIN_ROOT/logs/claude-nights-watch-daemon.pid" 9 | 10 | # Only show status if PID file exists 11 | if [ -f "$PID_FILE" ]; then 12 | PID=$(cat "$PID_FILE") 13 | if kill -0 "$PID" 2>/dev/null; then 14 | echo "🌙 Claude Nights Watch daemon is running (PID: $PID)" 15 | echo " Use /nights-watch status for details" 16 | else 17 | echo "⚠️ Nights Watch daemon PID file found but process not running" 18 | echo " Use /nights-watch start to restart" 19 | fi 20 | fi 21 | 22 | # Silent if daemon not configured (no PID file) 23 | exit 0 24 | 25 | -------------------------------------------------------------------------------- /.claude-plugin/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-nights-watch", 3 | "version": "1.0.0", 4 | "description": "Autonomous task execution daemon that monitors Claude usage windows and executes predefined tasks automatically. Keep Claude working 24/7 on your projects.", 5 | "author": { 6 | "name": "Aniket Karne", 7 | "email": "[email protected]", 8 | "url": "https://github.com/aniketkarne" 9 | }, 10 | "homepage": "https://github.com/aniketkarne/ClaudeNightsWatch", 11 | "repository": "https://github.com/aniketkarne/ClaudeNightsWatch", 12 | "license": "MIT", 13 | "keywords": [ 14 | "automation", 15 | "daemon", 16 | "task-execution", 17 | "monitoring", 18 | "autonomous", 19 | "productivity", 20 | "workflow", 21 | "ci-cd" 22 | ], 23 | "commands": "./commands/", 24 | "agents": "./agents/", 25 | "hooks": "./hooks/hooks.json", 26 | "mcpServers": "./.mcp.json" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Logs** 24 | If applicable, add log files to help explain your problem. Please sanitize any sensitive information. 25 | 26 | **System Information (please complete the following information):** 27 | - OS: [e.g. macOS, Ubuntu] 28 | - Claude CLI version: [e.g. 0.1.0] 29 | - ccusage installed: [yes/no] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | 34 | **Task and Rules Files** 35 | If relevant, please share your task.md and rules.md files (with sensitive info removed). -------------------------------------------------------------------------------- /hooks/scripts/session-end-prompt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Hook: Session end prompt 4 | # Reminds users about daemon if task.md exists but daemon isn't running 5 | 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | PLUGIN_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" 8 | PID_FILE="$PLUGIN_ROOT/logs/claude-nights-watch-daemon.pid" 9 | TASK_FILE="$(pwd)/task.md" 10 | 11 | # Check if task.md exists in current directory 12 | if [ -f "$TASK_FILE" ]; then 13 | # Check if daemon is running 14 | DAEMON_RUNNING=false 15 | if [ -f "$PID_FILE" ]; then 16 | PID=$(cat "$PID_FILE") 17 | if kill -0 "$PID" 2>/dev/null; then 18 | DAEMON_RUNNING=true 19 | fi 20 | fi 21 | 22 | # If task exists but daemon not running, suggest starting it 23 | if [ "$DAEMON_RUNNING" = false ]; then 24 | echo "" 25 | echo "💡 You have a task.md file but Nights Watch daemon isn't running." 26 | echo " Start autonomous execution: /nights-watch start" 27 | fi 28 | fi 29 | 30 | exit 0 31 | 32 | -------------------------------------------------------------------------------- /hooks/hooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "SessionStart": [ 4 | { 5 | "description": "Check Nights Watch daemon status on session start", 6 | "hooks": [ 7 | { 8 | "type": "command", 9 | "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/check-daemon-status.sh" 10 | } 11 | ] 12 | } 13 | ], 14 | "SessionEnd": [ 15 | { 16 | "description": "Optionally prompt to start daemon on session end", 17 | "hooks": [ 18 | { 19 | "type": "command", 20 | "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/session-end-prompt.sh" 21 | } 22 | ] 23 | } 24 | ], 25 | "PostToolUse": [ 26 | { 27 | "matcher": "Write|Edit", 28 | "description": "Log significant file modifications for task tracking", 29 | "hooks": [ 30 | { 31 | "type": "command", 32 | "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/log-file-changes.sh" 33 | } 34 | ] 35 | } 36 | ] 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /test/test-simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple test to verify basic functionality 4 | 5 | echo "=== Claude Nights Watch Simple Test ===" 6 | echo "" 7 | 8 | # Set paths 9 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 10 | export CLAUDE_NIGHTS_WATCH_DIR="$(dirname "$SCRIPT_DIR")" 11 | TASK_FILE="test-task-simple.md" 12 | RULES_FILE="test-rules-simple.md" 13 | 14 | # Temporarily use test files 15 | cd "$CLAUDE_NIGHTS_WATCH_DIR" 16 | mv task.md task.md.bak 2>/dev/null || true 17 | mv rules.md rules.md.bak 2>/dev/null || true 18 | cp test/$TASK_FILE task.md 19 | cp test/$RULES_FILE rules.md 20 | 21 | echo "Test files prepared:" 22 | echo "- Task: test-task-simple.md" 23 | echo "- Rules: test-rules-simple.md" 24 | echo "" 25 | 26 | # Run the test 27 | echo "Running test execution..." 28 | ./test/test-immediate-execution.sh 29 | 30 | # Restore original files 31 | mv task.md.bak task.md 2>/dev/null || true 32 | mv rules.md.bak rules.md 2>/dev/null || true 33 | 34 | echo "" 35 | echo "=== Test Complete ===" 36 | echo "Check the log file: $CLAUDE_NIGHTS_WATCH_DIR/logs/claude-nights-watch-test.log" 37 | echo "Check if test-output.txt was created" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Aniket Karne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/task.example.md: -------------------------------------------------------------------------------- 1 | # Example Task for Claude Nights Watch 2 | 3 | ## Project: Automated Code Review and Improvement 4 | 5 | ### Objectives: 6 | 1. Review all Python files in the src/ directory 7 | 2. Identify and fix any linting issues 8 | 3. Add missing docstrings to functions 9 | 4. Create a summary report of changes 10 | 11 | ### Specific Tasks: 12 | 13 | #### 1. Code Quality Check 14 | - Run pylint on all Python files 15 | - Fix any critical issues found 16 | - Ensure PEP 8 compliance 17 | 18 | #### 2. Documentation 19 | - Add docstrings to all public functions 20 | - Update README.md with any new features 21 | - Generate API documentation 22 | 23 | #### 3. Testing 24 | - Run existing test suite 25 | - Add tests for any uncovered functions 26 | - Ensure all tests pass 27 | 28 | #### 4. Final Report 29 | - Create a CHANGES.md file 30 | - List all modifications made 31 | - Include before/after metrics 32 | 33 | ### Constraints: 34 | - Do not modify any files outside the project directory 35 | - Create a new branch called 'automated-improvements' 36 | - Commit changes with descriptive messages 37 | 38 | ### Success Criteria: 39 | - All tests pass 40 | - No critical linting issues 41 | - Documentation coverage > 80% 42 | - Clean git history with atomic commits -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Please include a summary of the changes and the related issue. Please also include relevant motivation and context. 3 | 4 | Fixes # (issue) 5 | 6 | ## Type of change 7 | - [ ] Bug fix (non-breaking change which fixes an issue) 8 | - [ ] New feature (non-breaking change which adds functionality) 9 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 10 | - [ ] This change requires a documentation update 11 | 12 | ## Testing 13 | - [ ] I have tested this change with the test suite 14 | - [ ] I have tested this change with real tasks 15 | - [ ] I have verified that safety rules are still enforced 16 | - [ ] I have checked that logs capture relevant information 17 | 18 | ## Checklist: 19 | - [ ] My code follows the style guidelines of this project 20 | - [ ] I have performed a self-review of my own code 21 | - [ ] I have commented my code, particularly in hard-to-understand areas 22 | - [ ] I have made corresponding changes to the documentation 23 | - [ ] My changes generate no new warnings 24 | - [ ] I have added tests that prove my fix is effective or that my feature works 25 | - [ ] New and existing unit tests pass locally with my changes 26 | 27 | ## Safety Review 28 | - [ ] This change maintains safety in autonomous execution 29 | - [ ] This change does not bypass existing safety rules 30 | - [ ] This change has been tested with restrictive rules.md files -------------------------------------------------------------------------------- /commands/stop.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Stop the Claude Nights Watch daemon and halt autonomous task execution 3 | usage: /nights-watch stop 4 | examples: 5 | - /nights-watch stop 6 | --- 7 | 8 | # Stop Nights Watch Daemon 9 | 10 | Stops the running Claude Nights Watch daemon gracefully, halting all autonomous task execution monitoring. 11 | 12 | ## Usage 13 | 14 | ```bash 15 | /nights-watch stop 16 | ``` 17 | 18 | ## What It Does 19 | 20 | 1. Locates the running daemon process 21 | 2. Sends graceful shutdown signal (SIGTERM) 22 | 3. Waits up to 10 seconds for clean shutdown 23 | 4. Force kills if necessary (SIGKILL) 24 | 5. Cleans up PID file and resources 25 | 26 | ## Behavior 27 | 28 | - **Graceful Shutdown**: Daemon completes current operations before stopping 29 | - **10-Second Timeout**: If not stopped gracefully, force kill is applied 30 | - **Safe**: Any running Claude task is allowed to complete 31 | - **Clean State**: All PID files and locks are removed 32 | 33 | ## Examples 34 | 35 | ```bash 36 | /nights-watch stop 37 | ``` 38 | 39 | ## Implementation 40 | 41 | ```bash 42 | ${CLAUDE_PLUGIN_ROOT}/claude-nights-watch-manager.sh stop 43 | ``` 44 | 45 | ## Exit Codes 46 | 47 | - `0` - Daemon stopped successfully 48 | - `1` - Daemon was not running or error occurred 49 | 50 | ## See Also 51 | 52 | - `/nights-watch start` - Start the daemon 53 | - `/nights-watch restart` - Restart the daemon 54 | - `/nights-watch status` - Check if daemon is running 55 | 56 | -------------------------------------------------------------------------------- /test/test-todo-tasks.md: -------------------------------------------------------------------------------- 1 | # Task: Create a Simple Todo List Application 2 | 3 | ## Objective 4 | Build a basic command-line todo list application in Python with the following features: 5 | 6 | ## Requirements 7 | 8 | ### 1. Create Project Structure 9 | - Create a directory called `simple-todo-app` 10 | - Create the main Python file: `todo.py` 11 | - Create a README.md with usage instructions 12 | 13 | ### 2. Implement Core Features 14 | The todo app should support these commands: 15 | - `add "task description"` - Add a new todo item 16 | - `list` - Display all todo items with their IDs 17 | - `done ` - Mark a todo item as completed 18 | - `delete ` - Remove a todo item 19 | - `help` - Show available commands 20 | 21 | ### 3. Data Storage 22 | - Store todos in a JSON file called `todos.json` 23 | - Each todo should have: id, description, completed status, created_at timestamp 24 | 25 | ### 4. Code Quality 26 | - Add proper error handling for file operations 27 | - Include input validation 28 | - Add helpful error messages 29 | 30 | ### 5. Testing 31 | - Create a test file `test_todo.py` with at least 5 test cases 32 | - Test adding, listing, completing, and deleting todos 33 | - Test error cases (invalid IDs, empty inputs) 34 | 35 | ### 6. Documentation 36 | - Add docstrings to all functions 37 | - Create clear usage examples in README.md 38 | - Include installation instructions 39 | 40 | ## Deliverables 41 | 1. Working todo.py application 42 | 2. test_todo.py with passing tests 43 | 3. README.md with complete documentation 44 | 4. todos.json sample file 45 | 5. Create a summary report of what was implemented 46 | 47 | Please implement this step by step, testing each feature as you go. -------------------------------------------------------------------------------- /commands/restart.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Restart the Claude Nights Watch daemon to apply configuration changes 3 | usage: /nights-watch restart [--at TIME] 4 | examples: 5 | - /nights-watch restart 6 | - /nights-watch restart --at "09:00" 7 | --- 8 | 9 | # Restart Nights Watch Daemon 10 | 11 | Restart the Claude Nights Watch daemon by stopping the current instance and starting a new one. Useful after modifying task or rules files. 12 | 13 | ## Usage 14 | 15 | ```bash 16 | /nights-watch restart [OPTIONS] 17 | ``` 18 | 19 | ## Options 20 | 21 | - `--at TIME` - Schedule daemon to start monitoring at a specific time after restart 22 | - Format: `HH:MM` (today) or `YYYY-MM-DD HH:MM` (specific date/time) 23 | 24 | ## What It Does 25 | 26 | 1. Stops the currently running daemon gracefully 27 | 2. Waits 2 seconds for clean shutdown 28 | 3. Starts a new daemon instance 29 | 4. Loads latest task.md and rules.md files 30 | 5. Resumes monitoring with fresh configuration 31 | 32 | ## When to Restart 33 | 34 | Restart the daemon when you: 35 | - Modified `task.md` and want to apply changes 36 | - Updated `rules.md` safety constraints 37 | - Changed environment variables 38 | - Troubleshooting daemon issues 39 | - Want to reschedule start time 40 | 41 | ## Examples 42 | 43 | **Simple restart:** 44 | ```bash 45 | /nights-watch restart 46 | ``` 47 | 48 | **Restart with new schedule:** 49 | ```bash 50 | /nights-watch restart --at "09:00" 51 | ``` 52 | 53 | ## Implementation 54 | 55 | ```bash 56 | ${CLAUDE_PLUGIN_ROOT}/claude-nights-watch-manager.sh restart "$@" 57 | ``` 58 | 59 | ## Restart vs Stop/Start 60 | 61 | - **Restart**: Automated stop + start in one command 62 | - **Stop/Start**: Manual control over timing between operations 63 | 64 | Both achieve the same result, restart is just more convenient. 65 | 66 | ## See Also 67 | 68 | - `/nights-watch stop` - Stop daemon only 69 | - `/nights-watch start` - Start daemon only 70 | - `/nights-watch status` - Verify restart was successful 71 | 72 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.0.0] - 2025-01-15 9 | 10 | ### Added 11 | - Initial release of Claude Nights Watch 12 | - Autonomous task execution from task.md files 13 | - Safety rules enforcement from rules.md files 14 | - Comprehensive logging system with full prompt/response capture 15 | - Interactive setup wizard (`setup-nights-watch.sh`) 16 | - Daemon management system (`claude-nights-watch-manager.sh`) 17 | - Interactive log viewer (`view-logs.sh`) 18 | - Test suite with immediate execution testing 19 | - Scheduled start time support 20 | - Integration with ccusage for accurate timing 21 | - Fallback to time-based checking without ccusage 22 | - Adaptive monitoring intervals based on remaining time 23 | 24 | ### Features 25 | - **Core Daemon**: Monitors Claude usage windows and executes tasks 26 | - **Task Management**: Reads and executes tasks from markdown files 27 | - **Safety System**: Enforces rules and constraints for autonomous execution 28 | - **Logging**: Complete audit trail of all executions and responses 29 | - **Scheduling**: Start monitoring at specific times 30 | - **Testing**: Comprehensive test suite for validation 31 | 32 | ### Documentation 33 | - Complete README with installation and usage instructions 34 | - Example task and rules files 35 | - Testing documentation 36 | - Contributing guidelines 37 | - MIT License 38 | 39 | ### Safety 40 | - Uses `--dangerously-skip-permissions` with safety rule enforcement 41 | - Comprehensive example rules for common safety scenarios 42 | - Logging of all prompts and responses for audit purposes 43 | - Test scripts for validation before production use 44 | 45 | ## [Unreleased] 46 | 47 | ### Planned 48 | - Web dashboard for monitoring and management 49 | - Multiple task file support 50 | - Task scheduling and queuing 51 | - Integration with more timing tools 52 | - Enhanced error recovery mechanisms -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Testing Claude Nights Watch 2 | 3 | This directory contains test scripts and sample files for testing Claude Nights Watch functionality. 4 | 5 | ## Test Files 6 | 7 | ### Scripts 8 | - `test-immediate-execution.sh` - Run a single task immediately without waiting for the 5-hour window 9 | - `test-simple.sh` - Quick test with a simple task to verify basic functionality 10 | 11 | ### Sample Tasks 12 | - `test-task-simple.md` - Simple file creation task for basic testing 13 | - `test-rules-simple.md` - Minimal safety rules for the simple test 14 | 15 | ## Running Tests 16 | 17 | ### 1. Basic Functionality Test 18 | 19 | From the test directory: 20 | ```bash 21 | ./test-simple.sh 22 | ``` 23 | 24 | This will: 25 | - Copy test task/rules to the main directory 26 | - Execute the task immediately 27 | - Clean up after completion 28 | - Show you where to find the logs 29 | 30 | ### 2. Manual Test Execution 31 | 32 | To test with your own task: 33 | ```bash 34 | cd .. # Go to main directory 35 | cp test/test-immediate-execution.sh . 36 | ./test-immediate-execution.sh 37 | ``` 38 | 39 | ### 3. Testing the Daemon 40 | 41 | To test the full daemon workflow: 42 | ```bash 43 | cd .. # Go to main directory 44 | # Create your task.md and rules.md 45 | ./claude-nights-watch-manager.sh start 46 | # Monitor with: ./claude-nights-watch-manager.sh status 47 | ``` 48 | 49 | ## Test Safety 50 | 51 | The test scripts include safety features: 52 | - Preview prompts before execution 53 | - Ask for confirmation 54 | - Log everything to `logs/` directory 55 | - Use restricted test rules to prevent unwanted actions 56 | 57 | ## Troubleshooting Tests 58 | 59 | If tests fail: 60 | 1. Check logs in `../logs/claude-nights-watch-test.log` 61 | 2. Verify Claude CLI is installed: `which claude` 62 | 3. Ensure you have active Claude usage: `ccusage blocks` 63 | 4. Check file permissions: all `.sh` files should be executable 64 | 65 | ## Creating Your Own Tests 66 | 67 | 1. Create a new task file: `my-test-task.md` 68 | 2. Create corresponding rules: `my-test-rules.md` 69 | 3. Copy and modify `test-simple.sh` to use your files 70 | 4. Run and check logs for results -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Claude Nights Watch 2 | 3 | Thank you for your interest in contributing to Claude Nights Watch! 4 | 5 | ## How to Contribute 6 | 7 | ### Reporting Issues 8 | 9 | 1. Check if the issue already exists 10 | 2. Include: 11 | - Clear description of the problem 12 | - Steps to reproduce 13 | - Expected vs actual behavior 14 | - Log outputs (sanitized of sensitive data) 15 | - System information (OS, Claude CLI version) 16 | 17 | ### Submitting Pull Requests 18 | 19 | 1. Fork the repository from [https://github.com/aniketkarne/ClaudeNightsWatch](https://github.com/aniketkarne/ClaudeNightsWatch) 20 | 2. Create a feature branch (`git checkout -b feature/amazing-feature`) 21 | 3. Make your changes 22 | 4. Test thoroughly (see Testing section) 23 | 5. Commit with clear messages (`git commit -m 'Add amazing feature'`) 24 | 6. Push to your branch (`git push origin feature/amazing-feature`) 25 | 7. Open a Pull Request against the main repository 26 | 27 | ### Code Guidelines 28 | 29 | - Follow existing shell script conventions 30 | - Add comments for complex logic 31 | - Update documentation for new features 32 | - Include error handling 33 | - Test on both macOS and Linux if possible 34 | 35 | ### Testing 36 | 37 | Before submitting: 38 | 1. Run the test suite: `cd test && ./test-simple.sh` 39 | 2. Test your changes with real tasks 40 | 3. Verify logs capture all relevant information 41 | 4. Check that safety rules are respected 42 | 43 | ### Safety First 44 | 45 | When contributing: 46 | - Never add features that bypass safety rules 47 | - Always consider autonomous execution implications 48 | - Document any new risks or considerations 49 | - Default to safer options 50 | 51 | ### Documentation 52 | 53 | - Update README.md for user-facing changes 54 | - Add examples for new features 55 | - Document new configuration options 56 | - Update test documentation if needed 57 | 58 | ## Development Setup 59 | 60 | 1. Fork and clone the repository 61 | 2. Create a test task.md and rules.md 62 | 3. Run tests to ensure everything works 63 | 4. Make your changes 64 | 5. Test again thoroughly 65 | 66 | ## Questions? 67 | 68 | Feel free to open an issue for discussion before making large changes. -------------------------------------------------------------------------------- /commands/bin/nights-watch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch CLI - Plugin Command Wrapper 4 | # This script routes all /nights-watch commands to the appropriate handlers 5 | 6 | PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}" 7 | MANAGER_SCRIPT="$PLUGIN_ROOT/claude-nights-watch-manager.sh" 8 | SETUP_SCRIPT="$PLUGIN_ROOT/setup-nights-watch.sh" 9 | 10 | # Export plugin root for child processes 11 | export CLAUDE_NIGHTS_WATCH_DIR="${CLAUDE_NIGHTS_WATCH_DIR:-$(pwd)}" 12 | 13 | # Parse command 14 | COMMAND="${1:-help}" 15 | shift 16 | 17 | case "$COMMAND" in 18 | start) 19 | "$MANAGER_SCRIPT" start "$@" 20 | ;; 21 | stop) 22 | "$MANAGER_SCRIPT" stop "$@" 23 | ;; 24 | restart) 25 | "$MANAGER_SCRIPT" restart "$@" 26 | ;; 27 | status) 28 | "$MANAGER_SCRIPT" status "$@" 29 | ;; 30 | logs) 31 | "$MANAGER_SCRIPT" logs "$@" 32 | ;; 33 | task) 34 | "$MANAGER_SCRIPT" task "$@" 35 | ;; 36 | setup) 37 | "$SETUP_SCRIPT" "$@" 38 | ;; 39 | help|--help|-h) 40 | cat << 'EOF' 41 | Claude Nights Watch - Autonomous Task Execution Daemon 42 | 43 | Usage: /nights-watch [options] 44 | 45 | Commands: 46 | start [--at TIME] Start the daemon 47 | stop Stop the daemon 48 | restart Restart the daemon 49 | status Show daemon status 50 | logs [-f] View logs (use -f to follow) 51 | task View current task and rules 52 | setup Run interactive setup wizard 53 | help Show this help message 54 | 55 | Examples: 56 | /nights-watch start Start daemon immediately 57 | /nights-watch start --at "09:00" Start at 9 AM 58 | /nights-watch status Check daemon status 59 | /nights-watch logs -f Follow logs in real-time 60 | 61 | Documentation: 62 | Plugin Root: $PLUGIN_ROOT 63 | Examples: $PLUGIN_ROOT/examples/ 64 | 65 | For more information, see: https://github.com/aniketkarne/ClaudeNightsWatch 66 | EOF 67 | ;; 68 | *) 69 | echo "Unknown command: $COMMAND" 70 | echo "Use '/nights-watch help' for usage information" 71 | exit 1 72 | ;; 73 | esac 74 | 75 | -------------------------------------------------------------------------------- /commands/start.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Start the Claude Nights Watch daemon to begin autonomous task execution monitoring 3 | usage: /nights-watch start [--at TIME] 4 | examples: 5 | - /nights-watch start 6 | - /nights-watch start --at "09:00" 7 | - /nights-watch start --at "2025-10-12 14:30" 8 | --- 9 | 10 | # Start Nights Watch Daemon 11 | 12 | Starts the Claude Nights Watch daemon to monitor your Claude usage windows and execute tasks from `task.md` automatically. 13 | 14 | ## Usage 15 | 16 | ```bash 17 | /nights-watch start [OPTIONS] 18 | ``` 19 | 20 | ## Options 21 | 22 | - `--at TIME` - Schedule daemon to start monitoring at a specific time 23 | - Format: `HH:MM` (today) or `YYYY-MM-DD HH:MM` (specific date/time) 24 | - Daemon will wait until scheduled time before beginning task execution 25 | 26 | ## What It Does 27 | 28 | 1. Checks for required files (`task.md` in current directory) 29 | 2. Starts background daemon process 30 | 3. Begins monitoring Claude usage windows 31 | 4. Executes tasks when approaching the 5-hour limit 32 | 5. Logs all activity to `logs/claude-nights-watch-daemon.log` 33 | 34 | ## Prerequisites 35 | 36 | - `task.md` file must exist in current directory (create one if not present) 37 | - `rules.md` file is recommended for safety constraints (optional) 38 | - Claude CLI must be installed and configured 39 | 40 | ## Examples 41 | 42 | **Start immediately:** 43 | ```bash 44 | /nights-watch start 45 | ``` 46 | 47 | **Start at 9 AM today:** 48 | ```bash 49 | /nights-watch start --at "09:00" 50 | ``` 51 | 52 | **Start at specific date and time:** 53 | ```bash 54 | /nights-watch start --at "2025-10-12 14:30" 55 | ``` 56 | 57 | ## Implementation 58 | 59 | ```bash 60 | ${CLAUDE_PLUGIN_ROOT}/claude-nights-watch-manager.sh start "$@" 61 | ``` 62 | 63 | ## Safety Notice 64 | 65 | ⚠️ The daemon uses `--dangerously-skip-permissions` for autonomous execution. Always: 66 | - Test tasks manually first 67 | - Use comprehensive `rules.md` for safety constraints 68 | - Monitor logs regularly 69 | - Start with simple, non-destructive tasks 70 | 71 | ## See Also 72 | 73 | - `/nights-watch stop` - Stop the daemon 74 | - `/nights-watch status` - Check daemon status 75 | - `/nights-watch task` - View/edit current task 76 | - `/nights-watch logs` - View execution logs 77 | 78 | -------------------------------------------------------------------------------- /test/test-todo-rules.md: -------------------------------------------------------------------------------- 1 | # Safety Rules for Todo App Test 2 | 3 | ## CRITICAL SAFETY RULES: 4 | 5 | ### 1. Directory Restrictions 6 | - **ONLY** create files within the `simple-todo-app` subdirectory 7 | - **NEVER** modify files outside of this project directory 8 | - **NEVER** access parent directories or system files 9 | 10 | ### 2. File Operations 11 | - **ONLY** create these specific files: 12 | - `simple-todo-app/todo.py` 13 | - `simple-todo-app/test_todo.py` 14 | - `simple-todo-app/README.md` 15 | - `simple-todo-app/todos.json` 16 | - `simple-todo-app/IMPLEMENTATION_REPORT.md` 17 | - **NEVER** delete any existing files 18 | - **ALWAYS** use safe file operations with proper error handling 19 | 20 | ### 3. Command Restrictions 21 | - **NEVER** use `sudo` or administrative commands 22 | - **NEVER** install global packages 23 | - **ONLY** use Python standard library (no pip installs) 24 | - **NEVER** modify system Python or environment 25 | 26 | ### 4. Testing Rules 27 | - **ALWAYS** run tests in isolated manner 28 | - **NEVER** leave test artifacts outside the project directory 29 | - **CLEAN UP** any temporary files after testing 30 | 31 | ### 5. Git Rules (if needed) 32 | - **DO NOT** initialize git repositories 33 | - **DO NOT** make any commits 34 | - **ONLY** work with local files 35 | 36 | ## ALLOWED ACTIONS: 37 | 38 | 1. Create the `simple-todo-app` directory 39 | 2. Write Python code using only standard library 40 | 3. Create and modify files within the project directory 41 | 4. Run Python scripts for testing 42 | 5. Read and write JSON files 43 | 6. Generate documentation 44 | 45 | ## EXECUTION GUIDELINES: 46 | 47 | 1. Start by creating the project directory 48 | 2. Implement features incrementally 49 | 3. Test each feature after implementation 50 | 4. Document all functions properly 51 | 5. Create a comprehensive implementation report 52 | 53 | ## OUTPUT REQUIREMENTS: 54 | 55 | At the end of execution, ensure these files exist: 56 | - `simple-todo-app/todo.py` - Main application 57 | - `simple-todo-app/test_todo.py` - Test suite 58 | - `simple-todo-app/README.md` - Documentation 59 | - `simple-todo-app/todos.json` - Sample data file 60 | - `simple-todo-app/IMPLEMENTATION_REPORT.md` - Summary of what was done 61 | 62 | ## LOGGING: 63 | 64 | - Log each major step completed 65 | - Report any errors encountered 66 | - Summarize the final state of the project -------------------------------------------------------------------------------- /commands/status.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Check the status of Claude Nights Watch daemon and view current configuration 3 | usage: /nights-watch status 4 | examples: 5 | - /nights-watch status 6 | --- 7 | 8 | # Nights Watch Status 9 | 10 | Displays comprehensive status information about the Claude Nights Watch daemon, including running state, configuration, and recent activity. 11 | 12 | ## Usage 13 | 14 | ```bash 15 | /nights-watch status 16 | ``` 17 | 18 | ## Information Displayed 19 | 20 | ### Daemon State 21 | - **Running/Stopped**: Current daemon status 22 | - **PID**: Process ID if running 23 | - **Activity Status**: 24 | - ✅ ACTIVE - Currently monitoring and executing tasks 25 | - ⏰ WAITING - Waiting for scheduled start time 26 | 27 | ### Configuration 28 | - **Task File**: Location and line count of `task.md` 29 | - **Rules File**: Location and line count of `rules.md` (if present) 30 | - **Start Time**: Scheduled start time (if configured) 31 | - **Time Until Start**: Countdown if waiting for scheduled time 32 | 33 | ### Execution Info 34 | - **Recent Activity**: Last 5 log entries 35 | - **Next Execution**: Estimated time until next task execution 36 | - **Time Since Last Activity**: Hours/minutes since last Claude usage 37 | 38 | ## Status Indicators 39 | 40 | - `✅ ACTIVE` - Daemon is running and monitoring 41 | - `⏰ WAITING` - Daemon is running but waiting for start time 42 | - `❌ STOPPED` - Daemon is not running 43 | 44 | ## Examples 45 | 46 | **Check daemon status:** 47 | ```bash 48 | /nights-watch status 49 | ``` 50 | 51 | **Sample Output:** 52 | ``` 53 | [INFO] Daemon is running with PID 12345 54 | [INFO] Status: ✅ ACTIVE - Task execution monitoring enabled 55 | 56 | [TASK] Task file: /path/to/task.md (42 lines) 57 | [TASK] Rules file: /path/to/rules.md (106 lines) 58 | 59 | [INFO] Recent activity: 60 | [2025-10-11 14:30:00] Daemon started 61 | [2025-10-11 14:30:05] Task file loaded 62 | [2025-10-11 14:30:10] Monitoring began 63 | [2025-10-11 14:35:00] Next check in 10 minutes 64 | [2025-10-11 14:45:00] Time remaining: 234 minutes 65 | 66 | [INFO] Estimated time until next task execution: 3h 54m 67 | ``` 68 | 69 | ## Implementation 70 | 71 | ```bash 72 | ${CLAUDE_PLUGIN_ROOT}/claude-nights-watch-manager.sh status 73 | ``` 74 | 75 | ## See Also 76 | 77 | - `/nights-watch start` - Start the daemon 78 | - `/nights-watch logs` - View detailed logs 79 | - `/nights-watch task` - View current task configuration 80 | 81 | -------------------------------------------------------------------------------- /marketplace-example/plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Claude Nights Watch Marketplace", 3 | "description": "Official marketplace for Claude Nights Watch and related productivity plugins", 4 | "maintainer": { 5 | "name": "Aniket Karne", 6 | "email": "[email protected]", 7 | "url": "https://github.com/aniketkarne" 8 | }, 9 | "plugins": [ 10 | { 11 | "name": "claude-nights-watch", 12 | "repository": "https://github.com/aniketkarne/ClaudeNightsWatch", 13 | "description": "Autonomous task execution daemon that monitors Claude usage windows and executes predefined tasks automatically", 14 | "longDescription": "Transform Claude Code from an interactive assistant into an autonomous worker. Claude Nights Watch monitors your Claude usage windows (5-hour blocks) and automatically executes predefined tasks from a markdown file before each window expires - keeping your Claude session continuously active while accomplishing real work. Includes comprehensive safety rules, full audit logging, slash commands, specialized agent, smart hooks, and MCP server integration.", 15 | "version": "1.0.0", 16 | "author": "Aniket Karne", 17 | "homepage": "https://github.com/aniketkarne/ClaudeNightsWatch", 18 | "license": "MIT", 19 | "keywords": [ 20 | "automation", 21 | "daemon", 22 | "task-execution", 23 | "monitoring", 24 | "autonomous", 25 | "productivity", 26 | "workflow", 27 | "ci-cd", 28 | "devops", 29 | "continuous-integration" 30 | ], 31 | "categories": [ 32 | "automation", 33 | "productivity", 34 | "devops", 35 | "workflow" 36 | ], 37 | "features": [ 38 | "Autonomous task execution every 5 hours", 39 | "Smart usage window monitoring with ccusage integration", 40 | "Comprehensive safety rules system", 41 | "Full audit logging with prompts and responses", 42 | "7 slash commands for complete control", 43 | "Specialized Task Executor agent", 44 | "Smart hooks for session integration", 45 | "MCP server with 8 programmatic tools", 46 | "Scheduled start time support", 47 | "Interactive setup wizard" 48 | ], 49 | "requirements": { 50 | "claude": ">=1.0.0", 51 | "ccusage": "optional" 52 | }, 53 | "screenshots": [ 54 | "https://github.com/aniketkarne/ClaudeNightsWatch/raw/main/docs/screenshots/status.png", 55 | "https://github.com/aniketkarne/ClaudeNightsWatch/raw/main/docs/screenshots/logs.png" 56 | ], 57 | "documentation": "https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/PLUGIN_INSTALLATION.md", 58 | "featured": true, 59 | "verified": true, 60 | "downloads": 0, 61 | "rating": 5.0, 62 | "lastUpdated": "2025-10-11" 63 | } 64 | ] 65 | } 66 | 67 | -------------------------------------------------------------------------------- /claude-plugins-marketplace/plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Claude Nights Watch Marketplace", 3 | "description": "Official marketplace for Claude Nights Watch and related productivity plugins", 4 | "maintainer": { 5 | "name": "Aniket Karne", 6 | "email": "[email protected]", 7 | "url": "https://github.com/aniketkarne" 8 | }, 9 | "plugins": [ 10 | { 11 | "name": "claude-nights-watch", 12 | "repository": "https://github.com/aniketkarne/ClaudeNightsWatch", 13 | "description": "Autonomous task execution daemon that monitors Claude usage windows and executes predefined tasks automatically", 14 | "longDescription": "Transform Claude Code from an interactive assistant into an autonomous worker. Claude Nights Watch monitors your Claude usage windows (5-hour blocks) and automatically executes predefined tasks from a markdown file before each window expires - keeping your Claude session continuously active while accomplishing real work. Includes comprehensive safety rules, full audit logging, slash commands, specialized agent, smart hooks, and MCP server integration.", 15 | "version": "1.0.0", 16 | "author": "Aniket Karne", 17 | "homepage": "https://github.com/aniketkarne/ClaudeNightsWatch", 18 | "license": "MIT", 19 | "keywords": [ 20 | "automation", 21 | "daemon", 22 | "task-execution", 23 | "monitoring", 24 | "autonomous", 25 | "productivity", 26 | "workflow", 27 | "ci-cd", 28 | "devops", 29 | "continuous-integration" 30 | ], 31 | "categories": [ 32 | "automation", 33 | "productivity", 34 | "devops", 35 | "workflow" 36 | ], 37 | "features": [ 38 | "Autonomous task execution every 5 hours", 39 | "Smart usage window monitoring with ccusage integration", 40 | "Comprehensive safety rules system", 41 | "Full audit logging with prompts and responses", 42 | "7 slash commands for complete control", 43 | "Specialized Task Executor agent", 44 | "Smart hooks for session integration", 45 | "MCP server with 8 programmatic tools", 46 | "Scheduled start time support", 47 | "Interactive setup wizard" 48 | ], 49 | "requirements": { 50 | "claude": ">=1.0.0", 51 | "ccusage": "optional" 52 | }, 53 | "screenshots": [ 54 | "https://github.com/aniketkarne/ClaudeNightsWatch/raw/main/docs/screenshots/status.png", 55 | "https://github.com/aniketkarne/ClaudeNightsWatch/raw/main/docs/screenshots/logs.png" 56 | ], 57 | "documentation": "https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/PLUGIN_INSTALLATION.md", 58 | "featured": true, 59 | "verified": true, 60 | "downloads": 0, 61 | "rating": 5.0, 62 | "lastUpdated": "2025-10-11" 63 | } 64 | ] 65 | } 66 | 67 | -------------------------------------------------------------------------------- /commands/logs.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: View Claude Nights Watch execution logs with filtering and follow options 3 | usage: /nights-watch logs [OPTIONS] 4 | examples: 5 | - /nights-watch logs 6 | - /nights-watch logs -f 7 | - /nights-watch logs --follow 8 | --- 9 | 10 | # View Nights Watch Logs 11 | 12 | Display execution logs from the Claude Nights Watch daemon, including all prompts sent to Claude, responses received, and status messages. 13 | 14 | ## Usage 15 | 16 | ```bash 17 | /nights-watch logs [OPTIONS] 18 | ``` 19 | 20 | ## Options 21 | 22 | - `-f`, `--follow` - Follow log file in real-time (like `tail -f`) 23 | - No options - Show last 50 lines of logs 24 | 25 | ## What's Logged 26 | 27 | ### Daemon Activity 28 | - Start/stop events 29 | - Configuration loaded 30 | - Monitoring status updates 31 | - Error messages and warnings 32 | 33 | ### Task Execution 34 | - **Full Prompts**: Complete prompt sent to Claude (rules + task) 35 | - **Full Responses**: Everything Claude outputs during execution 36 | - **Timestamps**: Precise timing of all events 37 | - **Exit Codes**: Success/failure status 38 | 39 | ### Example Log Entry 40 | ``` 41 | [2025-10-11 14:30:00] === Claude Nights Watch Daemon Started === 42 | [2025-10-11 14:30:00] PID: 12345 43 | [2025-10-11 14:30:00] Task directory: /path/to/project 44 | [2025-10-11 14:30:05] Task file found at /path/to/project/task.md 45 | [2025-10-11 14:30:05] Rules file found at /path/to/project/rules.md 46 | [2025-10-11 15:25:00] Time remaining: 2 minutes 47 | [2025-10-11 15:27:00] Reset imminent (2 minutes), preparing to execute task... 48 | [2025-10-11 15:28:00] Starting task execution from task.md... 49 | 50 | === PROMPT SENT TO CLAUDE === 51 | IMPORTANT RULES TO FOLLOW: 52 | [... rules content ...] 53 | ---END OF RULES--- 54 | 55 | TASK TO EXECUTE: 56 | [... task content ...] 57 | ---END OF TASK--- 58 | === END OF PROMPT === 59 | 60 | === CLAUDE RESPONSE START === 61 | [... Claude's complete response ...] 62 | === CLAUDE RESPONSE END === 63 | 64 | [2025-10-11 15:42:00] Task execution completed successfully 65 | ``` 66 | 67 | ## Examples 68 | 69 | **View recent logs:** 70 | ```bash 71 | /nights-watch logs 72 | ``` 73 | 74 | **Follow logs in real-time:** 75 | ```bash 76 | /nights-watch logs -f 77 | ``` 78 | 79 | **Use interactive log viewer:** 80 | ```bash 81 | # For more advanced viewing with filtering 82 | ${CLAUDE_PLUGIN_ROOT}/view-logs.sh 83 | ``` 84 | 85 | ## Implementation 86 | 87 | ```bash 88 | ${CLAUDE_PLUGIN_ROOT}/claude-nights-watch-manager.sh logs "$@" 89 | ``` 90 | 91 | ## Interactive Log Viewer 92 | 93 | For advanced log viewing with filtering options, use the interactive viewer: 94 | 95 | ```bash 96 | ${CLAUDE_PLUGIN_ROOT}/view-logs.sh 97 | ``` 98 | 99 | Features: 100 | - Browse all log files 101 | - View full logs or last 50 lines 102 | - Filter to see only prompts 103 | - Filter to see only responses 104 | - Search for errors 105 | - Real-time following 106 | 107 | ## Log Location 108 | 109 | Logs are stored in: 110 | ``` 111 | ${CLAUDE_PLUGIN_ROOT}/logs/claude-nights-watch-daemon.log 112 | ``` 113 | 114 | ## See Also 115 | 116 | - `/nights-watch status` - Check daemon status 117 | - `/nights-watch task` - View current task 118 | 119 | -------------------------------------------------------------------------------- /examples/rules.example.md: -------------------------------------------------------------------------------- 1 | # Safety Rules for Claude Nights Watch 2 | 3 | ## CRITICAL RULES - NEVER VIOLATE THESE: 4 | 5 | ### 1. File System Safety 6 | - **NEVER** use `rm -rf` or force deletion commands 7 | - **NEVER** modify files outside the current project directory 8 | - **NEVER** access or modify system files (/etc, /usr, /System, etc.) 9 | - **ALWAYS** create backups before modifying important files 10 | 11 | ### 2. Git Safety 12 | - **NEVER** force push to main/master branches 13 | - **NEVER** rewrite published git history 14 | - **NEVER** delete remote branches without explicit permission 15 | - **ALWAYS** create feature branches for changes 16 | - **ALWAYS** use descriptive commit messages 17 | 18 | ### 3. Security Rules 19 | - **NEVER** commit passwords, API keys, or secrets 20 | - **NEVER** expose sensitive information in logs 21 | - **NEVER** disable security features 22 | - **NEVER** run commands with sudo/admin privileges 23 | - **ALWAYS** use environment variables for sensitive data 24 | 25 | ### 4. Network Safety 26 | - **NEVER** perform port scanning or network attacks 27 | - **NEVER** access unauthorized external services 28 | - **NEVER** download executables from untrusted sources 29 | - **ALWAYS** verify HTTPS certificates 30 | 31 | ### 5. Resource Management 32 | - **NEVER** create infinite loops or resource-intensive operations 33 | - **NEVER** consume excessive disk space (>1GB) 34 | - **NEVER** spawn more than 10 concurrent processes 35 | - **ALWAYS** clean up temporary files 36 | 37 | ## BEST PRACTICES: 38 | 39 | ### Development Workflow 40 | 1. Always run tests before committing 41 | 2. Use semantic versioning for releases 42 | 3. Follow existing code style and conventions 43 | 4. Document all significant changes 44 | 5. Review changes before finalizing 45 | 46 | ### Error Handling 47 | 1. Catch and log all errors appropriately 48 | 2. Never suppress error messages 49 | 3. Fail gracefully with helpful error messages 50 | 4. Create rollback plans for risky operations 51 | 52 | ### Communication 53 | 1. Log all significant actions 54 | 2. Create summary reports after task completion 55 | 3. Highlight any issues or concerns 56 | 4. Ask for clarification if tasks are ambiguous 57 | 58 | ## ALLOWED ACTIONS: 59 | 60 | ### Code Operations 61 | - Read and analyze source code 62 | - Create new files in the project directory 63 | - Modify existing project files 64 | - Run linters and formatters 65 | - Execute test suites 66 | - Build and compile code 67 | 68 | ### Git Operations 69 | - Create and switch branches 70 | - Stage and commit changes 71 | - View git history and diffs 72 | - Create pull requests 73 | - Tag releases 74 | 75 | ### Documentation 76 | - Generate documentation 77 | - Update README files 78 | - Create changelog entries 79 | - Add code comments 80 | - Write tutorials or guides 81 | 82 | ### Dependency Management 83 | - Install project dependencies 84 | - Update package versions (following semver) 85 | - Audit for security vulnerabilities 86 | - Generate dependency graphs 87 | 88 | ## FORBIDDEN ACTIONS: 89 | 90 | 1. Accessing user's personal files 91 | 2. Modifying IDE or editor configurations 92 | 3. Changing system settings 93 | 4. Installing global packages without permission 94 | 5. Accessing credentials or secrets 95 | 6. Making external API calls without explicit permission 96 | 7. Modifying production configurations 97 | 8. Deleting user data or backups 98 | 99 | ## EXECUTION LIMITS: 100 | 101 | - Maximum execution time: 1 hour per session 102 | - Maximum file size for creation: 10MB 103 | - Maximum number of files to modify: 100 104 | - Maximum commits per session: 20 105 | 106 | Remember: When in doubt, choose the safer option or log the concern for human review. -------------------------------------------------------------------------------- /commands/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Interactive setup wizard for Claude Nights Watch configuration 3 | usage: /nights-watch setup 4 | examples: 5 | - /nights-watch setup 6 | --- 7 | 8 | # Nights Watch Setup Wizard 9 | 10 | Launch the interactive setup wizard to configure Claude Nights Watch for the first time or modify existing configuration. 11 | 12 | ## Usage 13 | 14 | ```bash 15 | /nights-watch setup 16 | ``` 17 | 18 | ## Setup Process 19 | 20 | The wizard guides you through: 21 | 22 | ### 1. Prerequisites Check 23 | - ✓ Verify Claude CLI is installed 24 | - ✓ Check for ccusage (for accurate timing) 25 | - ✓ Confirm required permissions 26 | 27 | ### 2. Task Configuration 28 | - Create or edit `task.md` file 29 | - Enter task instructions 30 | - Review and modify as needed 31 | 32 | ### 3. Safety Rules Configuration 33 | - Create or edit `rules.md` file 34 | - Choose from default safety templates 35 | - Customize rules for your project 36 | 37 | ### 4. Daemon Configuration 38 | - Choose to start daemon immediately or later 39 | - Optionally schedule a start time 40 | - Configure monitoring preferences 41 | 42 | ### 5. Summary 43 | - Review all configuration 44 | - Display available commands 45 | - Optionally start daemon 46 | 47 | ## Interactive Prompts 48 | 49 | ``` 50 | ================================ 51 | Claude Nights Watch Setup 52 | ================================ 53 | 54 | Checking prerequisites... 55 | ✓ Claude CLI found 56 | ! ccusage not found (will use time-based checking) 57 | To install ccusage: npm install -g ccusage 58 | 59 | === Task Configuration === 60 | task.md already exists 61 | Do you want to view/edit it? (y/n) 62 | 63 | === Safety Rules Configuration === 64 | Do you want to create safety rules? (recommended) (y/n) 65 | 66 | === Daemon Configuration === 67 | Do you want to start the daemon after setup? (y/n) 68 | Do you want to schedule a start time? (y/n) 69 | Enter start time (HH:MM for today, or YYYY-MM-DD HH:MM): 70 | 71 | === Setup Complete === 72 | ✓ Task file: /path/to/task.md 73 | ✓ Rules file: /path/to/rules.md 74 | ✓ Manager: /path/to/claude-nights-watch-manager.sh 75 | 76 | Available commands: 77 | /nights-watch start - Start the daemon 78 | /nights-watch stop - Stop the daemon 79 | /nights-watch status - Check daemon status 80 | /nights-watch logs - View logs 81 | /nights-watch task - View current task 82 | 83 | Starting daemon... 84 | ``` 85 | 86 | ## What Gets Created 87 | 88 | ### Files Created 89 | - `task.md` - Your task instructions (if doesn't exist) 90 | - `rules.md` - Safety rules (optional, if requested) 91 | - `logs/` - Directory for daemon logs (on first start) 92 | 93 | ### Files NOT Modified 94 | - Existing `task.md` - Only edited if you choose to 95 | - Existing `rules.md` - Only edited if you choose to 96 | - Any project files 97 | 98 | ## First-Time Setup 99 | 100 | If you're setting up for the first time: 101 | 102 | 1. Run `/nights-watch setup` 103 | 2. Create a simple test task: 104 | ```markdown 105 | # Test Task 106 | 1. Create a file called test-output.txt 107 | 2. Write "Hello from Claude Nights Watch" to it 108 | 3. Report success 109 | ``` 110 | 3. Add basic safety rules (wizard provides template) 111 | 4. Start daemon and monitor with `/nights-watch status` 112 | 5. Check logs with `/nights-watch logs -f` 113 | 114 | ## Implementation 115 | 116 | ```bash 117 | ${CLAUDE_PLUGIN_ROOT}/setup-nights-watch.sh 118 | ``` 119 | 120 | ## Environment Variables 121 | 122 | - `CLAUDE_NIGHTS_WATCH_DIR` - Set custom task directory (default: current directory) 123 | - `EDITOR` - Preferred text editor (default: nano) 124 | 125 | ## See Also 126 | 127 | - `/nights-watch start` - Start daemon after setup 128 | - `/nights-watch task` - View configured tasks 129 | - Example files: `${CLAUDE_PLUGIN_ROOT}/examples/` 130 | 131 | -------------------------------------------------------------------------------- /test/test-immediate-execution.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test script to execute task immediately without waiting for renewal window 4 | 5 | LOG_FILE="${CLAUDE_NIGHTS_WATCH_DIR:-$(pwd)}/logs/claude-nights-watch-test.log" 6 | TASK_FILE="${TASK_FILE:-task.md}" 7 | RULES_FILE="${RULES_FILE:-rules.md}" 8 | TASK_DIR="${CLAUDE_NIGHTS_WATCH_DIR:-$(pwd)}" 9 | 10 | # Ensure logs directory exists 11 | mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null 12 | 13 | # Function to log messages 14 | log_message() { 15 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" 16 | } 17 | 18 | # Function to prepare task with rules 19 | prepare_task_prompt() { 20 | local prompt="" 21 | 22 | # Add rules if they exist 23 | if [ -f "$TASK_DIR/$RULES_FILE" ]; then 24 | prompt="IMPORTANT RULES TO FOLLOW:\n\n" 25 | prompt+=$(cat "$TASK_DIR/$RULES_FILE") 26 | prompt+="\n\n---END OF RULES---\n\n" 27 | log_message "Applied rules from $RULES_FILE" 28 | fi 29 | 30 | # Add task content 31 | if [ -f "$TASK_DIR/$TASK_FILE" ]; then 32 | prompt+="TASK TO EXECUTE:\n\n" 33 | prompt+=$(cat "$TASK_DIR/$TASK_FILE") 34 | prompt+="\n\n---END OF TASK---\n\n" 35 | prompt+="Please read the above task, create a todo list from it, and then execute it step by step." 36 | else 37 | log_message "ERROR: Task file not found at $TASK_DIR/$TASK_FILE" 38 | return 1 39 | fi 40 | 41 | echo -e "$prompt" 42 | } 43 | 44 | # Main execution 45 | main() { 46 | log_message "=== Claude Nights Watch Test Execution Started ===" 47 | log_message "Task directory: $TASK_DIR" 48 | log_message "Log file: $LOG_FILE" 49 | 50 | # Check if task file exists 51 | if [ ! -f "$TASK_DIR/$TASK_FILE" ]; then 52 | log_message "ERROR: Task file not found at $TASK_DIR/$TASK_FILE" 53 | exit 1 54 | fi 55 | 56 | # Check if rules file exists 57 | if [ -f "$TASK_DIR/$RULES_FILE" ]; then 58 | log_message "Rules file found at $TASK_DIR/$RULES_FILE" 59 | else 60 | log_message "No rules file found. Consider creating $RULES_FILE for safety constraints" 61 | fi 62 | 63 | # Prepare the full prompt with rules and task 64 | log_message "Preparing task prompt..." 65 | local full_prompt=$(prepare_task_prompt) 66 | if [ $? -ne 0 ]; then 67 | exit 1 68 | fi 69 | 70 | log_message "Task prompt prepared ($(echo -e "$full_prompt" | wc -l) lines)" 71 | 72 | # Show what will be sent to Claude 73 | echo "" 74 | echo "=== PROMPT PREVIEW ===" 75 | echo -e "$full_prompt" | head -20 76 | echo "..." 77 | echo "=== END PREVIEW ===" 78 | echo "" 79 | 80 | read -p "Execute this task with Claude? (y/n) " -n 1 -r 81 | echo 82 | if [[ ! $REPLY =~ ^[Yy]$ ]]; then 83 | log_message "Execution cancelled by user" 84 | exit 0 85 | fi 86 | 87 | log_message "Executing task with Claude (autonomous mode)..." 88 | 89 | # Log the full prompt being sent 90 | echo "" >> "$LOG_FILE" 91 | echo "=== PROMPT SENT TO CLAUDE ===" >> "$LOG_FILE" 92 | echo -e "$full_prompt" >> "$LOG_FILE" 93 | echo "=== END OF PROMPT ===" >> "$LOG_FILE" 94 | echo "" >> "$LOG_FILE" 95 | 96 | # Execute task with Claude and log everything 97 | log_message "=== CLAUDE RESPONSE START ===" 98 | echo -e "$full_prompt" | claude --dangerously-skip-permissions 2>&1 | tee -a "$LOG_FILE" 99 | local result=${PIPESTATUS[0]} 100 | log_message "=== CLAUDE RESPONSE END ===" 101 | 102 | if [ $result -eq 0 ]; then 103 | log_message "Task execution completed successfully" 104 | else 105 | log_message "ERROR: Task execution failed with code $result" 106 | fi 107 | 108 | log_message "=== Test Execution Completed ===" 109 | echo "" 110 | echo "Full log saved to: $LOG_FILE" 111 | } 112 | 113 | # Run main function 114 | main -------------------------------------------------------------------------------- /view-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Log Viewer 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 6 | TASK_DIR="${CLAUDE_NIGHTS_WATCH_DIR:-$(pwd)}" 7 | LOG_DIR="$TASK_DIR/logs" 8 | 9 | # Colors 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[1;33m' 12 | BLUE='\033[0;34m' 13 | RED='\033[0;31m' 14 | NC='\033[0m' 15 | 16 | show_menu() { 17 | echo -e "${BLUE}=== Claude Nights Watch Logs ===${NC}" 18 | echo "" 19 | echo "Available log files:" 20 | echo "" 21 | 22 | if [ -d "$LOG_DIR" ]; then 23 | local i=1 24 | for log in "$LOG_DIR"/*.log; do 25 | if [ -f "$log" ]; then 26 | local basename=$(basename "$log") 27 | local size=$(du -h "$log" | cut -f1) 28 | local lines=$(wc -l < "$log") 29 | echo " $i) $basename (${size}, ${lines} lines)" 30 | ((i++)) 31 | fi 32 | done 33 | else 34 | echo " No logs found in $LOG_DIR" 35 | exit 1 36 | fi 37 | 38 | echo "" 39 | echo "Options:" 40 | echo " f) Follow latest log (tail -f)" 41 | echo " a) Show all logs concatenated" 42 | echo " p) Show only prompts sent to Claude" 43 | echo " r) Show only Claude responses" 44 | echo " e) Show only errors" 45 | echo " q) Quit" 46 | echo "" 47 | } 48 | 49 | view_log() { 50 | local log_file="$1" 51 | local mode="$2" 52 | 53 | case "$mode" in 54 | "full") 55 | less "$log_file" 56 | ;; 57 | "tail") 58 | tail -50 "$log_file" 59 | ;; 60 | "follow") 61 | tail -f "$log_file" 62 | ;; 63 | "prompts") 64 | awk '/=== PROMPT SENT TO CLAUDE ===/,/=== END OF PROMPT ===/' "$log_file" | less 65 | ;; 66 | "responses") 67 | awk '/=== CLAUDE RESPONSE START ===/,/=== CLAUDE RESPONSE END ===/' "$log_file" | less 68 | ;; 69 | "errors") 70 | grep -E "(ERROR|FAILED|Failed)" "$log_file" | less 71 | ;; 72 | esac 73 | } 74 | 75 | # Main loop 76 | while true; do 77 | clear 78 | show_menu 79 | 80 | read -p "Select option: " choice 81 | 82 | case "$choice" in 83 | [0-9]*) 84 | # Numeric selection - view specific log 85 | log_files=("$LOG_DIR"/*.log) 86 | selected_log="${log_files[$((choice-1))]}" 87 | if [ -f "$selected_log" ]; then 88 | echo "" 89 | echo "1) View full log" 90 | echo "2) View last 50 lines" 91 | echo "3) View only prompts" 92 | echo "4) View only responses" 93 | echo "5) View only errors" 94 | read -p "Select view mode: " view_mode 95 | 96 | case "$view_mode" in 97 | 1) view_log "$selected_log" "full" ;; 98 | 2) view_log "$selected_log" "tail" ;; 99 | 3) view_log "$selected_log" "prompts" ;; 100 | 4) view_log "$selected_log" "responses" ;; 101 | 5) view_log "$selected_log" "errors" ;; 102 | esac 103 | fi 104 | ;; 105 | f|F) 106 | # Follow latest log 107 | latest_log=$(ls -t "$LOG_DIR"/*.log 2>/dev/null | head -1) 108 | if [ -f "$latest_log" ]; then 109 | echo "Following $(basename "$latest_log")... (Ctrl+C to stop)" 110 | view_log "$latest_log" "follow" 111 | fi 112 | ;; 113 | a|A) 114 | # Show all logs 115 | cat "$LOG_DIR"/*.log | less 116 | ;; 117 | p|P) 118 | # Show all prompts 119 | cat "$LOG_DIR"/*.log | awk '/=== PROMPT SENT TO CLAUDE ===/,/=== END OF PROMPT ===/' | less 120 | ;; 121 | r|R) 122 | # Show all responses 123 | cat "$LOG_DIR"/*.log | awk '/=== CLAUDE RESPONSE START ===/,/=== CLAUDE RESPONSE END ===/' | less 124 | ;; 125 | e|E) 126 | # Show all errors 127 | grep -h -E "(ERROR|FAILED|Failed)" "$LOG_DIR"/*.log | less 128 | ;; 129 | q|Q) 130 | echo "Goodbye!" 131 | exit 0 132 | ;; 133 | esac 134 | 135 | echo "" 136 | read -p "Press Enter to continue..." 137 | done -------------------------------------------------------------------------------- /marketplace-example/README.md: -------------------------------------------------------------------------------- 1 | # Claude Nights Watch Marketplace 2 | 3 | Official marketplace for Claude Nights Watch and related productivity plugins for Claude Code. 4 | 5 | ## 🚀 Installation 6 | 7 | Add this marketplace to your Claude Code: 8 | 9 | ```bash 10 | claude plugins marketplace add https://github.com/aniketkarne/claude-plugins-marketplace 11 | ``` 12 | 13 | ## 📦 Available Plugins 14 | 15 | ### Claude Nights Watch 16 | 17 | **Autonomous task execution daemon for Claude CLI** 18 | 19 | Transform Claude from an interactive assistant into an autonomous worker that executes tasks automatically every 5 hours. 20 | 21 | **Install:** 22 | ```bash 23 | claude plugins add claude-nights-watch 24 | ``` 25 | 26 | **Key Features:** 27 | - 🤖 Autonomous task execution 28 | - ⏰ Smart usage window monitoring 29 | - 🛡️ Comprehensive safety rules 30 | - 📊 Full audit logging 31 | - 🎮 7 slash commands 32 | - 🤖 Specialized Task Executor agent 33 | - 🔌 MCP server integration 34 | - 📅 Scheduled execution 35 | 36 | **Quick Start:** 37 | ```bash 38 | # Install plugin 39 | claude plugins add claude-nights-watch 40 | 41 | # Run setup 42 | /nights-watch setup 43 | 44 | # Start daemon 45 | /nights-watch start 46 | 47 | # Monitor 48 | /nights-watch logs -f 49 | ``` 50 | 51 | **Documentation:** 52 | - [Full Installation Guide](https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/PLUGIN_INSTALLATION.md) 53 | - [Plugin README](https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/PLUGIN_README.md) 54 | - [Main Documentation](https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/README.md) 55 | 56 | **Use Cases:** 57 | - Daily development tasks (linting, testing, reporting) 58 | - Continuous code reviews 59 | - Automated documentation generation 60 | - Data processing pipelines 61 | - ETL workflows 62 | - CI/CD automation 63 | - Scheduled maintenance tasks 64 | 65 | **Safety:** 66 | - Comprehensive rules system 67 | - Full execution logging 68 | - Test suite included 69 | - Example templates provided 70 | - Progressive complexity approach 71 | 72 | [Learn More →](https://github.com/aniketkarne/ClaudeNightsWatch) 73 | 74 | --- 75 | 76 | ## 🤝 Contributing 77 | 78 | Want to add your plugin to this marketplace? 79 | 80 | ### Submission Guidelines 81 | 82 | 1. **Plugin must:** 83 | - Be in a public Git repository 84 | - Include `.claude-plugin/plugin.json` manifest 85 | - Follow Claude Code plugin specifications 86 | - Be well-documented with examples 87 | - Include safety documentation 88 | 89 | 2. **Submit a Pull Request:** 90 | - Fork this repository 91 | - Add your plugin to `plugins.json` 92 | - Follow the schema format 93 | - Include comprehensive description 94 | - Link to documentation 95 | 96 | 3. **Review Process:** 97 | - Code review for safety 98 | - Functionality testing 99 | - Documentation review 100 | - Community feedback 101 | 102 | ### Plugin Entry Template 103 | 104 | ```json 105 | { 106 | "name": "your-plugin-name", 107 | "repository": "https://github.com/you/your-plugin", 108 | "description": "Brief one-line description", 109 | "longDescription": "Detailed description with features and benefits", 110 | "version": "1.0.0", 111 | "author": "Your Name", 112 | "homepage": "https://...", 113 | "license": "MIT", 114 | "keywords": ["tag1", "tag2"], 115 | "categories": ["category1"], 116 | "features": ["feature 1", "feature 2"], 117 | "documentation": "https://..." 118 | } 119 | ``` 120 | 121 | ## 📊 Plugin Statistics 122 | 123 | | Plugin | Version | Downloads | Rating | 124 | |--------|---------|-----------|--------| 125 | | claude-nights-watch | 1.0.0 | - | ⭐⭐⭐⭐⭐ | 126 | 127 | ## 🔄 Updates 128 | 129 | To update your installed plugins: 130 | 131 | ```bash 132 | claude plugins update 133 | ``` 134 | 135 | ## 📞 Support 136 | 137 | ### For Marketplace Issues 138 | Open an issue on this repository: [GitHub Issues](https://github.com/aniketkarne/claude-plugins-marketplace/issues) 139 | 140 | ### For Plugin-Specific Issues 141 | Contact the plugin author or open an issue on their repository. 142 | 143 | ### Community 144 | - Discord: [Claude Community](https://discord.gg/claude) 145 | - Discussions: [GitHub Discussions](https://github.com/aniketkarne/claude-plugins-marketplace/discussions) 146 | 147 | ## 📄 License 148 | 149 | This marketplace is MIT licensed. Individual plugins may have different licenses - check each plugin's repository. 150 | 151 | ## 🙏 Acknowledgments 152 | 153 | - Claude Code team for the excellent plugin system 154 | - Plugin authors for their contributions 155 | - Community for feedback and support 156 | 157 | --- 158 | 159 | **Enhance your Claude Code experience with powerful plugins! 🚀** 160 | 161 | -------------------------------------------------------------------------------- /claude-plugins-marketplace/README.md: -------------------------------------------------------------------------------- 1 | # Claude Nights Watch Marketplace 2 | 3 | Official marketplace for Claude Nights Watch and related productivity plugins for Claude Code. 4 | 5 | ## 🚀 Installation 6 | 7 | Add this marketplace to your Claude Code: 8 | 9 | ```bash 10 | claude plugins marketplace add https://github.com/aniketkarne/claude-plugins-marketplace 11 | ``` 12 | 13 | ## 📦 Available Plugins 14 | 15 | ### Claude Nights Watch 16 | 17 | **Autonomous task execution daemon for Claude CLI** 18 | 19 | Transform Claude from an interactive assistant into an autonomous worker that executes tasks automatically every 5 hours. 20 | 21 | **Install:** 22 | ```bash 23 | claude plugins add claude-nights-watch 24 | ``` 25 | 26 | **Key Features:** 27 | - 🤖 Autonomous task execution 28 | - ⏰ Smart usage window monitoring 29 | - 🛡️ Comprehensive safety rules 30 | - 📊 Full audit logging 31 | - 🎮 7 slash commands 32 | - 🤖 Specialized Task Executor agent 33 | - 🔌 MCP server integration 34 | - 📅 Scheduled execution 35 | 36 | **Quick Start:** 37 | ```bash 38 | # Install plugin 39 | claude plugins add claude-nights-watch 40 | 41 | # Run setup 42 | /nights-watch setup 43 | 44 | # Start daemon 45 | /nights-watch start 46 | 47 | # Monitor 48 | /nights-watch logs -f 49 | ``` 50 | 51 | **Documentation:** 52 | - [Full Installation Guide](https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/PLUGIN_INSTALLATION.md) 53 | - [Plugin README](https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/PLUGIN_README.md) 54 | - [Main Documentation](https://github.com/aniketkarne/ClaudeNightsWatch/blob/main/README.md) 55 | 56 | **Use Cases:** 57 | - Daily development tasks (linting, testing, reporting) 58 | - Continuous code reviews 59 | - Automated documentation generation 60 | - Data processing pipelines 61 | - ETL workflows 62 | - CI/CD automation 63 | - Scheduled maintenance tasks 64 | 65 | **Safety:** 66 | - Comprehensive rules system 67 | - Full execution logging 68 | - Test suite included 69 | - Example templates provided 70 | - Progressive complexity approach 71 | 72 | [Learn More →](https://github.com/aniketkarne/ClaudeNightsWatch) 73 | 74 | --- 75 | 76 | ## 🤝 Contributing 77 | 78 | Want to add your plugin to this marketplace? 79 | 80 | ### Submission Guidelines 81 | 82 | 1. **Plugin must:** 83 | - Be in a public Git repository 84 | - Include `.claude-plugin/plugin.json` manifest 85 | - Follow Claude Code plugin specifications 86 | - Be well-documented with examples 87 | - Include safety documentation 88 | 89 | 2. **Submit a Pull Request:** 90 | - Fork this repository 91 | - Add your plugin to `plugins.json` 92 | - Follow the schema format 93 | - Include comprehensive description 94 | - Link to documentation 95 | 96 | 3. **Review Process:** 97 | - Code review for safety 98 | - Functionality testing 99 | - Documentation review 100 | - Community feedback 101 | 102 | ### Plugin Entry Template 103 | 104 | ```json 105 | { 106 | "name": "your-plugin-name", 107 | "repository": "https://github.com/you/your-plugin", 108 | "description": "Brief one-line description", 109 | "longDescription": "Detailed description with features and benefits", 110 | "version": "1.0.0", 111 | "author": "Your Name", 112 | "homepage": "https://...", 113 | "license": "MIT", 114 | "keywords": ["tag1", "tag2"], 115 | "categories": ["category1"], 116 | "features": ["feature 1", "feature 2"], 117 | "documentation": "https://..." 118 | } 119 | ``` 120 | 121 | ## 📊 Plugin Statistics 122 | 123 | | Plugin | Version | Downloads | Rating | 124 | |--------|---------|-----------|--------| 125 | | claude-nights-watch | 1.0.0 | - | ⭐⭐⭐⭐⭐ | 126 | 127 | ## 🔄 Updates 128 | 129 | To update your installed plugins: 130 | 131 | ```bash 132 | claude plugins update 133 | ``` 134 | 135 | ## 📞 Support 136 | 137 | ### For Marketplace Issues 138 | Open an issue on this repository: [GitHub Issues](https://github.com/aniketkarne/claude-plugins-marketplace/issues) 139 | 140 | ### For Plugin-Specific Issues 141 | Contact the plugin author or open an issue on their repository. 142 | 143 | ### Community 144 | - Discord: [Claude Community](https://discord.gg/claude) 145 | - Discussions: [GitHub Discussions](https://github.com/aniketkarne/claude-plugins-marketplace/discussions) 146 | 147 | ## 📄 License 148 | 149 | This marketplace is MIT licensed. Individual plugins may have different licenses - check each plugin's repository. 150 | 151 | ## 🙏 Acknowledgments 152 | 153 | - Claude Code team for the excellent plugin system 154 | - Plugin authors for their contributions 155 | - Community for feedback and support 156 | 157 | --- 158 | 159 | **Enhance your Claude Code experience with powerful plugins! 🚀** 160 | 161 | -------------------------------------------------------------------------------- /setup-nights-watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Setup Script 4 | # Interactive setup for autonomous task execution 5 | 6 | # Colors 7 | RED='\033[0;31m' 8 | GREEN='\033[0;32m' 9 | YELLOW='\033[1;33m' 10 | BLUE='\033[0;34m' 11 | NC='\033[0m' 12 | 13 | MANAGER_SCRIPT="$(cd "$(dirname "$0")" && pwd)/claude-nights-watch-manager.sh" 14 | TASK_FILE="task.md" 15 | RULES_FILE="rules.md" 16 | 17 | print_header() { 18 | echo -e "${BLUE}================================${NC}" 19 | echo -e "${BLUE}Claude Nights Watch Setup${NC}" 20 | echo -e "${BLUE}================================${NC}" 21 | echo "" 22 | } 23 | 24 | print_success() { 25 | echo -e "${GREEN}✓${NC} $1" 26 | } 27 | 28 | print_error() { 29 | echo -e "${RED}✗${NC} $1" 30 | } 31 | 32 | print_warning() { 33 | echo -e "${YELLOW}!${NC} $1" 34 | } 35 | 36 | check_claude() { 37 | if command -v claude &> /dev/null; then 38 | print_success "Claude CLI found" 39 | return 0 40 | else 41 | print_error "Claude CLI not found" 42 | echo "Please install Claude CLI first: https://docs.anthropic.com/en/docs/claude-code/quickstart" 43 | return 1 44 | fi 45 | } 46 | 47 | check_ccusage() { 48 | if command -v ccusage &> /dev/null || command -v bunx &> /dev/null || command -v npx &> /dev/null; then 49 | print_success "ccusage available (for accurate timing)" 50 | return 0 51 | else 52 | print_warning "ccusage not found (will use time-based checking)" 53 | echo "To install ccusage: npm install -g ccusage" 54 | return 0 # Not a fatal error 55 | fi 56 | } 57 | 58 | create_task_file() { 59 | if [ -f "$TASK_FILE" ]; then 60 | print_warning "task.md already exists" 61 | read -p "Do you want to view/edit it? (y/n) " -n 1 -r 62 | echo 63 | if [[ $REPLY =~ ^[Yy]$ ]]; then 64 | ${EDITOR:-nano} "$TASK_FILE" 65 | fi 66 | else 67 | echo "" 68 | echo "Creating task.md file..." 69 | echo "Enter your task (press Ctrl+D when done):" 70 | echo "" 71 | cat > "$TASK_FILE" 72 | print_success "Created task.md" 73 | fi 74 | } 75 | 76 | create_rules_file() { 77 | if [ -f "$RULES_FILE" ]; then 78 | print_warning "rules.md already exists" 79 | read -p "Do you want to view/edit it? (y/n) " -n 1 -r 80 | echo 81 | if [[ $REPLY =~ ^[Yy]$ ]]; then 82 | ${EDITOR:-nano} "$RULES_FILE" 83 | fi 84 | else 85 | echo "" 86 | read -p "Do you want to create safety rules? (recommended) (y/n) " -n 1 -r 87 | echo 88 | if [[ $REPLY =~ ^[Yy]$ ]]; then 89 | cat > "$RULES_FILE" << 'EOF' 90 | # Safety Rules for Claude Nights Watch 91 | 92 | ## CRITICAL RULES - NEVER VIOLATE THESE: 93 | 94 | 1. **NO DESTRUCTIVE COMMANDS**: Never run commands that could delete or damage files: 95 | - No `rm -rf` commands 96 | - No deletion of system files 97 | - No modifications to system configurations 98 | 99 | 2. **NO SENSITIVE DATA**: Never: 100 | - Access or expose passwords, API keys, or secrets 101 | - Commit sensitive information to repositories 102 | - Log sensitive data 103 | 104 | 3. **NO NETWORK ATTACKS**: Never perform: 105 | - Port scanning 106 | - DDoS attempts 107 | - Unauthorized access attempts 108 | 109 | 4. **STAY IN PROJECT SCOPE**: 110 | - Only work within the designated project directory 111 | - Do not access or modify files outside the project 112 | 113 | 5. **GIT SAFETY**: 114 | - Never force push to main/master branches 115 | - Always create feature branches for changes 116 | - Never rewrite published history 117 | 118 | ## BEST PRACTICES: 119 | 120 | 1. **TEST BEFORE PRODUCTION**: Always test changes in a safe environment 121 | 2. **BACKUP IMPORTANT DATA**: Create backups before major changes 122 | 3. **DOCUMENT CHANGES**: Keep clear records of what was modified 123 | 4. **RESPECT RATE LIMITS**: Don't overwhelm external services 124 | 5. **ERROR HANDLING**: Implement proper error handling and logging 125 | 126 | ## ALLOWED ACTIONS: 127 | 128 | - Create and modify project files 129 | - Run tests and builds 130 | - Create git commits on feature branches 131 | - Install project dependencies 132 | - Generate documentation 133 | - Refactor code 134 | - Fix bugs 135 | - Add new features as specified 136 | EOF 137 | print_success "Created rules.md with default safety rules" 138 | echo "" 139 | read -p "Do you want to edit the rules? (y/n) " -n 1 -r 140 | echo 141 | if [[ $REPLY =~ ^[Yy]$ ]]; then 142 | ${EDITOR:-nano} "$RULES_FILE" 143 | fi 144 | fi 145 | fi 146 | } 147 | 148 | setup_daemon() { 149 | echo "" 150 | echo "=== Daemon Configuration ===" 151 | echo "" 152 | 153 | read -p "Do you want to start the daemon after setup? (y/n) " -n 1 -r 154 | echo 155 | START_NOW=$REPLY 156 | 157 | if [[ $START_NOW =~ ^[Yy]$ ]]; then 158 | read -p "Do you want to schedule a start time? (y/n) " -n 1 -r 159 | echo 160 | 161 | if [[ $REPLY =~ ^[Yy]$ ]]; then 162 | echo "Enter start time (HH:MM for today, or YYYY-MM-DD HH:MM):" 163 | read START_TIME 164 | START_ARGS="--at $START_TIME" 165 | else 166 | START_ARGS="" 167 | fi 168 | fi 169 | } 170 | 171 | main() { 172 | print_header 173 | 174 | # Check prerequisites 175 | echo "Checking prerequisites..." 176 | check_claude || exit 1 177 | check_ccusage 178 | echo "" 179 | 180 | # Create/edit task file 181 | echo "=== Task Configuration ===" 182 | create_task_file 183 | echo "" 184 | 185 | # Create/edit rules file 186 | echo "=== Safety Rules Configuration ===" 187 | create_rules_file 188 | echo "" 189 | 190 | # Setup daemon 191 | setup_daemon 192 | 193 | # Summary 194 | echo "" 195 | echo "=== Setup Complete ===" 196 | print_success "Task file: $(pwd)/$TASK_FILE" 197 | if [ -f "$RULES_FILE" ]; then 198 | print_success "Rules file: $(pwd)/$RULES_FILE" 199 | fi 200 | print_success "Manager: $MANAGER_SCRIPT" 201 | echo "" 202 | 203 | # Show available commands 204 | echo "Available commands:" 205 | echo " ./claude-nights-watch-manager.sh start - Start the daemon" 206 | echo " ./claude-nights-watch-manager.sh stop - Stop the daemon" 207 | echo " ./claude-nights-watch-manager.sh status - Check daemon status" 208 | echo " ./claude-nights-watch-manager.sh logs - View logs" 209 | echo " ./claude-nights-watch-manager.sh task - View current task" 210 | echo "" 211 | 212 | # Start daemon if requested 213 | if [[ $START_NOW =~ ^[Yy]$ ]]; then 214 | echo "Starting daemon..." 215 | "$MANAGER_SCRIPT" start $START_ARGS 216 | else 217 | echo "To start the daemon later, run:" 218 | echo " ./claude-nights-watch-manager.sh start" 219 | fi 220 | 221 | echo "" 222 | print_warning "Remember: The daemon will execute tasks autonomously!" 223 | print_warning "Always review your task.md and rules.md files carefully." 224 | } 225 | 226 | # Run main function 227 | main -------------------------------------------------------------------------------- /agents/task-executor.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Specialized agent for autonomous task execution planning, monitoring, and daemon management 3 | capabilities: 4 | - autonomous-task-planning 5 | - daemon-management 6 | - task-monitoring 7 | - safety-rule-creation 8 | - workflow-automation 9 | - schedule-optimization 10 | --- 11 | 12 | # Task Executor Agent 13 | 14 | An autonomous agent specialized in planning, setting up, and managing long-running task execution workflows with Claude Nights Watch daemon. 15 | 16 | ## Core Expertise 17 | 18 | I am an expert in: 19 | - **Autonomous Task Planning**: Breaking down complex workflows into autonomous executable tasks 20 | - **Daemon Management**: Starting, stopping, monitoring, and troubleshooting the Nights Watch daemon 21 | - **Safety Engineering**: Creating comprehensive safety rules to prevent destructive actions 22 | - **Schedule Optimization**: Determining optimal timing for automated task execution 23 | - **Log Analysis**: Interpreting execution logs and identifying issues 24 | - **Workflow Design**: Designing multi-step autonomous workflows 25 | 26 | ## When Claude Should Invoke Me 27 | 28 | Invoke this agent when users need help with: 29 | 30 | ### Task Planning 31 | - "I want to automate my daily development workflow" 32 | - "How do I set up autonomous code reviews?" 33 | - "Create a task that runs tests and generates reports" 34 | - "What tasks can I safely automate?" 35 | 36 | ### Daemon Operations 37 | - "Start monitoring my usage windows" 38 | - "Check if my daemon is running" 39 | - "Why isn't my task executing?" 40 | - "How do I schedule tasks for specific times?" 41 | 42 | ### Safety and Rules 43 | - "What safety rules should I use?" 44 | - "Is it safe to automate database operations?" 45 | - "Create rules that prevent file deletion" 46 | - "Review my rules for security issues" 47 | 48 | ### Troubleshooting 49 | - "My daemon stopped working" 50 | - "Why did my task fail?" 51 | - "Interpret these logs" 52 | - "Debug autonomous execution issues" 53 | 54 | ## Capabilities in Detail 55 | 56 | ### 1. Autonomous Task Design 57 | I help users create effective `task.md` files that: 58 | - Are clear and unambiguous 59 | - Include proper error handling 60 | - Have well-defined success criteria 61 | - Respect safety boundaries 62 | - Are appropriate for autonomous execution 63 | 64 | **Example Workflow:** 65 | ```markdown 66 | # Daily Code Quality Check 67 | 68 | ## Objectives: 69 | 1. Run linting on modified files 70 | 2. Fix auto-fixable issues 71 | 3. Generate quality report 72 | 73 | ## Safety Constraints: 74 | - Only modify files in src/ directory 75 | - Never delete files 76 | - Create backup branch first 77 | 78 | ## Success Criteria: 79 | - All linting errors below threshold 80 | - Report generated in reports/ 81 | - Changes committed to feature branch 82 | ``` 83 | 84 | ### 2. Safety Rule Engineering 85 | I create comprehensive `rules.md` files with: 86 | - **Critical Rules**: Absolute prohibitions (e.g., no rm -rf) 87 | - **Best Practices**: Recommended approaches 88 | - **Allowed Actions**: Explicitly permitted operations 89 | - **Forbidden Actions**: Explicitly prohibited operations 90 | - **Resource Limits**: Maximum file sizes, execution times, etc. 91 | 92 | **Safety Layers:** 93 | - File system protection 94 | - Git safety (no force push, no history rewriting) 95 | - Network security (no unauthorized access) 96 | - Resource management (prevent infinite loops) 97 | - Data protection (no sensitive information exposure) 98 | 99 | ### 3. Daemon Management 100 | I can: 101 | - Start/stop/restart the daemon 102 | - Configure scheduled start times 103 | - Monitor daemon health 104 | - Interpret status information 105 | - Troubleshoot common issues 106 | - Optimize monitoring intervals 107 | 108 | ### 4. Log Analysis and Debugging 109 | I analyze logs to: 110 | - Identify why tasks failed 111 | - Track execution history 112 | - Find performance bottlenecks 113 | - Detect safety violations 114 | - Suggest improvements 115 | 116 | ### 5. Schedule Optimization 117 | I help determine: 118 | - Best times to start daemon 119 | - Optimal execution windows 120 | - How to coordinate with team workflows 121 | - Timezone considerations 122 | 123 | ## Integration with Nights Watch 124 | 125 | ### Available Commands 126 | I can use these slash commands: 127 | - `/nights-watch start [--at TIME]` - Start daemon 128 | - `/nights-watch stop` - Stop daemon 129 | - `/nights-watch status` - Check status 130 | - `/nights-watch logs [-f]` - View logs 131 | - `/nights-watch task` - View current task 132 | - `/nights-watch setup` - Interactive setup 133 | - `/nights-watch restart` - Restart daemon 134 | 135 | ### File Operations 136 | I work with: 137 | - `task.md` - Task definitions 138 | - `rules.md` - Safety constraints 139 | - `logs/claude-nights-watch-daemon.log` - Execution logs 140 | 141 | ### Environment Understanding 142 | I understand: 143 | - Claude CLI usage windows (5-hour blocks) 144 | - `ccusage` tool for accurate timing 145 | - `--dangerously-skip-permissions` implications 146 | - Daemon process management 147 | - Background execution patterns 148 | 149 | ## Example Interactions 150 | 151 | ### Example 1: First-Time Setup 152 | **User**: "I want to automate my test suite to run before each usage window expires" 153 | 154 | **My Response**: 155 | 1. Create a `task.md` file with clear test execution steps 156 | 2. Create safety rules preventing production modifications 157 | 3. Run `/nights-watch setup` for interactive configuration 158 | 4. Start daemon with `/nights-watch start` 159 | 5. Monitor initial execution with `/nights-watch logs -f` 160 | 161 | ### Example 2: Troubleshooting 162 | **User**: "My daemon says it's running but tasks aren't executing" 163 | 164 | **My Analysis**: 165 | 1. Check status: `/nights-watch status` 166 | 2. Review logs: `/nights-watch logs` 167 | 3. Verify task.md exists and is valid 168 | 4. Check if scheduled start time has been reached 169 | 5. Ensure ccusage is working or last activity timestamp is set 170 | 6. Suggest solutions based on findings 171 | 172 | ### Example 3: Safety Review 173 | **User**: "Review my task for safety issues" 174 | 175 | **My Process**: 176 | 1. Read task.md content 177 | 2. Identify potentially dangerous operations 178 | 3. Check if rules.md has appropriate safeguards 179 | 4. Suggest additional safety constraints 180 | 5. Recommend testing approach before autonomous execution 181 | 182 | ## Best Practices I Promote 183 | 184 | ### 1. Start Small 185 | - Begin with simple, read-only tasks 186 | - Test manually before autonomous execution 187 | - Gradually increase complexity 188 | 189 | ### 2. Comprehensive Safety 190 | - Always create rules.md 191 | - Use explicit allow/deny lists 192 | - Set resource limits 193 | - Log everything 194 | 195 | ### 3. Monitor Regularly 196 | - Check logs frequently initially 197 | - Watch for unexpected behavior 198 | - Verify success criteria are met 199 | 200 | ### 4. Iterate and Improve 201 | - Refine tasks based on execution results 202 | - Update rules as needed 203 | - Optimize timing and scheduling 204 | 205 | ### 5. Test in Isolation 206 | - Use separate test environments 207 | - Don't start with production systems 208 | - Verify rollback procedures 209 | 210 | ## Risk Assessment 211 | 212 | I evaluate tasks based on: 213 | - **Impact**: What could go wrong? 214 | - **Reversibility**: Can actions be undone? 215 | - **Scope**: What systems are affected? 216 | - **Permissions**: What access is needed? 217 | - **Dependencies**: What external factors exist? 218 | 219 | ## Common Patterns I Recognize 220 | 221 | ### Safe Patterns 222 | - Read-only operations (analysis, reporting) 223 | - Idempotent operations (can run multiple times safely) 224 | - Well-bounded operations (limited scope) 225 | - Logged operations (full audit trail) 226 | 227 | ### Risky Patterns 228 | - Deletion operations (rm, DROP, DELETE) 229 | - Production modifications 230 | - Network operations to external systems 231 | - Unbounded loops or recursion 232 | - Privilege escalation 233 | 234 | ## How I Help Users Succeed 235 | 236 | 1. **Planning Phase**: Design effective autonomous workflows 237 | 2. **Safety Phase**: Create robust safety constraints 238 | 3. **Setup Phase**: Configure daemon correctly 239 | 4. **Launch Phase**: Start monitoring with appropriate timing 240 | 5. **Monitor Phase**: Track execution and identify issues 241 | 6. **Optimize Phase**: Improve based on results 242 | 243 | ## Technical Knowledge 244 | 245 | I understand: 246 | - Shell scripting and daemon processes 247 | - Git workflows and safety 248 | - CI/CD patterns 249 | - Test automation 250 | - Log analysis 251 | - Process monitoring 252 | - Signal handling (SIGTERM, SIGKILL) 253 | - File system operations 254 | - Environment variables 255 | - Cron-like scheduling 256 | 257 | ## Limitations and Boundaries 258 | 259 | I do NOT: 260 | - Execute tasks directly (I help set up the daemon to do so) 261 | - Override safety rules without user consent 262 | - Recommend risky operations without clear warnings 263 | - Guarantee perfect execution (testing is essential) 264 | - Replace human judgment for critical operations 265 | 266 | ## Summary 267 | 268 | I'm your expert companion for autonomous task execution with Claude Nights Watch. I help you: 269 | - Design safe, effective autonomous workflows 270 | - Configure and manage the daemon 271 | - Troubleshoot issues 272 | - Optimize execution timing 273 | - Maintain safety and reliability 274 | 275 | **Invoke me whenever you need guidance on autonomous task execution, daemon management, or workflow automation.** 276 | 277 | -------------------------------------------------------------------------------- /claude-nights-watch-manager.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Manager - Start, stop, and manage the task execution daemon 4 | 5 | DAEMON_SCRIPT="$(cd "$(dirname "$0")" && pwd)/claude-nights-watch-daemon.sh" 6 | TASK_DIR="${CLAUDE_NIGHTS_WATCH_DIR:-$(pwd)}" 7 | PID_FILE="$TASK_DIR/logs/claude-nights-watch-daemon.pid" 8 | LOG_FILE="$TASK_DIR/logs/claude-nights-watch-daemon.log" 9 | START_TIME_FILE="$TASK_DIR/logs/claude-nights-watch-start-time" 10 | TASK_FILE="task.md" 11 | RULES_FILE="rules.md" 12 | 13 | # Colors for output 14 | RED='\033[0;31m' 15 | GREEN='\033[0;32m' 16 | YELLOW='\033[1;33m' 17 | BLUE='\033[0;34m' 18 | NC='\033[0m' # No Color 19 | 20 | print_status() { 21 | echo -e "${GREEN}[INFO]${NC} $1" 22 | } 23 | 24 | print_error() { 25 | echo -e "${RED}[ERROR]${NC} $1" 26 | } 27 | 28 | print_warning() { 29 | echo -e "${YELLOW}[WARNING]${NC} $1" 30 | } 31 | 32 | print_task() { 33 | echo -e "${BLUE}[TASK]${NC} $1" 34 | } 35 | 36 | start_daemon() { 37 | # Parse --at parameter if provided 38 | START_TIME="" 39 | if [ "$2" = "--at" ] && [ -n "$3" ]; then 40 | START_TIME="$3" 41 | 42 | # Validate and convert start time to epoch 43 | if [[ "$START_TIME" =~ ^[0-9]{2}:[0-9]{2}$ ]]; then 44 | # Format: "HH:MM" - assume today 45 | START_TIME="$(date '+%Y-%m-%d') $START_TIME:00" 46 | fi 47 | 48 | # Convert to epoch timestamp 49 | START_EPOCH=$(date -d "$START_TIME" +%s 2>/dev/null || date -j -f "%Y-%m-%d %H:%M:%S" "$START_TIME" +%s 2>/dev/null) 50 | 51 | if [ $? -ne 0 ]; then 52 | print_error "Invalid time format. Use 'HH:MM' or 'YYYY-MM-DD HH:MM'" 53 | return 1 54 | fi 55 | 56 | # Store start time 57 | echo "$START_EPOCH" > "$START_TIME_FILE" 58 | print_status "Daemon will start monitoring at: $(date -d "@$START_EPOCH" 2>/dev/null || date -r "$START_EPOCH")" 59 | else 60 | # Remove any existing start time (start immediately) 61 | rm -f "$START_TIME_FILE" 2>/dev/null 62 | fi 63 | 64 | # Check if task file exists 65 | if [ ! -f "$TASK_DIR/$TASK_FILE" ]; then 66 | print_warning "Task file not found at $TASK_DIR/$TASK_FILE" 67 | print_warning "Please create a task.md file with your tasks before starting the daemon" 68 | read -p "Do you want to continue anyway? (y/n) " -n 1 -r 69 | echo 70 | if [[ ! $REPLY =~ ^[Yy]$ ]]; then 71 | return 1 72 | fi 73 | fi 74 | 75 | if [ -f "$PID_FILE" ]; then 76 | PID=$(cat "$PID_FILE") 77 | if kill -0 "$PID" 2>/dev/null; then 78 | print_error "Daemon is already running with PID $PID" 79 | return 1 80 | fi 81 | fi 82 | 83 | print_status "Starting Claude Nights Watch daemon..." 84 | print_status "Task directory: $TASK_DIR" 85 | 86 | # Export task directory for daemon 87 | export CLAUDE_NIGHTS_WATCH_DIR="$TASK_DIR" 88 | nohup "$DAEMON_SCRIPT" > /dev/null 2>&1 & 89 | 90 | # Wait for daemon to start with retry logic 91 | for i in {1..5}; do 92 | sleep 1 93 | if [ -f "$PID_FILE" ]; then 94 | PID=$(cat "$PID_FILE") 95 | if kill -0 "$PID" 2>/dev/null; then 96 | print_status "Daemon started successfully with PID $PID" 97 | 98 | if [ -f "$START_TIME_FILE" ]; then 99 | START_EPOCH=$(cat "$START_TIME_FILE") 100 | print_status "Will begin task execution at: $(date -d "@$START_EPOCH" 2>/dev/null || date -r "$START_EPOCH")" 101 | fi 102 | print_status "Logs: $LOG_FILE" 103 | 104 | # Show task preview 105 | if [ -f "$TASK_DIR/$TASK_FILE" ]; then 106 | echo "" 107 | print_task "Task preview (first 5 lines):" 108 | head -5 "$TASK_DIR/$TASK_FILE" | sed 's/^/ /' 109 | echo " ..." 110 | fi 111 | 112 | return 0 113 | fi 114 | fi 115 | if [ $i -eq 5 ]; then 116 | print_error "Failed to start daemon" 117 | return 1 118 | fi 119 | done 120 | } 121 | 122 | stop_daemon() { 123 | if [ ! -f "$PID_FILE" ]; then 124 | print_warning "Daemon is not running (no PID file found)" 125 | return 1 126 | fi 127 | 128 | PID=$(cat "$PID_FILE") 129 | 130 | if ! kill -0 "$PID" 2>/dev/null; then 131 | print_warning "Daemon is not running (process $PID not found)" 132 | rm -f "$PID_FILE" 133 | return 1 134 | fi 135 | 136 | print_status "Stopping daemon with PID $PID..." 137 | kill "$PID" 138 | 139 | # Wait for graceful shutdown 140 | for i in {1..10}; do 141 | if ! kill -0 "$PID" 2>/dev/null; then 142 | print_status "Daemon stopped successfully" 143 | rm -f "$PID_FILE" 144 | return 0 145 | fi 146 | sleep 1 147 | done 148 | 149 | # Force kill if still running 150 | print_warning "Daemon did not stop gracefully, forcing..." 151 | kill -9 "$PID" 2>/dev/null 152 | rm -f "$PID_FILE" 153 | print_status "Daemon stopped" 154 | } 155 | 156 | status_daemon() { 157 | if [ ! -f "$PID_FILE" ]; then 158 | print_status "Daemon is not running" 159 | return 1 160 | fi 161 | 162 | PID=$(cat "$PID_FILE") 163 | 164 | if kill -0 "$PID" 2>/dev/null; then 165 | print_status "Daemon is running with PID $PID" 166 | 167 | # Check start time status 168 | if [ -f "$START_TIME_FILE" ]; then 169 | start_epoch=$(cat "$START_TIME_FILE") 170 | current_epoch=$(date +%s) 171 | 172 | if [ "$current_epoch" -ge "$start_epoch" ]; then 173 | print_status "Status: ✅ ACTIVE - Task execution monitoring enabled" 174 | else 175 | time_until_start=$((start_epoch - current_epoch)) 176 | hours=$((time_until_start / 3600)) 177 | minutes=$(((time_until_start % 3600) / 60)) 178 | print_status "Status: ⏰ WAITING - Will activate in ${hours}h ${minutes}m" 179 | print_status "Start time: $(date -d "@$start_epoch" 2>/dev/null || date -r "$start_epoch")" 180 | fi 181 | else 182 | print_status "Status: ✅ ACTIVE - Task execution monitoring enabled" 183 | fi 184 | 185 | # Show task status 186 | echo "" 187 | if [ -f "$TASK_DIR/$TASK_FILE" ]; then 188 | print_task "Task file: $TASK_DIR/$TASK_FILE ($(wc -l < "$TASK_DIR/$TASK_FILE") lines)" 189 | else 190 | print_warning "Task file not found at $TASK_DIR/$TASK_FILE" 191 | fi 192 | 193 | if [ -f "$TASK_DIR/$RULES_FILE" ]; then 194 | print_task "Rules file: $TASK_DIR/$RULES_FILE ($(wc -l < "$TASK_DIR/$RULES_FILE") lines)" 195 | else 196 | print_status "No rules file (consider creating $RULES_FILE for safety)" 197 | fi 198 | 199 | # Show recent activity 200 | if [ -f "$LOG_FILE" ]; then 201 | echo "" 202 | print_status "Recent activity:" 203 | tail -5 "$LOG_FILE" | sed 's/^/ /' 204 | fi 205 | 206 | # Show next execution estimate (only if active) 207 | if [ ! -f "$START_TIME_FILE" ] || [ "$current_epoch" -ge "$(cat "$START_TIME_FILE" 2>/dev/null || echo 0)" ]; then 208 | if [ -f "$HOME/.claude-last-activity" ]; then 209 | last_activity=$(cat "$HOME/.claude-last-activity") 210 | current_time=$(date +%s) 211 | time_diff=$((current_time - last_activity)) 212 | remaining=$((18000 - time_diff)) 213 | 214 | if [ $remaining -gt 0 ]; then 215 | hours=$((remaining / 3600)) 216 | minutes=$(((remaining % 3600) / 60)) 217 | echo "" 218 | print_status "Estimated time until next task execution: ${hours}h ${minutes}m" 219 | fi 220 | fi 221 | fi 222 | 223 | return 0 224 | else 225 | print_warning "Daemon is not running (process $PID not found)" 226 | rm -f "$PID_FILE" 227 | return 1 228 | fi 229 | } 230 | 231 | restart_daemon() { 232 | print_status "Restarting daemon..." 233 | stop_daemon 234 | sleep 2 235 | start_daemon "$@" 236 | } 237 | 238 | show_logs() { 239 | if [ ! -f "$LOG_FILE" ]; then 240 | print_error "No log file found" 241 | return 1 242 | fi 243 | 244 | if [ "$1" = "-f" ]; then 245 | tail -f "$LOG_FILE" 246 | else 247 | tail -50 "$LOG_FILE" 248 | fi 249 | } 250 | 251 | show_task() { 252 | if [ ! -f "$TASK_DIR/$TASK_FILE" ]; then 253 | print_error "No task file found at $TASK_DIR/$TASK_FILE" 254 | return 1 255 | fi 256 | 257 | echo "" 258 | print_task "Current task ($TASK_DIR/$TASK_FILE):" 259 | echo "============================================" 260 | cat "$TASK_DIR/$TASK_FILE" 261 | echo "============================================" 262 | 263 | if [ -f "$TASK_DIR/$RULES_FILE" ]; then 264 | echo "" 265 | print_task "Current rules ($TASK_DIR/$RULES_FILE):" 266 | echo "============================================" 267 | cat "$TASK_DIR/$RULES_FILE" 268 | echo "============================================" 269 | fi 270 | } 271 | 272 | # Main command handling 273 | case "$1" in 274 | start) 275 | start_daemon "$@" 276 | ;; 277 | stop) 278 | stop_daemon 279 | ;; 280 | restart) 281 | stop_daemon 282 | sleep 2 283 | start_daemon "$@" 284 | ;; 285 | status) 286 | status_daemon 287 | ;; 288 | logs) 289 | show_logs "$2" 290 | ;; 291 | task) 292 | show_task 293 | ;; 294 | *) 295 | echo "Claude Nights Watch - Autonomous Task Execution Daemon" 296 | echo "" 297 | echo "Usage: $0 {start|stop|restart|status|logs|task} [options]" 298 | echo "" 299 | echo "Commands:" 300 | echo " start - Start the daemon" 301 | echo " start --at TIME - Start daemon but begin monitoring at specified time" 302 | echo " Examples: --at '09:00' or --at '2025-01-28 14:30'" 303 | echo " stop - Stop the daemon" 304 | echo " restart - Restart the daemon" 305 | echo " status - Show daemon status" 306 | echo " logs - Show recent logs (use 'logs -f' to follow)" 307 | echo " task - Display current task and rules" 308 | echo "" 309 | echo "The daemon will:" 310 | echo " - Monitor your Claude usage blocks" 311 | echo " - Execute tasks from task.md when renewal is needed" 312 | echo " - Apply rules from rules.md for safe autonomous execution" 313 | echo " - Prevent gaps in your 5-hour usage windows" 314 | echo "" 315 | echo "Environment:" 316 | echo " CLAUDE_NIGHTS_WATCH_DIR - Set task directory (default: current dir)" 317 | ;; 318 | esac -------------------------------------------------------------------------------- /mcp-server/nights-watch-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch MCP Server 4 | # Provides programmatic tools for Claude to control and query the daemon 5 | 6 | PLUGIN_ROOT="${NIGHTS_WATCH_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" 7 | MANAGER_SCRIPT="$PLUGIN_ROOT/claude-nights-watch-manager.sh" 8 | DAEMON_SCRIPT="$PLUGIN_ROOT/claude-nights-watch-daemon.sh" 9 | PID_FILE="$PLUGIN_ROOT/logs/claude-nights-watch-daemon.pid" 10 | LOG_FILE="$PLUGIN_ROOT/logs/claude-nights-watch-daemon.log" 11 | 12 | # MCP Protocol Implementation 13 | # This is a simplified MCP server that responds to tool calls 14 | 15 | handle_initialize() { 16 | cat << 'EOF' 17 | { 18 | "jsonrpc": "2.0", 19 | "id": 1, 20 | "result": { 21 | "protocolVersion": "2024-11-05", 22 | "capabilities": { 23 | "tools": {} 24 | }, 25 | "serverInfo": { 26 | "name": "nights-watch", 27 | "version": "1.0.0" 28 | } 29 | } 30 | } 31 | EOF 32 | } 33 | 34 | handle_list_tools() { 35 | cat << 'EOF' 36 | { 37 | "jsonrpc": "2.0", 38 | "id": 2, 39 | "result": { 40 | "tools": [ 41 | { 42 | "name": "get_daemon_status", 43 | "description": "Get the current status of the Nights Watch daemon including PID, running state, and configuration", 44 | "inputSchema": { 45 | "type": "object", 46 | "properties": {}, 47 | "required": [] 48 | } 49 | }, 50 | { 51 | "name": "start_daemon", 52 | "description": "Start the Nights Watch daemon with optional scheduled start time", 53 | "inputSchema": { 54 | "type": "object", 55 | "properties": { 56 | "start_time": { 57 | "type": "string", 58 | "description": "Optional start time in format HH:MM or YYYY-MM-DD HH:MM" 59 | } 60 | } 61 | } 62 | }, 63 | { 64 | "name": "stop_daemon", 65 | "description": "Stop the running Nights Watch daemon", 66 | "inputSchema": { 67 | "type": "object", 68 | "properties": {}, 69 | "required": [] 70 | } 71 | }, 72 | { 73 | "name": "get_logs", 74 | "description": "Retrieve recent log entries from the daemon", 75 | "inputSchema": { 76 | "type": "object", 77 | "properties": { 78 | "lines": { 79 | "type": "number", 80 | "description": "Number of recent lines to retrieve (default: 50)" 81 | } 82 | } 83 | } 84 | }, 85 | { 86 | "name": "read_task", 87 | "description": "Read the current task.md file content", 88 | "inputSchema": { 89 | "type": "object", 90 | "properties": { 91 | "path": { 92 | "type": "string", 93 | "description": "Optional path to task.md (default: ./task.md)" 94 | } 95 | } 96 | } 97 | }, 98 | { 99 | "name": "read_rules", 100 | "description": "Read the current rules.md file content", 101 | "inputSchema": { 102 | "type": "object", 103 | "properties": { 104 | "path": { 105 | "type": "string", 106 | "description": "Optional path to rules.md (default: ./rules.md)" 107 | } 108 | } 109 | } 110 | }, 111 | { 112 | "name": "write_task", 113 | "description": "Write or update the task.md file", 114 | "inputSchema": { 115 | "type": "object", 116 | "properties": { 117 | "content": { 118 | "type": "string", 119 | "description": "The task content to write" 120 | }, 121 | "path": { 122 | "type": "string", 123 | "description": "Optional path to task.md (default: ./task.md)" 124 | } 125 | }, 126 | "required": ["content"] 127 | } 128 | }, 129 | { 130 | "name": "write_rules", 131 | "description": "Write or update the rules.md file", 132 | "inputSchema": { 133 | "type": "object", 134 | "properties": { 135 | "content": { 136 | "type": "string", 137 | "description": "The rules content to write" 138 | }, 139 | "path": { 140 | "type": "string", 141 | "description": "Optional path to rules.md (default: ./rules.md)" 142 | } 143 | }, 144 | "required": ["content"] 145 | } 146 | } 147 | ] 148 | } 149 | } 150 | EOF 151 | } 152 | 153 | get_daemon_status() { 154 | local status="stopped" 155 | local pid="" 156 | local details="" 157 | 158 | if [ -f "$PID_FILE" ]; then 159 | pid=$(cat "$PID_FILE") 160 | if kill -0 "$pid" 2>/dev/null; then 161 | status="running" 162 | details="Daemon is running with PID $pid" 163 | else 164 | details="PID file exists but process not running" 165 | fi 166 | else 167 | details="Daemon is not running (no PID file)" 168 | fi 169 | 170 | cat << EOF 171 | { 172 | "jsonrpc": "2.0", 173 | "id": 3, 174 | "result": { 175 | "content": [ 176 | { 177 | "type": "text", 178 | "text": $(jq -n --arg s "$status" --arg p "$pid" --arg d "$details" '{status: $s, pid: $p, details: $d}' | jq -c .) 179 | } 180 | ] 181 | } 182 | } 183 | EOF 184 | } 185 | 186 | start_daemon() { 187 | local start_time="$1" 188 | local args="" 189 | 190 | if [ -n "$start_time" ]; then 191 | args="--at \"$start_time\"" 192 | fi 193 | 194 | local output=$(eval "$MANAGER_SCRIPT start $args" 2>&1) 195 | local exit_code=$? 196 | 197 | cat << EOF 198 | { 199 | "jsonrpc": "2.0", 200 | "id": 4, 201 | "result": { 202 | "content": [ 203 | { 204 | "type": "text", 205 | "text": $(echo "$output" | jq -Rs .) 206 | } 207 | ], 208 | "isError": $([ $exit_code -ne 0 ] && echo "true" || echo "false") 209 | } 210 | } 211 | EOF 212 | } 213 | 214 | stop_daemon() { 215 | local output=$("$MANAGER_SCRIPT" stop 2>&1) 216 | local exit_code=$? 217 | 218 | cat << EOF 219 | { 220 | "jsonrpc": "2.0", 221 | "id": 5, 222 | "result": { 223 | "content": [ 224 | { 225 | "type": "text", 226 | "text": $(echo "$output" | jq -Rs .) 227 | } 228 | ], 229 | "isError": $([ $exit_code -ne 0 ] && echo "true" || echo "false") 230 | } 231 | } 232 | EOF 233 | } 234 | 235 | get_logs() { 236 | local lines="${1:-50}" 237 | 238 | if [ -f "$LOG_FILE" ]; then 239 | local logs=$(tail -n "$lines" "$LOG_FILE") 240 | cat << EOF 241 | { 242 | "jsonrpc": "2.0", 243 | "id": 6, 244 | "result": { 245 | "content": [ 246 | { 247 | "type": "text", 248 | "text": $(echo "$logs" | jq -Rs .) 249 | } 250 | ] 251 | } 252 | } 253 | EOF 254 | else 255 | cat << EOF 256 | { 257 | "jsonrpc": "2.0", 258 | "id": 6, 259 | "result": { 260 | "content": [ 261 | { 262 | "type": "text", 263 | "text": "No log file found" 264 | } 265 | ] 266 | } 267 | } 268 | EOF 269 | fi 270 | } 271 | 272 | read_task() { 273 | local path="${1:-./task.md}" 274 | 275 | if [ -f "$path" ]; then 276 | local content=$(cat "$path") 277 | cat << EOF 278 | { 279 | "jsonrpc": "2.0", 280 | "id": 7, 281 | "result": { 282 | "content": [ 283 | { 284 | "type": "text", 285 | "text": $(echo "$content" | jq -Rs .) 286 | } 287 | ] 288 | } 289 | } 290 | EOF 291 | else 292 | cat << EOF 293 | { 294 | "jsonrpc": "2.0", 295 | "id": 7, 296 | "result": { 297 | "content": [ 298 | { 299 | "type": "text", 300 | "text": "Task file not found at $path" 301 | } 302 | ], 303 | "isError": true 304 | } 305 | } 306 | EOF 307 | fi 308 | } 309 | 310 | read_rules() { 311 | local path="${1:-./rules.md}" 312 | 313 | if [ -f "$path" ]; then 314 | local content=$(cat "$path") 315 | cat << EOF 316 | { 317 | "jsonrpc": "2.0", 318 | "id": 8, 319 | "result": { 320 | "content": [ 321 | { 322 | "type": "text", 323 | "text": $(echo "$content" | jq -Rs .) 324 | } 325 | ] 326 | } 327 | } 328 | EOF 329 | else 330 | cat << EOF 331 | { 332 | "jsonrpc": "2.0", 333 | "id": 8, 334 | "result": { 335 | "content": [ 336 | { 337 | "type": "text", 338 | "text": "Rules file not found at $path" 339 | } 340 | ], 341 | "isError": true 342 | } 343 | } 344 | EOF 345 | fi 346 | } 347 | 348 | write_task() { 349 | local content="$1" 350 | local path="${2:-./task.md}" 351 | 352 | echo "$content" > "$path" 353 | 354 | cat << EOF 355 | { 356 | "jsonrpc": "2.0", 357 | "id": 9, 358 | "result": { 359 | "content": [ 360 | { 361 | "type": "text", 362 | "text": "Task file written to $path" 363 | } 364 | ] 365 | } 366 | } 367 | EOF 368 | } 369 | 370 | write_rules() { 371 | local content="$1" 372 | local path="${2:-./rules.md}" 373 | 374 | echo "$content" > "$path" 375 | 376 | cat << EOF 377 | { 378 | "jsonrpc": "2.0", 379 | "id": 10, 380 | "result": { 381 | "content": [ 382 | { 383 | "type": "text", 384 | "text": "Rules file written to $path" 385 | } 386 | ] 387 | } 388 | } 389 | EOF 390 | } 391 | 392 | # Main MCP server loop 393 | # Read JSON-RPC requests from stdin and respond on stdout 394 | while IFS= read -r line; do 395 | method=$(echo "$line" | jq -r '.method // empty') 396 | 397 | case "$method" in 398 | "initialize") 399 | handle_initialize 400 | ;; 401 | "tools/list") 402 | handle_list_tools 403 | ;; 404 | "tools/call") 405 | tool_name=$(echo "$line" | jq -r '.params.name') 406 | case "$tool_name" in 407 | "get_daemon_status") 408 | get_daemon_status 409 | ;; 410 | "start_daemon") 411 | start_time=$(echo "$line" | jq -r '.params.arguments.start_time // empty') 412 | start_daemon "$start_time" 413 | ;; 414 | "stop_daemon") 415 | stop_daemon 416 | ;; 417 | "get_logs") 418 | lines=$(echo "$line" | jq -r '.params.arguments.lines // 50') 419 | get_logs "$lines" 420 | ;; 421 | "read_task") 422 | path=$(echo "$line" | jq -r '.params.arguments.path // "./task.md"') 423 | read_task "$path" 424 | ;; 425 | "read_rules") 426 | path=$(echo "$line" | jq -r '.params.arguments.path // "./rules.md"') 427 | read_rules "$path" 428 | ;; 429 | "write_task") 430 | content=$(echo "$line" | jq -r '.params.arguments.content') 431 | path=$(echo "$line" | jq -r '.params.arguments.path // "./task.md"') 432 | write_task "$content" "$path" 433 | ;; 434 | "write_rules") 435 | content=$(echo "$line" | jq -r '.params.arguments.content') 436 | path=$(echo "$line" | jq -r '.params.arguments.path // "./rules.md"') 437 | write_rules "$content" "$path" 438 | ;; 439 | esac 440 | ;; 441 | esac 442 | done 443 | 444 | -------------------------------------------------------------------------------- /verify-plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Plugin Verification Script 4 | # Automated testing and validation 5 | 6 | set -e 7 | 8 | PLUGIN_ROOT="$(cd "$(dirname "$0")" && pwd)" 9 | PASS=0 10 | FAIL=0 11 | 12 | # Colors 13 | GREEN='\033[0;32m' 14 | RED='\033[0;31m' 15 | YELLOW='\033[1;33m' 16 | BLUE='\033[0;34m' 17 | NC='\033[0m' 18 | 19 | print_test() { 20 | echo -e "${BLUE}[TEST]${NC} $1" 21 | } 22 | 23 | print_pass() { 24 | echo -e "${GREEN}[PASS]${NC} $1" 25 | ((PASS++)) 26 | } 27 | 28 | print_fail() { 29 | echo -e "${RED}[FAIL]${NC} $1" 30 | ((FAIL++)) 31 | } 32 | 33 | print_warn() { 34 | echo -e "${YELLOW}[WARN]${NC} $1" 35 | } 36 | 37 | print_header() { 38 | echo "" 39 | echo -e "${BLUE}================================${NC}" 40 | echo -e "${BLUE}$1${NC}" 41 | echo -e "${BLUE}================================${NC}" 42 | echo "" 43 | } 44 | 45 | # Test 1: Plugin Structure 46 | test_structure() { 47 | print_header "Testing Plugin Structure" 48 | 49 | print_test "Checking required directories..." 50 | 51 | if [ -d "$PLUGIN_ROOT/.claude-plugin" ]; then 52 | print_pass ".claude-plugin/ directory exists" 53 | else 54 | print_fail ".claude-plugin/ directory missing" 55 | fi 56 | 57 | if [ -d "$PLUGIN_ROOT/commands" ]; then 58 | print_pass "commands/ directory exists" 59 | else 60 | print_fail "commands/ directory missing" 61 | fi 62 | 63 | if [ -d "$PLUGIN_ROOT/agents" ]; then 64 | print_pass "agents/ directory exists" 65 | else 66 | print_fail "agents/ directory missing" 67 | fi 68 | 69 | if [ -d "$PLUGIN_ROOT/hooks" ]; then 70 | print_pass "hooks/ directory exists" 71 | else 72 | print_fail "hooks/ directory missing" 73 | fi 74 | 75 | if [ -d "$PLUGIN_ROOT/mcp-server" ]; then 76 | print_pass "mcp-server/ directory exists" 77 | else 78 | print_fail "mcp-server/ directory missing" 79 | fi 80 | } 81 | 82 | # Test 2: Plugin Manifest 83 | test_manifest() { 84 | print_header "Testing Plugin Manifest" 85 | 86 | MANIFEST="$PLUGIN_ROOT/.claude-plugin/plugin.json" 87 | 88 | if [ -f "$MANIFEST" ]; then 89 | print_pass "plugin.json exists" 90 | 91 | print_test "Validating JSON syntax..." 92 | if jq empty "$MANIFEST" 2>/dev/null; then 93 | print_pass "plugin.json is valid JSON" 94 | else 95 | print_fail "plugin.json has invalid JSON syntax" 96 | fi 97 | 98 | print_test "Checking required fields..." 99 | 100 | NAME=$(jq -r '.name // empty' "$MANIFEST") 101 | if [ -n "$NAME" ]; then 102 | print_pass "name field present: $NAME" 103 | else 104 | print_fail "name field missing" 105 | fi 106 | 107 | VERSION=$(jq -r '.version // empty' "$MANIFEST") 108 | if [ -n "$VERSION" ]; then 109 | print_pass "version field present: $VERSION" 110 | else 111 | print_fail "version field missing" 112 | fi 113 | 114 | DESCRIPTION=$(jq -r '.description // empty' "$MANIFEST") 115 | if [ -n "$DESCRIPTION" ]; then 116 | print_pass "description field present" 117 | else 118 | print_fail "description field missing" 119 | fi 120 | else 121 | print_fail "plugin.json missing" 122 | fi 123 | } 124 | 125 | # Test 3: Command Files 126 | test_commands() { 127 | print_header "Testing Command Files" 128 | 129 | COMMANDS=( 130 | "start.md" 131 | "stop.md" 132 | "restart.md" 133 | "status.md" 134 | "logs.md" 135 | "task.md" 136 | "setup.md" 137 | ) 138 | 139 | for cmd in "${COMMANDS[@]}"; do 140 | if [ -f "$PLUGIN_ROOT/commands/$cmd" ]; then 141 | print_pass "Command file: $cmd" 142 | else 143 | print_fail "Command file missing: $cmd" 144 | fi 145 | done 146 | 147 | if [ -f "$PLUGIN_ROOT/commands/bin/nights-watch" ]; then 148 | print_pass "Command wrapper exists" 149 | 150 | if [ -x "$PLUGIN_ROOT/commands/bin/nights-watch" ]; then 151 | print_pass "Command wrapper is executable" 152 | else 153 | print_fail "Command wrapper not executable" 154 | fi 155 | else 156 | print_fail "Command wrapper missing" 157 | fi 158 | } 159 | 160 | # Test 4: Agent Files 161 | test_agents() { 162 | print_header "Testing Agent Files" 163 | 164 | if [ -f "$PLUGIN_ROOT/agents/task-executor.md" ]; then 165 | print_pass "Task Executor agent exists" 166 | 167 | if grep -q "^---$" "$PLUGIN_ROOT/agents/task-executor.md"; then 168 | print_pass "Agent has frontmatter" 169 | else 170 | print_fail "Agent missing frontmatter" 171 | fi 172 | else 173 | print_fail "Task Executor agent missing" 174 | fi 175 | } 176 | 177 | # Test 5: Hooks Configuration 178 | test_hooks() { 179 | print_header "Testing Hooks Configuration" 180 | 181 | if [ -f "$PLUGIN_ROOT/hooks/hooks.json" ]; then 182 | print_pass "hooks.json exists" 183 | 184 | if jq empty "$PLUGIN_ROOT/hooks/hooks.json" 2>/dev/null; then 185 | print_pass "hooks.json is valid JSON" 186 | else 187 | print_fail "hooks.json has invalid JSON" 188 | fi 189 | else 190 | print_fail "hooks.json missing" 191 | fi 192 | 193 | HOOK_SCRIPTS=( 194 | "check-daemon-status.sh" 195 | "session-end-prompt.sh" 196 | "log-file-changes.sh" 197 | ) 198 | 199 | for script in "${HOOK_SCRIPTS[@]}"; do 200 | SCRIPT_PATH="$PLUGIN_ROOT/hooks/scripts/$script" 201 | if [ -f "$SCRIPT_PATH" ]; then 202 | print_pass "Hook script exists: $script" 203 | 204 | if [ -x "$SCRIPT_PATH" ]; then 205 | print_pass "Hook script executable: $script" 206 | else 207 | print_fail "Hook script not executable: $script" 208 | fi 209 | else 210 | print_fail "Hook script missing: $script" 211 | fi 212 | done 213 | } 214 | 215 | # Test 6: MCP Server 216 | test_mcp() { 217 | print_header "Testing MCP Server" 218 | 219 | if [ -f "$PLUGIN_ROOT/.mcp.json" ]; then 220 | print_pass ".mcp.json exists" 221 | 222 | if jq empty "$PLUGIN_ROOT/.mcp.json" 2>/dev/null; then 223 | print_pass ".mcp.json is valid JSON" 224 | else 225 | print_fail ".mcp.json has invalid JSON" 226 | fi 227 | else 228 | print_fail ".mcp.json missing" 229 | fi 230 | 231 | if [ -f "$PLUGIN_ROOT/mcp-server/nights-watch-server.sh" ]; then 232 | print_pass "MCP server script exists" 233 | 234 | if [ -x "$PLUGIN_ROOT/mcp-server/nights-watch-server.sh" ]; then 235 | print_pass "MCP server script executable" 236 | else 237 | print_fail "MCP server script not executable" 238 | fi 239 | else 240 | print_fail "MCP server script missing" 241 | fi 242 | } 243 | 244 | # Test 7: Core Scripts 245 | test_core_scripts() { 246 | print_header "Testing Core Scripts" 247 | 248 | SCRIPTS=( 249 | "claude-nights-watch-daemon.sh" 250 | "claude-nights-watch-manager.sh" 251 | "setup-nights-watch.sh" 252 | "view-logs.sh" 253 | ) 254 | 255 | for script in "${SCRIPTS[@]}"; do 256 | if [ -f "$PLUGIN_ROOT/$script" ]; then 257 | print_pass "Core script exists: $script" 258 | 259 | if [ -x "$PLUGIN_ROOT/$script" ]; then 260 | print_pass "Core script executable: $script" 261 | else 262 | print_warn "Core script not executable: $script (will attempt to fix)" 263 | chmod +x "$PLUGIN_ROOT/$script" 2>/dev/null && print_pass "Fixed: $script" || print_fail "Could not fix: $script" 264 | fi 265 | else 266 | print_fail "Core script missing: $script" 267 | fi 268 | done 269 | } 270 | 271 | # Test 8: Documentation 272 | test_documentation() { 273 | print_header "Testing Documentation" 274 | 275 | DOCS=( 276 | "README.md" 277 | "PLUGIN_README.md" 278 | "PLUGIN_INSTALLATION.md" 279 | "PLUGIN_TESTING.md" 280 | "MARKETPLACE_SETUP.md" 281 | "CHANGELOG.md" 282 | "LICENSE" 283 | ) 284 | 285 | for doc in "${DOCS[@]}"; do 286 | if [ -f "$PLUGIN_ROOT/$doc" ]; then 287 | print_pass "Documentation exists: $doc" 288 | else 289 | print_warn "Documentation missing: $doc" 290 | fi 291 | done 292 | } 293 | 294 | # Test 9: Examples 295 | test_examples() { 296 | print_header "Testing Example Files" 297 | 298 | if [ -d "$PLUGIN_ROOT/examples" ]; then 299 | print_pass "examples/ directory exists" 300 | 301 | if [ -f "$PLUGIN_ROOT/examples/task.example.md" ]; then 302 | print_pass "Task example exists" 303 | else 304 | print_warn "Task example missing" 305 | fi 306 | 307 | if [ -f "$PLUGIN_ROOT/examples/rules.example.md" ]; then 308 | print_pass "Rules example exists" 309 | else 310 | print_warn "Rules example missing" 311 | fi 312 | else 313 | print_warn "examples/ directory missing" 314 | fi 315 | } 316 | 317 | # Test 10: Marketplace Files 318 | test_marketplace() { 319 | print_header "Testing Marketplace Files" 320 | 321 | if [ -d "$PLUGIN_ROOT/marketplace-example" ]; then 322 | print_pass "marketplace-example/ directory exists" 323 | 324 | if [ -f "$PLUGIN_ROOT/marketplace-example/plugins.json" ]; then 325 | print_pass "Marketplace plugins.json exists" 326 | 327 | if jq empty "$PLUGIN_ROOT/marketplace-example/plugins.json" 2>/dev/null; then 328 | print_pass "Marketplace plugins.json is valid JSON" 329 | else 330 | print_fail "Marketplace plugins.json has invalid JSON" 331 | fi 332 | else 333 | print_fail "Marketplace plugins.json missing" 334 | fi 335 | 336 | if [ -f "$PLUGIN_ROOT/marketplace-example/README.md" ]; then 337 | print_pass "Marketplace README exists" 338 | else 339 | print_warn "Marketplace README missing" 340 | fi 341 | else 342 | print_warn "marketplace-example/ directory missing" 343 | fi 344 | } 345 | 346 | # Run all tests 347 | main() { 348 | echo "" 349 | echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}" 350 | echo -e "${BLUE}║ Claude Nights Watch Plugin Verification ║${NC}" 351 | echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}" 352 | echo "" 353 | echo "Plugin Root: $PLUGIN_ROOT" 354 | 355 | test_structure 356 | test_manifest 357 | test_commands 358 | test_agents 359 | test_hooks 360 | test_mcp 361 | test_core_scripts 362 | test_documentation 363 | test_examples 364 | test_marketplace 365 | 366 | # Summary 367 | print_header "Test Summary" 368 | 369 | TOTAL=$((PASS + FAIL)) 370 | 371 | echo -e "${GREEN}Passed:${NC} $PASS" 372 | echo -e "${RED}Failed:${NC} $FAIL" 373 | echo -e "${BLUE}Total:${NC} $TOTAL" 374 | echo "" 375 | 376 | if [ $FAIL -eq 0 ]; then 377 | CONFIDENCE=100 378 | echo -e "${GREEN}✅ All tests passed!${NC}" 379 | echo -e "${GREEN}🎉 Confidence Score: ${CONFIDENCE}%${NC}" 380 | echo -e "${GREEN}✅ Plugin is ready for distribution!${NC}" 381 | exit 0 382 | elif [ $FAIL -lt 5 ]; then 383 | CONFIDENCE=$((PASS * 100 / TOTAL)) 384 | echo -e "${YELLOW}⚠️ Some tests failed${NC}" 385 | echo -e "${YELLOW}📊 Confidence Score: ${CONFIDENCE}%${NC}" 386 | if [ $CONFIDENCE -ge 90 ]; then 387 | echo -e "${YELLOW}✅ Plugin is acceptable for release with minor fixes${NC}" 388 | exit 0 389 | else 390 | echo -e "${YELLOW}⚠️ Plugin needs fixes before release${NC}" 391 | exit 1 392 | fi 393 | else 394 | CONFIDENCE=$((PASS * 100 / TOTAL)) 395 | echo -e "${RED}❌ Multiple tests failed${NC}" 396 | echo -e "${RED}📊 Confidence Score: ${CONFIDENCE}%${NC}" 397 | echo -e "${RED}❌ Plugin is not ready for release${NC}" 398 | exit 1 399 | fi 400 | } 401 | 402 | # Check if jq is available 403 | if ! command -v jq &> /dev/null; then 404 | print_warn "jq not installed - JSON validation will be skipped" 405 | print_warn "Install jq for full validation: brew install jq (macOS) or apt-get install jq (Linux)" 406 | echo "" 407 | fi 408 | 409 | main 410 | 411 | -------------------------------------------------------------------------------- /claude-nights-watch-daemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Daemon - Autonomous Task Execution 4 | # Based on claude-auto-renew-daemon but executes tasks instead of simple renewals 5 | 6 | LOG_FILE="${CLAUDE_NIGHTS_WATCH_DIR:-$(dirname "$0")}/logs/claude-nights-watch-daemon.log" 7 | PID_FILE="${CLAUDE_NIGHTS_WATCH_DIR:-$(dirname "$0")}/logs/claude-nights-watch-daemon.pid" 8 | LAST_ACTIVITY_FILE="$HOME/.claude-last-activity" 9 | START_TIME_FILE="${CLAUDE_NIGHTS_WATCH_DIR:-$(dirname "$0")}/logs/claude-nights-watch-start-time" 10 | TASK_FILE="task.md" 11 | RULES_FILE="rules.md" 12 | TASK_DIR="${CLAUDE_NIGHTS_WATCH_DIR:-$(pwd)}" 13 | 14 | # Ensure logs directory exists 15 | mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null 16 | 17 | # Function to log messages 18 | log_message() { 19 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" 20 | } 21 | 22 | # Function to handle shutdown 23 | cleanup() { 24 | log_message "Daemon shutting down..." 25 | rm -f "$PID_FILE" 26 | exit 0 27 | } 28 | 29 | # Set up signal handlers 30 | trap cleanup SIGTERM SIGINT 31 | 32 | # Function to check if we're past the start time 33 | is_start_time_reached() { 34 | if [ ! -f "$START_TIME_FILE" ]; then 35 | # No start time set, always active 36 | return 0 37 | fi 38 | 39 | local start_epoch=$(cat "$START_TIME_FILE") 40 | local current_epoch=$(date +%s) 41 | 42 | if [ "$current_epoch" -ge "$start_epoch" ]; then 43 | return 0 # Start time reached 44 | else 45 | return 1 # Still waiting 46 | fi 47 | } 48 | 49 | # Function to get time until start 50 | get_time_until_start() { 51 | if [ ! -f "$START_TIME_FILE" ]; then 52 | echo "0" 53 | return 54 | fi 55 | 56 | local start_epoch=$(cat "$START_TIME_FILE") 57 | local current_epoch=$(date +%s) 58 | local diff=$((start_epoch - current_epoch)) 59 | 60 | if [ "$diff" -le 0 ]; then 61 | echo "0" 62 | else 63 | echo "$diff" 64 | fi 65 | } 66 | 67 | # Function to get ccusage command 68 | get_ccusage_cmd() { 69 | if command -v ccusage &> /dev/null; then 70 | echo "ccusage" 71 | elif command -v bunx &> /dev/null; then 72 | echo "bunx ccusage" 73 | elif command -v npx &> /dev/null; then 74 | echo "npx ccusage@latest" 75 | else 76 | return 1 77 | fi 78 | } 79 | 80 | # Function to get minutes until reset 81 | get_minutes_until_reset() { 82 | local ccusage_cmd=$(get_ccusage_cmd) 83 | if [ $? -ne 0 ]; then 84 | return 1 85 | fi 86 | 87 | # Use JSON output with --active flag to get only the active block 88 | local json_output=$($ccusage_cmd blocks --json --active 2>/dev/null) 89 | 90 | if [ -z "$json_output" ]; then 91 | # No active block - fallback to 0 like original behavior 92 | echo "0" 93 | return 0 94 | fi 95 | 96 | # Extract endTime from the active block 97 | local end_time=$(echo "$json_output" | grep '"endTime"' | head -1 | sed 's/.*"endTime": *"\([^"]*\)".*/\1/') 98 | 99 | if [ -z "$end_time" ]; then 100 | echo "0" 101 | return 0 102 | fi 103 | 104 | # Convert ISO timestamp to Unix epoch for calculation 105 | local end_epoch 106 | if command -v date &> /dev/null; then 107 | # Try different date command formats (macOS vs Linux) 108 | # Linux format (handles UTC properly) 109 | if end_epoch=$(date -d "$end_time" +%s 2>/dev/null); then 110 | : 111 | # macOS format - need to specify UTC timezone 112 | elif end_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S.000Z" "$end_time" +%s 2>/dev/null); then 113 | : 114 | # macOS format without milliseconds in UTC 115 | elif end_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$end_time" +%s 2>/dev/null); then 116 | : 117 | # Try stripping the .000Z and parsing in UTC 118 | elif stripped_time=$(echo "$end_time" | sed 's/\.000Z$/Z/') && end_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$stripped_time" +%s 2>/dev/null); then 119 | : 120 | else 121 | log_message "Could not parse ccusage endTime format: $end_time" >&2 122 | echo "0" 123 | return 0 124 | fi 125 | else 126 | echo "0" 127 | return 0 128 | fi 129 | 130 | local current_epoch=$(date +%s) 131 | local time_diff=$((end_epoch - current_epoch)) 132 | local remaining_minutes=$((time_diff / 60)) 133 | 134 | if [ "$remaining_minutes" -gt 0 ]; then 135 | echo $remaining_minutes 136 | else 137 | echo "0" 138 | return 0 139 | fi 140 | } 141 | 142 | # Function to prepare task with rules 143 | prepare_task_prompt() { 144 | local prompt="" 145 | 146 | # Add rules if they exist 147 | if [ -f "$TASK_DIR/$RULES_FILE" ]; then 148 | prompt="IMPORTANT RULES TO FOLLOW:\n\n" 149 | prompt+=$(cat "$TASK_DIR/$RULES_FILE") 150 | prompt+="\n\n---END OF RULES---\n\n" 151 | log_message "Applied rules from $RULES_FILE" 152 | fi 153 | 154 | # Add task content 155 | if [ -f "$TASK_DIR/$TASK_FILE" ]; then 156 | prompt+="TASK TO EXECUTE:\n\n" 157 | prompt+=$(cat "$TASK_DIR/$TASK_FILE") 158 | prompt+="\n\n---END OF TASK---\n\n" 159 | prompt+="Please read the above task, create a todo list from it, and then execute it step by step." 160 | else 161 | log_message "ERROR: Task file not found at $TASK_DIR/$TASK_FILE" 162 | return 1 163 | fi 164 | 165 | echo -e "$prompt" 166 | } 167 | 168 | # Function to execute task 169 | execute_task() { 170 | log_message "Starting task execution from $TASK_FILE..." 171 | 172 | if ! command -v claude &> /dev/null; then 173 | log_message "ERROR: claude command not found" 174 | return 1 175 | fi 176 | 177 | # Check if task file exists 178 | if [ ! -f "$TASK_DIR/$TASK_FILE" ]; then 179 | log_message "ERROR: Task file not found at $TASK_DIR/$TASK_FILE" 180 | return 1 181 | fi 182 | 183 | # Prepare the full prompt with rules and task 184 | local full_prompt=$(prepare_task_prompt) 185 | if [ $? -ne 0 ]; then 186 | return 1 187 | fi 188 | 189 | log_message "Executing task with Claude (autonomous mode)..." 190 | 191 | # Log the full prompt being sent 192 | echo "" >> "$LOG_FILE" 193 | echo "=== PROMPT SENT TO CLAUDE ===" >> "$LOG_FILE" 194 | echo -e "$full_prompt" >> "$LOG_FILE" 195 | echo "=== END OF PROMPT ===" >> "$LOG_FILE" 196 | echo "" >> "$LOG_FILE" 197 | 198 | # Execute task with Claude in autonomous mode 199 | # Log everything - both the execution and the response 200 | log_message "=== CLAUDE RESPONSE START ===" 201 | (echo -e "$full_prompt" | claude --dangerously-skip-permissions 2>&1) | tee -a "$LOG_FILE" & 202 | local pid=$! 203 | 204 | # Monitor execution (no timeout for complex tasks) 205 | log_message "Task execution started (PID: $pid)" 206 | 207 | # Wait for completion 208 | wait $pid 209 | local result=$? 210 | 211 | log_message "=== CLAUDE RESPONSE END ===" 212 | 213 | if [ $result -eq 0 ]; then 214 | log_message "Task execution completed successfully" 215 | date +%s > "$LAST_ACTIVITY_FILE" 216 | return 0 217 | else 218 | log_message "ERROR: Task execution failed with code $result" 219 | return 1 220 | fi 221 | } 222 | 223 | # Function to calculate next check time 224 | calculate_sleep_duration() { 225 | local minutes_remaining=$(get_minutes_until_reset) 226 | 227 | if [ -n "$minutes_remaining" ] && [ "$minutes_remaining" -gt 0 ]; then 228 | log_message "Time remaining: $minutes_remaining minutes" >&2 229 | 230 | if [ "$minutes_remaining" -le 5 ]; then 231 | # Check every 30 seconds when close to reset 232 | echo 30 233 | elif [ "$minutes_remaining" -le 30 ]; then 234 | # Check every 2 minutes when within 30 minutes 235 | echo 120 236 | else 237 | # Check every 10 minutes otherwise 238 | echo 600 239 | fi 240 | else 241 | # Fallback: check based on last activity 242 | if [ -f "$LAST_ACTIVITY_FILE" ]; then 243 | local last_activity=$(cat "$LAST_ACTIVITY_FILE") 244 | local current_time=$(date +%s) 245 | local time_diff=$((current_time - last_activity)) 246 | local remaining=$((18000 - time_diff)) # 5 hours = 18000 seconds 247 | 248 | if [ "$remaining" -le 300 ]; then # 5 minutes 249 | echo 30 250 | elif [ "$remaining" -le 1800 ]; then # 30 minutes 251 | echo 120 252 | else 253 | echo 600 254 | fi 255 | else 256 | # No info available, check every 5 minutes 257 | echo 300 258 | fi 259 | fi 260 | } 261 | 262 | # Main daemon loop 263 | main() { 264 | # Check if already running 265 | if [ -f "$PID_FILE" ]; then 266 | OLD_PID=$(cat "$PID_FILE") 267 | if kill -0 "$OLD_PID" 2>/dev/null; then 268 | echo "Daemon already running with PID $OLD_PID" 269 | exit 1 270 | else 271 | log_message "Removing stale PID file" 272 | rm -f "$PID_FILE" 273 | fi 274 | fi 275 | 276 | # Save PID 277 | echo $$ > "$PID_FILE" 278 | 279 | log_message "=== Claude Nights Watch Daemon Started ===" 280 | log_message "PID: $$" 281 | log_message "Logs: $LOG_FILE" 282 | log_message "Task directory: $TASK_DIR" 283 | 284 | 285 | # Check for task file 286 | if [ ! -f "$TASK_DIR/$TASK_FILE" ]; then 287 | log_message "WARNING: Task file not found at $TASK_DIR/$TASK_FILE" 288 | log_message "Please create a task.md file with your tasks" 289 | fi 290 | 291 | # Check for rules file 292 | if [ -f "$TASK_DIR/$RULES_FILE" ]; then 293 | log_message "Rules file found at $TASK_DIR/$RULES_FILE" 294 | else 295 | log_message "No rules file found. Consider creating $RULES_FILE for safety constraints" 296 | fi 297 | 298 | # Check for start time 299 | if [ -f "$START_TIME_FILE" ]; then 300 | start_epoch=$(cat "$START_TIME_FILE") 301 | log_message "Start time configured: $(date -d "@$start_epoch" 2>/dev/null || date -r "$start_epoch")" 302 | else 303 | log_message "No start time set - will begin monitoring immediately" 304 | fi 305 | 306 | # Check ccusage availability 307 | if ! get_ccusage_cmd &> /dev/null; then 308 | log_message "WARNING: ccusage not found. Using time-based checking." 309 | log_message "Install ccusage for more accurate timing: npm install -g ccusage" 310 | fi 311 | 312 | # Main loop 313 | while true; do 314 | # Check if we're past start time 315 | if ! is_start_time_reached; then 316 | time_until_start=$(get_time_until_start) 317 | hours=$((time_until_start / 3600)) 318 | minutes=$(((time_until_start % 3600) / 60)) 319 | seconds=$((time_until_start % 60)) 320 | 321 | if [ "$hours" -gt 0 ]; then 322 | log_message "Waiting for start time (${hours}h ${minutes}m remaining)..." 323 | sleep 300 # Check every 5 minutes when waiting 324 | elif [ "$minutes" -gt 2 ]; then 325 | log_message "Waiting for start time (${minutes}m ${seconds}s remaining)..." 326 | sleep 60 # Check every minute when close 327 | elif [ "$time_until_start" -gt 10 ]; then 328 | log_message "Waiting for start time (${minutes}m ${seconds}s remaining)..." 329 | sleep 10 # Check every 10 seconds when very close 330 | else 331 | log_message "Waiting for start time (${seconds}s remaining)..." 332 | sleep 2 # Check every 2 seconds when imminent 333 | fi 334 | continue 335 | fi 336 | 337 | # If we just reached start time, log it 338 | if [ -f "$START_TIME_FILE" ]; then 339 | # Check if this is the first time we're active 340 | if [ ! -f "${START_TIME_FILE}.activated" ]; then 341 | log_message "✅ Start time reached! Beginning task execution monitoring..." 342 | touch "${START_TIME_FILE}.activated" 343 | fi 344 | fi 345 | 346 | # Get minutes until reset 347 | minutes_remaining=$(get_minutes_until_reset) 348 | 349 | # Check if we should execute task 350 | should_execute=false 351 | 352 | if [ -n "$minutes_remaining" ] && [ "$minutes_remaining" -gt 0 ]; then 353 | if [ "$minutes_remaining" -le 2 ]; then 354 | should_execute=true 355 | log_message "Reset imminent ($minutes_remaining minutes), preparing to execute task..." 356 | fi 357 | else 358 | # Fallback check 359 | if [ -f "$LAST_ACTIVITY_FILE" ]; then 360 | last_activity=$(cat "$LAST_ACTIVITY_FILE") 361 | current_time=$(date +%s) 362 | time_diff=$((current_time - last_activity)) 363 | 364 | if [ $time_diff -ge 18000 ]; then 365 | should_execute=true 366 | log_message "5 hours elapsed since last activity, executing task..." 367 | fi 368 | else 369 | # No activity recorded, safe to start 370 | should_execute=true 371 | log_message "No previous activity recorded, starting initial task execution..." 372 | fi 373 | fi 374 | 375 | # Execute task if needed 376 | if [ "$should_execute" = true ]; then 377 | # Check if task file exists before execution 378 | if [ ! -f "$TASK_DIR/$TASK_FILE" ]; then 379 | log_message "ERROR: Cannot execute - task file not found at $TASK_DIR/$TASK_FILE" 380 | log_message "Waiting 5 minutes before next check..." 381 | sleep 300 382 | continue 383 | fi 384 | 385 | # Wait a bit to ensure we're in the renewal window 386 | sleep 60 387 | 388 | # Try to execute task 389 | if execute_task; then 390 | log_message "Task execution completed!" 391 | # Sleep for 5 minutes after successful execution 392 | sleep 300 393 | else 394 | log_message "Task execution failed, will retry in 1 minute" 395 | sleep 60 396 | fi 397 | fi 398 | 399 | # Calculate how long to sleep 400 | sleep_duration=$(calculate_sleep_duration) 401 | log_message "Next check in $((sleep_duration / 60)) minutes" 402 | 403 | # Sleep until next check 404 | sleep "$sleep_duration" 405 | done 406 | } 407 | 408 | # Start the daemon 409 | main -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Claude Nights Watch 🌙 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | [![Shell Script](https://img.shields.io/badge/Language-Shell-green.svg)](https://www.gnu.org/software/bash/) 5 | [![Claude Code Plugin](https://img.shields.io/badge/Plugin-Ready-blue.svg)](https://docs.anthropic.com/claude-code) 6 | [![GitHub stars](https://img.shields.io/github/stars/aniketkarne/ClaudeNightsWatch.svg?style=social&label=Star)](https://github.com/aniketkarne/ClaudeNightsWatch) 7 | 8 | **🚀 NEW: Now available as a Claude Code Plugin!** 🎉 9 | 10 | Autonomous task execution system for Claude CLI that monitors your usage windows and executes predefined tasks automatically. Built on top of the claude-auto-renew concept but instead of simple renewals, it executes complex tasks from a task.md file. 11 | 12 | **⚠️ Warning**: This tool uses `--dangerously-skip-permissions` for autonomous execution. Use with caution! 13 | 14 | ## 🎯 Two Ways to Use Claude Nights Watch 15 | 16 | Choose your preferred installation method: 17 | 18 | ### 🔥 **Recommended: Claude Code Plugin** (NEW!) 19 | - **Seamless Integration**: Works directly within Claude Code 20 | - **7 Slash Commands**: `/nights-watch start/stop/status/logs/task/setup/restart` 21 | - **AI Agent Integration**: Built-in Task Executor agent for autonomous guidance 22 | - **Smart Hooks**: Automatic session integration and activity logging 23 | - **MCP Server**: Programmatic control tools for Claude 24 | - **Enhanced UX**: Better error handling and user experience 25 | 26 | ### ⚡ **Original Daemon Method** (Legacy) 27 | - **Standalone Operation**: Works independently of Claude Code 28 | - **Script-based Control**: Direct shell script execution 29 | - **Proven Reliability**: Battle-tested daemon implementation 30 | - **Full Feature Set**: All core functionality available 31 | 32 | ## 🎯 Overview 33 | 34 | Claude Nights Watch extends the auto-renewal concept to create a fully autonomous task execution system. When your Claude usage window is about to expire, instead of just saying "hi", it reads your `task.md` file and executes the defined tasks autonomously. 35 | 36 | ### Key Features 37 | 38 | - 🤖 **Autonomous Execution**: Runs tasks without manual intervention 39 | - 📋 **Task-Based Workflow**: Define tasks in a simple markdown file 40 | - 🛡️ **Safety Rules**: Configure safety constraints in `rules.md` 41 | - ⏰ **Smart Timing**: Uses ccusage for accurate timing or falls back to time-based checking 42 | - 📅 **Scheduled Start**: Can be configured to start at a specific time 43 | - 📊 **Comprehensive Logging**: Track all activities and executions 44 | - 🔄 **Based on Proven Code**: Built on the reliable claude-auto-renew daemon 45 | 46 | ## 🚀 Quick Start 47 | 48 | ### Prerequisites 49 | 50 | 1. [Claude CLI](https://docs.anthropic.com/en/docs/claude-code/quickstart) installed and configured 51 | 2. (Optional) [ccusage](https://www.npmjs.com/package/ccusage) for accurate timing: 52 | ```bash 53 | npm install -g ccusage 54 | ``` 55 | 56 | --- 57 | 58 | ## 🔥 Plugin Installation (Recommended) 59 | 60 | ### Option 1: Install as Claude Code Plugin 61 | 62 | ```bash 63 | # Method 1: From marketplace (when available) 64 | claude plugins marketplace add https://github.com/aniketkarne/claude-plugins-marketplace 65 | claude plugins add claude-nights-watch 66 | 67 | # Method 2: Direct from GitHub 68 | claude plugins add https://github.com/aniketkarne/ClaudeNightsWatch 69 | 70 | # Method 3: From local directory (for development) 71 | claude plugins add /path/to/ClaudeNightsWatch 72 | ``` 73 | 74 | ### Plugin Usage 75 | 76 | ```bash 77 | # Interactive setup 78 | /nights-watch setup 79 | 80 | # Start daemon 81 | /nights-watch start 82 | 83 | # Check status 84 | /nights-watch status 85 | 86 | # View logs in real-time 87 | /nights-watch logs -f 88 | 89 | # Stop daemon 90 | /nights-watch stop 91 | ``` 92 | 93 | **Plugin Features:** 94 | - 🎯 **7 Slash Commands**: `/nights-watch start/stop/status/logs/task/setup/restart` 95 | - 🧠 **AI Agent**: Built-in Task Executor for autonomous guidance 96 | - 🔗 **MCP Server**: 8 programmatic tools for Claude control 97 | - 🎣 **Smart Hooks**: Automatic session integration 98 | 99 | --- 100 | 101 | ## ⚡ Original Daemon Installation (Legacy) 102 | 103 | ### Option 2: Install as Standalone Daemon 104 | 105 | 1. Clone this repository: 106 | ```bash 107 | git clone https://github.com/aniketkarne/ClaudeNightsWatch.git 108 | cd ClaudeNightsWatch 109 | ``` 110 | 111 | 2. Make scripts executable: 112 | ```bash 113 | chmod +x *.sh 114 | ``` 115 | 116 | 3. Run the interactive setup: 117 | ```bash 118 | ./setup-nights-watch.sh 119 | ``` 120 | 121 | ### Daemon Usage 122 | 123 | 1. **Create your task file** (`task.md`): 124 | ```markdown 125 | # Daily Development Tasks 126 | 127 | 1. Run linting on all source files 128 | 2. Update dependencies to latest versions 129 | 3. Run the test suite 130 | 4. Generate coverage report 131 | 5. Create a summary of changes 132 | ``` 133 | 134 | 2. **Create safety rules** (`rules.md`): 135 | ```markdown 136 | # Safety Rules 137 | 138 | - Never delete files without backing up 139 | - Only work within the project directory 140 | - Always create feature branches for changes 141 | - Never commit sensitive information 142 | ``` 143 | 144 | 3. **Start the daemon**: 145 | ```bash 146 | ./claude-nights-watch-manager.sh start 147 | ``` 148 | 149 | **Legacy Commands:** 150 | ```bash 151 | ./claude-nights-watch-manager.sh start [--at TIME] # Start daemon 152 | ./claude-nights-watch-manager.sh stop # Stop daemon 153 | ./claude-nights-watch-manager.sh status # Check status 154 | ./claude-nights-watch-manager.sh logs [-f] # View logs 155 | ./claude-nights-watch-manager.sh task # View task/rules 156 | ./claude-nights-watch-manager.sh restart # Restart daemon 157 | ``` 158 | 159 | ## 📝 Configuration 160 | 161 | ### Task File (task.md) 162 | 163 | The task file contains the instructions that Claude will execute. It should be clear, specific, and well-structured. See `examples/task.example.md` for a comprehensive example. 164 | 165 | ### Rules File (rules.md) 166 | 167 | The rules file defines safety constraints and best practices. It's prepended to every task execution to ensure safe autonomous operation. See `examples/rules.example.md` for recommended rules. 168 | 169 | ### Environment Variables 170 | 171 | - `CLAUDE_NIGHTS_WATCH_DIR`: Set the directory containing task.md and rules.md (default: current directory) 172 | 173 | ## 🎮 Commands 174 | 175 | ### 🔥 Plugin Commands (NEW!) 176 | 177 | When using as a Claude Code plugin, use slash commands: 178 | 179 | ```bash 180 | # Start the daemon 181 | /nights-watch start 182 | 183 | # Start with scheduled time 184 | /nights-watch start --at "09:00" 185 | /nights-watch start --at "2025-01-28 14:30" 186 | 187 | # Stop the daemon 188 | /nights-watch stop 189 | 190 | # Check status 191 | /nights-watch status 192 | 193 | # View logs 194 | /nights-watch logs 195 | /nights-watch logs -f # Follow mode 196 | 197 | # View current task and rules 198 | /nights-watch task 199 | 200 | # Interactive setup 201 | /nights-watch setup 202 | 203 | # Restart daemon 204 | /nights-watch restart 205 | ``` 206 | 207 | ### ⚡ Original Daemon Commands (Legacy) 208 | 209 | For standalone daemon usage: 210 | 211 | ```bash 212 | # Start the daemon 213 | ./claude-nights-watch-manager.sh start 214 | 215 | # Start with scheduled time 216 | ./claude-nights-watch-manager.sh start --at "09:00" 217 | ./claude-nights-watch-manager.sh start --at "2025-01-28 14:30" 218 | 219 | # Stop the daemon 220 | ./claude-nights-watch-manager.sh stop 221 | 222 | # Check status 223 | ./claude-nights-watch-manager.sh status 224 | 225 | # View logs 226 | ./claude-nights-watch-manager.sh logs 227 | ./claude-nights-watch-manager.sh logs -f # Follow mode 228 | 229 | # Use interactive log viewer 230 | ./view-logs.sh 231 | 232 | # View current task and rules 233 | ./claude-nights-watch-manager.sh task 234 | 235 | # Restart daemon 236 | ./claude-nights-watch-manager.sh restart 237 | ``` 238 | 239 | --- 240 | 241 | ## 🧠 Plugin Features (NEW!) 242 | 243 | ### Task Executor Agent 244 | Built-in AI agent that helps with: 245 | - **Autonomous Task Planning**: Designing effective autonomous workflows 246 | - **Safety Rule Creation**: Building comprehensive safety constraints 247 | - **Daemon Management**: Starting, stopping, and troubleshooting 248 | - **Log Analysis**: Interpreting execution results and identifying issues 249 | 250 | **Usage:** 251 | ``` 252 | User: "Help me create an autonomous code review workflow" 253 | Agent: Provides expert guidance and can execute /nights-watch commands 254 | 255 | User: "My daemon isn't working, help debug" 256 | Agent: Analyzes logs and suggests solutions 257 | ``` 258 | 259 | ### MCP Server Integration 260 | Provides 8 programmatic tools for Claude: 261 | - `get_daemon_status` - Query daemon state 262 | - `start_daemon` - Start with optional schedule 263 | - `stop_daemon` - Stop the daemon 264 | - `get_logs` - Retrieve log entries 265 | - `read_task` - Read task.md content 266 | - `read_rules` - Read rules.md content 267 | - `write_task` - Update task.md 268 | - `write_rules` - Update rules.md 269 | 270 | ### Smart Hooks 271 | Automatic integration with Claude Code sessions: 272 | - **SessionStart**: Shows daemon status when starting Claude Code 273 | - **SessionEnd**: Prompts to start daemon if tasks are configured 274 | - **PostToolUse**: Logs file modifications for audit trail 275 | 276 | These hooks run silently in the background, enhancing your workflow. 277 | 278 | ## 🔧 How It Works 279 | 280 | 1. **Monitoring**: The daemon continuously monitors your Claude usage windows 281 | 2. **Timing**: When approaching the 5-hour limit (within 2 minutes), it prepares for execution 282 | 3. **Task Preparation**: Reads both `rules.md` and `task.md`, combining them into a single prompt 283 | 4. **Autonomous Execution**: Executes the task using `claude --dangerously-skip-permissions` 284 | 5. **Logging**: All activities are logged to `logs/claude-nights-watch-daemon.log` 285 | 286 | ### Timing Logic 287 | 288 | - **With ccusage**: Gets accurate remaining time from the API 289 | - **Without ccusage**: Falls back to timestamp-based checking 290 | - **Adaptive intervals**: 291 | - \>30 minutes remaining: Check every 10 minutes 292 | - 5-30 minutes remaining: Check every 2 minutes 293 | - <5 minutes remaining: Check every 30 seconds 294 | 295 | ## ⚠️ Safety Considerations 296 | 297 | **IMPORTANT**: This tool runs Claude with the `--dangerously-skip-permissions` flag, meaning it will execute tasks without asking for confirmation. 298 | 299 | ### Best Practices: 300 | 301 | 1. **Always test tasks manually first** before setting up autonomous execution 302 | 2. **Use comprehensive rules.md** to prevent destructive actions 303 | 3. **Start with simple, safe tasks** and gradually increase complexity 304 | 4. **Monitor logs regularly** to ensure proper execution 305 | 5. **Keep backups** of important data 306 | 6. **Run in isolated environments** when possible 307 | 308 | ### Recommended Restrictions: 309 | 310 | - Limit file system access to project directories 311 | - Prohibit deletion commands 312 | - Prevent system modifications 313 | - Restrict network access 314 | - Set resource limits 315 | 316 | ## 📁 File Structure 317 | 318 | ### 🔥 Plugin Structure (NEW!) 319 | 320 | When installed as a Claude Code plugin, the following structure is used: 321 | 322 | ``` 323 | claude-nights-watch/ 324 | ├── .claude-plugin/ # Plugin metadata (NEW!) 325 | │ └── plugin.json # Plugin manifest 326 | ├── commands/ # Slash command definitions (NEW!) 327 | │ ├── bin/nights-watch # Command wrapper 328 | │ ├── start.md # Start command documentation 329 | │ ├── stop.md # Stop command documentation 330 | │ ├── status.md # Status command documentation 331 | │ ├── logs.md # Logs command documentation 332 | │ ├── task.md # Task command documentation 333 | │ ├── setup.md # Setup command documentation 334 | │ └── restart.md # Restart command documentation 335 | ├── agents/ # AI agents (NEW!) 336 | │ └── task-executor.md # Autonomous task planning agent 337 | ├── hooks/ # Event handlers (NEW!) 338 | │ ├── hooks.json # Hook configuration 339 | │ └── scripts/ # Hook implementation scripts 340 | │ ├── check-daemon-status.sh # Session start hook 341 | │ ├── session-end-prompt.sh # Session end hook 342 | │ └── log-file-changes.sh # File modification hook 343 | ├── mcp-server/ # Model Context Protocol (NEW!) 344 | │ └── nights-watch-server.sh # MCP server implementation 345 | ├── .mcp.json # MCP server configuration (NEW!) 346 | └── [original files continue below...] 347 | ``` 348 | 349 | ### ⚡ Original Daemon Structure (Legacy) 350 | 351 | The original standalone daemon structure: 352 | 353 | ``` 354 | claude-nights-watch/ 355 | ├── claude-nights-watch-daemon.sh # Core daemon process 356 | ├── claude-nights-watch-manager.sh # Daemon management interface 357 | ├── setup-nights-watch.sh # Interactive setup script 358 | ├── view-logs.sh # Interactive log viewer 359 | ├── README.md # This file 360 | ├── LICENSE # MIT License 361 | ├── CONTRIBUTING.md # Contribution guidelines 362 | ├── CHANGELOG.md # Version history 363 | ├── SUMMARY.md # Project summary 364 | ├── .gitignore # Git ignore file 365 | ├── .github/ # GitHub templates 366 | │ ├── ISSUE_TEMPLATE/ 367 | │ │ ├── bug_report.md 368 | │ │ └── feature_request.md 369 | │ └── pull_request_template.md 370 | ├── logs/ # All logs stored here (created on first run) 371 | ├── examples/ # Example files 372 | │ ├── task.example.md # Example task file 373 | │ └── rules.example.md # Example rules file 374 | └── test/ # Test scripts and files 375 | ├── README.md # Testing documentation 376 | ├── test-immediate-execution.sh # Direct task execution test 377 | ├── test-simple.sh # Simple functionality test 378 | ├── test-task-simple.md # Simple test task 379 | ├── test-rules-simple.md # Simple test rules 380 | ├── test-plugin-comprehensive.sh # Plugin functionality tests (NEW!) 381 | └── test-functional-realworld.sh # Real-world testing suite (NEW!) 382 | ``` 383 | 384 | ## 📊 Logging 385 | 386 | All logs are stored in the `logs/` directory within the project. Each log contains: 387 | 388 | - **Timestamps**: Every action is timestamped 389 | - **Full Prompts**: Complete prompt sent to Claude (rules + task) 390 | - **Full Responses**: Everything Claude outputs 391 | - **Status Messages**: Success/failure indicators 392 | 393 | ### Viewing Logs 394 | 395 | Use the interactive log viewer: 396 | ```bash 397 | ./view-logs.sh 398 | ``` 399 | 400 | Features: 401 | - Browse all log files 402 | - View full logs or last 50 lines 403 | - Filter to see only prompts sent to Claude 404 | - Filter to see only Claude's responses 405 | - Search for errors 406 | - Follow logs in real-time 407 | 408 | ## 🧪 Testing 409 | 410 | ### Plugin Testing (NEW!) 411 | 412 | Comprehensive test suites for the Claude Code plugin: 413 | 414 | ```bash 415 | cd test 416 | 417 | # Run comprehensive plugin structure tests 418 | ./test-plugin-comprehensive.sh 419 | 420 | # Run functional and real-world testing 421 | ./test-functional-realworld.sh 422 | 423 | # Run immediate execution test (no waiting) 424 | ./test-immediate-execution.sh 425 | 426 | # Run simple functionality test 427 | ./test-simple.sh 428 | ``` 429 | 430 | ### Original Daemon Testing (Legacy) 431 | 432 | ```bash 433 | cd test 434 | ./test-simple.sh # Run a simple test 435 | ``` 436 | 437 | See `test/README.md` for detailed testing instructions. 438 | 439 | **New Testing Features:** 440 | - **Plugin Structure Validation**: Tests all plugin components 441 | - **Functional Testing**: Validates script execution and integration 442 | - **Real-World Simulation**: Tests complete user workflows 443 | - **Environment Testing**: Validates plugin environment variables 444 | - **Error Handling**: Tests graceful failure scenarios 445 | 446 | ## 🐛 Troubleshooting 447 | 448 | ### Plugin Issues (NEW!) 449 | 450 | **Plugin not loading:** 451 | ```bash 452 | # Check plugin installation 453 | claude plugins list | grep claude-nights-watch 454 | 455 | # Reinstall if needed 456 | claude plugins remove claude-nights-watch 457 | claude plugins add https://github.com/aniketkarne/ClaudeNightsWatch 458 | 459 | # Debug mode 460 | claude --debug 461 | ``` 462 | 463 | **Commands not working:** 464 | ```bash 465 | # Verify command scripts are executable 466 | ls -la ~/.claude/plugins/claude-nights-watch/commands/bin/ 467 | 468 | # Make executable if needed 469 | chmod +x ~/.claude/plugins/claude-nights-watch/commands/bin/nights-watch 470 | ``` 471 | 472 | **Agent not responding:** 473 | - Ensure plugin is properly installed 474 | - Check that agent file exists in `agents/task-executor.md` 475 | - Verify MCP server is running 476 | 477 | ### Original Daemon Issues (Legacy) 478 | 479 | **Daemon won't start:** 480 | - Check if Claude CLI is installed: `which claude` 481 | - Verify task.md exists in the working directory 482 | - Check logs: `./claude-nights-watch-manager.sh logs` 483 | 484 | **Tasks not executing:** 485 | - Verify you have remaining Claude usage: `ccusage blocks` 486 | - Check if past scheduled start time 487 | - Ensure task.md is not empty 488 | - Review logs for errors 489 | 490 | **Timing issues:** 491 | - Install ccusage for better accuracy: `npm install -g ccusage` 492 | - Check system time is correct 493 | - Verify `.claude-last-activity` timestamp 494 | 495 | ### Common Issues (Both Methods) 496 | 497 | **Permission errors:** 498 | ```bash 499 | # Ensure all scripts are executable 500 | chmod +x *.sh 501 | chmod +x commands/bin/nights-watch 502 | chmod +x hooks/scripts/*.sh 503 | chmod +x mcp-server/*.sh 504 | ``` 505 | 506 | **Missing dependencies:** 507 | - Install Claude CLI: https://docs.anthropic.com/claude-code 508 | - Install ccusage: `npm install -g ccusage` (recommended) 509 | - Verify bash is available: `which bash` 510 | 511 | **Configuration issues:** 512 | - Check task.md exists and has content 513 | - Verify rules.md exists (recommended) 514 | - Ensure proper file permissions 515 | - Check environment variables 516 | 517 | ## 🤝 Contributing 518 | 519 | Contributions are welcome! Please follow these steps: 520 | 521 | 1. **Fork the repository** on GitHub 522 | 2. **Clone your fork** locally 523 | 3. **Create a feature branch** (`git checkout -b feature/amazing-feature`) 524 | 4. **Make your changes** following our guidelines 525 | 5. **Test thoroughly** using the test suite 526 | 6. **Commit your changes** (`git commit -m 'Add amazing feature'`) 527 | 7. **Push to your fork** (`git push origin feature/amazing-feature`) 528 | 8. **Create a Pull Request** on GitHub 529 | 530 | Please ensure: 531 | - Code follows existing style 532 | - Safety is prioritized 533 | - Documentation is updated 534 | - Examples are provided 535 | - Tests pass 536 | 537 | See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines. 538 | 539 | ## 📄 License 540 | 541 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 542 | 543 | ## 🙏 Acknowledgments 544 | 545 | - **Created by**: [Aniket Karne](https://github.com/aniketkarne) 546 | - **Built on top of**: The excellent [CCAutoRenew](https://github.com/aniketkarne/CCAutoRenew) project 547 | - **Plugin Development**: Complete Claude Code plugin implementation with slash commands, AI agents, MCP server, and smart hooks 548 | - **Enhanced Features**: Task Executor agent, comprehensive testing suites, and dual installation methods 549 | - **Thanks to**: The Claude CLI team for the amazing tool and plugin system 550 | 551 | --- 552 | 553 | --- 554 | 555 | ## 🔄 Migration Guide 556 | 557 | ### From Daemon to Plugin 558 | 559 | If you're currently using the standalone daemon and want to migrate to the plugin: 560 | 561 | 1. **Install the plugin**: 562 | ```bash 563 | claude plugins add https://github.com/aniketkarne/ClaudeNightsWatch 564 | ``` 565 | 566 | 2. **Copy your existing files**: 567 | ```bash 568 | # Copy your task.md and rules.md to the new location 569 | cp task.md rules.md ~/.claude/plugins/claude-nights-watch/ 570 | ``` 571 | 572 | 3. **Update your workflow**: 573 | - Replace `./claude-nights-watch-manager.sh start` with `/nights-watch start` 574 | - Replace `./claude-nights-watch-manager.sh logs` with `/nights-watch logs` 575 | - Use `/nights-watch setup` for configuration 576 | 577 | 4. **Benefits you'll gain**: 578 | - 🤖 **AI Agent Integration**: Get help from the Task Executor agent 579 | - 🎯 **Better Commands**: More intuitive slash command interface 580 | - 🔗 **MCP Integration**: Programmatic control for Claude 581 | - 🎣 **Smart Hooks**: Automatic session integration 582 | 583 | ### From Plugin to Daemon 584 | 585 | If you prefer the standalone approach: 586 | 587 | 1. **Install as daemon**: 588 | ```bash 589 | git clone https://github.com/aniketkarne/ClaudeNightsWatch.git 590 | cd ClaudeNightsWatch 591 | chmod +x *.sh 592 | ``` 593 | 594 | 2. **Copy your plugin files**: 595 | ```bash 596 | # Copy from plugin location to daemon location 597 | cp ~/.claude/plugins/claude-nights-watch/task.md . 598 | cp ~/.claude/plugins/claude-nights-watch/rules.md . 599 | ``` 600 | 601 | 3. **Use daemon commands**: 602 | ```bash 603 | ./claude-nights-watch-manager.sh start 604 | ``` 605 | 606 | **Note**: The plugin method is recommended for new users due to better integration and enhanced features. 607 | 608 | --- 609 | 610 | **Remember**: With great automation comes great responsibility. Always review your tasks and rules carefully before enabling autonomous execution! 🚨 -------------------------------------------------------------------------------- /test/test-plugin-comprehensive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Plugin Comprehensive Test Suite 4 | # Tests all aspects of the plugin implementation and functionality 5 | 6 | set -e 7 | 8 | # Colors for output 9 | RED='\033[0;31m' 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[1;33m' 12 | BLUE='\033[0;34m' 13 | NC='\033[0m' # No Color 14 | 15 | PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)" 16 | TEST_DIR="$PLUGIN_ROOT/test" 17 | TEMP_DIR="$TEST_DIR/temp-test" 18 | PASS=0 19 | FAIL=0 20 | 21 | print_header() { 22 | echo "" 23 | echo -e "${BLUE}================================${NC}" 24 | echo -e "${BLUE}$1${NC}" 25 | echo -e "${BLUE}================================${NC}" 26 | echo "" 27 | } 28 | 29 | print_test() { 30 | echo -e "${BLUE}[TEST]${NC} $1" 31 | } 32 | 33 | print_pass() { 34 | echo -e "${GREEN}[PASS]${NC} $1" 35 | ((PASS++)) 36 | } 37 | 38 | print_fail() { 39 | echo -e "${RED}[FAIL]${NC} $1" 40 | ((FAIL++)) 41 | } 42 | 43 | print_warn() { 44 | echo -e "${YELLOW}[WARN]${NC} $1" 45 | } 46 | 47 | print_info() { 48 | echo -e "${BLUE}[INFO]${NC} $1" 49 | } 50 | 51 | # Cleanup function 52 | cleanup() { 53 | if [ -d "$TEMP_DIR" ]; then 54 | rm -rf "$TEMP_DIR" 55 | print_info "Cleaned up temporary test directory" 56 | fi 57 | } 58 | 59 | # Set up cleanup trap 60 | trap cleanup EXIT 61 | 62 | # Test 1: Plugin Structure Validation 63 | test_plugin_structure() { 64 | print_header "Testing Plugin Structure" 65 | 66 | print_test "Checking required plugin directories..." 67 | 68 | # Required directories for Claude Code plugin 69 | REQUIRED_DIRS=( 70 | ".claude-plugin" 71 | "commands" 72 | "agents" 73 | "hooks" 74 | "mcp-server" 75 | ) 76 | 77 | for dir in "${REQUIRED_DIRS[@]}"; do 78 | if [ -d "$PLUGIN_ROOT/$dir" ]; then 79 | print_pass "Directory exists: $dir" 80 | else 81 | print_fail "Directory missing: $dir" 82 | fi 83 | done 84 | 85 | print_test "Checking required plugin files..." 86 | 87 | # Required files 88 | REQUIRED_FILES=( 89 | ".claude-plugin/plugin.json" 90 | ".mcp.json" 91 | "hooks/hooks.json" 92 | ) 93 | 94 | for file in "${REQUIRED_FILES[@]}"; do 95 | if [ -f "$PLUGIN_ROOT/$file" ]; then 96 | print_pass "File exists: $file" 97 | else 98 | print_fail "File missing: $file" 99 | fi 100 | done 101 | } 102 | 103 | # Test 2: Plugin Manifest Validation 104 | test_plugin_manifest() { 105 | print_header "Testing Plugin Manifest" 106 | 107 | MANIFEST="$PLUGIN_ROOT/.claude-plugin/plugin.json" 108 | 109 | print_test "Validating plugin.json JSON syntax..." 110 | if jq empty "$MANIFEST" 2>/dev/null; then 111 | print_pass "plugin.json is valid JSON" 112 | else 113 | print_fail "plugin.json has invalid JSON syntax" 114 | return 1 115 | fi 116 | 117 | print_test "Checking required manifest fields..." 118 | 119 | # Required fields 120 | REQUIRED_FIELDS=("name" "version" "description" "author") 121 | 122 | for field in "${REQUIRED_FIELDS[@]}"; do 123 | value=$(jq -r ".${field} // empty" "$MANIFEST") 124 | if [ -n "$value" ] && [ "$value" != "null" ]; then 125 | print_pass "Field present: $field = $value" 126 | else 127 | print_fail "Field missing or null: $field" 128 | fi 129 | done 130 | 131 | print_test "Checking component paths..." 132 | 133 | # Check that component paths are specified 134 | commands_path=$(jq -r '.commands // empty' "$MANIFEST") 135 | agents_path=$(jq -r '.agents // empty' "$MANIFEST") 136 | hooks_path=$(jq -r '.hooks // empty' "$MANIFEST") 137 | mcp_path=$(jq -r '.mcpServers // empty' "$MANIFEST") 138 | 139 | if [ -n "$commands_path" ]; then 140 | print_pass "Commands path: $commands_path" 141 | else 142 | print_fail "Commands path missing" 143 | fi 144 | 145 | if [ -n "$agents_path" ]; then 146 | print_pass "Agents path: $agents_path" 147 | else 148 | print_fail "Agents path missing" 149 | fi 150 | 151 | if [ -n "$hooks_path" ]; then 152 | print_pass "Hooks path: $hooks_path" 153 | else 154 | print_fail "Hooks path missing" 155 | fi 156 | 157 | if [ -n "$mcp_path" ]; then 158 | print_pass "MCP servers path: $mcp_path" 159 | else 160 | print_fail "MCP servers path missing" 161 | fi 162 | } 163 | 164 | # Test 3: Command Files Validation 165 | test_command_files() { 166 | print_header "Testing Command Files" 167 | 168 | COMMANDS_DIR="$PLUGIN_ROOT/commands" 169 | 170 | print_test "Checking for command files..." 171 | 172 | # Expected command files 173 | EXPECTED_COMMANDS=( 174 | "start.md" 175 | "stop.md" 176 | "restart.md" 177 | "status.md" 178 | "logs.md" 179 | "task.md" 180 | "setup.md" 181 | ) 182 | 183 | for cmd in "${EXPECTED_COMMANDS[@]}"; do 184 | if [ -f "$COMMANDS_DIR/$cmd" ]; then 185 | print_pass "Command file exists: $cmd" 186 | 187 | # Check for required frontmatter 188 | if grep -q "^---$" "$COMMANDS_DIR/$cmd"; then 189 | print_pass "Command has frontmatter: $cmd" 190 | else 191 | print_warn "Command missing frontmatter: $cmd" 192 | fi 193 | 194 | # Check for description field 195 | if grep -q "description:" "$COMMANDS_DIR/$cmd"; then 196 | print_pass "Command has description: $cmd" 197 | else 198 | print_warn "Command missing description: $cmd" 199 | fi 200 | else 201 | print_fail "Command file missing: $cmd" 202 | fi 203 | done 204 | 205 | print_test "Checking command wrapper..." 206 | 207 | if [ -f "$COMMANDS_DIR/bin/nights-watch" ]; then 208 | print_pass "Command wrapper exists" 209 | 210 | if [ -x "$COMMANDS_DIR/bin/nights-watch" ]; then 211 | print_pass "Command wrapper is executable" 212 | else 213 | print_fail "Command wrapper not executable" 214 | fi 215 | else 216 | print_fail "Command wrapper missing" 217 | fi 218 | } 219 | 220 | # Test 4: Agent Files Validation 221 | test_agent_files() { 222 | print_header "Testing Agent Files" 223 | 224 | AGENTS_DIR="$PLUGIN_ROOT/agents" 225 | 226 | print_test "Checking for agent files..." 227 | 228 | if [ -f "$AGENTS_DIR/task-executor.md" ]; then 229 | print_pass "Task Executor agent exists" 230 | 231 | # Check for required frontmatter 232 | if grep -q "^---$" "$AGENTS_DIR/task-executor.md"; then 233 | print_pass "Agent has frontmatter" 234 | 235 | # Check for required fields 236 | if grep -q "description:" "$AGENTS_DIR/task-executor.md"; then 237 | print_pass "Agent has description" 238 | else 239 | print_fail "Agent missing description" 240 | fi 241 | 242 | if grep -q "capabilities:" "$AGENTS_DIR/task-executor.md"; then 243 | print_pass "Agent has capabilities" 244 | else 245 | print_fail "Agent missing capabilities" 246 | fi 247 | else 248 | print_fail "Agent missing frontmatter" 249 | fi 250 | else 251 | print_fail "Task Executor agent missing" 252 | fi 253 | } 254 | 255 | # Test 5: Hooks Configuration Validation 256 | test_hooks_config() { 257 | print_header "Testing Hooks Configuration" 258 | 259 | HOOKS_FILE="$PLUGIN_ROOT/hooks/hooks.json" 260 | 261 | print_test "Validating hooks.json..." 262 | 263 | if [ -f "$HOOKS_FILE" ]; then 264 | print_pass "hooks.json exists" 265 | 266 | if jq empty "$HOOKS_FILE" 2>/dev/null; then 267 | print_pass "hooks.json is valid JSON" 268 | else 269 | print_fail "hooks.json has invalid JSON" 270 | fi 271 | 272 | # Check for expected hook events 273 | if jq -e '.hooks.SessionStart // empty' "$HOOKS_FILE" >/dev/null 2>&1; then 274 | print_pass "SessionStart hook configured" 275 | else 276 | print_warn "SessionStart hook not configured" 277 | fi 278 | 279 | if jq -e '.hooks.PostToolUse // empty' "$HOOKS_FILE" >/dev/null 2>&1; then 280 | print_pass "PostToolUse hook configured" 281 | else 282 | print_warn "PostToolUse hook not configured" 283 | fi 284 | else 285 | print_fail "hooks.json missing" 286 | fi 287 | 288 | print_test "Checking hook scripts..." 289 | 290 | HOOK_SCRIPTS_DIR="$PLUGIN_ROOT/hooks/scripts" 291 | EXPECTED_SCRIPTS=( 292 | "check-daemon-status.sh" 293 | "session-end-prompt.sh" 294 | "log-file-changes.sh" 295 | ) 296 | 297 | for script in "${EXPECTED_SCRIPTS[@]}"; do 298 | script_path="$HOOK_SCRIPTS_DIR/$script" 299 | if [ -f "$script_path" ]; then 300 | print_pass "Hook script exists: $script" 301 | 302 | if [ -x "$script_path" ]; then 303 | print_pass "Hook script executable: $script" 304 | else 305 | print_fail "Hook script not executable: $script" 306 | fi 307 | else 308 | print_fail "Hook script missing: $script" 309 | fi 310 | done 311 | } 312 | 313 | # Test 6: MCP Server Validation 314 | test_mcp_server() { 315 | print_header "Testing MCP Server" 316 | 317 | MCP_CONFIG="$PLUGIN_ROOT/.mcp.json" 318 | MCP_SERVER="$PLUGIN_ROOT/mcp-server/nights-watch-server.sh" 319 | 320 | print_test "Checking MCP configuration..." 321 | 322 | if [ -f "$MCP_CONFIG" ]; then 323 | print_pass "MCP config exists" 324 | 325 | if jq empty "$MCP_CONFIG" 2>/dev/null; then 326 | print_pass "MCP config is valid JSON" 327 | else 328 | print_fail "MCP config has invalid JSON" 329 | fi 330 | 331 | # Check for nights-watch server 332 | if jq -e '.mcpServers."nights-watch" // empty' "$MCP_CONFIG" >/dev/null 2>&1; then 333 | print_pass "nights-watch MCP server configured" 334 | else 335 | print_fail "nights-watch MCP server not configured" 336 | fi 337 | else 338 | print_fail "MCP config missing" 339 | fi 340 | 341 | print_test "Checking MCP server script..." 342 | 343 | if [ -f "$MCP_SERVER" ]; then 344 | print_pass "MCP server script exists" 345 | 346 | if [ -x "$MCP_SERVER" ]; then 347 | print_pass "MCP server script executable" 348 | else 349 | print_fail "MCP server script not executable" 350 | fi 351 | else 352 | print_fail "MCP server script missing" 353 | fi 354 | } 355 | 356 | # Test 7: Core Scripts Validation 357 | test_core_scripts() { 358 | print_header "Testing Core Scripts" 359 | 360 | CORE_SCRIPTS=( 361 | "claude-nights-watch-daemon.sh" 362 | "claude-nights-watch-manager.sh" 363 | "setup-nights-watch.sh" 364 | "view-logs.sh" 365 | ) 366 | 367 | for script in "${CORE_SCRIPTS[@]}"; do 368 | if [ -f "$PLUGIN_ROOT/$script" ]; then 369 | print_pass "Core script exists: $script" 370 | 371 | if [ -x "$PLUGIN_ROOT/$script" ]; then 372 | print_pass "Core script executable: $script" 373 | else 374 | print_warn "Core script not executable: $script" 375 | fi 376 | else 377 | print_fail "Core script missing: $script" 378 | fi 379 | done 380 | } 381 | 382 | # Test 8: Documentation Validation 383 | test_documentation() { 384 | print_header "Testing Documentation" 385 | 386 | DOC_FILES=( 387 | "README.md" 388 | "CHANGELOG.md" 389 | "CONTRIBUTING.md" 390 | "LICENSE" 391 | ) 392 | 393 | for doc in "${DOC_FILES[@]}"; do 394 | if [ -f "$PLUGIN_ROOT/$doc" ]; then 395 | print_pass "Documentation exists: $doc" 396 | else 397 | print_fail "Documentation missing: $doc" 398 | fi 399 | done 400 | 401 | # Check for plugin-specific docs 402 | PLUGIN_DOCS=( 403 | "PLUGIN_README.md" 404 | "PLUGIN_INSTALLATION.md" 405 | "PLUGIN_TESTING.md" 406 | "MARKETPLACE_SETUP.md" 407 | ) 408 | 409 | for doc in "${PLUGIN_DOCS[@]}"; do 410 | if [ -f "$PLUGIN_ROOT/$doc" ]; then 411 | print_info "Plugin doc exists: $doc" 412 | fi 413 | done 414 | } 415 | 416 | # Test 9: Examples Validation 417 | test_examples() { 418 | print_header "Testing Example Files" 419 | 420 | if [ -d "$PLUGIN_ROOT/examples" ]; then 421 | print_pass "examples/ directory exists" 422 | 423 | EXAMPLE_FILES=( 424 | "task.example.md" 425 | "rules.example.md" 426 | ) 427 | 428 | for example in "${EXAMPLE_FILES[@]}"; do 429 | if [ -f "$PLUGIN_ROOT/examples/$example" ]; then 430 | print_pass "Example file exists: $example" 431 | else 432 | print_warn "Example file missing: $example" 433 | fi 434 | done 435 | else 436 | print_warn "examples/ directory missing" 437 | fi 438 | } 439 | 440 | # Test 10: Marketplace Files Validation 441 | test_marketplace() { 442 | print_header "Testing Marketplace Files" 443 | 444 | MARKETPLACE_DIR="$PLUGIN_ROOT/marketplace-example" 445 | 446 | if [ -d "$MARKETPLACE_DIR" ]; then 447 | print_pass "marketplace-example/ directory exists" 448 | 449 | if [ -f "$MARKETPLACE_DIR/plugins.json" ]; then 450 | print_pass "Marketplace plugins.json exists" 451 | 452 | if jq empty "$MARKETPLACE_DIR/plugins.json" 2>/dev/null; then 453 | print_pass "Marketplace plugins.json is valid JSON" 454 | else 455 | print_fail "Marketplace plugins.json has invalid JSON" 456 | fi 457 | else 458 | print_fail "Marketplace plugins.json missing" 459 | fi 460 | 461 | if [ -f "$MARKETPLACE_DIR/README.md" ]; then 462 | print_pass "Marketplace README exists" 463 | else 464 | print_warn "Marketplace README missing" 465 | fi 466 | else 467 | print_warn "marketplace-example/ directory missing" 468 | fi 469 | } 470 | 471 | # Test 11: Integration Test Simulation 472 | test_integration_simulation() { 473 | print_header "Testing Integration Simulation" 474 | 475 | print_test "Creating temporary test environment..." 476 | 477 | # Create temp directory for testing 478 | mkdir -p "$TEMP_DIR" 479 | 480 | print_test "Copying plugin files to temp directory..." 481 | 482 | # Copy essential plugin components 483 | cp -r "$PLUGIN_ROOT/.claude-plugin" "$TEMP_DIR/" 484 | cp -r "$PLUGIN_ROOT/commands" "$TEMP_DIR/" 485 | cp -r "$PLUGIN_ROOT/agents" "$TEMP_DIR/" 486 | cp -r "$PLUGIN_ROOT/hooks" "$TEMP_DIR/" 487 | cp "$PLUGIN_ROOT/.mcp.json" "$TEMP_DIR/" 488 | 489 | if [ -d "$TEMP_DIR/.claude-plugin" ] && [ -d "$TEMP_DIR/commands" ] && [ -d "$TEMP_DIR/agents" ]; then 490 | print_pass "Plugin structure copied successfully" 491 | else 492 | print_fail "Failed to copy plugin structure" 493 | fi 494 | 495 | print_test "Testing plugin.json in temp environment..." 496 | 497 | if [ -f "$TEMP_DIR/.claude-plugin/plugin.json" ]; then 498 | print_pass "Plugin manifest accessible in temp environment" 499 | 500 | # Test JSON validity 501 | if jq empty "$TEMP_DIR/.claude-plugin/plugin.json" 2>/dev/null; then 502 | print_pass "Plugin manifest valid in temp environment" 503 | else 504 | print_fail "Plugin manifest invalid in temp environment" 505 | fi 506 | else 507 | print_fail "Plugin manifest not accessible in temp environment" 508 | fi 509 | 510 | print_test "Testing command structure..." 511 | 512 | if [ -f "$TEMP_DIR/commands/bin/nights-watch" ]; then 513 | print_pass "Command wrapper accessible" 514 | 515 | # Test if wrapper can find manager script (would fail in real env but structure ok) 516 | if "$TEMP_DIR/commands/bin/nights-watch" help >/dev/null 2>&1; then 517 | print_pass "Command wrapper functional" 518 | else 519 | print_warn "Command wrapper has dependency issues (expected in test env)" 520 | fi 521 | else 522 | print_fail "Command wrapper not accessible" 523 | fi 524 | } 525 | 526 | # Test 12: Environment Variables Test 527 | test_environment_variables() { 528 | print_header "Testing Environment Variables" 529 | 530 | print_test "Testing CLAUDE_PLUGIN_ROOT simulation..." 531 | 532 | # This simulates what would happen when Claude Code sets the environment variable 533 | export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" 534 | 535 | # Test that scripts can access this (they use it in practice) 536 | if [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ "$CLAUDE_PLUGIN_ROOT" = "$PLUGIN_ROOT" ]; then 537 | print_pass "CLAUDE_PLUGIN_ROOT environment variable works" 538 | else 539 | print_fail "CLAUDE_PLUGIN_ROOT environment variable issue" 540 | fi 541 | 542 | # Test that the variable is used correctly in hook scripts 543 | if grep -q "CLAUDE_PLUGIN_ROOT" "$PLUGIN_ROOT/hooks/scripts/check-daemon-status.sh"; then 544 | print_pass "Hook scripts use CLAUDE_PLUGIN_ROOT correctly" 545 | else 546 | print_fail "Hook scripts don't use CLAUDE_PLUGIN_ROOT" 547 | fi 548 | 549 | unset CLAUDE_PLUGIN_ROOT 550 | } 551 | 552 | # Test 13: File Permissions Test 553 | test_file_permissions() { 554 | print_header "Testing File Permissions" 555 | 556 | print_test "Checking script executability..." 557 | 558 | # Core scripts 559 | CORE_SCRIPTS=( 560 | "claude-nights-watch-daemon.sh" 561 | "claude-nights-watch-manager.sh" 562 | "setup-nights-watch.sh" 563 | "view-logs.sh" 564 | ) 565 | 566 | for script in "${CORE_SCRIPTS[@]}"; do 567 | if [ -x "$PLUGIN_ROOT/$script" ]; then 568 | print_pass "Core script executable: $script" 569 | else 570 | print_warn "Core script not executable: $script" 571 | fi 572 | done 573 | 574 | # Plugin scripts 575 | PLUGIN_SCRIPTS=( 576 | "commands/bin/nights-watch" 577 | "mcp-server/nights-watch-server.sh" 578 | ) 579 | 580 | for script in "${PLUGIN_SCRIPTS[@]}"; do 581 | if [ -x "$PLUGIN_ROOT/$script" ]; then 582 | print_pass "Plugin script executable: $script" 583 | else 584 | print_fail "Plugin script not executable: $script" 585 | fi 586 | done 587 | } 588 | 589 | # Test 14: Content Validation 590 | test_content_validation() { 591 | print_header "Testing Content Validation" 592 | 593 | print_test "Checking for placeholder content..." 594 | 595 | # Check that files don't contain placeholder text 596 | if grep -r "PLACEHOLDER\|TODO\|FIXME" "$PLUGIN_ROOT/commands/" "$PLUGIN_ROOT/agents/" "$PLUGIN_ROOT/hooks/"; then 597 | print_warn "Found placeholder content in plugin files" 598 | else 599 | print_pass "No placeholder content found" 600 | fi 601 | 602 | print_test "Checking for broken links..." 603 | 604 | # Check for common broken link patterns 605 | if grep -r "https://example.com\|http://example.com" "$PLUGIN_ROOT/"; then 606 | print_warn "Found example URLs in documentation" 607 | else 608 | print_pass "No broken example URLs found" 609 | fi 610 | } 611 | 612 | # Test 15: Dependencies Check 613 | test_dependencies() { 614 | print_header "Testing Dependencies" 615 | 616 | print_test "Checking for jq (JSON validation)..." 617 | 618 | if command -v jq >/dev/null 2>&1; then 619 | print_pass "jq is available for JSON validation" 620 | else 621 | print_warn "jq not available - JSON validation limited" 622 | fi 623 | 624 | print_test "Checking for required external tools..." 625 | 626 | # These are needed for the plugin to function 627 | REQUIRED_TOOLS=("bash" "cat" "echo" "date" "grep" "sed" "awk") 628 | 629 | for tool in "${REQUIRED_TOOLS[@]}"; do 630 | if command -v "$tool" >/dev/null 2>&1; then 631 | print_pass "Required tool available: $tool" 632 | else 633 | print_fail "Required tool missing: $tool" 634 | fi 635 | done 636 | } 637 | 638 | # Run all tests 639 | main() { 640 | echo "" 641 | echo -e "${BLUE}╔════════════════════════════════════════════════════╗${NC}" 642 | echo -e "${BLUE}║ Claude Nights Watch Plugin Comprehensive Test ║${NC}" 643 | echo -e "${BLUE}╚════════════════════════════════════════════════════╝${NC}" 644 | echo "" 645 | echo "Plugin Root: $PLUGIN_ROOT" 646 | echo "Test Directory: $TEST_DIR" 647 | echo "" 648 | 649 | # Run all test functions 650 | test_plugin_structure 651 | test_plugin_manifest 652 | test_command_files 653 | test_agent_files 654 | test_hooks_config 655 | test_mcp_server 656 | test_core_scripts 657 | test_documentation 658 | test_examples 659 | test_marketplace 660 | test_integration_simulation 661 | test_environment_variables 662 | test_file_permissions 663 | test_content_validation 664 | test_dependencies 665 | 666 | # Summary 667 | print_header "Test Summary" 668 | 669 | TOTAL=$((PASS + FAIL)) 670 | 671 | echo -e "${GREEN}Passed:${NC} $PASS" 672 | echo -e "${RED}Failed:${NC} $FAIL" 673 | echo -e "${BLUE}Total:${NC} $TOTAL" 674 | echo "" 675 | 676 | # Calculate confidence percentage 677 | if [ $TOTAL -gt 0 ]; then 678 | CONFIDENCE=$((PASS * 100 / TOTAL)) 679 | echo -e "${BLUE}Confidence Score:${NC} ${CONFIDENCE}%" 680 | else 681 | CONFIDENCE=0 682 | echo -e "${YELLOW}No tests completed${NC}" 683 | fi 684 | 685 | echo "" 686 | 687 | # Provide assessment 688 | if [ $FAIL -eq 0 ]; then 689 | echo -e "${GREEN}✅ ALL TESTS PASSED!${NC}" 690 | echo -e "${GREEN}🎉 Plugin structure is 100% correct and ready for distribution${NC}" 691 | echo -e "${GREEN}📦 Plugin should install and function properly in Claude Code${NC}" 692 | elif [ $FAIL -lt 3 ]; then 693 | echo -e "${YELLOW}⚠️ MOST TESTS PASSED${NC}" 694 | echo -e "${YELLOW}📊 Plugin structure is ${CONFIDENCE}% correct${NC}" 695 | if [ $CONFIDENCE -ge 90 ]; then 696 | echo -e "${YELLOW}✅ Plugin should work with minor fixes${NC}" 697 | else 698 | echo -e "${YELLOW}⚠️ Plugin needs fixes before distribution${NC}" 699 | fi 700 | else 701 | echo -e "${RED}❌ MULTIPLE TESTS FAILED${NC}" 702 | echo -e "${RED}📊 Plugin structure is ${CONFIDENCE}% correct${NC}" 703 | echo -e "${RED}❌ Plugin needs significant fixes before distribution${NC}" 704 | fi 705 | 706 | echo "" 707 | echo -e "${BLUE}Plugin can be installed with:${NC}" 708 | echo " claude plugins add $PLUGIN_ROOT" 709 | echo "" 710 | echo -e "${BLUE}Or from GitHub:${NC}" 711 | echo " claude plugins add https://github.com/aniketkarne/ClaudeNightsWatch" 712 | echo "" 713 | 714 | # Exit with appropriate code 715 | if [ $FAIL -eq 0 ]; then 716 | exit 0 717 | else 718 | exit 1 719 | fi 720 | } 721 | 722 | # Run the test suite 723 | main 724 | 725 | -------------------------------------------------------------------------------- /test/test-functional-realworld.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Claude Nights Watch Plugin - Functional & Real-World Testing Suite 4 | # Tests actual functionality and simulates real-world usage scenarios 5 | 6 | set -e 7 | 8 | # Colors for output 9 | RED='\033[0;31m' 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[1;33m' 12 | BLUE='\033[0;34m' 13 | NC='\033[0m' # No Color 14 | 15 | PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)" 16 | TEST_DIR="$PLUGIN_ROOT/test" 17 | TEMP_DIR="$TEST_DIR/functional-test" 18 | PASS=0 19 | FAIL=0 20 | WARN=0 21 | 22 | print_header() { 23 | echo "" 24 | echo -e "${BLUE}================================${NC}" 25 | echo -e "${BLUE}$1${NC}" 26 | echo -e "${BLUE}================================${NC}" 27 | echo "" 28 | } 29 | 30 | print_test() { 31 | echo -e "${BLUE}[TEST]${NC} $1" 32 | } 33 | 34 | print_pass() { 35 | echo -e "${GREEN}[PASS]${NC} $1" 36 | ((PASS++)) 37 | } 38 | 39 | print_fail() { 40 | echo -e "${RED}[FAIL]${NC} $1" 41 | ((FAIL++)) 42 | } 43 | 44 | print_warn() { 45 | echo -e "${YELLOW}[WARN]${NC} $1" 46 | ((WARN++)) 47 | } 48 | 49 | print_info() { 50 | echo -e "${BLUE}[INFO]${NC} $1" 51 | } 52 | 53 | # Cleanup function 54 | cleanup() { 55 | if [ -d "$TEMP_DIR" ]; then 56 | rm -rf "$TEMP_DIR" 57 | print_info "Cleaned up functional test directory" 58 | fi 59 | } 60 | 61 | # Set up cleanup trap 62 | trap cleanup EXIT 63 | 64 | # Initialize test environment 65 | setup_test_environment() { 66 | print_header "Setting Up Test Environment" 67 | 68 | print_test "Creating temporary test environment..." 69 | 70 | # Create temp directory for testing 71 | mkdir -p "$TEMP_DIR" 72 | 73 | # Copy plugin files for testing 74 | cp -r "$PLUGIN_ROOT/.claude-plugin" "$TEMP_DIR/" 75 | cp -r "$PLUGIN_ROOT/commands" "$TEMP_DIR/" 76 | cp -r "$PLUGIN_ROOT/agents" "$TEMP_DIR/" 77 | cp -r "$PLUGIN_ROOT/hooks" "$TEMP_DIR/" 78 | cp "$PLUGIN_ROOT/.mcp.json" "$TEMP_DIR/" 79 | 80 | if [ -d "$TEMP_DIR/.claude-plugin" ] && [ -d "$TEMP_DIR/commands" ]; then 81 | print_pass "Test environment created successfully" 82 | else 83 | print_fail "Failed to create test environment" 84 | exit 1 85 | fi 86 | 87 | print_test "Setting up test project structure..." 88 | 89 | # Create test project files 90 | mkdir -p "$TEMP_DIR/test-project" 91 | cd "$TEMP_DIR/test-project" 92 | 93 | # Create test task file 94 | cat > task.md << 'EOF' 95 | # Test Automation Task 96 | 97 | ## Objectives: 98 | 1. Verify autonomous execution works 99 | 2. Test file creation and modification 100 | 101 | ## Tasks: 102 | 1. Create a file called test-output.txt 103 | 2. Write "Autonomous execution successful!" to it 104 | 3. Create a timestamp file 105 | 4. Report completion status 106 | EOF 107 | 108 | # Create test rules file 109 | cat > rules.md << 'EOF' 110 | # Test Safety Rules 111 | 112 | ## CRITICAL RULES: 113 | - Only create files in current directory 114 | - Never delete any files 115 | - Never modify existing files outside project 116 | - Always log all actions 117 | 118 | ## ALLOWED: 119 | - Create new files in project directory 120 | - Read existing files 121 | - Write to created files only 122 | EOF 123 | 124 | cd "$PLUGIN_ROOT" 125 | print_pass "Test project structure created" 126 | } 127 | 128 | # Test 1: Plugin Installation Simulation 129 | test_plugin_installation() { 130 | print_header "Testing Plugin Installation Simulation" 131 | 132 | print_test "Simulating plugin installation process..." 133 | 134 | # Test that plugin.json is accessible 135 | if [ -f "$TEMP_DIR/.claude-plugin/plugin.json" ]; then 136 | print_pass "Plugin manifest accessible for installation" 137 | 138 | # Validate JSON structure 139 | if jq -e '.name == "claude-nights-watch"' "$TEMP_DIR/.claude-plugin/plugin.json" >/dev/null 2>&1; then 140 | print_pass "Plugin name correctly configured" 141 | else 142 | print_fail "Plugin name not correctly configured" 143 | fi 144 | 145 | # Check version 146 | VERSION=$(jq -r '.version' "$TEMP_DIR/.claude-plugin/plugin.json") 147 | if [ "$VERSION" = "1.0.0" ]; then 148 | print_pass "Plugin version correctly set: $VERSION" 149 | else 150 | print_fail "Plugin version incorrect: $VERSION" 151 | fi 152 | else 153 | print_fail "Plugin manifest not accessible" 154 | fi 155 | 156 | print_test "Testing component path resolution..." 157 | 158 | # Test that all component paths are resolvable 159 | COMMANDS_PATH=$(jq -r '.commands' "$TEMP_DIR/.claude-plugin/plugin.json") 160 | if [ -d "$TEMP_DIR/$COMMANDS_PATH" ]; then 161 | print_pass "Commands path resolves correctly" 162 | else 163 | print_fail "Commands path does not resolve" 164 | fi 165 | 166 | MCP_PATH=$(jq -r '.mcpServers' "$TEMP_DIR/.claude-plugin/plugin.json") 167 | if [ -f "$TEMP_DIR/$MCP_PATH" ]; then 168 | print_pass "MCP servers path resolves correctly" 169 | else 170 | print_fail "MCP servers path does not resolve" 171 | fi 172 | } 173 | 174 | # Test 2: Command Execution Testing 175 | test_command_execution() { 176 | print_header "Testing Command Execution" 177 | 178 | print_test "Testing command wrapper functionality..." 179 | 180 | COMMAND_WRAPPER="$TEMP_DIR/commands/bin/nights-watch" 181 | 182 | if [ -x "$COMMAND_WRAPPER" ]; then 183 | print_pass "Command wrapper is executable" 184 | 185 | # Test help command (should work without Claude CLI) 186 | if "$COMMAND_WRAPPER" help >/dev/null 2>&1; then 187 | print_pass "Command wrapper help function works" 188 | else 189 | print_warn "Command wrapper help function has issues (expected without Claude CLI)" 190 | fi 191 | 192 | # Test invalid command handling 193 | if "$COMMAND_WRAPPER" invalid-command 2>&1 | grep -q "Unknown command"; then 194 | print_pass "Command wrapper handles invalid commands correctly" 195 | else 196 | print_fail "Command wrapper does not handle invalid commands properly" 197 | fi 198 | 199 | else 200 | print_fail "Command wrapper is not executable" 201 | fi 202 | 203 | print_test "Testing individual command files..." 204 | 205 | # Test that command files exist and have proper structure 206 | COMMANDS_DIR="$TEMP_DIR/commands" 207 | EXPECTED_COMMANDS=("start.md" "stop.md" "status.md" "logs.md" "task.md" "setup.md" "restart.md") 208 | 209 | for cmd in "${EXPECTED_COMMANDS[@]}"; do 210 | if [ -f "$COMMANDS_DIR/$cmd" ]; then 211 | print_pass "Command file exists: $cmd" 212 | 213 | # Check for frontmatter 214 | if grep -q "^---$" "$COMMANDS_DIR/$cmd"; then 215 | print_pass "Command has frontmatter: $cmd" 216 | else 217 | print_warn "Command missing frontmatter: $cmd" 218 | fi 219 | else 220 | print_fail "Command file missing: $cmd" 221 | fi 222 | done 223 | } 224 | 225 | # Test 3: Environment Variables Simulation 226 | test_environment_variables() { 227 | print_header "Testing Environment Variables" 228 | 229 | print_test "Simulating CLAUDE_PLUGIN_ROOT environment..." 230 | 231 | # Simulate what Claude Code would set 232 | export CLAUDE_PLUGIN_ROOT="$TEMP_DIR" 233 | 234 | # Test that hook scripts use the environment variable correctly 235 | if grep -q "CLAUDE_PLUGIN_ROOT" "$TEMP_DIR/hooks/scripts/check-daemon-status.sh"; then 236 | print_pass "Hook scripts reference CLAUDE_PLUGIN_ROOT" 237 | 238 | # Test that the path would resolve correctly 239 | HOOK_SCRIPT="$TEMP_DIR/hooks/scripts/check-daemon-status.sh" 240 | if [ -x "$HOOK_SCRIPT" ]; then 241 | print_pass "Hook script is executable" 242 | 243 | # Test script execution (will fail due to missing Claude, but should run) 244 | if timeout 5s bash "$HOOK_SCRIPT" >/dev/null 2>&1; then 245 | print_pass "Hook script executes without syntax errors" 246 | else 247 | print_warn "Hook script has runtime dependencies (expected)" 248 | fi 249 | else 250 | print_fail "Hook script is not executable" 251 | fi 252 | else 253 | print_fail "Hook scripts don't use CLAUDE_PLUGIN_ROOT" 254 | fi 255 | 256 | print_test "Testing MCP server environment variables..." 257 | 258 | if grep -q "CLAUDE_PLUGIN_ROOT" "$TEMP_DIR/mcp-server/nights-watch-server.sh"; then 259 | print_pass "MCP server references CLAUDE_PLUGIN_ROOT" 260 | else 261 | print_fail "MCP server doesn't use CLAUDE_PLUGIN_ROOT" 262 | fi 263 | 264 | unset CLAUDE_PLUGIN_ROOT 265 | } 266 | 267 | # Test 4: Core Script Functionality 268 | test_core_scripts() { 269 | print_header "Testing Core Script Functionality" 270 | 271 | print_test "Testing manager script configuration parsing..." 272 | 273 | MANAGER_SCRIPT="$PLUGIN_ROOT/claude-nights-watch-manager.sh" 274 | 275 | if [ -x "$MANAGER_SCRIPT" ]; then 276 | print_pass "Manager script is executable" 277 | 278 | # Test help function 279 | if "$MANAGER_SCRIPT" --help >/dev/null 2>&1 || "$MANAGER_SCRIPT" help >/dev/null 2>&1; then 280 | print_pass "Manager script help function works" 281 | else 282 | print_warn "Manager script help function not accessible" 283 | fi 284 | 285 | # Test status command (should work without daemon running) 286 | if "$MANAGER_SCRIPT" status >/dev/null 2>&1; then 287 | print_pass "Manager script status command works" 288 | else 289 | print_warn "Manager script status command has issues" 290 | fi 291 | else 292 | print_fail "Manager script is not executable" 293 | fi 294 | 295 | print_test "Testing daemon script configuration..." 296 | 297 | DAEMON_SCRIPT="$PLUGIN_ROOT/claude-nights-watch-daemon.sh" 298 | 299 | if [ -x "$DAEMON_SCRIPT" ]; then 300 | print_pass "Daemon script is executable" 301 | 302 | # Test that script can parse its configuration without running 303 | if timeout 3s bash -c "$DAEMON_SCRIPT --help 2>/dev/null || echo 'Script structure OK'" 2>/dev/null; then 304 | print_pass "Daemon script structure is valid" 305 | else 306 | print_fail "Daemon script has structural issues" 307 | fi 308 | else 309 | print_fail "Daemon script is not executable" 310 | fi 311 | 312 | print_test "Testing setup script functionality..." 313 | 314 | SETUP_SCRIPT="$PLUGIN_ROOT/setup-nights-watch.sh" 315 | 316 | if [ -x "$SETUP_SCRIPT" ]; then 317 | print_pass "Setup script is executable" 318 | 319 | # Test help function 320 | if "$SETUP_SCRIPT" --help >/dev/null 2>&1; then 321 | print_pass "Setup script help function works" 322 | else 323 | print_warn "Setup script help function not accessible" 324 | fi 325 | else 326 | print_fail "Setup script is not executable" 327 | fi 328 | } 329 | 330 | # Test 5: Hook Scripts Testing 331 | test_hook_scripts() { 332 | print_header "Testing Hook Scripts" 333 | 334 | HOOKS_DIR="$TEMP_DIR/hooks/scripts" 335 | 336 | print_test "Testing daemon status check hook..." 337 | 338 | STATUS_HOOK="$HOOKS_DIR/check-daemon-status.sh" 339 | if [ -x "$STATUS_HOOK" ]; then 340 | print_pass "Status hook script is executable" 341 | 342 | # Test script logic (should handle missing PID file gracefully) 343 | if timeout 3s bash "$STATUS_HOOK" >/dev/null 2>&1; then 344 | print_pass "Status hook executes without errors" 345 | else 346 | print_warn "Status hook has runtime issues (expected)" 347 | fi 348 | else 349 | print_fail "Status hook script not executable" 350 | fi 351 | 352 | print_test "Testing session end prompt hook..." 353 | 354 | END_HOOK="$HOOKS_DIR/session-end-prompt.sh" 355 | if [ -x "$END_HOOK" ]; then 356 | print_pass "Session end hook script is executable" 357 | 358 | # Test script logic 359 | if timeout 3s bash "$END_HOOK" >/dev/null 2>&1; then 360 | print_pass "Session end hook executes without errors" 361 | else 362 | print_warn "Session end hook has runtime issues (expected)" 363 | fi 364 | else 365 | print_fail "Session end hook script not executable" 366 | fi 367 | 368 | print_test "Testing file change logging hook..." 369 | 370 | LOG_HOOK="$HOOKS_DIR/log-file-changes.sh" 371 | if [ -x "$LOG_HOOK" ]; then 372 | print_pass "File logging hook script is executable" 373 | 374 | # Test script logic 375 | if timeout 3s bash "$LOG_HOOK" >/dev/null 2>&1; then 376 | print_pass "File logging hook executes without errors" 377 | else 378 | print_warn "File logging hook has runtime issues (expected)" 379 | fi 380 | else 381 | print_fail "File logging hook script not executable" 382 | fi 383 | } 384 | 385 | # Test 6: MCP Server Testing 386 | test_mcp_server() { 387 | print_header "Testing MCP Server" 388 | 389 | MCP_SERVER="$TEMP_DIR/mcp-server/nights-watch-server.sh" 390 | 391 | print_test "Testing MCP server script structure..." 392 | 393 | if [ -x "$MCP_SERVER" ]; then 394 | print_pass "MCP server script is executable" 395 | 396 | # Test that script has proper structure (can be parsed) 397 | if grep -q "handle_initialize" "$MCP_SERVER" && grep -q "handle_list_tools" "$MCP_SERVER"; then 398 | print_pass "MCP server has required handler functions" 399 | else 400 | print_fail "MCP server missing required handlers" 401 | fi 402 | 403 | # Test that script handles JSON-RPC protocol 404 | if grep -q "jsonrpc" "$MCP_SERVER"; then 405 | print_pass "MCP server implements JSON-RPC protocol" 406 | else 407 | print_fail "MCP server doesn't implement JSON-RPC" 408 | fi 409 | else 410 | print_fail "MCP server script not executable" 411 | fi 412 | 413 | print_test "Testing MCP configuration..." 414 | 415 | MCP_CONFIG="$TEMP_DIR/.mcp.json" 416 | if [ -f "$MCP_CONFIG" ]; then 417 | print_pass "MCP configuration exists" 418 | 419 | # Validate JSON 420 | if jq empty "$MCP_CONFIG" 2>/dev/null; then 421 | print_pass "MCP configuration is valid JSON" 422 | else 423 | print_fail "MCP configuration has invalid JSON" 424 | fi 425 | 426 | # Check server definition 427 | if jq -e '.mcpServers."nights-watch"' "$MCP_CONFIG" >/dev/null 2>&1; then 428 | print_pass "nights-watch MCP server is defined" 429 | else 430 | print_fail "nights-watch MCP server not defined" 431 | fi 432 | else 433 | print_fail "MCP configuration missing" 434 | fi 435 | } 436 | 437 | # Test 7: Task and Rules Processing 438 | test_task_processing() { 439 | print_header "Testing Task and Rules Processing" 440 | 441 | print_test "Testing task file format and content..." 442 | 443 | TASK_FILE="$TEMP_DIR/test-project/task.md" 444 | if [ -f "$TASK_FILE" ]; then 445 | print_pass "Test task file exists" 446 | 447 | # Check that it has proper markdown structure 448 | if grep -q "^# " "$TASK_FILE" && grep -q "## Tasks:" "$TASK_FILE"; then 449 | print_pass "Task file has proper structure" 450 | else 451 | print_warn "Task file structure could be improved" 452 | fi 453 | 454 | # Check file size (should be reasonable) 455 | FILE_SIZE=$(wc -c < "$TASK_FILE") 456 | if [ "$FILE_SIZE" -gt 100 ] && [ "$FILE_SIZE" -lt 10000 ]; then 457 | print_pass "Task file size is reasonable: $FILE_SIZE bytes" 458 | else 459 | print_warn "Task file size may be too small or too large: $FILE_SIZE bytes" 460 | fi 461 | else 462 | print_fail "Test task file missing" 463 | fi 464 | 465 | print_test "Testing rules file format and content..." 466 | 467 | RULES_FILE="$TEMP_DIR/test-project/rules.md" 468 | if [ -f "$RULES_FILE" ]; then 469 | print_pass "Test rules file exists" 470 | 471 | # Check for critical rules section 472 | if grep -q "## CRITICAL RULES:" "$RULES_FILE" && grep -q "## ALLOWED:" "$RULES_FILE"; then 473 | print_pass "Rules file has proper safety structure" 474 | else 475 | print_warn "Rules file structure could be improved" 476 | fi 477 | else 478 | print_fail "Test rules file missing" 479 | fi 480 | } 481 | 482 | # Test 8: Integration Points Testing 483 | test_integration_points() { 484 | print_header "Testing Integration Points" 485 | 486 | print_test "Testing plugin-to-daemon integration..." 487 | 488 | # Test that manager script can find daemon script 489 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" status >/dev/null 2>&1; then 490 | print_pass "Manager script can access daemon components" 491 | else 492 | print_fail "Manager script cannot access daemon components" 493 | fi 494 | 495 | print_test "Testing command wrapper integration..." 496 | 497 | # Test that command wrapper can find manager script 498 | COMMAND_TEST="$TEMP_DIR/commands/bin/nights-watch" 499 | if [ -x "$COMMAND_TEST" ]; then 500 | # This will fail due to missing Claude, but should find the script 501 | if "$COMMAND_TEST" status 2>&1 | grep -q "claude-nights-watch-manager.sh" || [ $? -eq 0 ]; then 502 | print_pass "Command wrapper can locate manager script" 503 | else 504 | print_warn "Command wrapper cannot locate manager script" 505 | fi 506 | fi 507 | 508 | print_test "Testing environment variable propagation..." 509 | 510 | # Simulate Claude Code environment 511 | export CLAUDE_PLUGIN_ROOT="$TEMP_DIR" 512 | export CLAUDE_NIGHTS_WATCH_DIR="$TEMP_DIR/test-project" 513 | 514 | # Test that scripts use these variables correctly 515 | if grep -q "CLAUDE_PLUGIN_ROOT" "$TEMP_DIR/hooks/scripts/check-daemon-status.sh"; then 516 | print_pass "Hook scripts use CLAUDE_PLUGIN_ROOT" 517 | 518 | # Test variable substitution 519 | HOOK_SCRIPT="$TEMP_DIR/hooks/scripts/check-daemon-status.sh" 520 | if [ -x "$HOOK_SCRIPT" ]; then 521 | # This tests that the script can handle the environment variable 522 | if timeout 2s bash "$HOOK_SCRIPT" >/dev/null 2>&1; then 523 | print_pass "Hook script handles environment variables correctly" 524 | else 525 | print_warn "Hook script has environment variable issues (may be expected)" 526 | fi 527 | fi 528 | fi 529 | 530 | unset CLAUDE_PLUGIN_ROOT CLAUDE_NIGHTS_WATCH_DIR 531 | } 532 | 533 | # Test 9: Error Handling Testing 534 | test_error_handling() { 535 | print_header "Testing Error Handling" 536 | 537 | print_test "Testing missing task file handling..." 538 | 539 | # Create a test scenario with missing task file 540 | MISSING_TASK_DIR="$TEMP_DIR/missing-task-test" 541 | mkdir -p "$MISSING_TASK_DIR" 542 | 543 | # Test manager script behavior with missing task file 544 | cd "$MISSING_TASK_DIR" 545 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" status 2>&1 | grep -q "not found" || [ $? -ne 0 ]; then 546 | print_pass "Manager script handles missing task file appropriately" 547 | else 548 | print_fail "Manager script doesn't handle missing task file correctly" 549 | fi 550 | 551 | print_test "Testing invalid command handling..." 552 | 553 | # Test command wrapper with invalid arguments 554 | cd "$TEMP_DIR" 555 | if "$TEMP_DIR/commands/bin/nights-watch" invalid-command 2>&1 | grep -q "Unknown command"; then 556 | print_pass "Command wrapper handles invalid commands correctly" 557 | else 558 | print_fail "Command wrapper doesn't handle invalid commands" 559 | fi 560 | 561 | print_test "Testing malformed JSON handling..." 562 | 563 | # Test plugin.json with syntax errors (backup and restore) 564 | ORIGINAL_PLUGIN="$TEMP_DIR/.claude-plugin/plugin.json" 565 | cp "$ORIGINAL_PLUGIN" "$ORIGINAL_PLUGIN.backup" 566 | 567 | # Create malformed JSON 568 | echo '{"invalid": json}' > "$ORIGINAL_PLUGIN" 569 | 570 | # Test that scripts handle this gracefully 571 | if ! jq . "$ORIGINAL_PLUGIN" >/dev/null 2>&1; then 572 | print_pass "Malformed JSON is detectable" 573 | 574 | # Restore original 575 | mv "$ORIGINAL_PLUGIN.backup" "$ORIGINAL_PLUGIN" 576 | print_pass "Original plugin.json restored" 577 | else 578 | print_fail "Malformed JSON not detected" 579 | # Restore anyway 580 | mv "$ORIGINAL_PLUGIN.backup" "$ORIGINAL_PLUGIN" 2>/dev/null || true 581 | fi 582 | } 583 | 584 | # Test 10: Performance and Resource Testing 585 | test_performance_resources() { 586 | print_header "Testing Performance and Resources" 587 | 588 | print_test "Testing script loading times..." 589 | 590 | # Test that scripts start quickly 591 | START_TIME=$(date +%s.%N) 592 | if timeout 2s bash "$PLUGIN_ROOT/claude-nights-watch-manager.sh" --help >/dev/null 2>&1; then 593 | END_TIME=$(date +%s.%N) 594 | LOAD_TIME=$(echo "$END_TIME - $START_TIME" | bc 2>/dev/null || echo "0.1") 595 | 596 | if (( $(echo "$LOAD_TIME < 1.0" | bc -l 2>/dev/null || echo "1") )); then 597 | print_pass "Manager script loads quickly: ${LOAD_TIME}s" 598 | else 599 | print_warn "Manager script loads slowly: ${LOAD_TIME}s" 600 | fi 601 | else 602 | print_warn "Cannot measure script load time" 603 | fi 604 | 605 | print_test "Testing file sizes and resource usage..." 606 | 607 | # Check that files are not excessively large 608 | PLUGIN_SIZE=$(du -sb "$PLUGIN_ROOT" 2>/dev/null | cut -f1 || echo "0") 609 | if [ "$PLUGIN_SIZE" -lt 10485760 ]; then # 10MB limit 610 | print_pass "Plugin size is reasonable: $PLUGIN_SIZE bytes" 611 | else 612 | print_warn "Plugin size is large: $PLUGIN_SIZE bytes" 613 | fi 614 | 615 | # Count total files 616 | FILE_COUNT=$(find "$PLUGIN_ROOT" -type f | wc -l) 617 | if [ "$FILE_COUNT" -lt 50 ]; then 618 | print_pass "File count is reasonable: $FILE_COUNT files" 619 | else 620 | print_warn "File count is high: $FILE_COUNT files" 621 | fi 622 | } 623 | 624 | # Test 11: Real-World Usage Simulation 625 | test_real_world_simulation() { 626 | print_header "Testing Real-World Usage Simulation" 627 | 628 | print_test "Simulating typical user workflow..." 629 | 630 | cd "$TEMP_DIR/test-project" 631 | 632 | # Step 1: Check initial status 633 | print_test "Step 1: Checking initial daemon status..." 634 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" status >/dev/null 2>&1; then 635 | print_pass "Initial status check works" 636 | else 637 | print_warn "Initial status check has issues (expected without daemon)" 638 | fi 639 | 640 | # Step 2: Test task viewing 641 | print_test "Step 2: Viewing current task..." 642 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" task 2>/dev/null | grep -q "Current task"; then 643 | print_pass "Task viewing works" 644 | else 645 | print_warn "Task viewing has issues" 646 | fi 647 | 648 | # Step 3: Test setup process 649 | print_test "Step 3: Testing setup script..." 650 | if timeout 5s bash "$PLUGIN_ROOT/setup-nights-watch.sh" --help >/dev/null 2>&1; then 651 | print_pass "Setup script help works" 652 | else 653 | print_warn "Setup script help not accessible" 654 | fi 655 | 656 | print_test "Simulating plugin command usage..." 657 | 658 | # Test command wrapper in realistic scenario 659 | cd "$TEMP_DIR" 660 | if "$TEMP_DIR/commands/bin/nights-watch" status 2>&1 | head -5 | grep -q "Daemon is not running"; then 661 | print_pass "Command wrapper provides helpful status information" 662 | else 663 | print_warn "Command wrapper status output could be improved" 664 | fi 665 | 666 | print_test "Testing cross-component integration..." 667 | 668 | # Test that all components can work together 669 | if [ -f "$TEMP_DIR/.claude-plugin/plugin.json" ] && \ 670 | [ -d "$TEMP_DIR/commands" ] && \ 671 | [ -d "$TEMP_DIR/hooks" ] && \ 672 | [ -f "$TEMP_DIR/.mcp.json" ]; then 673 | print_pass "All plugin components are properly integrated" 674 | else 675 | print_fail "Plugin components are not properly integrated" 676 | fi 677 | } 678 | 679 | # Test 12: Edge Cases and Error Scenarios 680 | test_edge_cases() { 681 | print_header "Testing Edge Cases and Error Scenarios" 682 | 683 | print_test "Testing with missing directories..." 684 | 685 | # Test behavior when logs directory doesn't exist 686 | MISSING_LOGS_DIR="$TEMP_DIR/missing-logs" 687 | mkdir -p "$MISSING_LOGS_DIR" 688 | 689 | # Remove logs directory if it exists 690 | if [ -d "$MISSING_LOGS_DIR/logs" ]; then 691 | rmdir "$MISSING_LOGS_DIR/logs" 692 | fi 693 | 694 | cd "$MISSING_LOGS_DIR" 695 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" logs 2>&1 | grep -q "No log file" || [ $? -ne 0 ]; then 696 | print_pass "Manager script handles missing logs directory correctly" 697 | else 698 | print_fail "Manager script doesn't handle missing logs directory" 699 | fi 700 | 701 | print_test "Testing with empty task file..." 702 | 703 | # Create empty task file 704 | EMPTY_TASK_DIR="$TEMP_DIR/empty-task" 705 | mkdir -p "$EMPTY_TASK_DIR" 706 | cd "$EMPTY_TASK_DIR" 707 | touch task.md 708 | 709 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" task 2>&1 | grep -q "task.md" || [ $? -eq 0 ]; then 710 | print_pass "Manager script handles empty task file correctly" 711 | else 712 | print_fail "Manager script doesn't handle empty task file" 713 | fi 714 | 715 | print_test "Testing with special characters in paths..." 716 | 717 | # Create directory with spaces and special characters 718 | SPECIAL_DIR="$TEMP_DIR/special chars-@test" 719 | mkdir -p "$SPECIAL_DIR" 720 | cd "$SPECIAL_DIR" 721 | echo "# Test task" > task.md 722 | 723 | if "$PLUGIN_ROOT/claude-nights-watch-manager.sh" task >/dev/null 2>&1; then 724 | print_pass "Manager script handles special characters in paths" 725 | else 726 | print_warn "Manager script has issues with special characters" 727 | fi 728 | } 729 | 730 | # Run all tests 731 | main() { 732 | echo "" 733 | echo -e "${BLUE}╔════════════════════════════════════════════════════╗${NC}" 734 | echo -e "${BLUE}║ Claude Nights Watch Plugin - Functional Test ║${NC}" 735 | echo -e "${BLUE}╚════════════════════════════════════════════════════╝${NC}" 736 | echo "" 737 | echo "Plugin Root: $PLUGIN_ROOT" 738 | echo "Test Directory: $TEST_DIR" 739 | echo "" 740 | 741 | # Run all test functions 742 | setup_test_environment 743 | test_plugin_installation 744 | test_command_execution 745 | test_environment_variables 746 | test_core_scripts 747 | test_hook_scripts 748 | test_mcp_server 749 | test_task_processing 750 | test_integration_points 751 | test_error_handling 752 | test_performance_resources 753 | test_real_world_simulation 754 | test_edge_cases 755 | 756 | # Summary 757 | print_header "Functional Test Summary" 758 | 759 | TOTAL=$((PASS + FAIL + WARN)) 760 | 761 | echo -e "${GREEN}Passed:${NC} $PASS" 762 | echo -e "${RED}Failed:${NC} $FAIL" 763 | echo -e "${YELLOW}Warnings:${NC} $WARN" 764 | echo -e "${BLUE}Total:${NC} $TOTAL" 765 | echo "" 766 | 767 | # Calculate success rate 768 | if [ $TOTAL -gt 0 ]; then 769 | SUCCESS_RATE=$((PASS * 100 / TOTAL)) 770 | echo -e "${BLUE}Success Rate:${NC} ${SUCCESS_RATE}%" 771 | else 772 | SUCCESS_RATE=0 773 | echo -e "${YELLOW}No tests completed${NC}" 774 | fi 775 | 776 | echo "" 777 | 778 | # Provide assessment 779 | if [ $FAIL -eq 0 ]; then 780 | echo -e "${GREEN}✅ ALL CRITICAL TESTS PASSED!${NC}" 781 | echo -e "${GREEN}🎉 Plugin functionality is solid${NC}" 782 | if [ $WARN -eq 0 ]; then 783 | echo -e "${GREEN}💯 Perfect functional implementation${NC}" 784 | else 785 | echo -e "${YELLOW}⚠️ Minor issues noted but functionality is good${NC}" 786 | fi 787 | elif [ $FAIL -lt 3 ]; then 788 | echo -e "${YELLOW}⚠️ MOST TESTS PASSED${NC}" 789 | echo -e "${YELLOW}📊 Success rate: ${SUCCESS_RATE}%${NC}" 790 | echo -e "${YELLOW}✅ Plugin should work with minor fixes${NC}" 791 | else 792 | echo -e "${RED}❌ MULTIPLE TESTS FAILED${NC}" 793 | echo -e "${RED}📊 Success rate: ${SUCCESS_RATE}%${NC}" 794 | echo -e "${RED}❌ Plugin needs fixes before use${NC}" 795 | fi 796 | 797 | echo "" 798 | echo -e "${BLUE}Key Findings:${NC}" 799 | if [ $SUCCESS_RATE -ge 90 ]; then 800 | echo " ✅ Plugin structure and functionality are excellent" 801 | echo " ✅ Ready for real-world Claude Code installation" 802 | echo " ✅ All core components working correctly" 803 | elif [ $SUCCESS_RATE -ge 70 ]; then 804 | echo " ⚠️ Plugin mostly functional but needs some fixes" 805 | echo " ✅ Core functionality should work in Claude Code" 806 | echo " ⚠️ Some edge cases may cause issues" 807 | else 808 | echo " ❌ Plugin has significant functional issues" 809 | echo " ❌ Needs substantial fixes before Claude Code use" 810 | echo " ❌ Core functionality may not work properly" 811 | fi 812 | 813 | echo "" 814 | echo -e "${BLUE}Recommended Next Steps:${NC}" 815 | echo " 1. Install in real Claude Code environment" 816 | echo " 2. Test all commands: /nights-watch start, status, logs, etc." 817 | echo " 3. Test agent integration with natural language queries" 818 | echo " 4. Test MCP server with programmatic tool usage" 819 | echo " 5. Validate end-to-end autonomous task execution" 820 | 821 | # Exit with appropriate code 822 | if [ $FAIL -eq 0 ]; then 823 | exit 0 824 | else 825 | exit 1 826 | fi 827 | } 828 | 829 | # Run the test suite 830 | main 831 | 832 | --------------------------------------------------------------------------------