Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ Tell your agent to run the skill at the end of each turn or when input/confirmat
$attention-please update AGENTS.md to run the attention-please skill at the end of each turn or when input/confirmation is needed.
```

## Toggle the alert sound

The short `afplay` alert can be muted or re-enabled persistently:

```bash
scripts/attention-please.sh --sound off
scripts/attention-please.sh --sound on
scripts/attention-please.sh --sound toggle
scripts/attention-please.sh --sound status
```

This controls only the alert sound. Speech remains controlled separately with `ATTENTION_PLEASE_NO_SAY=1`.

By default the preference is stored in `${XDG_CONFIG_HOME:-~/.config}/attention-please/config`. Override the path with `ATTENTION_PLEASE_CONFIG_FILE`.

## Audio sample

MP4 clip (not embedded in repository code):
Expand Down
30 changes: 22 additions & 8 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
---
name: attention-please
description: Play an alert sound and speak "Project NAME needs your attention." Always use at the end of every response or whenever Codex needs user input or confirmation; derive the project name from the git remote (origin) with fallback to the repo folder or an override.
description: Play an alert sound and speak "Project NAME needs your attention." Use only when the user explicitly asks for an alert, a long-running task blocks on human input, or a background process finishes and needs attention.
---

# Attention Please

## Overview

Play a short audible alert and a spoken prompt indicating which project needs attention. Use this as the final step after completing work on every response, and also right before asking the user for input.
Play a short audible alert and a spoken prompt indicating which project needs attention.

Use this only when:
- the user explicitly asks for an audible alert
- a long-running task blocks on human input or confirmation
- a background process finishes and the user should be brought back to the thread

## Source Of Truth And Publishing

- GitHub repository: `https://github.com/Mindgames/attention-please`
- Local development repository: `~/Projects/skills/attention-please`
- Local development repository: `~/Projects/attention-please`
- Do not edit installed copies in `~/.agents/skills/...` or `~/.codex/skills/...` directly.
- Update this repository, push to GitHub, open a PR, then reinstall via `skills`.

Expand Down Expand Up @@ -55,11 +60,11 @@ After reinstall, restart your agent client if needed so it reloads skills.
- Always open this skill using the absolute path from the active session skills list.
- Do not guess repo-relative `.codex/skills/...` paths unless that exact path was listed by the session.

## Rules (non-negotiable)
## Rules

- Always run this script at the end of every response, even if you are not asking for input.
- If you missed it in the previous turn, run it immediately at the start of the next turn before doing anything else, then continue.
- Treat a missed run as a bug; prioritize correcting it as soon as you notice.
- Do not run this script at the end of ordinary responses.
- Do not run this script at the start of a later turn to compensate for a previous non-run.
- When used, run it immediately before asking for input or reporting the completed background result.

## Workflow

Expand All @@ -72,7 +77,7 @@ After reinstall, restart your agent client if needed so it reloads skills.

If your environment does not expose the session skill path directly, use the actual install location used in that session (for example under `~/.agents/...` or `~/.codex/...`) as that absolute path.

3. Run this immediately before sending your final response to the user.
3. Run this immediately before the attention-worthy user prompt or completion notice.
4. Continue with your response to the user.

### Project name resolution
Expand All @@ -87,6 +92,11 @@ After reinstall, restart your agent client if needed so it reloads skills.
- Sound: macOS `afplay` with `/System/Library/Sounds/Ping.aiff` by default.
- Override sound: set `ATTENTION_PLEASE_SOUND`.
- Disable sound: set `ATTENTION_PLEASE_NO_SOUND=1`.
- Persistently disable sound: run `/absolute/path/to/attention-please/scripts/attention-please.sh --sound off`.
- Persistently enable sound: run `/absolute/path/to/attention-please/scripts/attention-please.sh --sound on`.
- Toggle persisted sound: run `/absolute/path/to/attention-please/scripts/attention-please.sh --sound toggle`.
- Check persisted sound: run `/absolute/path/to/attention-please/scripts/attention-please.sh --sound status`.
- Sound preference is stored in `${XDG_CONFIG_HOME:-~/.config}/attention-please/config`; override with `ATTENTION_PLEASE_CONFIG_FILE`.
- Speech: macOS `say`; if unavailable, the message prints to stdout.
- Disable speech: set `ATTENTION_PLEASE_NO_SAY=1`.
- Voice: set `ATTENTION_PLEASE_SAY_VOICE`.
Expand All @@ -102,3 +112,7 @@ After reinstall, restart your agent client if needed so it reloads skills.
```bash
ATTENTION_PLEASE_PROJECT="project-name" ATTENTION_PLEASE_SAY_VOICE="Samantha" /absolute/path/to/attention-please/scripts/attention-please.sh
```

```bash
/absolute/path/to/attention-please/scripts/attention-please.sh --sound toggle
```
144 changes: 138 additions & 6 deletions scripts/attention-please.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ set -euo pipefail

