1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# Desktop Modes Tool Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Let the assistant read and set the desktop modes dnd, presentation, nolock and quiet.
**Architecture:** One tool file (`mode.toml`) and one script (`mode.sh`) in the existing tool format, calling `statusctl`. No change to `assistant.py`. Spec: `docs/superpowers/specs/2026-10-19-mode-tool-design.md`.
**Tech Stack:** bash, statusctl (`~/bin`, on the daemon's PATH).
---
### Task 1: mode.sh with its check
**Files:**
- Create: `examples/tools/test-mode.sh`
- Create: `examples/tools/mode.sh`
- [ ] **Step 1: Write the check.** It runs `mode.sh` against a throwaway
`XDG_RUNTIME_DIR`, so the real modes are never touched.
```bash
#!/bin/bash
# test-mode.sh: self-check for mode.sh against a scratch runtime dir.
set -u
here=$(dirname "$0")
export XDG_RUNTIME_DIR=$(mktemp -d)
trap 'rm -rf "$XDG_RUNTIME_DIR"' EXIT
fail=0
check() { [ "$2" = "$3" ] || { echo "FAIL $1: got '$2', want '$3'"; fail=1; }; }
check "read missing" "$("$here/mode.sh" dnd "")" "dnd: off"
check "set on" "$("$here/mode.sh" dnd 1)" "dnd: on"
check "file written" "$(cat "$XDG_RUNTIME_DIR/status.dnd")" "1"
check "set off" "$("$here/mode.sh" dnd 0)" "dnd: off"
"$here/mode.sh" dnd yes >/dev/null 2>&1; check "bad value exit" "$?" "1"
"$here/mode.sh" bogus 1 >/dev/null 2>&1; check "bad mode exit" "$?" "1"
echo 1 > "$XDG_RUNTIME_DIR/status.gaming"
check "gaming hold" "$("$here/mode.sh" presentation 0)" \
"presentation: on (held on by a running game; manual setting off)"
[ $fail = 0 ] && echo "ok"
exit $fail
```
- [ ] **Step 2: Run it, expect failure** (`mode.sh` missing).
Run: `bash examples/tools/test-mode.sh`
- [ ] **Step 3: Write `examples/tools/mode.sh`** (GPLv2 header as in `playing.sh`).
```bash
set -uo pipefail
mode=${1:-}
on=${2:-}
case "$on" in
"") ;;
0|1) statusctl "$mode" set "$on" || exit 1 ;;
*) echo "on must be 1, 0 or empty, not: $on" >&2; exit 1 ;;
esac
# Read back rather than echo the request: a set that did not land must not
# be reported as done. An unknown mode fails here, in statusctl.
v=$(statusctl "$mode" get) || exit 1
[ "$v" = 1 ] && s=on || s=off
# statusctl get reads only the manual half of presentation; a running game
# holds it on regardless (see statusctl's gaming_on).
g=$(tr -d '[:space:]' < "${XDG_RUNTIME_DIR:-/tmp}/status.gaming" 2>/dev/null)
if [ "$mode" = presentation ] && [ "$g" = 1 ]; then
echo "presentation: on (held on by a running game; manual setting $s)"
else
echo "$mode: $s"
fi
```
- [ ] **Step 4: Run the check, expect `ok`.**
Run: `chmod +x examples/tools/mode.sh examples/tools/test-mode.sh && examples/tools/test-mode.sh`
### Task 2: mode.toml
**Files:**
- Create: `examples/tools/mode.toml`
- [ ] **Step 1: Write it**, exactly as in the spec's "Tool" section.
- [ ] **Step 2: Self-check still passes:** `python3 assistant.py --test`.
- [ ] **Step 3: Commit** `examples/tools/mode.sh`, `test-mode.sh`, `mode.toml`.
### Task 3: Install and live check
- [ ] **Step 1:** `cp examples/tools/mode.sh examples/tools/mode.toml ~/.config/desktop-assistant/tools/` then `./assistant.sh restart`.
- [ ] **Step 2:** Live, over the socket: 10 phrasings (it/en, on/off/query),
note tool call, arguments, reply, and `statusctl <mode> get` after. Restore
each mode to its starting value afterwards.
- [ ] **Step 3:** Set-then-ask after unrelated history: the model calls the
tool again.
- [ ] **Step 4:** Record results under Findings in `AGENTS.md`, mark the spec
implemented, commit.
|