Loads cron/jobs.json and asserts: unique names, valid cron expressions
(APScheduler parseable), bool enabled field; kind:"shell" entries must
have non-empty channel, non-empty command list of strings, valid
report_on, and timeout within [1, 3600] when present; claude entries
must have non-empty prompt, valid model, list-typed allowed_tools.
Sanity-checks that shell commands reference existing scripts in the
repo and that no imported claude prompt still points at /home/moltbot/clawd/.
Manual sequence Marius runs AFTER the PR merges. Pre-flight (tests,
backups), stop-services, ANAF live-state copy, dashboard migration,
memory inversion (clawd/memory -> echo-core/memory), systemd, crontab,
OpenClaw decommission, verification, and rollback path. Flagged at the
top as human-only -- no AI agent should auto-execute these steps.
Idempotent sed-based rewrite of any crontab line that references
/home/moltbot/clawd/tools/backup_config.sh so it points at the
echo-core copy. Safe to re-run; prints a single status line either way.
Grep across personality/ for clawd|openclaw|clawdbot found no hits after
Lane A's earlier sweep. Single remaining operational reference to the
now-decommissioned night-execute cron has been softened to a generic
'add to approved-tasks.md' note.
Imported claude jobs default channel to echo-work; grup-sprijin-5feb
and grup-sprijin-pregatire route to echo-sprijin. Existing echo-core
channel is preserved.
Runs tools/migrations/import_openclaw_jobs_2026-04.py against the real
openclaw jobs.json with --skip daily-morning-checks,archive-tasks,
monica-ion-blog,diagnostic-platou-financiar.
Imports 13 claude jobs: morning/evening report + coaching, exercise-snack-1/2/3,
weekly-planning-sun, content-discovery, provocare-reminder, grup-sprijin-5feb,
grup-sprijin-pregatire, heartbeat-2h. All import DISABLED except heartbeat-2h
(preserving its openclaw enabled flag). Cron schedules shifted UTC -> Bucharest.
clawd -> echo-core path rewrites applied.
heartbeat-2h imported with an empty openclaw prompt; filled with a minimal
status-check prompt so scheduler doesn't error on execution.
Adds 5 kind:"shell" jobs (anaf-monitor, security-audit-daily,
kb-index-refresh, archive-tasks-daily, backup-config) and the new
insights-extract claude job (disabled placeholder). All cron schedules
are Europe/Bucharest local time. Decomposes openclaw's daily-morning-checks
mega-prompt per the Issue 15 eng-review decision.
Audit-trail tool that translates OpenClaw's nested jobs.json schema
(schedule.expr with optional tz, payload.message, agentId, state) into
echo-core's flat schema. UTC -> Europe/Bucharest cron conversion with
DST-aware offset; Bucharest-tagged source expressions pass through
unchanged. Rewrites `cd ~/clawd` / `/home/moltbot/clawd/` -> echo-core
without matching `clawd-archive` or `clawdbot` substrings.
Built-in skip list covers night-execute and antfarm/feature-dev/*; YouTube:
prefix is auto-skipped. --dry-run, --skip-disabled, --skip, --channel,
--source, --target flags. Duplicate job names in target are skipped with
a warning; existing entries are preserved.
Four checks:
- The script file exists at the expected path.
- The source contains the marker print statement (fast regression guard).
- Running the script against an empty config produces a matching marker
(^GSTACK-CRON: changes=\d+$) with changes=0.
- The marker is the last non-empty line of stdout so tailers can parse it.
The runtime test copies the script into a tmp cwd so that the script's
SCRIPT_DIR-relative state files (hashes.json, versions.json, snapshots/,
monitor.log) don't pollute the repo.
The Echo-Core scheduler's report_on='changes' contract parses
^GSTACK-CRON: changes=\d+$ from stdout to decide whether to forward
the run's output to a channel. monitor_v2.py now prints that marker
as its final stdout line with num_changes from the current run.
Also switches the success return value from len(all_changes) to 0.
Previously, any run that detected changes (N>0) exited with a non-zero
status, which the scheduler treats as an error (always forwarded,
ignoring report_on). Exit code now signals only fatal errors; the
marker carries the change count.
Adds four new test groups to tests/test_scheduler.py:
- TestTimezone: asserts AsyncIOScheduler is constructed with Europe/Bucharest.
- TestShellKind: 16 cases covering add_shell_job validation (duplicate name
across claude/shell, invalid cron, empty/non-list/non-string command,
bad report_on, bad timeout bounds/type, empty channel, custom report_on
and timeout pass-through).
- TestShellExecute: 14 cases covering the report_on contract:
- exit 0 + marker N>0 → forwards stdout
- exit 0 + marker N==0 → silent
- exit 0 + no marker → silent + warning
- report_on=always and =never variants
- non-zero exit reports stderr even when report_on='never'
- TimeoutExpired and launch exceptions report '[cron:X] Error: ...'
- per-job timeout passed to subprocess.run; default 300 when None
- subprocess.run receives the job's command list verbatim
- stdout trimmed to 1500 ch; stderr trimmed to 500 ch
- TestBackwardCompat: a jobs.json entry without a 'kind' field dispatches
to _execute_claude_job (never to _execute_shell_job); the existing Claude
add_job/run_job round-trip still works with the old CLI invocation.
- TestMarkerRegex: parametrised positive/negative cases for _MARKER_RE.
- AsyncIOScheduler now runs in Europe/Bucharest so cron strings in jobs.json
match local wall-clock time.
- New add_shell_job() validates name, cron, command list, channel, report_on
(always|changes|never), and optional timeout (1..3600s). Existing add_job()
stays untouched for the Claude path.
- _execute_job dispatches on job['kind'] (default 'claude'); legacy jobs
without the field still route to the Claude executor. Refactored the
Claude path into _execute_claude_job; new _execute_shell_job runs
subprocess with _safe_env + PROJECT_ROOT cwd.
- Shell semantics: non-zero exit always forwards stderr (trimmed to 500 ch)
as '[cron:NAME] exit CODE: STDERR' regardless of report_on. On exit 0,
'always' forwards stdout (trimmed to 1500 ch), 'never' stays silent, and
'changes' parses the GSTACK-CRON marker (^GSTACK-CRON: changes=\d+$) and
forwards stdout only when N>0; missing/malformed marker logs a warning
and stays silent.
- Timeout honoured per-job (falls back to JOB_TIMEOUT=300s).
Loop through consecutive newsletter numbers until one is missing, so
backlog gets delivered in a single run. Use httpx for 404 check and
point to absolute claude binary path for cron. Enable job in config.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Digest summarizes unread emails via Claude CLI; forward sends raw
content (split to 4096 chars). Wired as /email digest and
/email forward slash commands, plus instant per-guild sync on ready.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Beehiiv redirects non-existent newsletters to /?404=... with HTTP 302.
With follow_redirects=True, the final 200 was misread as "newsletter exists".
Fix: disable redirect following so only a direct HTTP 200 = newsletter real.
Also reset state back to last_sent=13 (real).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- heartbeat saves unread whitelisted emails via email_process --save --json
- fix: add --add-dir so Claude CLI subprocess can access memory/ symlink
- email_check/process: use BODY.PEEK[] to avoid marking emails as read
- email_process: simplify credential loading via credential_store only
- config: heartbeat interval 30→120min, quiet hours end 08→07
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KB and embeddings reindex status messages were being sent to Discord
as heartbeat results. These are internal housekeeping — now logged
instead of surfaced to the user.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Switch Bash permission patterns from space to colon separator
- Add memory.bak/ to .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- claude_session: replace 10 individual git command patterns with single Bash(git *) wildcard
- generate_pdf: add italic/bold-oblique font loading and render_rich_text() for inline bold/italic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fast commands for git, email, calendar, notes, search, reminders, and
diagnostics — all execute instantly without Claude CLI. Incremental
embeddings indexing in heartbeat (1h cooldown) + inline indexing after
/note, /jurnal, /email save. Fix Ollama URL (localhost → 10.0.20.161),
fix email_process.py KB path (kb/ → memory/kb/).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Calendar no longer bypasses quiet hours. First run after quiet hours
sends full daily summary, subsequent runs only remind for next event
within 45 min with deduplication. Calendar cooldown set to 30 min.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Heartbeat system overhaul:
- Fix email/calendar checks to parse JSON output correctly
- Add per-check cooldowns and quiet hours config
- Send findings to Discord channel instead of just logging
- Auto-reindex KB when stale files detected
- Claude CLI called only if HEARTBEAT.md has extra instructions
- All settings configurable via config.json heartbeat section
Move hardcoded values to config.json:
- allowed_tools list (claude_session.py)
- Ollama URL/model (memory_search.py now reads ollama.url from config)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Git commands without arguments (git push, git status, git diff, etc.) were not matched by the existing wildcard patterns. Added bare variants and git stash support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SOUL.md: don't retract excessively when information is correct.
TOOLS.md: verify tools before claiming unavailable, offer alternatives.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Email tools now use credential_store (keyring) as primary source
with env/.env as fallback. Removes plaintext password from email_check.py.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace all ~/clawd and ~/.clawdbot paths with ~/echo-core equivalents
in tools (git_commit, ralph_prd_generator, backup_config, lead-gen)
- Update personality files: TOOLS.md repo/paths, AGENTS.md security audit cmd
- Migrate HANDOFF.md architectural decisions to docs/architecture.md
- Tighten credentials/ dir to 700, add to .gitignore
- Add .claude/ and *.pid to .gitignore
- Various adapter, router, and session improvements from prior work
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from --output-format json to --output-format stream-json --verbose
so that _run_claude() parses all assistant text blocks (not just the final
result field). Discord/Telegram/WhatsApp now receive every intermediate
message Claude writes between tool calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CLAUDE.md rewritten to clearly establish Echo's identity and role.
claude_session.py now passes --allowedTools to Claude CLI in both
start_session() and resume_session(), with explicit tool whitelist:
- File tools (Read/Edit/Write/Glob/Grep) + WebFetch/WebSearch (read-only)
- Bash restricted by command prefix (git, python, npm, docker, systemctl)
- SSH/SCP/rsync limited to local network (10.0.20.*)
- curl/wget excluded to prevent data exfiltration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setup.sh now installs eco → ~/.local/bin/eco (symlink to cli.py).
README.md updated with full eco command reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10-step bash wizard (setup.sh) that guides through: prerequisites check,
venv setup, bot identity, Discord/Telegram/WhatsApp bridge configuration,
config.json merge, systemd service installation, and health checks.
Idempotent — safe to re-run, preserves existing config and secrets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>