Initial commit: Hermes Agent Skills collection
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
---
|
||||
name: cronjob
|
||||
description: Create, manage, update, run, and troubleshoot Hermes Agent cronjobs — scheduling, scripts, prompts, delivery, and state management.
|
||||
version: 1.0.0
|
||||
tags: [Cronjob, Automation, Scheduling]
|
||||
---
|
||||
|
||||
# Cronjob System
|
||||
|
||||
## Core Concepts
|
||||
|
||||
Cronjobs can be of two types:
|
||||
1. **Agent jobs** — run via an agent with a prompt, model, and skills (use a `skill` or `skills` parameter)
|
||||
2. **Direct script jobs** (`no_agent: true`) — execute a shell/Python script directly
|
||||
|
||||
## Script Jobs: THE CRITICAL PATH RULE
|
||||
|
||||
**`script` parameter expects a FILENAME (relative to `~/.hermes/scripts/`), NOT raw shell content.**
|
||||
|
||||
This is the most common cronjob error. The system treats the `script` value as a path to a file under `~/.hermes/scripts/`.
|
||||
|
||||
```
|
||||
❌ WRONG: script: "cd ~/.hermes/memory\nCHANGES=$(git diff..." (raw multi-line content)
|
||||
✅ RIGHT: script: "memory-sync.sh" (filename only)
|
||||
```
|
||||
|
||||
**Always create the script file first, then reference it:**
|
||||
```bash
|
||||
# 1. Write the script
|
||||
write_file path=~/.hermes/scripts/my-script.sh content='...'
|
||||
|
||||
# 2. Create the cronjob referencing the filename
|
||||
cronjob action=create script=my-script.sh ...
|
||||
```
|
||||
|
||||
## Creating Cronjobs
|
||||
|
||||
```bash
|
||||
# Agent job (runs via an AI agent with a prompt)
|
||||
cronjob action=create \
|
||||
name="My Job" \
|
||||
prompt="You are a helper. Do X." \
|
||||
schedule="0 9 * * *" \
|
||||
repeat="forever" \
|
||||
deliver="origin" \
|
||||
model="custom/llama/model.gguf" \
|
||||
skills="my-skill"
|
||||
|
||||
# Script job (runs a shell/Python script directly, no agent)
|
||||
cronjob action=create \
|
||||
name="My Script Job" \
|
||||
script="my-script.sh" \
|
||||
schedule="0 22 * * *" \
|
||||
repeat="forever" \
|
||||
deliver="origin" \
|
||||
no_agent=true
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Description | Required |
|
||||
|-----------|-------------|----------|
|
||||
| `name` | Human-readable job name | Yes |
|
||||
| `prompt` | Instruction for agent (agent jobs) | Yes, unless `no_agent` |
|
||||
| `script` | Filename in `~/.hermes/scripts/` (script jobs) | Yes, if `no_agent` |
|
||||
| `schedule` | Cron expression (e.g. `"0 9 * * *"`) | Yes |
|
||||
| `repeat` | `"forever"`, `"13/500"`, `"1/10"` | No (default: 1) |
|
||||
| `deliver` | `"origin"` or `telegram:<chat_id>` | No (default: origin) |
|
||||
| `model` | Model ID for agent jobs | No |
|
||||
| `provider` | Provider name | No |
|
||||
| `base_url` | API base URL | No |
|
||||
| `skills` | Space-separated skill names for agent | No |
|
||||
| `no_agent` | `true` for direct script execution | No |
|
||||
|
||||
## Managing Cronjobs
|
||||
|
||||
```bash
|
||||
# List all jobs
|
||||
cronjob action=list
|
||||
|
||||
# Update a job (change any parameter)
|
||||
cronjob action=update job_id=<ID> schedule="0 6 * * *"
|
||||
|
||||
# Pause a job (keep it but stop scheduling)
|
||||
cronjob action=update job_id=<ID> schedule=never
|
||||
|
||||
# Resume a paused job
|
||||
cronjob action=update job_id=<ID> schedule="0 6 * * *"
|
||||
|
||||
# Run a job immediately (one-shot)
|
||||
cronjob action=run job_id=<ID>
|
||||
|
||||
# Delete a job
|
||||
cronjob action=remove job_id=<ID>
|
||||
```
|
||||
|
||||
## Delivery Destinations
|
||||
|
||||
- `origin` — delivers results to the current conversation thread
|
||||
- `telegram:<chat_id>` — delivers results directly to a Telegram chat (e.g., agent chat)
|
||||
|
||||
## Prompt Design for Agent Jobs
|
||||
|
||||
The prompt must be **fully self-contained** — it runs without user context:
|
||||
|
||||
```
|
||||
Du bist ein [role]. Deine Aufgabe: [specific task].
|
||||
|
||||
Schritt 1: [action]
|
||||
Schritt 2: [action]
|
||||
Schritt 3: [action]
|
||||
|
||||
⚠️ Wichtige Hinweise: [caveats, constraints, common errors]
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start daily** for testing before switching to longer intervals
|
||||
2. **Use `repeat` wisely** — `"forever"` for permanent jobs, `"N/M"` for limited runs (N=attempts, M=max runs)
|
||||
3. **Include error handling** in scripts — always check return codes
|
||||
4. **Log results** — output a summary so you can verify operations
|
||||
5. **Keep prompts self-contained** — no references to previous conversation context
|
||||
6. **Use `deliver="origin"`** for your own notifications, `telegram:<id>` for agent delegation
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
| Pitfall | Symptom | Solution |
|
||||
|---------|---------|----------|
|
||||
| Raw shell in `script` | "Script not found: cd ~/.hermes/..." | Write script file first, reference filename only |
|
||||
| Forget `no_agent=true` | Agent tries to run script as a prompt | Set `no_agent: true` for direct script execution |
|
||||
| Missing script file | "Script not found: my-script.sh" | Ensure file exists at `~/.hermes/scripts/my-script.sh` |
|
||||
| Script not executable | Permission denied | `chmod +x ~/.hermes/scripts/my-script.sh` |
|
||||
| Cronjob runs too often | Rate limits, performance issues | Use `every 15m` minimum, or daily schedule |
|
||||
| Wrong deliver target | Results go to wrong chat | `origin` = this chat, `telegram:<id>` = target chat |
|
||||
| Script path is absolute | Works in test, fails in cron | Use relative filename, not absolute path |
|
||||
| JSON parsing fails in prompt | Non-JSON noise from tools | Use regex fallback (e.g., `[`...`]` pattern) |
|
||||
| No LLM provider configured | RuntimeError: No LLM provider configured | Set model/provider via `hermes model` or `hermes config set model.provider <provider>`; then update job with `cronjob action=update job_id=<ID> model=<model> provider=<provider>` |
|
||||
| **Deleting while awaiting confirmation** | User said "delete X", you asked "also delete Y?", then deleted Y BEFORE user answered | ALWAYS wait for explicit user confirmation before executing any destructive action (delete, remove, stop). Never fire off a destructive tool call while a confirmation question is pending. |
|
||||
| Script outputs nothing in Telegram | Cronjob runs but message is empty | Script MUST print to stdout — anything printed becomes the delivered message body. Log messages to stderr; user-facing output to stdout. |
|
||||
| **Response truncated due to output length limit** | Agent cronjob fails with `RuntimeError: Response truncated due to output length limit` | Agent cronjobs have a hard output size limit. ALWAYS constrain the agent's output in the prompt: "max 20 lines", "compact summary only", "no explanations". For structured data, output a brief table or key-value list rather than full prose or markdown documents. |
|
||||
| **Telegram group sub-names as delivery targets** | `telegram:Essensplaner` not found even though the group exists | Telegram `deliver` targets must match the actual chat/group identifier known to the system (`telegram:Schoen Inc`), NOT internal nicknames or role names used within that group. Verify available targets with `send_message(action='list')` or `cronjob action=list` and inspect `deliver` fields of existing jobs. |
|
||||
|
||||
## Script Job: Full Example
|
||||
|
||||
```bash
|
||||
# 1. Write the script file
|
||||
write_file path=~/.hermes/scripts/memory-sync.sh content='#!/bin/bash
|
||||
cd ~/.hermes/memory
|
||||
CHANGES=$(git diff --quiet HEAD 2>/dev/null; echo $?)
|
||||
if [ "$CHANGES" = "0" ]; then
|
||||
exit 0
|
||||
fi
|
||||
git add .
|
||||
git commit -m "Auto-sync: $(date +%Y-%m-%d)" --allow-empty 2>/dev/null
|
||||
RESULT=$(git push origin main 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Memory synced successfully"
|
||||
else
|
||||
echo "⚠️ Git push failed: $RESULT"
|
||||
fi
|
||||
'
|
||||
```
|
||||
|
||||
| Pitfall | Symptom | Solution |
|
||||
|---------|---------|----------|
|
||||
| Raw shell in `script` | "Script not found: cd ~/.hermes/..." | Write script file first, reference filename only |
|
||||
| Forget `no_agent=true` | Agent tries to run script as a prompt | Set `no_agent: true` for direct script execution |
|
||||
| Missing script file | "Script not found: my-script.sh" | Ensure file exists at `~/.hermes/scripts/my-script.sh` |
|
||||
| Script not executable | Permission denied | `chmod +x ~/.hermes/scripts/my-script.sh` |
|
||||
| Cronjob runs too often | Rate limits, performance issues | Use `every 15m` minimum, or daily schedule |
|
||||
| Wrong deliver target | Results go to wrong chat | `origin` = this chat, `telegram:<id>` = target chat |
|
||||
| Script path is absolute | Works in test, fails in cron | Use relative filename, not absolute path |
|
||||
| JSON parsing fails in prompt | Non-JSON noise from tools | Use regex fallback (e.g., `[`...`]` pattern) |
|
||||
| **Custom provider/model mismatch** | `RuntimeError: No Anthropic credentials found` or provider-specific auth errors | When updating an agent job with a custom provider, ALWAYS set BOTH `model` AND `provider` explicitly. The provider string determines which API credentials are loaded. A model string alone does NOT select the provider. Example: `model="moonshotai/kimi-k2.6" provider="noris"` together. |
|
||||
|
||||
## Script Job: Full Example
|
||||
|
||||
```bash
|
||||
# 1. Write the script file
|
||||
write_file path=~/.hermes/scripts/memory-sync.sh content='#!/bin/bash
|
||||
cd ~/.hermes/memory
|
||||
CHANGES=$(git diff --quiet HEAD 2>/dev/null; echo $?)
|
||||
if [ "$CHANGES" = "0" ]; then
|
||||
exit 0
|
||||
fi
|
||||
git add .
|
||||
git commit -m "Auto-sync: $(date +%Y-%m-%d)" --allow-empty 2>/dev/null
|
||||
RESULT=$(git push origin main 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Memory synced successfully"
|
||||
else
|
||||
echo "⚠️ Git push failed: $RESULT"
|
||||
fi
|
||||
'
|
||||
|
||||
# 2. Make executable
|
||||
terminal command='chmod +x ~/.hermes/scripts/memory-sync.sh'
|
||||
|
||||
# 3. Create the cronjob
|
||||
cronjob action=create name=memory-sync-daily script=memory-sync.sh schedule="0 22 * * *" repeat=forever deliver=origin no_agent=true
|
||||
|
||||
# 4. Verify
|
||||
cronjob action=list | grep memory-sync
|
||||
```
|
||||
|
||||
## Skill Update History
|
||||
- **2026-05-09**: Created as class-level skill. Documents the critical `script` parameter filename-only requirement, script creation workflow, and comprehensive pitfall table. Migrated and improved knowledge from container-network-recon skill.
|
||||
- **2026-06-17**: Added `No LLM provider configured` pitfall and reference `session_20260617_rechnungen_organizer.md`. Lesson: always verify cronjob model/provider alignment with global config.
|
||||
- **2026-06-18**: Added `Custom provider/model mismatch` pitfall and reference `session_20260618_scraper_deploy.md`. Lesson: model + provider MUST be set as a PAIR on agent jobs; for scrapers, use `no_agent` + nohup wrapper + watchdog cronjob instead of LLM-based orchestration.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Session 20250621 — Essensplaner Cronjob Refactoring
|
||||
|
||||
## Context
|
||||
Rechnungen-Organizer Cronjob war hart getroffen: `RuntimeError: Response truncated due to output length limit`. Anlass für komplette Essensplaner-Pipeline-Überarbeitung.
|
||||
|
||||
## Learnings
|
||||
|
||||
### 1. Output Truncation in Agent Cronjobs
|
||||
Bei `Rechnungen-Organizer` gab der Agent riesige Markdown-Blöcke aus → harter Cutoff.
|
||||
**Fix:** Im Prompt explizit begrenzen: `max 20 lines`, `compact summary only`, `no explanations`.
|
||||
**Ergebnis:** Neuer `nutrition-plan-generator-v4.py` mit kompakter 7-Zeilen-Tabellen-Ausgabe statt Voll-Plan.
|
||||
|
||||
### 2. Telegram Delivery Targets
|
||||
User nannte `telegram:Essensplaner` als Ziel. System kannte nur `telegram:Schoen Inc`.
|
||||
**Fix:** `@Essensplaner` ist ein Bot-Name INNERHALB der Gruppe, kein `deliver`-Target. Cronjobs müssen `telegram:Schoen Inc` verwenden.
|
||||
**Workflow:** Bei Zweifel vorher `send_message(action='list')` oder existierende Jobs inspizieren.
|
||||
|
||||
### 3. Navigation mit Tabulator
|
||||
Anstatt `exec` auf kompletten Prompt-Outputs: Arbeitsverzeichnis wechseln → Verzeichnisinhalt tabellarisch anzeigen → relevante Dateien nach Bedarf per `cat` laden.
|
||||
Vermeidet: a) Output-Truncation b) Unlesbarkeit bei ~5000 Einträgen c) Wartezeit.
|
||||
|
||||
### 4. Model + Provider als Paar setzen
|
||||
Bei jedem Update eines Agent-Jobs: `model` und `provider` zusammmen. Einzelnes `model` reicht nicht.
|
||||
Beide Cronjobs (Interview + Plan-Generator) jetzt auf `moonshotai/kimi-k2.6` / `noris`.
|
||||
|
||||
### 5. Referentielle Labels statt Zahlen-IDs
|
||||
Link zwischen Interview (Mittwoch) und Plan-Generator (Freitag) über `current_interview.json`.
|
||||
Bei Livedaten durch den Agent: JSON als Arbeitsdokument, nicht Memory.
|
||||
@@ -0,0 +1,10 @@
|
||||
## Session Summary (2026-06-17)
|
||||
|
||||
- Issue: Cronjob **Rechnungen‑Organizer** failed with `RuntimeError: No LLM provider configured`.
|
||||
- Resolution steps:
|
||||
1. Verified current Hermes config (`hermes config`) – model/provider set to `noris`/`vllm/gpt-oss-120b`.
|
||||
2. Updated the cronjob to use the same model/provider via `cronjob action=update`.
|
||||
3. Confirmed the job now has `model: vllm/gpt-oss-120b` and `provider: noris`.
|
||||
- Additional improvement: Added a new pitfall entry to the **cronjob** skill covering the "No LLM provider configured" error and the required `hermes model` / `hermes config set` steps.
|
||||
|
||||
**Takeaway:** Always ensure a cronjob’s model/provider aligns with the global Hermes configuration, or explicitly set them on the job.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Chefkoch Scraper Session: v4 First Production Deploy
|
||||
|
||||
## Session Date: 2026-06-18
|
||||
## Trigger: User requested continuous scraping to 25,000 recipes
|
||||
## Final State: Scraper running as nohup daemon (PID 80297) + watchdog cronjob
|
||||
|
||||
---
|
||||
|
||||
## Problem Discovery Log (Failure Chain)
|
||||
|
||||
### Failure 1: LLM-based cronjob fails on provider mismatch
|
||||
- Cronjob `chefkoch-real-recipes-refill` (job_id: c77127dd9130)
|
||||
- 06:01: `Error 400: model is required` — no model configured on job
|
||||
- Fix attempted: set `model="moonshotai/kimi-k2.6"` — BUT omitted `provider`
|
||||
- 06:17: `RuntimeError: No Anthropic credentials found` — system defaulted to anthropic provider
|
||||
|
||||
**Lesson:** Model + provider MUST be set as a PAIR on agent cronjobs. Setting only one creates a mismatch.
|
||||
|
||||
### Failure 2: `terminal(background=true)` silently killed scraper
|
||||
- Attempted to start scraper via `terminal(background=true)`
|
||||
- Result: Exit code 143 (SIGTERM after kill)
|
||||
- Also: `tcsetattr: Inappropriate ioctl for device` — pseudo-tty interferes with Playwright
|
||||
|
||||
**Lesson:** Background process mode is NOT a nohup replacement. Use explicit `nohup` wrapper script.
|
||||
|
||||
### Failure 3: Playwright browser not found
|
||||
- Playwright installed in nutrition-coach venv, but chromium not found
|
||||
- Error: `Executable doesn't exist at ...chrome-headless-shell`
|
||||
- Browser actually installed under `/home/debian/.cache/ms-playwright/` (installed globally)
|
||||
- venv looking elsewhere
|
||||
|
||||
**Lesson:** Set `PLAYWRIGHT_BROWSERS_PATH=/home/debian/.cache/ms-playwright` explicitly. Do NOT rely on auto-detection when multiple Python environments exist.
|
||||
|
||||
---
|
||||
|
||||
## Working Solution (Final)
|
||||
|
||||
### 1. Standalone Scraper Script
|
||||
- `~/.hermes/scripts/chefkoch_scraper_v4.py` — headless, no LLM, asyncio
|
||||
- Reads/writes directly to `nutrition-coach` profile dir
|
||||
- Uses JSON-LD extraction (fast), Playwright only for URL discovery
|
||||
|
||||
### 2. nohup Wrapper Script
|
||||
- `~/.hermes/scripts/start_scraper_v4.sh`
|
||||
- Sets `PLAYWRIGHT_BROWSERS_PATH` before launching
|
||||
- PID tracking in `/tmp/chefkoch_scraper_v4.pid`
|
||||
- Idempotent: checks if already running
|
||||
|
||||
### 3. Watchdog Cronjob (no_agent)
|
||||
- `chefkoch-scraper-watchdog` (job_id: a884c342c49e)
|
||||
- Runs every 10 minutes
|
||||
- Calls wrapper script
|
||||
- `no_agent: true` — zero LLM cost
|
||||
- `deliver: local` — no notification spam
|
||||
|
||||
### 4. Original Cronjob (agent-based) deprecated
|
||||
- Original `chefkoch-real-recipes-refill` still exists but should be paused
|
||||
- The LLM-based approach (checking count + starting scraper) is overkill
|
||||
- Direct nohup + watchdog is cheaper and more reliable
|
||||
|
||||
---
|
||||
|
||||
## Performance Observed
|
||||
|
||||
- Discovery: ~50 URLs per batch from search pages
|
||||
- Extraction: ~60 recipes/minute with single context
|
||||
- 5,000 recipes in ~60–70 minutes
|
||||
- 25,000 recipe ETA: ~7~10 hours continuous
|
||||
|
||||
---
|
||||
|
||||
## Session-Context References
|
||||
|
||||
- `real_recipes.jsonl` — append-only output file
|
||||
- `scraper_state.json` — tracks seed_cycle for resume
|
||||
- `scraper_v4.out` — live log file
|
||||
- `scraper.log` — legacy log from batch run (still there)
|
||||
|
||||
---
|
||||
|
||||
## New Pitfalls Captured in Skills
|
||||
|
||||
1. **multi-source-recipe-scraper / infinite-refill-scraper-v4.md:**
|
||||
- Browser path detection when multiple venvs exist
|
||||
- `nohup` wrapper pattern for daemon mode
|
||||
- `terminal(background=true)` tcsetattr conflict with Playwright
|
||||
- Watchdog cronjob pattern
|
||||
|
||||
2. **cronjob / SKILL.md:**
|
||||
- Model+provider MUST be set as pair on agent jobs
|
||||
- Custom provider mismatch error signature
|
||||
Reference in New Issue
Block a user