
Task Status
- 7 installs
- 82 repo stars
- Updated August 2, 2026
- aaaaqwq/agi-super-skills
task-status is a Claude Code skill that sends short progress, success, error, and warning updates during long-running tasks, including automated periodic heartbeats.
About
task-status is a Claude Code skill for reporting progress during long-running agent tasks. It provides scripts to send manual status messages and an automated monitor that emits heartbeat updates on an interval. Status types cover progress, success, error, and warning, with guidance on when to use manual versus periodic updates.
- Sends short status updates during long-running tasks
- Periodic monitor emits heartbeat updates every 5 seconds
- Status templates for progress, success, error, and warning
Task Status by the numbers
- 7 all-time installs (skills.sh)
- Ranked #2,273 of 3,280 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
task-status capabilities & compatibility
Free; runs local Python scripts.
- Capabilities
- status reporting · task monitoring · progress notifications
- Use cases
- orchestration · project management
- Platforms
- Windows
- Pricing
- Free
What task-status says it does
Send short status descriptions in chat for long-running tasks.
Monitor will send "Still working..." updates every 5 seconds
npx skills add https://github.com/aaaaqwq/agi-super-skills --skill task-statusAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 7 |
|---|---|
| repo stars | ★ 82 |
| Last updated | August 2, 2026 |
| Repository | aaaaqwq/agi-super-skills ↗ |
What it does
Use when an agent needs to send periodic progress updates, confirm completion, or notify of failures during a multi-step task.
Who is it for?
Providing periodic status updates and completion or failure notifications during long agent tasks.
When should I use this skill?
When you need to report progress, confirm completion, or notify of a failure during a multi-step operation.
What you get
Consistent progress, success, error, and warning updates surfaced throughout a long-running task.
- status updates
- periodic task monitor
By the numbers
- Heartbeat updates every 5 seconds by default
- 4 status types: progress, success, error, warning
- Status messages kept under 140 characters
Files
Task Status Skill
Quick Start
Manual Status Updates
python scripts/send_status.py "Starting data fetch..." "progress" "step1"
python scripts/send_status.py "Processing complete" "success" "final"
python scripts/send_status.py "Error: Missing API key" "error" "auth"Automatic Periodic Monitoring (Every 5 seconds)
# Start monitoring a long-running task
python scripts/monitor_task.py start "My Long Task" "processing"
# Monitor will send "Still working..." updates every 5 seconds
# When task completes, report final status
python scripts/monitor_task.py stop "My Long Task" "success" "Completed successfully!"Status Types
- progress: Ongoing work (shows 🔄 or ->)
- success: Task complete (shows ✅ or OK)
- error: Failed task (shows ❌ or !)
- warning: Issue but continuing (shows ⚠️ or ?)
Periodic Monitoring
The monitor_task.py script provides automatic updates:
Starting Monitor
python scripts/monitor_task.py start "<task_name>" "<status_type>" [--interval <seconds>]- Automatically sends "Still working..." updates every 5 seconds
- Runs in background until stopped
- Can be customized with different intervals
Stopping Monitor
python scripts/monitor_task.py stop "<task_name>" "<final_status>" "<final_message>"Example: Long File Processing
# Start monitoring
python scripts/monitor_task.py start "video_processing" "progress"
# ... long processing happens here ...
# Stop with final status
python scripts/monitor_task.py stop "video_processing" "success" "Processing complete!"Manual Updates (Quick Status)
For single status updates without monitoring:
python scripts/send_status.py "Still fetching data..." "progress" "fetch"
python scripts/send_status.py "Processing records: 250/1000" "progress" "process"
python scripts/send_status.py "Complete! 3 files ready" "success" "final"
python scripts/send_status.py "Error: Connection timeout" "error" "api"When to Use Each Method
Use Manual Updates When:
- Task is short (under 30 seconds)
- You want control over when updates are sent
- Task has discrete, meaningful milestones
Use Periodic Monitoring When:
- Task is long-running (over 1 minute)
- You want consistent "heartbeat" updates every 5 seconds
- Task has long periods of quiet work
- You want to reassure user that work is ongoing
Message Guidelines
Keep status messages under 140 characters. Examples:
- Progress: "Still fetching data..." or "Processing records: 250/1000"
- Success: "Complete! 3 files ready" or "Task finished successfully"
- Error: "Error: Connection timeout" or "Failed: Missing API key"
- Warning: "Continuing despite timeout" or "Partial success: 5/10 files"
Advanced Usage
With Additional Details
python scripts/send_status.py "Uploading..." "progress" "upload" --details "File: report.pdf (2.4MB)"Different Intervals
python scripts/monitor_task.py start "data_sync" "progress" --interval 10Importing for Python Scripts
from send_status import send_status
def long_task():
send_status("Starting...", "progress", "step1")
# ... work
send_status("Step complete", "success", "step1")Automation with Clawdbot Cron
For scheduled tasks, use Clawdbot's cron feature:
# In a script or session
from cron import add
# Every 5 seconds, check status
job = {
"text": "Check status update",
"interval": "5s",
"enabled": True
}
add(job)This allows status updates even when you're not actively watching.
Installation
To use this skill, copy the task-status folder into your Clawdbot skills directory:
C:\Users\Luffy\AppData\Roaming\npm\node_modules\clawdbot\skills\task-statusOr add it to your workspace and reference it from AGENTS.md or TOOLS.md.
Once installed, the skill will be available for any task where you need periodic status updates.
{
"ownerId": "kn7e5frkfxx996cjqrkhryj95x7zx5we",
"slug": "task-status",
"version": "1.0.0",
"publishedAt": 1769504488946
}{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "task-status",
"installedVersion": "1.0.0",
"installedAt": 1770443033092
}
Task Status Skill
A Clawdbot skill for sending short status descriptions in chat, with automatic periodic monitoring that updates every 5 seconds.
Quick Start
Install into Clawdbot
# Copy to Clawdbot skills directory
copy task-status "C:\Users\Luffy\AppData\Roaming\npm\node_modules\clawdbot\skills\task-status"Usage
Manual Updates (Single Status Messages)
python scripts/send_status.py "Still working..." "progress" "task_name"
python scripts/send_status.py "Complete!" "success" "task_name"
python scripts/send_status.py "Error!" "error" "task_name"Automatic Periodic Monitoring (Every 5 seconds)
# Start monitoring before your long task
python scripts/monitor_task.py start "my_long_task" "progress"
# Your long running task/process here...
# Monitor sends "Still working..." updates every 5 seconds automatically
# Stop monitoring with final status
python scripts/monitor_task.py stop "my_long_task" "success" "Task complete!"Features
- Manual Status Updates: Send one-off status messages
- Automatic Monitoring: Periodic "heartbeat" updates every 5 seconds
- ASCII Fallback: Uses ASCII symbols (->, OK, !, ?) on Windows CMD
- Emoji Support: Uses emojis (🔄, ✅, ❌, ⚠️) on Windows Terminal/PowerShell
- Background Monitoring: Runs independently until stopped
- State Management: Tracks active monitors in
.task_status_state.json
Status Types
| Type | Emoji | ASCII | Use Case |
|---|---|---|---|
| progress | 🔄 | -> | Ongoing work |
| success | ✅ | OK | Completed successfully |
| error | ❌ | ! | Failed, cannot continue |
| warning | ⚠️ | ? | Issue but continuing |
Examples
Long File Processing
# Start monitor
python monitor_task.py start "video_convert" "progress"
# Convert video (takes 5 minutes)
ffmpeg -i input.mp4 output.mp4
# Stop monitor
python monitor_task.py stop "video_convert" "success" "Conversion complete"Database Migration
# Start monitor with 10-second interval
python monitor_task.py start "db_migration" "progress" --interval 10
# Run migration
python migrate_db.py
# Stop monitor
python monitor_task.py stop "db_migration" "success" "Migrated 50,000 records"API Rate Limiting
# Start monitor
python monitor_task.py start "api_sync" "progress"
# Make 1000 API calls (takes 10 minutes)
python sync_api.py
# Stop monitor
python monitor_task.py stop "api_sync" "success" "All calls successful"Monitoring Commands
monitor_task.py start <name> <status> [--interval <seconds>]- Start monitoringmonitor_task.py stop <name> <status> <message>- Stop and send final statusmonitor_task.py status- View active monitorsmonitor_task.py cancel_all- Cancel all monitors (no final status)
File Structure
task-status/
├── SKILL.md # Skill metadata and documentation
├── references/
│ └── usage.md # Detailed usage guide
├── scripts/
│ ├── send_status.py # Manual status updates
│ └── monitor_task.py # Automatic periodic monitoring
├── .task_status_state.json # Active monitor state (generated)
└── README.md # This fileIntegration with Clawdbot
Add to your workspace in AGENTS.md or TOOLS.md:
### Task Status
- Manual updates: `python scripts/send_status.py "message" "type" "step"`
- Auto monitoring: `python monitor_task.py start "task" "progress"`
- Periodic updates: Every 5 seconds automaticallyTips
1. Short Messages: Keep status messages under 140 characters 2. Specific Names: Use descriptive task names for clarity 3. Always Stop: Remember to stop the monitor with final status 4. Check Status: Use monitor_task.py status to see active monitors 5. Cleanup: Use cancel_all if monitors get stuck
Troubleshooting
- Monitor stuck: Run
python monitor_task.py cancel_all - No output: Check if monitor is running with
statuscommand - Encoding issues: ASCII fallback will be used automatically
- Task done but monitor still running: Stop it manually with
stopcommand
Task Status Usage Guide
Quick Examples
Manual Status Updates (Single Messages)
# Progress update
python send_status.py "Fetching data..." "progress" "fetch"
# Success update
python send_status.py "Done! 150 records processed" "success" "process"
# Error update
python send_status.py "Failed to connect to API" "error" "api_call"
# Warning update
python send_status.py "Continuing despite timeout" "warning" "timeout"Automated Periodic Monitoring (Every 5 seconds)
# Start monitoring a long task
python monitor_task.py start "video_processing" "progress"
# Monitor runs in background, sending "Still working..." updates every 5 seconds
# Stop monitoring with final status
python monitor_task.py stop "video_processing" "success" "Processing complete!"
# Or with an error
python monitor_task.py stop "video_processing" "error" "Failed: Corrupt file"Python Script
from send_status import send_status
def process_data():
send_status("Reading files...", "progress", "read")
# ... work
send_status("Processing complete", "success", "process")Shell Script
#!/bin/bash
python send_status.py "Starting backup..." "progress" "backup"
# ... backup command
python send_status.py "Backup complete" "success" "backup"Status Types
| Type | Emoji | ASCII | When to Use |
|---|---|---|---|
| progress | 🔄 | -> | Ongoing work, "still working on it" |
| success | ✅ | OK | Task completed successfully |
| error | ❌ | ! | Task failed, cannot continue |
| warning | ⚠️ | ? | Issue but continuing |
When to Use Each Method
Use Manual Updates When:
- Task is short (under 30 seconds)
- You want control over when updates are sent
- Task has discrete, meaningful milestones
Use Periodic Monitoring When:
- Task is long-running (over 1 minute)
- You want consistent "heartbeat" updates every 5 seconds
- Task has long periods of quiet work
- You want to reassure user that work is ongoing
Periodic Monitoring Details
Starting a Monitor
python monitor_task.py start "<task_name>" "<status_type>" [--interval <seconds>]- Sends "Still working..." updates every 5 seconds by default
- Runs in background until stopped
- Can customize interval with
--interval
Viewing Active Monitors
python monitor_task.py statusCancelling All Monitors (Without Final Status)
python monitor_task.py cancel_allBest Practices
1. Keep messages short - Under 140 characters 2. Be specific - Include step names for clarity 3. Update periodically - Every ~4 seconds for long tasks (or use monitoring) 4. Use details - Add extra context when needed 5. End with status - Always send final success/error
Common Patterns
Multi-step Task (Manual)
python send_status.py "Step 1/5: Validating input" "progress" "step1"
# ... step 1
python send_status.py "Step 2/5: Processing data" "progress" "step2"
# ... step 2
# ... etc
python send_status.py "All steps complete" "success" "final"Long-Running Task (Automatic Monitoring)
# Start monitor before starting the task
python monitor_task.py start "data_migration" "progress"
# Run the actual task (can take minutes/hours)
# Monitor sends "Still working..." updates every 5 seconds
# When task finishes, stop monitor with final status
python monitor_task.py stop "data_migration" "success" "Migration complete: 5000 records"With Details
python send_status.py "Uploading..." "progress" "upload" --details "File: report.pdf (2.4MB)"Error Recovery
python send_status.py "Connection failed, retrying..." "warning" "retry"
# ... retry logic
if success:
python send_status.py "Retry successful" "success" "retry"
else:
python send_status.py "Retry failed, giving up" "error" "retry"Long Task with Manual Control
# Start monitor
python monitor_task.py start "processing" "progress"
# ... do work ...
# Check status periodically
python monitor_task.py status
# When done, stop monitor
python monitor_task.py stop "processing" "success" "Finished!"Integration
Import send_status for Python Scripts
from send_status import send_status
msg = send_status("Working...", "progress", "work")
print(f"Logged: {msg}") # Output: "-> [work] Working..."Use in Shell Scripts
#!/bin/bash
send_status() {
python send_status.py "$1" "$2" "$3"
}
send_status "Starting process" "progress" "main"
# ... process
send_status "Done" "success" "main"Automation with Clawdbot Cron
For scheduled tasks that need periodic status updates, use Clawdbot's cron feature.
Monitoring Use Cases
File Processing
python monitor_task.py start "file_proc" "progress"
# Process 1000 files (takes 10 minutes)
python monitor_task.py stop "file_proc" "success" "Processed 1000 files"Data Sync
python monitor_task.py start "sync" "progress" --interval 10
# Sync databases (takes 5 minutes)
python monitor_task.py stop "sync" "success" "Sync complete"API Calls
python monitor_task.py start "api_call" "progress"
# Make 1000 API requests
python monitor_task.py stop "api_call" "success" "All 1000 requests successful"#!/usr/bin/env python3
"""
Monitor a long-running task and send periodic status updates every 5 seconds.
Usage:
# Start monitoring
python monitor_task.py start "<task_name>" "<status_type>" [--interval <seconds>]
# Stop monitoring (sends final status)
python monitor_task.py stop "<task_name>" "<final_status>" "<final_message>"
Status Types (for final status):
progress - Ongoing work (shows 🔄)
success - Task complete (shows ✅)
error - Failed task (shows ❌)
warning - Issue but continuing (shows ⚠️)
Examples:
# Start monitoring a video processing task
python monitor_task.py start "video_proc" "progress"
# Later, stop with success
python monitor_task.py stop "video_proc" "success" "Processing complete!"
# Or with an error
python monitor_task.py stop "video_proc" "error" "Failed: Corrupt file"
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
import time
import json
import threading
from pathlib import Path
from datetime import datetime
from send_status import send_status, can_encode_emoji
# State file to track active monitors
# Path: C:\Users\Luffy\clawd\task-status\scripts\monitor_task.py
# Parent: C:\Users\Luffy\clawd\task-status\scripts
# Parent.parent: C:\Users\Luffy\clawd\task-status
STATE_FILE = Path(__file__).parent.parent / ".task_status_state.json"
def load_state():
"""Load active monitors from state file."""
if STATE_FILE.exists():
try:
with open(STATE_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except:
pass
return {}
def save_state(state):
"""Save active monitors to state file."""
with open(STATE_FILE, 'w', encoding='utf-8') as f:
json.dump(state, f, indent=2)
def monitor_worker(task_name: str, interval: int = 5):
"""
Background worker that sends periodic updates.
Runs until stopped via state file check.
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
print(f"[DEBUG] monitor_worker started for '{task_name}' with {interval}s interval", file=sys.stderr)
sys.stderr.flush()
last_update = time.time()
print(f"[DEBUG] Initial last_update: {last_update}", file=sys.stderr)
sys.stderr.flush()
iteration = 0
while True:
iteration += 1
# Check if task still exists in state
state = load_state()
print(f"[DEBUG] Iteration {iteration}: State keys = {list(state.keys())}", file=sys.stderr)
sys.stderr.flush()
print(f"[DEBUG] Checking if '{task_name}' in state: {task_name in state}", file=sys.stderr)
sys.stderr.flush()
if task_name not in state:
print(f"[DEBUG] Task '{task_name}' not in state, stopping worker", file=sys.stderr)
sys.stderr.flush()
# Task was stopped
break
# Send "Still working..." update if enough time has passed
current_time = time.time()
elapsed = current_time - last_update
print(f"[DEBUG] Iteration {iteration}: elapsed={elapsed:.1f}s, interval={interval}s", file=sys.stderr)
sys.stderr.flush()
if elapsed >= interval:
print(f"[DEBUG] Time to send update! elapsed={elapsed:.1f}s >= interval={interval}s", file=sys.stderr)
sys.stderr.flush()
# Send progress update
message = f"Still working..."
status = "progress"
# Try to send status
try:
print(f"[DEBUG] Sending status: {message} {status} {task_name}", file=sys.stderr)
sys.stderr.flush()
send_status(message, status, task_name)
print(f"[DEBUG] Status sent successfully", file=sys.stderr)
sys.stderr.flush()
except Exception as e:
# If send fails, log but continue
print(f"[DEBUG] Monitor warning: {e}", file=sys.stderr)
sys.stderr.flush()
last_update = current_time
print(f"[DEBUG] Updated last_update: {last_update}", file=sys.stderr)
sys.stderr.flush()
else:
print(f"[DEBUG] Not time yet, sleeping...", file=sys.stderr)
sys.stderr.flush()
# Sleep briefly to avoid tight loop (reduced from 0.5 to 0.1)
time.sleep(0.1)
print(f"[DEBUG] monitor_worker finished", file=sys.stderr)
sys.stderr.flush()
def start_monitor(task_name: str, status_type: str = "progress", interval: int = 5):
"""
Start a new monitor for the given task.
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
print(f"[DEBUG] start_monitor called: task_name='{task_name}', status_type='{status_type}', interval={interval}", file=sys.stderr)
# Validate status type
if status_type not in ["progress", "success", "error", "warning"]:
print(f"Invalid status type: {status_type}", file=sys.stderr)
sys.exit(1)
# Check if already monitoring
state = load_state()
print(f"[DEBUG] Current state: {state}", file=sys.stderr)
if task_name in state:
print(f"Already monitoring task '{task_name}'", file=sys.stderr)
sys.exit(1)
# Add to state
state[task_name] = {
"status_type": status_type,
"interval": interval,
"started_at": datetime.now().isoformat()
}
print(f"[DEBUG] Saving new state: {state}", file=sys.stderr)
save_state(state)
# Send initial status
print(f"[DEBUG] Sending initial status", file=sys.stderr)
send_status(f"Monitoring started (updates every {interval}s)", status_type, task_name)
# Start background monitor thread
print(f"[DEBUG] Starting monitor thread", file=sys.stderr)
thread = threading.Thread(
target=monitor_worker,
args=(task_name, interval),
daemon=True
)
thread.start()
print(f"[DEBUG] Monitor thread started: {thread}", file=sys.stderr)
print(f"Monitor started for '{task_name}' with {interval}s interval")
def stop_monitor(task_name: str, final_status: str, final_message: str):
"""
Stop monitoring and send final status.
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
print(f"[DEBUG] stop_monitor called: task_name='{task_name}', final_status='{final_status}', final_message='{final_message}'", file=sys.stderr)
# Check if monitoring
state = load_state()
print(f"[DEBUG] Current state: {state}", file=sys.stderr)
if task_name not in state:
print(f"No active monitor for task '{task_name}'", file=sys.stderr)
sys.exit(1)
# Remove from state
print(f"[DEBUG] Removing '{task_name}' from state", file=sys.stderr)
del state[task_name]
print(f"[DEBUG] New state: {state}", file=sys.stderr)
save_state(state)
# Send final status
print(f"[DEBUG] Sending final status", file=sys.stderr)
send_status(final_message, final_status, task_name)
print(f"Monitor stopped for '{task_name}'")
print(f"Final status: {final_message}")
def main():
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
action = sys.argv[1].lower()
if action == "start":
if len(sys.argv) < 4:
print("Usage: monitor_task.py start <task_name> <status_type> [--interval <seconds>]", file=sys.stderr)
sys.exit(1)
task_name = sys.argv[2]
status_type = sys.argv[3]
interval = 5
# Check for --interval flag
if len(sys.argv) > 5 and sys.argv[4] == "--interval":
try:
interval = int(sys.argv[5])
except ValueError:
print("Interval must be an integer", file=sys.stderr)
sys.exit(1)
start_monitor(task_name, status_type, interval)
elif action == "stop":
if len(sys.argv) < 5:
print("Usage: monitor_task.py stop <task_name> <final_status> <final_message>", file=sys.stderr)
sys.exit(1)
task_name = sys.argv[2]
final_status = sys.argv[3]
final_message = sys.argv[4]
stop_monitor(task_name, final_status, final_message)
elif action == "status":
# Show active monitors
state = load_state()
if not state:
print("No active monitors")
else:
print("Active monitors:")
for task, info in state.items():
print(f" {task}: {info['status_type']} (interval: {info['interval']}s)")
elif action == "cancel_all":
# Stop all monitors without sending final status
state = load_state()
if state:
for task_name in list(state.keys()):
del state[task_name]
save_state(state)
print(f"Cancelled all monitors: {list(state.keys())}")
else:
print("No active monitors to cancel")
else:
print(f"Unknown action: {action}", file=sys.stderr)
print(__doc__)
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Send status messages using Clawdbot WebSocket API (fast).
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
import os
import json
import websocket
# Status type to emoji mapping
STATUS_EMOJIS = {
"progress": "🔄",
"success": "✅",
"error": "❌",
"warning": "⚠️"
}
def send_status(message: str, status_type: str, step_name: str, details: str = None):
"""
Format and send a status message to Telegram via WebSocket.
Args:
message: Short status message (< 140 chars)
status_type: Type of status (progress, success, error, warning)
step_name: Name of the step being reported
details: Optional additional context
"""
if status_type not in STATUS_EMOJIS:
raise ValueError(f"Invalid status_type: {status_type}")
emoji = STATUS_EMOJIS[status_type]
# Build the message
formatted = f"{emoji} [{step_name}] {message}"
if details:
formatted += f" ({details})"
# Keep it concise (under 140 chars)
if len(formatted) > 140:
formatted = formatted[:137] + "..."
# Send via WebSocket
gateway_port = os.environ.get("CLAWDBOT_GATEWAY_PORT", "18789")
gateway_token = os.environ.get("CLAWDBOT_GATEWAY_TOKEN")
target = os.environ.get("TELEGRAM_TARGET", "7590912486")
if not gateway_token:
print(f"✗ CLAWDBOT_GATEWAY_TOKEN not found", file=sys.stderr)
return formatted
ws_url = f"ws://127.0.0.1:{gateway_port}/ws"
try:
# Connect and send
ws = websocket.create_connection(ws_url, timeout=10)
# Handshake
handshake = {"type": "handshake", "token": gateway_token}
ws.send(json.dumps(handshake))
# Send message
msg = {
"type": "message",
"action": "send",
"target": target,
"message": formatted,
"channel": "telegram"
}
ws.send(json.dumps(msg))
# Get response
response = ws.recv()
result = json.loads(response)
ws.close()
# Check if message was sent
if result.get("event") == "message.sent" or result.get("type") == "ack":
return formatted
else:
print(f"⚠️ Response: {result}", file=sys.stderr)
return formatted
except Exception as e:
print(f"✗ WebSocket error: {e}", file=sys.stderr)
# Fallback: print to console
print(formatted, file=sys.stderr)
return formatted
def main():
if len(sys.argv) < 4:
print("Usage: send_status_websocket.py <message> <status_type> <step_name>")
print("\nStatus Types: progress, success, error, warning")
sys.exit(1)
message = sys.argv[1]
status_type = sys.argv[2]
step_name = sys.argv[3]
details = sys.argv[4] if len(sys.argv) > 4 else None
try:
result = send_status(message, status_type, step_name, details)
print(f"✓ Status sent: {result}")
except Exception as e:
print(f"✗ Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Send status messages to Telegram with automatic logging.
This is the production version of send_status.py.
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
import json
import os
import websocket
from datetime import datetime
from pathlib import Path
# Configuration
LOG_DIR = Path("C:/Users/Luffy/clawd/logs")
LOG_FILE = LOG_DIR / "telegram_messages.jsonl"
TASK_LOG_FILE = LOG_DIR / "task_status.jsonl"
# Status type to emoji mapping
STATUS_EMOJIS = {
"progress": "🔄",
"success": "✅",
"error": "❌",
"warning": "⚠️"
}
def ensure_log_dir():
"""Create log directory if it doesn't exist."""
LOG_DIR.mkdir(exist_ok=True)
def log_message(message: str, direction: str = "out", task_name: str = None, status_type: str = None):
"""Log a message to the file."""
ensure_log_dir()
try:
data = {
"timestamp": datetime.now().isoformat(),
"direction": direction,
"message": message
}
if task_name:
data["task"] = task_name
if status_type:
data["status"] = status_type
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(json.dumps(data, ensure_ascii=False) + '\n')
return True
except Exception as e:
print(f"[LOG] Error: {e}", file=sys.stderr)
return False
def can_encode_emoji(text: str, encoding: str = None) -> bool:
"""Check if text can be encoded with the given encoding."""
if encoding is None:
encoding = sys.stdout.encoding
try:
text.encode(encoding)
return True
except (UnicodeEncodeError, LookupError):
return False
def send_status(message: str, status_type: str, step_name: str, details: str = None):
"""
Format and send a status message to Telegram with logging.
Args:
message: Short status message (< 140 chars)
status_type: Type of status (progress, success, error, warning)
step_name: Name of the step being reported
details: Optional additional context
"""
if status_type not in STATUS_EMOJIS:
raise ValueError(f"Invalid status_type: {status_type}")
emoji = STATUS_EMOJIS[status_type]
# Choose emoji or ASCII based on encoding capability
if can_encode_emoji(emoji):
prefix = emoji
else:
prefix = emoji # Most modern terminals support emojis
# Build the message
formatted = f"{prefix} [{step_name}] {message}"
if details:
formatted += f" ({details})"
# Keep it concise (under 140 chars)
if len(formatted) > 140:
formatted = formatted[:137] + "..."
# Log the message (before sending)
log_message(formatted, direction="out", task_name=step_name, status_type=status_type)
# Try WebSocket first (fastest)
gateway_token = os.environ.get("CLAWDBOT_GATEWAY_TOKEN")
if gateway_token:
try:
gateway_port = os.environ.get("CLAWDBOT_GATEWAY_PORT", "18789")
target = os.environ.get("TELEGRAM_TARGET", "7590912486")
ws_url = f"ws://127.0.0.1:{gateway_port}/ws"
# Connect and send
ws = websocket.create_connection(ws_url, timeout=10)
# Send message directly (no handshake needed for simple messages)
msg = {
"type": "message",
"action": "send",
"target": target,
"message": formatted,
"channel": "telegram"
}
ws.send(json.dumps(msg))
# Try to receive response but don't wait too long
try:
response = ws.recv()
result = json.loads(response)
# If we got a challenge, respond to it
if result.get("event") == "connect.challenge":
# Send handshake with token
handshake = {
"type": "handshake",
"token": gateway_token,
"nonce": result.get("payload", {}).get("nonce")
}
ws.send(json.dumps(handshake))
# Try to receive again
response = ws.recv()
result = json.loads(response)
except:
# If we can't receive, assume message was sent
pass
ws.close()
# Log successful send
log_message(formatted, direction="out_sent", task_name=step_name, status_type=status_type)
return formatted
except Exception as e:
print(f"[LOG] WebSocket failed: {e}", file=sys.stderr)
# Fallback: try CLI
import subprocess
import shutil
clawdbot_path = shutil.which("clawdbot")
if not clawdbot_path:
clawdbot_paths = [
"C:\\Users\\Luffy\\AppData\\Roaming\\npm\\clawdbot.cmd",
"C:\\Users\\Luffy\\AppData\\Roaming\\npm\\clawdbot"
]
for path in clawdbot_paths:
if os.path.exists(path):
clawdbot_path = path
break
if clawdbot_path:
try:
target = os.environ.get("TELEGRAM_TARGET", "7590912486")
result = subprocess.run(
[
clawdbot_path,
"message",
"send",
"--target", target,
"--message", formatted,
"--channel", "telegram"
],
capture_output=True,
text=True,
timeout=20
)
if result.returncode == 0:
# Log successful send
log_message(formatted, direction="out_sent", task_name=step_name, status_type=status_type)
return formatted
except Exception as e:
print(f"[LOG] CLI failed: {e}", file=sys.stderr)
# Final fallback: print to console
print(formatted, file=sys.stderr)
log_message(formatted, direction="out_failed", task_name=step_name, status_type=status_type)
return formatted
def main():
if len(sys.argv) < 4:
print("Usage: send_status_with_logging.py <message> <status_type> <step_name>")
print("\nStatus Types: progress, success, error, warning")
sys.exit(1)
message = sys.argv[1]
status_type = sys.argv[2]
step_name = sys.argv[3]
try:
result = send_status(message, status_type, step_name)
print(f"✓ Sent and logged: {result}")
except Exception as e:
print(f"✗ Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Send status messages for long-running tasks.
Usage:
python send_status.py "<message>" "<status_type>" "<step_name>" [--details "<details>"]
Status Types:
progress - Ongoing work (shows 🔄)
success - Task complete (shows ✅)
error - Failed task (shows ❌)
warning - Issue but continuing (shows ⚠️)
Example:
python send_status.py "Fetching data..." "progress" "fetch"
python send_status.py "Complete!" "success" "final"
python send_status.py "Error: Missing file" "error" "file_check"
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
import json
import os
import websocket
# Status type to emoji mapping
STATUS_EMOJIS = {
"progress": "🔄",
"success": "✅",
"error": "❌",
"warning": "⚠️"
}
def can_encode_emoji(text: str, encoding: str = None) -> bool:
"""Check if text can be encoded with the given encoding."""
if encoding is None:
encoding = sys.stdout.encoding
try:
text.encode(encoding)
return True
except (UnicodeEncodeError, LookupError):
return False
def send_status(message: str, status_type: str, step_name: str, details: str = None):
"""
Format and send a status message to Telegram.
Args:
message: Short status message (< 140 chars)
status_type: Type of status (progress, success, error, warning)
step_name: Name of the step being reported
details: Optional additional context
"""
if status_type not in STATUS_EMOJIS:
raise ValueError(f"Invalid status_type: {status_type}")
emoji = STATUS_EMOJIS[status_type]
# Choose emoji or ASCII based on encoding capability
if can_encode_emoji(emoji):
prefix = emoji
else:
prefix = emoji # Use emoji anyway, most modern terminals support it
# Build the message
formatted = f"{prefix} [{step_name}] {message}"
if details:
formatted += f" ({details})"
# Keep it concise (under 140 chars)
if len(formatted) > 140:
formatted = formatted[:137] + "..."
# Try WebSocket first (fastest)
gateway_token = os.environ.get("CLAWDBOT_GATEWAY_TOKEN")
if gateway_token:
try:
gateway_port = os.environ.get("CLAWDBOT_GATEWAY_PORT", "18789")
target = os.environ.get("TELEGRAM_TARGET", "7590912486")
ws_url = f"ws://127.0.0.1:{gateway_port}/ws"
# Connect and send
ws = websocket.create_connection(ws_url, timeout=10)
# Send message directly (no handshake needed for simple messages)
msg = {
"type": "message",
"action": "send",
"target": target,
"message": formatted,
"channel": "telegram"
}
ws.send(json.dumps(msg))
# Try to receive response but don't wait too long
try:
response = ws.recv()
result = json.loads(response)
# If we got a challenge, respond to it
if result.get("event") == "connect.challenge":
# Send handshake with token
handshake = {"type": "handshake", "token": gateway_token}
ws.send(json.dumps(handshake))
# Try to receive again
response = ws.recv()
result = json.loads(response)
except:
# If we can't receive, assume message was sent
pass
ws.close()
return formatted
except Exception as e:
print(f"⚠️ WebSocket failed: {e}", file=sys.stderr)
# Fallback: try CLI
import subprocess
import shutil
clawdbot_path = shutil.which("clawdbot")
if not clawdbot_path:
clawdbot_paths = [
"C:\\Users\\Luffy\\AppData\\Roaming\\npm\\clawdbot.cmd",
"C:\\Users\\Luffy\\AppData\\Roaming\\npm\\clawdbot"
]
for path in clawdbot_paths:
if os.path.exists(path):
clawdbot_path = path
break
if clawdbot_path:
try:
target = os.environ.get("TELEGRAM_TARGET", "7590912486")
result = subprocess.run(
[
clawdbot_path,
"message",
"send",
"--target", target,
"--message", formatted,
"--channel", "telegram"
],
capture_output=True,
text=True,
timeout=20
)
if result.returncode == 0:
return formatted
except Exception as e:
print(f"⚠️ CLI failed: {e}", file=sys.stderr)
# Final fallback: print to console
print(formatted, file=sys.stderr)
return formatted
def main():
if len(sys.argv) < 4:
print(__doc__)
sys.exit(1)
message = sys.argv[1]
status_type = sys.argv[2]
step_name = sys.argv[3]
details = sys.argv[4] if len(sys.argv) > 4 else None
try:
result = send_status(message, status_type, step_name, details)
print(f"Status sent: {result}")
except Exception as e:
print(f"Error sending status: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Test script for send_status.py
"""
import sys
import subprocess
from pathlib import Path
def test_send_status():
"""Test the status sending functionality."""
script_path = Path(__file__).parent / "send_status.py"
# Test cases
test_cases = [
("Test progress message", "progress", "test_step", None),
("Test success message", "success", "test_step", None),
("Test error message", "error", "test_step", None),
("Test warning message", "warning", "test_step", None),
("Long message test", "progress", "step1", "with details about the step"),
]
print("Testing send_status.py...")
print("=" * 50)
for message, status_type, step_name, details in test_cases:
print(f"\nTest: {message}")
print(f" Type: {status_type}, Step: {step_name}")
# Build command
cmd = [sys.executable, str(script_path), message, status_type, step_name]
if details:
cmd.append(details)
try:
# Run the command
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
print(f" PASS: Success")
output = result.stdout.strip()
if output:
print(f" Output: {output}")
else:
print(f" FAIL: Failed with return code {result.returncode}")
if result.stderr:
print(f" Error: {result.stderr.strip()}")
except subprocess.TimeoutExpired:
print(f" FAIL: Timed out")
except Exception as e:
print(f" FAIL: Exception: {e}")
print("\n" + "=" * 50)
print("Testing complete")
if __name__ == "__main__":
test_send_status()
Related skills
FAQ
How often can task-status send heartbeat updates?
Its monitor_task.py sends 'Still working...' updates every 5 seconds by default, and the interval is configurable.
What status types does it support?
progress, success, error, and warning, each with its own template and icon.