Skip to content
Merged
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
60 changes: 31 additions & 29 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
{
"permissions": {
"allow": [
"Bash(omarchy:*)",
"Bash(omarchy-shell:*)",
"Bash(omarchy-battery-limit:*)",
"Bash(omarchy-als:*)",
"Bash(omarchy-aquarium-toggle:*)",
"Bash(omarchy-theme-set:*)",
"Bash(omarchy-notification-send:*)",
"Bash(omarchy-launch:*)",
"Bash(omarchy-capture-screenshot:*)",
"Bash(brightnessctl:*)",
"Bash(wpctl:*)",
"Bash(nmcli:*)",
"Bash(bluetoothctl:*)",
"Bash(hyprctl:*)",
"Bash(playerctl:*)",
"Bash(brightnessctl:*)",
"Bash(cat:*)",
"Bash(date:*)",
"Bash(uptime:*)",
"Bash(free:*)",
"Bash(df:*)",
"Bash(pgrep:*)",
"Bash(ls:*)",
"Bash(free:*)",
"Bash(grep:*)",
"Bash(omarchy-jarvis note:*)",
"Edit(memory/**)",
"Write(memory/**)",
"Bash(timedatectl:*)",
"WebSearch",
"Read(~/Jarvis/**)",
"Edit(~/Jarvis/todo/**)",
"Edit(~/Jarvis/output/**)",
"Bash(grim:*)",
"Bash(wl-paste:*)",
"Bash(hyprctl:*)",
"Bash(ls:*)",
"Bash(macarchy-als:*)",
"Bash(macarchy-battery-limit:*)",
"Bash(nmcli:*)",
"Bash(omarchy-aquarium-toggle:*)",
"Bash(omarchy-capture-screenshot:*)",
"Bash(omarchy-jarvis lastcmd:*)",
"Bash(omarchy-jarvis missed:*)",
"Read(~/.cache/jarvis/**)"
"Bash(omarchy-jarvis note:*)",
"Bash(omarchy-jarvis routine:*)",
"Bash(omarchy-jarvis routines:*)",
"Bash(omarchy-launch:*)",
"Bash(omarchy-notification-send:*)",
"Bash(omarchy-shell:*)",
"Bash(omarchy-theme-set:*)",
"Bash(omarchy:*)",
"Bash(pgrep:*)",
"Bash(playerctl:*)",
"Bash(timedatectl:*)",
"Bash(uptime:*)",
"Bash(wl-paste:*)",
"Bash(wpctl:*)",
"Edit(memory/**)",
"Edit(~/Jarvis/output/**)",
"Edit(~/Jarvis/todo/**)",
"Read(~/.cache/jarvis/**)",
"Read(~/Jarvis/**)",
"WebSearch",
"Write(memory/**)"
],
"deny": [
"Bash(omarchy system:*)",
Expand All @@ -47,4 +49,4 @@
"Bash(pkexec:*)"
]
}
}
}
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
steps:
- uses: actions/checkout@v4

