Refactor remaining skills with resources pattern and allowed-tools
- k8s-quick-status: Add scripts/quick-status.sh, allowed-tools - sysadmin-health: Add scripts/health-check.sh, allowed-tools - usage: Add scripts/usage_report.py, simplify SKILL.md - programmer-add-project: Add allowed-tools All skills now: - Have executable scripts for main operations - Use allowed-tools to restrict capabilities - Have improved descriptions with trigger phrases - Follow the "Skill with Bundled Resources" pattern 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,43 +1,61 @@
|
|||||||
---
|
---
|
||||||
name: k8s-quick-status
|
name: k8s-quick-status
|
||||||
description: Quick cluster health pulse check
|
description: Quick cluster health pulse check. Use when asked about cluster status, k8s health, or kubernetes overview.
|
||||||
model: haiku
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
---
|
---
|
||||||
|
|
||||||
# K8s Quick Status Skill
|
# K8s Quick Status Skill
|
||||||
|
|
||||||
Performs a lightweight health pulse check on the Raspberry Pi Kubernetes cluster.
|
Performs a lightweight health pulse check on the Raspberry Pi Kubernetes cluster.
|
||||||
|
|
||||||
|
## Quick Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.claude/skills/k8s-quick-status/scripts/quick-status.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run individual checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Node status
|
||||||
|
kubectl get nodes -o wide
|
||||||
|
|
||||||
|
# Unhealthy pods
|
||||||
|
kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded
|
||||||
|
|
||||||
|
# Warning events
|
||||||
|
kubectl get events -A --field-selector=type=Warning --sort-by='.lastTimestamp' | tail -10
|
||||||
|
|
||||||
|
# ArgoCD apps
|
||||||
|
argocd app list
|
||||||
|
```
|
||||||
|
|
||||||
## Checks Performed
|
## Checks Performed
|
||||||
|
|
||||||
### Node Status
|
| Check | Command | What It Shows |
|
||||||
- Node readiness (`kubectl get nodes`)
|
|-------|---------|---------------|
|
||||||
- Node conditions (MemoryPressure, DiskPressure, PIDPressure)
|
| Nodes | `kubectl get nodes` | Readiness, conditions |
|
||||||
|
| Pods | `kubectl get pods -A` | Unhealthy pods |
|
||||||
### Pod Health
|
| Restarts | jsonpath query | Pods with >5 restarts |
|
||||||
- Unhealthy pods (`kubectl get pods -A --field-selector=status.phase!=Running`)
|
| Events | `kubectl get events` | Recent warnings |
|
||||||
- High restart counts (pods with >5 restarts)
|
| ArgoCD | `argocd app list` | Sync status |
|
||||||
|
|
||||||
### Recent Events
|
|
||||||
- Warning events in last hour (`kubectl get events -A --field-selector=type=Warning --sort-by='.lastTimestamp'`)
|
|
||||||
|
|
||||||
### ArgoCD Status
|
|
||||||
- Application sync status (`argocd app list`)
|
|
||||||
- Any OutOfSync or Degraded apps
|
|
||||||
|
|
||||||
## Output Format
|
## Output Format
|
||||||
|
|
||||||
Report as structured summary with:
|
Report as structured summary:
|
||||||
- Overall status (healthy/warning/critical)
|
- **Overall status**: healthy/warning/critical
|
||||||
- Node summary table
|
- **Node summary**: table of nodes
|
||||||
- Unhealthy pods list (if any)
|
- **Issues**: unhealthy pods, high restarts
|
||||||
- ArgoCD app status
|
- **ArgoCD**: sync status
|
||||||
- Immediate concerns (if any)
|
- **Concerns**: immediate action items
|
||||||
|
|
||||||
## Autonomy
|
|
||||||
|
|
||||||
This skill is read-only and can run without confirmation.
|
|
||||||
|
|
||||||
## When to Use
|
## When to Use
|
||||||
|
|
||||||
Use this skill for a quick pulse check. For comprehensive analysis with metrics and detailed recommendations, use the `/cluster-status` command which invokes the full `cluster-health-check` workflow.
|
- Quick pulse check: use this skill
|
||||||
|
- Comprehensive analysis: use `/cluster-status` command
|
||||||
|
|
||||||
|
## Autonomy
|
||||||
|
|
||||||
|
Read-only - runs without confirmation.
|
||||||
|
|||||||
28
skills/k8s-quick-status/scripts/quick-status.sh
Executable file
28
skills/k8s-quick-status/scripts/quick-status.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Quick Kubernetes cluster status check
|
||||||
|
# Returns structured output for easy parsing
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== Node Status ==="
|
||||||
|
kubectl get nodes -o wide 2>/dev/null || echo "Error: Cannot reach cluster"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Unhealthy Pods ==="
|
||||||
|
kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded 2>/dev/null | grep -v "^NAMESPACE" || echo "All pods healthy"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== High Restart Pods ==="
|
||||||
|
kubectl get pods -A -o jsonpath='{range .items[?(@.status.containerStatuses[0].restartCount>5)]}{.metadata.namespace}/{.metadata.name}: {.status.containerStatuses[0].restartCount} restarts{"\n"}{end}' 2>/dev/null || echo "No high restart pods"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Recent Warning Events ==="
|
||||||
|
kubectl get events -A --field-selector=type=Warning --sort-by='.lastTimestamp' 2>/dev/null | tail -10 || echo "No warning events"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== ArgoCD Apps ==="
|
||||||
|
if command -v argocd &>/dev/null; then
|
||||||
|
argocd app list --output wide 2>/dev/null || echo "ArgoCD not accessible"
|
||||||
|
else
|
||||||
|
echo "ArgoCD CLI not installed"
|
||||||
|
fi
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: programmer-add-project
|
name: programmer-add-project
|
||||||
description: Register a new project with the programmer agent system
|
description: Register a new project with the programmer agent system. Use when adding a new codebase or project.
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Write
|
||||||
---
|
---
|
||||||
|
|
||||||
# Add Project to Programmer Agent
|
# Add Project to Programmer Agent
|
||||||
|
|||||||
@@ -1,51 +1,70 @@
|
|||||||
---
|
---
|
||||||
name: sysadmin-health
|
name: sysadmin-health
|
||||||
description: Run comprehensive health check on Arch Linux workstation
|
description: Comprehensive health check on Arch Linux workstation. Use when asked about system health, disk space, memory, updates, or services.
|
||||||
model: haiku
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
---
|
---
|
||||||
|
|
||||||
# Sysadmin Health Check Skill
|
# Sysadmin Health Check Skill
|
||||||
|
|
||||||
Performs a comprehensive health check on the local Arch Linux workstation.
|
Performs a comprehensive health check on the local Arch Linux workstation.
|
||||||
|
|
||||||
|
## Quick Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.claude/skills/sysadmin-health/scripts/health-check.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run individual checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Resources
|
||||||
|
df -h / /home
|
||||||
|
free -h
|
||||||
|
uptime
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
checkupdates | wc -l
|
||||||
|
yay -Qua | wc -l
|
||||||
|
pacman -Qtdq | wc -l
|
||||||
|
|
||||||
|
# Services
|
||||||
|
systemctl --failed
|
||||||
|
systemctl --user --failed
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
journalctl -p err --since "24 hours ago" -n 10
|
||||||
|
```
|
||||||
|
|
||||||
## Checks Performed
|
## Checks Performed
|
||||||
|
|
||||||
### System Resources
|
| Category | Checks |
|
||||||
- Disk usage (`df -h`)
|
|----------|--------|
|
||||||
- Memory usage (`free -h`)
|
| **Resources** | Disk, memory, swap, CPU load |
|
||||||
- Swap usage
|
| **Packages** | Pacman updates, AUR updates, orphans |
|
||||||
- CPU load (`uptime`)
|
| **Services** | Failed systemd services (system + user) |
|
||||||
- Top processes by resource usage
|
| **Security** | Recent logins, failed SSH attempts |
|
||||||
|
| **Logs** | Recent errors in journal |
|
||||||
### Package Status
|
|
||||||
- Pending pacman updates (`checkupdates`)
|
|
||||||
- AUR updates available (`yay -Qua`)
|
|
||||||
- Orphaned packages (`pacman -Qtdq`)
|
|
||||||
- Package cache size (`du -sh /var/cache/pacman/pkg`)
|
|
||||||
|
|
||||||
### Service Status
|
|
||||||
- Failed systemd services (`systemctl --failed`)
|
|
||||||
- Key services status (NetworkManager, sshd, etc.)
|
|
||||||
|
|
||||||
### User Timers (Claude Automation)
|
|
||||||
- Active user timers (`systemctl --user list-timers`)
|
|
||||||
- Failed user services (`systemctl --user --failed`)
|
|
||||||
- Recent timer runs (`journalctl --user -u k8s-agent-health-check --since "24 hours ago" -n 5`)
|
|
||||||
|
|
||||||
### Security
|
|
||||||
- Recent failed login attempts (`journalctl -u sshd --since "24 hours ago" | grep -i failed`)
|
|
||||||
- Last logins (`last -n 5`)
|
|
||||||
|
|
||||||
### Logs
|
|
||||||
- Recent errors in journal (`journalctl -p err --since "24 hours ago" -n 20`)
|
|
||||||
|
|
||||||
## Output Format
|
## Output Format
|
||||||
|
|
||||||
Report as structured summary with:
|
Report as structured summary:
|
||||||
- Overall health status (healthy/warning/critical)
|
- **Overall health**: healthy/warning/critical
|
||||||
- Issues found (if any)
|
- **Resource usage**: disk %, memory %
|
||||||
- Recommended actions
|
- **Pending updates**: count
|
||||||
|
- **Failed services**: list
|
||||||
|
- **Issues found**: with recommendations
|
||||||
|
|
||||||
|
## Thresholds
|
||||||
|
|
||||||
|
| Metric | Warning | Critical |
|
||||||
|
|--------|---------|----------|
|
||||||
|
| Disk usage | >80% | >95% |
|
||||||
|
| Memory usage | >80% | >95% |
|
||||||
|
| Pending updates | >20 | >50 |
|
||||||
|
| Failed services | any | any critical |
|
||||||
|
|
||||||
## Autonomy
|
## Autonomy
|
||||||
|
|
||||||
This skill is read-only and can run without confirmation.
|
Read-only - runs without confirmation.
|
||||||
|
|||||||
45
skills/sysadmin-health/scripts/health-check.sh
Executable file
45
skills/sysadmin-health/scripts/health-check.sh
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Comprehensive Arch Linux health check
|
||||||
|
# Returns structured output for easy parsing
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== System Resources ==="
|
||||||
|
echo "--- Disk Usage ---"
|
||||||
|
df -h / /home 2>/dev/null || df -h
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--- Memory ---"
|
||||||
|
free -h
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--- Load ---"
|
||||||
|
uptime
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Package Status ==="
|
||||||
|
echo "--- Pending Updates ---"
|
||||||
|
checkupdates 2>/dev/null | wc -l | xargs echo "Pacman updates:"
|
||||||
|
yay -Qua 2>/dev/null | wc -l | xargs echo "AUR updates:" || echo "AUR updates: N/A"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--- Orphaned Packages ---"
|
||||||
|
pacman -Qtdq 2>/dev/null | wc -l | xargs echo "Orphans:" || echo "Orphans: 0"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Service Status ==="
|
||||||
|
echo "--- Failed Services ---"
|
||||||
|
systemctl --failed --no-pager 2>/dev/null || echo "No failed services"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--- Failed User Services ---"
|
||||||
|
systemctl --user --failed --no-pager 2>/dev/null || echo "No failed user services"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Recent Errors ==="
|
||||||
|
journalctl -p err --since "24 hours ago" -n 10 --no-pager 2>/dev/null || echo "No recent errors"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Security ==="
|
||||||
|
echo "--- Recent Logins ---"
|
||||||
|
last -n 5 2>/dev/null || echo "No login history"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: usage
|
name: usage
|
||||||
description: Track and report model usage across sessions
|
description: Track and report model usage across sessions. Use when asked about usage, stats, or session history.
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
@@ -10,13 +10,13 @@ allowed-tools:
|
|||||||
|
|
||||||
Query session history and report usage statistics.
|
Query session history and report usage statistics.
|
||||||
|
|
||||||
## Data Sources
|
## Quick Command
|
||||||
|
|
||||||
| Source | Status | Data Available |
|
```bash
|
||||||
|--------|--------|----------------|
|
python3 ~/.claude/skills/usage/scripts/usage_report.py [range]
|
||||||
| `history/index.json` | Available | Session IDs, start times |
|
```
|
||||||
| `history/*.jsonl` | Future | Session content, commands, tokens |
|
|
||||||
| `usage/config.json` | Available | Log level, preferences |
|
Ranges: `today`, `week` (default), `month`, `all`
|
||||||
|
|
||||||
## Command Routing
|
## Command Routing
|
||||||
|
|
||||||
@@ -27,11 +27,29 @@ Query session history and report usage statistics.
|
|||||||
| `/usage week` | Last 7 days |
|
| `/usage week` | Last 7 days |
|
||||||
| `/usage month` | Last 30 days |
|
| `/usage month` | Last 30 days |
|
||||||
| `/usage all` | All time stats |
|
| `/usage all` | All time stats |
|
||||||
| `/usage --by agent` | Group by agent |
|
|
||||||
| `/usage --by skill` | Group by skill |
|
## Data Sources
|
||||||
| `/usage --by model` | Group by model tier |
|
|
||||||
| `/usage --set-level <level>` | Set log level |
|
| Source | Status | Data |
|
||||||
| `/usage --show-config` | Show current config |
|
|--------|--------|------|
|
||||||
|
| `history/index.json` | Available | Session IDs, start times |
|
||||||
|
| `usage/config.json` | Available | Log level, preferences |
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
```
|
||||||
|
📊 Usage Summary — Week
|
||||||
|
|
||||||
|
Sessions: 12
|
||||||
|
Total time: 8h 32m
|
||||||
|
Days active: 5
|
||||||
|
Model: opus (primary)
|
||||||
|
|
||||||
|
📅 Sessions by Date:
|
||||||
|
2026-01-01: 3 sessions (95m)
|
||||||
|
2025-12-31: 4 sessions (120m)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
## Log Levels
|
## Log Levels
|
||||||
|
|
||||||
@@ -41,111 +59,6 @@ Query session history and report usage statistics.
|
|||||||
| `standard` | + agents, skills, tokens (default) |
|
| `standard` | + agents, skills, tokens (default) |
|
||||||
| `detailed` | + commands, delegations, errors |
|
| `detailed` | + commands, delegations, errors |
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
### Query Sessions
|
|
||||||
|
|
||||||
```python
|
|
||||||
import json
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from pathlib import Path
|
|
||||||
from zoneinfo import ZoneInfo
|
|
||||||
|
|
||||||
LOCAL_TZ = ZoneInfo('America/Los_Angeles')
|
|
||||||
HISTORY_DIR = Path.home() / ".claude/state/personal-assistant/history"
|
|
||||||
CONFIG_PATH = Path.home() / ".claude/state/usage/config.json"
|
|
||||||
|
|
||||||
def load_sessions():
|
|
||||||
index_path = HISTORY_DIR / "index.json"
|
|
||||||
if not index_path.exists():
|
|
||||||
return []
|
|
||||||
with open(index_path) as f:
|
|
||||||
data = json.load(f)
|
|
||||||
return data.get("sessions", [])
|
|
||||||
|
|
||||||
def filter_sessions(sessions, range_type="week"):
|
|
||||||
now = datetime.now(LOCAL_TZ)
|
|
||||||
if range_type == "today":
|
|
||||||
cutoff = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
elif range_type == "week":
|
|
||||||
cutoff = now - timedelta(days=7)
|
|
||||||
elif range_type == "month":
|
|
||||||
cutoff = now - timedelta(days=30)
|
|
||||||
else: # all
|
|
||||||
return sessions
|
|
||||||
|
|
||||||
filtered = []
|
|
||||||
for s in sessions:
|
|
||||||
started = datetime.fromisoformat(s["started"])
|
|
||||||
if started >= cutoff:
|
|
||||||
filtered.append(s)
|
|
||||||
return filtered
|
|
||||||
|
|
||||||
def estimate_duration(sessions):
|
|
||||||
"""Estimate session duration from consecutive start times."""
|
|
||||||
if not sessions:
|
|
||||||
return []
|
|
||||||
|
|
||||||
sorted_sessions = sorted(sessions, key=lambda s: s["started"])
|
|
||||||
for i, session in enumerate(sorted_sessions):
|
|
||||||
if i + 1 < len(sorted_sessions):
|
|
||||||
start = datetime.fromisoformat(session["started"])
|
|
||||||
next_start = datetime.fromisoformat(sorted_sessions[i + 1]["started"])
|
|
||||||
duration = (next_start - start).total_seconds() / 60
|
|
||||||
# Cap at 2 hours (likely session gap)
|
|
||||||
session["duration_mins"] = min(duration, 120)
|
|
||||||
else:
|
|
||||||
# Current/last session: estimate 30 mins
|
|
||||||
session["duration_mins"] = 30
|
|
||||||
return sorted_sessions
|
|
||||||
```
|
|
||||||
|
|
||||||
### Generate Report
|
|
||||||
|
|
||||||
```python
|
|
||||||
def generate_report(sessions, range_type="week"):
|
|
||||||
total_mins = sum(s.get("duration_mins", 0) for s in sessions)
|
|
||||||
hours = int(total_mins // 60)
|
|
||||||
mins = int(total_mins % 60)
|
|
||||||
|
|
||||||
report = f"""
|
|
||||||
📊 Usage Summary — {range_type.title()}
|
|
||||||
|
|
||||||
Sessions: {len(sessions)}
|
|
||||||
Total time: {hours}h {mins}m
|
|
||||||
Model: opus (primary)
|
|
||||||
"""
|
|
||||||
return report
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Format
|
|
||||||
|
|
||||||
```
|
|
||||||
📊 Usage Summary — Last 7 Days
|
|
||||||
|
|
||||||
Sessions: 12
|
|
||||||
Total time: 8h 32m
|
|
||||||
Model: opus (primary)
|
|
||||||
|
|
||||||
┌─────────────┬──────────┬────────┐
|
|
||||||
│ Agent │ Sessions │ Time │
|
|
||||||
├─────────────┼──────────┼────────┤
|
|
||||||
│ PA │ 12 │ 8h 32m │
|
|
||||||
└─────────────┴──────────┴────────┘
|
|
||||||
|
|
||||||
Note: Detailed metrics available when session
|
|
||||||
content logging is enabled.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
|
|
||||||
When `history/*.jsonl` files are available:
|
|
||||||
- Parse actual commands used
|
|
||||||
- Detect skill invocations
|
|
||||||
- Count delegations (sonnet/haiku)
|
|
||||||
- Estimate token usage
|
|
||||||
- Track errors
|
|
||||||
|
|
||||||
## Policy
|
## Policy
|
||||||
|
|
||||||
- Read-only operations
|
- Read-only operations
|
||||||
|
|||||||
109
skills/usage/scripts/usage_report.py
Executable file
109
skills/usage/scripts/usage_report.py
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate usage statistics from session history."""
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
LOCAL_TZ = ZoneInfo('America/Los_Angeles')
|
||||||
|
HISTORY_DIR = Path.home() / ".claude/state/personal-assistant/history"
|
||||||
|
CONFIG_PATH = Path.home() / ".claude/state/usage/config.json"
|
||||||
|
|
||||||
|
|
||||||
|
def load_sessions():
|
||||||
|
"""Load sessions from history index."""
|
||||||
|
index_path = HISTORY_DIR / "index.json"
|
||||||
|
if not index_path.exists():
|
||||||
|
return []
|
||||||
|
with open(index_path) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data.get("sessions", [])
|
||||||
|
|
||||||
|
|
||||||
|
def filter_sessions(sessions, range_type="week"):
|
||||||
|
"""Filter sessions by time range."""
|
||||||
|
now = datetime.now(LOCAL_TZ)
|
||||||
|
if range_type == "today":
|
||||||
|
cutoff = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
elif range_type == "week":
|
||||||
|
cutoff = now - timedelta(days=7)
|
||||||
|
elif range_type == "month":
|
||||||
|
cutoff = now - timedelta(days=30)
|
||||||
|
else: # all
|
||||||
|
return sessions
|
||||||
|
|
||||||
|
filtered = []
|
||||||
|
for s in sessions:
|
||||||
|
try:
|
||||||
|
started = datetime.fromisoformat(s["started"])
|
||||||
|
if started.tzinfo is None:
|
||||||
|
started = started.replace(tzinfo=LOCAL_TZ)
|
||||||
|
if started >= cutoff:
|
||||||
|
filtered.append(s)
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
continue
|
||||||
|
return filtered
|
||||||
|
|
||||||
|
|
||||||
|
def estimate_duration(sessions):
|
||||||
|
"""Estimate session duration from consecutive start times."""
|
||||||
|
if not sessions:
|
||||||
|
return []
|
||||||
|
|
||||||
|
sorted_sessions = sorted(sessions, key=lambda s: s["started"])
|
||||||
|
for i, session in enumerate(sorted_sessions):
|
||||||
|
if i + 1 < len(sorted_sessions):
|
||||||
|
start = datetime.fromisoformat(session["started"])
|
||||||
|
next_start = datetime.fromisoformat(sorted_sessions[i + 1]["started"])
|
||||||
|
duration = (next_start - start).total_seconds() / 60
|
||||||
|
# Cap at 2 hours (likely session gap)
|
||||||
|
session["duration_mins"] = min(duration, 120)
|
||||||
|
else:
|
||||||
|
# Current/last session: estimate 30 mins
|
||||||
|
session["duration_mins"] = 30
|
||||||
|
return sorted_sessions
|
||||||
|
|
||||||
|
|
||||||
|
def generate_report(sessions, range_type="week"):
|
||||||
|
"""Generate usage report."""
|
||||||
|
sessions = estimate_duration(sessions)
|
||||||
|
total_mins = sum(s.get("duration_mins", 0) for s in sessions)
|
||||||
|
hours = int(total_mins // 60)
|
||||||
|
mins = int(total_mins % 60)
|
||||||
|
|
||||||
|
# Group by date
|
||||||
|
by_date = {}
|
||||||
|
for s in sessions:
|
||||||
|
date = s["started"][:10]
|
||||||
|
by_date.setdefault(date, []).append(s)
|
||||||
|
|
||||||
|
report = f"""📊 Usage Summary — {range_type.title()}
|
||||||
|
|
||||||
|
Sessions: {len(sessions)}
|
||||||
|
Total time: {hours}h {mins}m
|
||||||
|
Days active: {len(by_date)}
|
||||||
|
Model: opus (primary)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if by_date:
|
||||||
|
report += "\n📅 Sessions by Date:\n"
|
||||||
|
for date in sorted(by_date.keys(), reverse=True)[:7]:
|
||||||
|
count = len(by_date[date])
|
||||||
|
day_mins = sum(s.get("duration_mins", 0) for s in by_date[date])
|
||||||
|
report += f" {date}: {count} sessions ({int(day_mins)}m)\n"
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
range_type = sys.argv[1] if len(sys.argv) > 1 else "week"
|
||||||
|
|
||||||
|
sessions = load_sessions()
|
||||||
|
filtered = filter_sessions(sessions, range_type)
|
||||||
|
report = generate_report(filtered, range_type)
|
||||||
|
print(report)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user