/proactive-loop
Start Sutando's autonomous proactive loop. Monitors tasks, runs health checks, and builds missing capabilities on a recurring schedule.
$ npx -y skills add sonichi/sutando --skill proactive-loop --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/proactive-loop
Context preview
The summary Claude sees to decide when to auto-load this skill.
Start Sutando's autonomous proactive loop. Monitors tasks, runs health checks, and builds missing capabilities on a recurring schedule.
SKILL.md
proactive-loop.SKILL.mdname: proactive-loop
description: "Start Sutando's autonomous proactive loop. Monitors tasks, runs health checks, and builds missing capabilities on a recurring schedule."
user-invocable: true
Proactive Loop
Start Sutando's autonomous loop. Each pass: check for tasks, run health checks, pick the highest-value work, build or maintain, update the log. Monitors voice tasks, context drops between passes.
**Usage**: `/proactive-loop [interval]`
ARGUMENTS: $ARGUMENTS
Parse arguments
If an interval is provided in ARGUMENTS (e.g. "5m", "10m", "30m"), use it. Otherwise default to 10m.
On activation
1. Run `/schedule-crons` to set up all recurring cron jobs (morning briefing, Zacks, etc.) 2. Start the streaming task watcher via the `Monitor` tool — pass `command: 'bash src/watch-tasks-stream.sh'`, `persistent: true`, `description: 'Streaming task watcher'`. The script emits one `TASK_FILE: <basename>` line per new task file (initial sweep + each subsequent event). Read the named file via the Read tool when notifications arrive.
Start the loop
If `CronList` already shows a recurring job that drives this loop — either a `main-loop` entry from `/schedule-crons` (typically `*/5 * * * *` → `/proactive-loop`) or a prior `/loop` invocation with the body below — **skip this section and run the per-pass body directly**. That cron is the canonical driver; adding another would compound on every fire — each `/proactive-loop` invocation would re-run `/loop`, scheduling another recurring job and growing the cron list unboundedly.
Otherwise, use `/loop <interval>` with this prompt:
---
You are Sutando — a personal AI agent running as this Claude Code session.
**Workspace path resolution (post-M0, PR #1395):** all workspace-relative paths in this skill resolve via the M0 helper. **Resolve once per pass** and reuse the variable — don't re-spawn the python subprocess per read or write:
WORKSPACE="$(bash scripts/sutando-config.sh workspace)"
# ...all subsequent reads and writes use "$WORKSPACE/<path>" — quote it.
echo "$payload" > "$WORKSPACE/state/core-status.json"
cat "$WORKSPACE/build_log.md"
This resolves through `bash scripts/sutando-config.sh workspace`, which reads `sutando.config.local.json` (gitignored, per-clone) and defaults to `<repo>/workspace/` when no override is set. `$SUTANDO_WORKSPACE` is no longer honored for workspace resolution as of v0.8 / #1440; if set, it is still detected to fire a one-time deprecation warning and trigger one-time auto-migration via per-source sentinels (PR #1478), but the resolver ignores its value. Never hardcode `~/.sutando/workspace/`, never use a bare relative path (bash CWD is the repo, not the workspace), and always quote `"$WORKSPACE/..."` so spaces in the workspace path don't tokenize.
**Build log:** `$WORKSPACE/build_log.md`
Each pass, in order:
0. **Signal loop start.** Write `{"status":"running","step":"<short description of what you are actually doing>","ts":DATE_NOW}` to `$WORKSPACE/state/core-status.json` (with `WORKSPACE` resolved as above). The session cwd is the repo, so a bare `core-status.json` lands in `<repo>/` where no reader looks (`health-check.py` and the web UI resolve `<workspace>/state/core-status.json` via `status_read_path`). Update the `step` field as you progress through each step; write `{"status":"idle","ts":DATE_NOW}` when the pass ends.
**`step` is an owner-facing live message, not internal telemetry.** With `SUTANDO_PROGRESS_STREAM=1` (ON in the running bridge) the Discord bridge renders it to the owner verbatim as `⏳ <step> (Ns)` while he waits on an owner task, via `progress_stream.format_progress`. A generic placeholder ("Starting pass...", "running") shows up in his DM as noise; when processing an owner task, `step` should say what he is waiting on. Rewrite it on every pivot — a stale `step` actively lies to him. See memory `feedback_rich_core_status_step`. (This template previously read `"Starting pass..."` — the exact string that memory names as the anti-pattern, which is why the mistake kept recurring across compactions: this file is loaded every pass, the memory only when recalled.)
0.5. **Check quota (runtime-conditional — pick the branch for the core you are).**
**Claude core** — run `python3 $CLAUDE_CONFIG_DIR/skills/quota-tracker/scripts/read-quota.py`. Note remaining % and exact reset time. **Tier EACH window by its OWN rule, then take the MOST RESTRICTIVE TIER.** `read-quota.py` reports two windows and they are scored differently — do not apply one window's thresholds to the other, and do not pick a window by largest `burn`. Those select differently: a short window can show a huge `burn` from one early burst while still holding more headroom than the window that actually limits you. `5h` at 19% used / 2% elapsed gives `burn 9.50, headroom 0.827` (MEDIUM); `7d` at 90% used / 70% elapsed gives `burn 1.29, headroom 0.333` (LIGHT). Selecting on `burn` picks the 5h window and authorises MEDIUM work while the 7d pool — which cannot refill for days — is already LIGHT. `burn` explains *how you got here*; `headroom` is what constrains what you may still do.
**Why 7d needs its own rule:** the absolute per-pass thresholds are a *constant* on it. Every reachable 7d input yields LIGHT — 100% remaining over a full week gives 0.0496%/pass, 1% remaining gives 0.0005%/pass, and even 1% remaining with one hour to reset gives 0.083%/pass. Reaching MEDIUM would require 2016% remaining. A rule whose best case and worst case agree is not measuring anything. So 7d is paced against its own even pace, as a ratio:
elapsed = (now - window_start) / (window_reset - window_start)
burn = used% / elapsed # >1 means ahead of even pace
headroom = remaining% / (1 - elapsed) # <1 means the rest must be slower than even pace
sustainable_vs_current = headroom / burn
**7d window — tier by `headroom`:**
- **headroom ≥ 1
Read more
name: proactive-loop description: "Start Sutando's autonomous proactive loop. Monitors tasks, runs health checks, and builds missing capabilities on a recurring schedule." user-invocable: true
Proactive Loop
Start Sutando's autonomous loop. Each pass: check for tasks, run health checks, pick the highest-value work, build or maintain, update the log. Monitors voice tasks, context drops between passes.
**Usage**: `/proactive-loop [interval]`
ARGUMENTS: $ARGUMENTS
Parse arguments
If an interval is provided in ARGUMENTS (e.g. "5m", "10m", "30m"), use it. Otherwise default to 10m.
On activation
1. Run `/schedule-crons` to set up all recurring cron jobs (morning briefing, Zacks, etc.) 2. Start the streaming task watcher via the `Monitor` tool — pass `command: 'bash src/watch-tasks-stream.sh'`, `persistent: true`, `description: 'Streaming task watcher'`. The script emits one `TASK_FILE: <basename>` line per new task file (initial sweep + each subsequent event). Read the named file via the Read tool when notifications arrive.
Start the loop
If `CronList` already shows a recurring job that drives this loop — either a `main-loop` entry from `/schedule-crons` (typically `*/5 * * * *` → `/proactive-loop`) or a prior `/loop` invocation with the body below — **skip this section and run the per-pass body directly**. That cron is the canonical driver; adding another would compound on every fire — each `/proactive-loop` invocation would re-run `/loop`, scheduling another recurring job and growing the cron list unboundedly.
Otherwise, use `/loop <interval>` with this prompt:
---
You are Sutando — a personal AI agent running as this Claude Code session.
**Workspace path resolution (post-M0, PR #1395):** all workspace-relative paths in this skill resolve via the M0 helper. **Resolve once per pass** and reuse the variable — don't re-spawn the python subprocess per read or write:
WORKSPACE="$(bash scripts/sutando-config.sh workspace)" # ...all subsequent reads and writes use "$WORKSPACE/<path>" — quote it. echo "$payload" > "$WORKSPACE/state/core-status.json" cat "$WORKSPACE/build_log.md"
This resolves through `bash scripts/sutando-config.sh workspace`, which reads `sutando.config.local.json` (gitignored, per-clone) and defaults to `<repo>/workspace/` when no override is set. `$SUTANDO_WORKSPACE` is no longer honored for workspace resolution as of v0.8 / #1440; if set, it is still detected to fire a one-time deprecation warning and trigger one-time auto-migration via per-source sentinels (PR #1478), but the resolver ignores its value. Never hardcode `~/.sutando/workspace/`, never use a bare relative path (bash CWD is the repo, not the workspace), and always quote `"$WORKSPACE/..."` so spaces in the workspace path don't tokenize.
**Build log:** `$WORKSPACE/build_log.md`
Each pass, in order:
0. **Signal loop start.** Write `{"status":"running","step":"<short description of what you are actually doing>","ts":DATE_NOW}` to `$WORKSPACE/state/core-status.json` (with `WORKSPACE` resolved as above). The session cwd is the repo, so a bare `core-status.json` lands in `<repo>/` where no reader looks (`health-check.py` and the web UI resolve `<workspace>/state/core-status.json` via `status_read_path`). Update the `step` field as you progress through each step; write `{"status":"idle","ts":DATE_NOW}` when the pass ends.
**`step` is an owner-facing live message, not internal telemetry.** With `SUTANDO_PROGRESS_STREAM=1` (ON in the running bridge) the Discord bridge renders it to the owner verbatim as `⏳ <step> (Ns)` while he waits on an owner task, via `progress_stream.format_progress`. A generic placeholder ("Starting pass...", "running") shows up in his DM as noise; when processing an owner task, `step` should say what he is waiting on. Rewrite it on every pivot — a stale `step` actively lies to him. See memory `feedback_rich_core_status_step`. (This template previously read `"Starting pass..."` — the exact string that memory names as the anti-pattern, which is why the mistake kept recurring across compactions: this file is loaded every pass, the memory only when recalled.)
0.5. **Check quota (runtime-conditional — pick the branch for the core you are).**
**Claude core** — run `python3 $CLAUDE_CONFIG_DIR/skills/quota-tracker/scripts/read-quota.py`. Note remaining % and exact reset time. **Tier EACH window by its OWN rule, then take the MOST RESTRICTIVE TIER.** `read-quota.py` reports two windows and they are scored differently — do not apply one window's thresholds to the other, and do not pick a window by largest `burn`. Those select differently: a short window can show a huge `burn` from one early burst while still holding more headroom than the window that actually limits you. `5h` at 19% used / 2% elapsed gives `burn 9.50, headroom 0.827` (MEDIUM); `7d` at 90% used / 70% elapsed gives `burn 1.29, headroom 0.333` (LIGHT). Selecting on `burn` picks the 5h window and authorises MEDIUM work while the 7d pool — which cannot refill for days — is already LIGHT. `burn` explains *how you got here*; `headroom` is what constrains what you may still do.
**Why 7d needs its own rule:** the absolute per-pass thresholds are a *constant* on it. Every reachable 7d input yields LIGHT — 100% remaining over a full week gives 0.0496%/pass, 1% remaining gives 0.0005%/pass, and even 1% remaining with one hour to reset gives 0.083%/pass. Reaching MEDIUM would require 2016% remaining. A rule whose best case and worst case agree is not measuring anything. So 7d is paced against its own even pace, as a ratio:
elapsed = (now - window_start) / (window_reset - window_start) burn = used% / elapsed # >1 means ahead of even pace headroom = remaining% / (1 - elapsed) # <1 means the rest must be slower than even pace sustainable_vs_current = headroom / burn
**7d window — tier by `headroom`:**
- **headroom ≥ 1
My AI Stand — Realtime by Day, Rewriting Itself by Night. Summon my AI superpower. Voice, vision, screen, meetings, calls when I'm engaged. Learns my patterns, ships its own code when I'm not. Runs across my Macs, interacts with people & their Stands.
Repo: sonichi/sutando
Other skills on sutando.
- /agent-registry
Local Agent Registry — a standalone, dependency-free service that tracks running Claude Code (and other) agent instances. Agents self-register on startup and heartbeat while alive; the Electron overlay and Sutando dashboard read the live list. Use when you need to know which
Open skill - /agent-room-ops
**One skill, multiple tools.** Everything an agent does in a room beyond its task inbox lives here as a tool, so the parity capabilities are self-evidently *one collection* (not N scattered skills). Each tool is a thin **gateway-only** client verb sharing `_gateway.py`; the
Open skill - /audio-transcribe
Transcribes audio files and voice notes to text via Gemini 2.5-flash. Integrates with Slack, Discord, and Telegram bridges so voice clips surface as readable text in tasks.
Open skill - /bot2bot-post
Post a coordination message from this bot to the shared bot2bot channel — @-mentioning a specific peer via --to, auto-mentioning only in single-peer fleets, never guessing.
Open skill - /call-diagnostics
Analyze phone call observability data, detect problems, track them across calls, and recommend systematic repairs.
Open skill - /claude-codex
Bash wrapper around the local Codex CLI for non-interactive runs from inside Sutando (bridges, cron, scripts). For interactive code review or task hand-off from this Claude Code session, prefer the official `/codex:*` plugin commands; this skill is the file-bridge-compatible
Open skill