# tests/test_touchbar.py skips itself when the macarchy-dfr engine is
# tests/test_touchbar.py skips itself when the macarchy-touchbar engine is
# not around — which is always, here — but it is a pytest module and
# needs pytest importable to reach its own skip. This is the only
# dependency the suite installs, and it buys the skip, not a run.
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ Tout se pilote en ligne de commande :
`omarchy-shell phmatray.notification-center open|close|toggle`,
`omarchy-shell macarchy.control-center open|close|toggle`,
`omarchy-shell notifications toggleDnd|dndState|clear`.
- Daemons : `omarchy-battery-limit toggle|status` (plafond 80 %),
`omarchy-als toggle|status` (auto-luminosité ; status montre paused),
- Daemons : `macarchy-battery-limit toggle|status` (plafond 80 %),
`macarchy-als toggle|status` (auto-luminosité ; status montre paused),
`omarchy-aquarium-toggle [status]` (fond d'écran vivant).
- Luminosité : `brightnessctl -d apple-panel-bl set N%` (écran),
`brightnessctl -d kbd_backlight set N%` (clavier).
- Volume : `wpctl set-volume @DEFAULT_AUDIO_SINK@ N%`,
`wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle`.
- Batterie : `omarchy-battery-limit status` — la ligne contient
- Batterie : `macarchy-battery-limit status` — la ligne contient
« battery now N% ». N'essaie ni upower, ni acpi, ni /sys — pas
autorisés dans cette session.
- Réseau : `nmcli` (Wi-Fi), `bluetoothctl` (Bluetooth).
Expand Down
6 changes: 3 additions & 3 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,19 +246,19 @@ son propre groupe de processus, est ce qui rend l'annulation vraie — tuer le
groupe est ce qui atteint les sous-processus que `claude` a lancés pour ses
propres outils, ce qu'un pid nu ne fait jamais.

**Touch Bar** — avec [macarchy-dfr](https://github.com/macarchy/macarchy-dfr)
**Touch Bar** — avec [macarchy-touchbar](https://github.com/macarchy/macarchy-touchbar)
installé, le plugin est aussi un `touchbar-module` : le poisson s'installe
sur la barre elle-même, là où était la touche micro — un appui pour
déclencher, un appui long pour ouvrir sa page du Control Center. La barre
est à lui pendant qu'il travaille : elle se remplit d'un vumètre pendant
qu'il écoute (nourri par le daemon de réveil), tape le transcript au fur
et à mesure qu'il réfléchit, et tape sa réponse au fur et à mesure qu'elle
est parlée ; une ✕ interrompt. `bin/jarvis` et `jarvis-wake.py` publient
via `macarchy-dfr macarchy.jarvis state|heard|reply|level|emote|abort` —
via `macarchy-touchbar macarchy.jarvis state|heard|reply|level|emote|abort` —
rien de différent de la machine à états qui signale n'importe quel autre
auditeur. `install.sh` copie [`plugin/touchbar.py`](plugin/touchbar.py)
avec le reste du plugin, et le daemon le récupère au prochain
`macarchy-dfr reload`.
`macarchy-touchbar reload`.

## Le faire tien

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,17 +230,17 @@ has moved. That, plus running each external stage in its own process group, is
what makes cancelling real — killing the group is what reaches the
subprocesses `claude` spawned for its own tools, which a bare pid never does.

**Touch Bar** — with [macarchy-dfr](https://github.com/macarchy/macarchy-dfr)
**Touch Bar** — with [macarchy-touchbar](https://github.com/macarchy/macarchy-touchbar)
installed, the plugin is also a `touchbar-module`: the fish sits on the bar
itself, where the mic key used to be — tap to press, long-press to open his
Control Center page. The bar is his while he works: it fills with a meter
while he listens (fed by the wake daemon), types the transcript out as he
thinks, and types his reply as it is spoken; a ✕ interrupts. `bin/jarvis`
and `jarvis-wake.py` publish over
`macarchy-dfr macarchy.jarvis state|heard|reply|level|emote|abort` — no
`macarchy-touchbar macarchy.jarvis state|heard|reply|level|emote|abort` — no
different from the FSM signalling any other listener. `install.sh` copies
[`plugin/touchbar.py`](plugin/touchbar.py) with the rest of the plugin, and
the daemon picks it up on `macarchy-dfr reload`.
the daemon picks it up on `macarchy-touchbar reload`.

## Making him yours

Expand Down
10 changes: 5 additions & 5 deletions bin/jarvis
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ log() { printf '%s %s\n' "$(date +%FT%T)" "$*" >>"$LOG"; }
notify() { notify-send -a "Jarvis" -t 5000 "$1" "${2-}" 2>/dev/null || true; }

# Animate the Babel fish; harmless when the shell or plugin is not running.
# The same words reach the Touch Bar (macarchy-dfr's macarchy.jarvis module).
# The same words reach the Touch Bar (macarchy-touchbar's macarchy.jarvis module).
mascot() {
omarchy-shell macarchy.jarvis "$@" >/dev/null 2>&1 || true
touchbar "$@"
Expand All @@ -208,7 +208,7 @@ mascot() {
# The Touch Bar's share of the mascot channel. `setState` is the one verb
# NOT forwarded here: on_state_change sends the bar the raw state, because
# the bar tells followup from listening and transcribing from thinking where
# the QML aliases them. Costs one macarchy-dfr client per call (~30 ms).
# the QML aliases them. Costs one macarchy-touchbar client per call (~30 ms).
touchbar() {
local verb=$1
shift
Expand All @@ -218,7 +218,7 @@ touchbar() {
esac
# A newline inside a reply breaks the one-line IPC: collapse it to a space.
set -- "${@//$'\n'/ }"
macarchy-dfr macarchy.jarvis "$verb" "$@" >/dev/null 2>&1 || true
macarchy-touchbar macarchy.jarvis "$verb" "$@" >/dev/null 2>&1 || true
}

# Everything the body says about the CURRENT exchange goes through mascot()
Expand Down Expand Up @@ -538,8 +538,8 @@ on_state_change() {
[[ $1 == cancelling ]] || mascot setState "$sprite"
# The Touch Bar wears the raw state (its module tells followup from
# listening). The suite's hundreds of transitions land in a fake
# macarchy-dfr on its PATH, never on the real bar.
macarchy-dfr macarchy.jarvis state "$1" >/dev/null 2>&1 || true
# macarchy-touchbar on its PATH, never on the real bar.
macarchy-touchbar macarchy.jarvis state "$1" >/dev/null 2>&1 || true
# One truth about the quiet hours, and it is this one: the mascot used to
# hardcode its own 23-7 night and curled up asleep whatever the soul said.
printf '%s' "$(quiet_now && echo 1 || echo 0)" >"$STATE_DIR/quiet"
Expand Down
2 changes: 1 addition & 1 deletion bin/jarvis-fsm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#
# Eight, and the one-word name of the current one lives in $RUN_DIR/state.
# That file is a PUBLIC CONTRACT — bin/jarvis-wake.py reads it, and so does
# the out-of-repo macarchy-dfr Touch Bar daemon — so it stays exactly one
# the out-of-repo macarchy-touchbar Touch Bar daemon — so it stays exactly one
# lowercase word and nothing else, forever.
#
# idle at rest, and the only state anything autonomous may
Expand Down
4 changes: 2 additions & 2 deletions bin/jarvis_wake_level.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

jarvis-wake.py lit le micro par trames de 80 ms ; pendant que Jarvis écoute,
le RMS de chaque trame devient un niveau 0–1 publié sur la prise de
macarchy-dfr, dix fois par seconde au plus, et le vumètre de la barre bouge
macarchy-touchbar, dix fois par seconde au plus, et le vumètre de la barre bouge
avec la voix. Pas de daemon, pas de prise, une réponse lente : rien, en
silence — c'est de la décoration, et une réponse retardée lui coûte plus
qu'elle ne vaut.
Expand All @@ -26,7 +26,7 @@ def level_of(rms, floor):

def sock_path():
base = os.environ.get("XDG_RUNTIME_DIR") or f"/run/user/{os.getuid()}"
return os.path.join(base, "macarchy-dfr", "sock")
return os.path.join(base, "macarchy-touchbar", "sock")


class LevelSender:
Expand Down
8 changes: 4 additions & 4 deletions memory/HEARTBEAT_PROMPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ Une commande par appel, chacune telle qu'elle est écrite ici : une ligne
`a && b` ou `a ; b` est refusée en bloc, et `cat`, `grep` ou `ls` hors de
ce dépôt sont refusés aussi — n'essaie ni /sys, ni upower, ni acpi.

1. Batterie : `omarchy-battery-limit status` — une ligne de la forme
1. Batterie : `macarchy-battery-limit status` — une ligne de la forme
« charge window: 75-80% (battery now N%) ». Signale : sous 15 % ; ou
une fenêtre à 100 (mode voyage) — suggère de remettre la limite.
2. Disque : `df -h /` — signale au-dessus de 90 %.
3. Daemons : `omarchy-als status` (le daemon doit tourner),
`pgrep -f "macarchy-dfr daemon"` (Touch Bar — c'est un script python,
jamais `pgrep -x`), `pgrep -f omarchy-pinch`. Signale tout daemon mort.
3. Daemons : `macarchy-als status` (le daemon doit tourner),
`pgrep -f "macarchy-touchbar daemon"` (Touch Bar — c'est un script python,
jamais `pgrep -x`), `pgrep -f macarchy-pinch`. Signale tout daemon mort.
4. Mémoire : `grep -c "^- \[" memory/FAILURES.md` — seules ces lignes
sont des entrées (le reste est commentaire). Au-delà de cinq, mentionne
qu'un rêve serait bienvenu.
Expand Down
8 changes: 4 additions & 4 deletions plugin/touchbar.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Jarvis sur la Touch Bar. Chargé en mémoire par macarchy-dfr (kind
"""Jarvis sur la Touch Bar. Chargé en mémoire par macarchy-touchbar (kind
`touchbar-module`), nourri par bin/jarvis et le wake daemon à travers
`macarchy-dfr macarchy.jarvis <verbe>`.
`macarchy-touchbar macarchy.jarvis <verbe>`.

Au repos, le poisson est un bouton (tap = press, appui long = sa page du
Control Center) qui porte l'état et les émotions comme le service QML. Le
Expand All @@ -14,8 +14,8 @@
import os
import weakref

from macarchy_dfr.layout import Layout, Row
from macarchy_dfr.widgets import Button, Label, Meter, Sprite
from macarchy_touchbar.layout import Layout, Row
from macarchy_touchbar.widgets import Button, Label, Meter, Sprite

SPRITES_DIR = os.environ.get("JARVIS_SPRITES") or os.path.expanduser("~/.local/share/jarvis/sprites")
RUN_DIR = os.environ.get("JARVIS_RUN") or os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/tmp"), "jarvis")
Expand Down
5 changes: 0 additions & 5 deletions tests/bin/macarchy-dfr

This file was deleted.

5 changes: 5 additions & 0 deletions tests/bin/macarchy-touchbar
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# The Touch Bar channel, recorded instead of dropped: every state, heard,
# reply, emote and abort the FSM sends lands in $FAKE_TOUCHBAR_LOG.
printf '%s\n' "$*" >>"${FAKE_TOUCHBAR_LOG:-/dev/null}"
exit 0
16 changes: 8 additions & 8 deletions tests/run
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ sandbox() {
JARVIS_WAKE=0 \
FAKE_CLAUDE_LOG="$SB/claude.log" FAKE_PIPER_LOG="$SB/piper.log" \
FAKE_PLAY_LOG="$SB/play.log" FAKE_SHELL_LOG="$SB/shell.log" \
FAKE_DFR_LOG="$SB/dfr.log" \
FAKE_TOUCHBAR_LOG="$SB/touchbar.log" \
FAKE_VOXTYPE_LOG="$SB/voxtype.log" FAKE_NOTIFY_LOG="$SB/notify.log" \
FAKE_PACTL_LOG="$SB/pactl.log"
# The soul lives in the repository, not in the sandbox — so a test that
Expand Down Expand Up @@ -374,7 +374,7 @@ assert "groupe enregistré" waitfor test -s "$JARVIS_RUN/pids/record"
rec=$(cat "$JARVIS_RUN/pids/record" 2>/dev/null)
assert "enregistreur vivant" alive "$rec"
assert "le poisson suit" grep -q 'macarchy.jarvis setState listening' "$FAKE_SHELL_LOG"
assert "la Touch Bar suit" grep -q '^macarchy.jarvis state listening$' "$FAKE_DFR_LOG"
assert "la Touch Bar suit" grep -q '^macarchy.jarvis state listening$' "$FAKE_TOUCHBAR_LOG"
"$JARVIS" cancel
assert "état idle" state_is idle
assert "enregistreur arrêté" waitfor dead "$rec"
Expand Down Expand Up @@ -680,7 +680,7 @@ assert "la précédente reste" journal_has "Quelle heure est-il"
assert "le panneau ne montre plus rien" test ! -e "$JARVIS_RUN/last-ask" -a ! -e "$JARVIS_RUN/last-reply"
assert "tracé" grep -q 'oublié : « Et la météo ? »' "$JARVIS_MEMORY/trace/$(today).log"
assert "la bulle est vidée" grep -q 'macarchy.jarvis heard $' "$FAKE_SHELL_LOG"
assert "la Touch Bar aussi" grep -q '^macarchy.jarvis heard $' "$FAKE_DFR_LOG"
assert "la Touch Bar aussi" grep -q '^macarchy.jarvis heard $' "$FAKE_TOUCHBAR_LOG"
assert "sans voix en --quiet" test ! -s "$FAKE_PIPER_LOG"
assert "oublié encore" "$JARVIS" undo --quiet
assert "et plus rien à oublier" not "$JARVIS" undo --quiet
Expand Down Expand Up @@ -885,7 +885,7 @@ assert "la réponse est garée" grep -q 'disque' "$JARVIS_STATE/reply-pending"
assert "l'émotion est garée" test "$(cat "$JARVIS_STATE/emote-pending")" = worried
"$JARVIS" cancel
assert "l'aparté est arrivé" grep -q 'macarchy.jarvis reply.*disque' "$FAKE_SHELL_LOG"
assert "l'aparté est sur la Touch Bar" grep -q 'macarchy.jarvis reply.*disque' "$FAKE_DFR_LOG"
assert "l'aparté est sur la Touch Bar" grep -q 'macarchy.jarvis reply.*disque' "$FAKE_TOUCHBAR_LOG"
assert "l'émotion aussi" grep -q 'macarchy.jarvis emote worried' "$FAKE_SHELL_LOG"
assert "plus rien en attente" test ! -e "$JARVIS_STATE/reply-pending"

Expand All @@ -903,7 +903,7 @@ t "un saut de ligne dans la réponse n'écrase pas l'IPC vers la Touch Bar"
printf 'idle' >"$JARVIS_RUN/state"
FAKE_CLAUDE_REPLY=$'Deux\nlignes' "$JARVIS" ask --quiet "et alors"
assert "la Touch Bar reçoit une seule ligne aplatie" \
grep -q '^macarchy.jarvis reply Deux lignes$' "$FAKE_DFR_LOG"
grep -q '^macarchy.jarvis reply Deux lignes$' "$FAKE_TOUCHBAR_LOG"

# ---- the gesture of aborting: chantier 3
t "presser pendant la réflexion : trop tôt ne fait rien, à temps annule"
Expand Down Expand Up @@ -1007,15 +1007,15 @@ assert "aucun cas raté" python3 "$TESTS_DIR/test_wake_fsm.py"
assert "aucun cas raté sur le niveau" python3 "$TESTS_DIR/test_wake_level.py"

# ---- la Touch Bar
t "touch bar: le module, chargé par le moteur de macarchy-dfr"
# test_touchbar.py skips itself when the macarchy-dfr engine is absent, but it
t "touch bar: le module, chargé par le moteur de macarchy-touchbar"
# test_touchbar.py skips itself when the macarchy-touchbar engine is absent, but it
# needs pytest to be importable to reach that skip. A bare runner — or a
# contributor who has not installed pytest — has neither, and failing there
# says nothing about their change.
if python3 -c 'import pytest' 2>/dev/null; then
# Exit 5 is "no tests collected", which is exactly what test_touchbar.py's
# module-level skip produces when the engine is absent — a runner always,
# a laptop with a sibling macarchy-dfr never. Both 0 and 5 are the test
# a laptop with a sibling macarchy-touchbar never. Both 0 and 5 are the test
# doing its job; anything else is a real failure.
assert "les tests du module passent (ou sont sautés sans moteur)" \
bash -c 'python3 -m pytest -q "$1"; rc=$?; ((rc == 0 || rc == 5))' \
Expand Down
24 changes: 12 additions & 12 deletions tests/test_touchbar.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
"""Le poisson sur la Touch Bar, sans Touch Bar : le module plugin/touchbar.py
chargé par le moteur de macarchy-dfr en mémoire, avec une horloge factice.
chargé par le moteur de macarchy-touchbar en mémoire, avec une horloge factice.

Le moteur n'est pas une dépendance de Jarvis : sans lui (pas de
`macarchy-dfr` sur le PATH, pas de dépôt voisin), ces tests sont sautés."""
`macarchy-touchbar` sur le PATH, pas de dépôt voisin), ces tests sont sautés."""
import importlib.util
import os
import shutil
Expand All @@ -17,28 +17,28 @@

def _engine_root():
"""The first candidate that actually holds the engine: the real
`macarchy-dfr` on PATH (not a test's own fake, which has no
`macarchy_dfr/modules.py`), else the sibling checkout."""
exe = shutil.which("macarchy-dfr")
`macarchy-touchbar` on PATH (not a test's own fake, which has no
`macarchy_touchbar/modules.py`), else the sibling checkout."""
exe = shutil.which("macarchy-touchbar")
candidates = []
if exe:
candidates.append(os.path.dirname(os.path.dirname(os.path.realpath(exe))))
candidates.append(os.path.join(os.path.dirname(os.path.dirname(HERE)), "macarchy-dfr"))
candidates.append(os.path.join(os.path.dirname(os.path.dirname(HERE)), "macarchy-touchbar"))
for root in candidates:
if os.path.exists(os.path.join(root, "macarchy_dfr", "modules.py")):
if os.path.exists(os.path.join(root, "macarchy_touchbar", "modules.py")):
return root
return None


ROOT = _engine_root()
if ROOT is None:
pytest.skip("macarchy-dfr introuvable", allow_module_level=True)
pytest.skip("macarchy-touchbar introuvable", allow_module_level=True)
sys.path.insert(0, ROOT)

import cairo # noqa: E402
from macarchy_dfr.loop import EventLoop # noqa: E402
from macarchy_dfr.modules import ModuleHost, ModuleSpec, Registry # noqa: E402
from macarchy_dfr.widgets import Button, Label, Meter, Sprite # noqa: E402
from macarchy_touchbar.loop import EventLoop # noqa: E402
from macarchy_touchbar.modules import ModuleHost, ModuleSpec, Registry # noqa: E402
from macarchy_touchbar.widgets import Button, Label, Meter, Sprite # noqa: E402

SHEETS = {"idle": 6, "listening": 2, "thinking": 3, "speaking": 4, "cancel": 4, "sleeping": 4,
"tired": 2, "dnd": 2, "worried": 2, "proud": 2, "curious": 2, "celebrate": 3}
Expand Down Expand Up @@ -313,7 +313,7 @@ def test_speaking_types_the_reply_and_shows_the_tail_when_it_overflows(sprites):
rig.ipc("reply", "Il est dix heures.")
lay = rig.scene()
text = _text(lay)
from macarchy_dfr.geometry import Rect
from macarchy_touchbar.geometry import Rect
text.rect = Rect(0, 0, 200, 60) # narrow on purpose (fake measure = 8 px/char)
rig.advance(0.5)
assert text.text == "Il est dix heures."
Expand Down
2 changes: 1 addition & 1 deletion tests/test_wake_level.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""Le niveau de l'oreille vers la Touch Bar : l'échelle et l'envoi, sans
micro ni barre — une prise Unix éphémère joue macarchy-dfr."""
micro ni barre — une prise Unix éphémère joue macarchy-touchbar."""
import os
import socket
import sys
Expand Down
Loading