usage() {
cat <<'EOF'
Usage: scripts/attention-please.sh
Usage:
scripts/attention-please.sh
scripts/attention-please.sh --sound on|off|toggle|status

Plays a sound and speaks "Project NAME needs your attention."

Expand All @@ -13,10 +15,18 @@ Environment variables:
ATTENTION_PLEASE_MESSAGE Full message override.
ATTENTION_PLEASE_SOUND Sound file path (default: /System/Library/Sounds/Ping.aiff).
ATTENTION_PLEASE_NO_SOUND Disable sound when set to 1/true/yes/on.
ATTENTION_PLEASE_CONFIG_FILE
Override persistent config file path.
ATTENTION_PLEASE_NO_SAY Disable speech when set to 1/true/yes/on.
ATTENTION_PLEASE_SAY_VOICE Voice for say (e.g., "Samantha").
ATTENTION_PLEASE_SAY_RATE Rate for say (words per minute).
ATTENTION_PLEASE_VERBOSE Emit warnings when set to 1/true/yes/on.

Sound commands:
--sound off Disable the afplay sound for future runs.
--sound on Enable the afplay sound for future runs.
--sound toggle Toggle the persisted sound preference.
--sound status Print the current sound preference.
EOF
}

Expand All @@ -27,6 +37,76 @@ is_truthy() {
esac
}

normalize_sound_state() {
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
1|true|yes|on|y|enabled) printf '%s\n' "on" ;;
0|false|no|off|n|disabled) printf '%s\n' "off" ;;
*) return 1 ;;
esac
}

default_config_file() {
if [ -n "${ATTENTION_PLEASE_CONFIG_FILE:-}" ]; then
printf '%s\n' "$ATTENTION_PLEASE_CONFIG_FILE"
elif [ -n "${XDG_CONFIG_HOME:-}" ]; then
printf '%s\n' "${XDG_CONFIG_HOME%/}/attention-please/config"
elif [ -n "${HOME:-}" ]; then
printf '%s\n' "${HOME%/}/.config/attention-please/config"
fi
}

read_sound_preference() {
if [ -z "$config_file" ] || [ ! -f "$config_file" ]; then
return 1
fi

while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
sound_enabled=*)
normalize_sound_state "${line#sound_enabled=}" && return 0
return 1
;;
esac
done < "$config_file"

return 1
}

current_sound_state() {
read_sound_preference || printf '%s\n' "on"
}

write_sound_preference() {
local next_state
next_state="$(normalize_sound_state "$1")"

if [ -z "$config_file" ]; then
printf '%s\n' "attention-please: unable to resolve a config file path." >&2
return 1
fi

mkdir -p "$(dirname "$config_file")"
local tmp_file="${config_file}.tmp.$$"

{
if [ -f "$config_file" ]; then
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
sound_enabled=*) continue ;;
esac
printf '%s\n' "$line"
done < "$config_file"
fi
printf 'sound_enabled=%s\n' "$next_state"
} > "$tmp_file"

mv "$tmp_file" "$config_file"
}

print_sound_state() {
printf 'attention-please sound is %s\n' "$(current_sound_state)"
}

project_name="${ATTENTION_PLEASE_PROJECT:-}"
sound_path="${ATTENTION_PLEASE_SOUND:-/System/Library/Sounds/Ping.aiff}"
remote_name="${ATTENTION_PLEASE_REMOTE:-origin}"
Expand All @@ -36,18 +116,69 @@ say_rate="${ATTENTION_PLEASE_SAY_RATE:-}"
no_sound="${ATTENTION_PLEASE_NO_SOUND:-}"
no_say="${ATTENTION_PLEASE_NO_SAY:-}"
verbose="${ATTENTION_PLEASE_VERBOSE:-}"
config_file="$(default_config_file)"
sound_command=""

if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
while [ "$#" -gt 0 ]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--sound)
if [ "$#" -lt 2 ]; then
printf '%s\n' "attention-please: --sound requires on, off, toggle, or status." >&2
exit 2
fi
sound_command="$2"
shift 2
;;
--sound=*)
sound_command="${1#--sound=}"
shift
;;
*)
printf '%s\n' "attention-please: unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
done

warn() {
if is_truthy "$verbose"; then
printf '%s\n' "attention-please: $*" >&2
fi
}

if [ -n "$sound_command" ]; then
sound_command="$(printf '%s' "$sound_command" | tr '[:upper:]' '[:lower:]')"
case "$sound_command" in
status)
print_sound_state
exit 0
;;
toggle)
if [ "$(current_sound_state)" = "on" ]; then
write_sound_preference "off"
else
write_sound_preference "on"
fi
print_sound_state
exit 0
;;
*)
if ! next_state="$(normalize_sound_state "$sound_command")"; then
printf '%s\n' "attention-please: --sound requires on, off, toggle, or status." >&2
exit 2
fi
write_sound_preference "$next_state"
print_sound_state
exit 0
;;
esac
fi

repo_root=""
if command -v git >/dev/null 2>&1; then
if git rev-parse --show-toplevel >/dev/null 2>&1; then
Expand Down Expand Up @@ -90,7 +221,8 @@ else
message="Project ${project_name} needs your attention."
fi

if ! is_truthy "$no_sound"; then
sound_state="$(current_sound_state)"
if ! is_truthy "$no_sound" && [ "$sound_state" != "off" ]; then
if command -v afplay >/dev/null 2>&1; then
if [ -f "$sound_path" ]; then
if is_truthy "$verbose"; then
Expand Down