feat(kvb-coach): run the short coach at the first prompt of every full hour
🧪 Test / 🧪 Lock + lint (push) Canceled after 0s

hooks/reminder.sh claims the current clock hour with an atomic mkdir under ~/.claude/.coach-hours/, so only the first prompt of the hour across all agents gets the COACH HOUR hint (at most three lines, most urgent area first, then the request); earlier hours are removed. enable-plugins.js registers it next to the kvb-dream reminder. test_coach covers once per hour, silence within the hour and the cleanup; test_dream checks the single registration. Operator order 2026-11-03.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-03 16:14:46 +01:00
1 parent 5f30644ca6
commit 735bbc9b77
6 files changed
+53 -2

No files matched your search

+28
View File
@@ -11,6 +11,7 @@ from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SCRIPT = REPO_ROOT / "skills" / "kvb-coach" / "scripts" / "coach.py"
HOOK = REPO_ROOT / "skills" / "kvb-coach" / "hooks" / "reminder.sh"
class TestCoach(unittest.TestCase):
@@ -62,5 +63,32 @@ class TestCoach(unittest.TestCase):
self.assertEqual(self.profile.read_text(encoding="utf-8"), "{broken")
class TestCoachHour(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.hours = Path(self.tmp.name) / "hours"
def _run(self, hour: str) -> str:
return subprocess.run(
["bash", str(HOOK)],
check=True,
capture_output=True,
text=True,
env={
"PATH": "/usr/bin:/bin",
"HOME": self.tmp.name,
"COACH_STAMP_DIR": str(self.hours),
"COACH_HOUR": hour,
},
).stdout
def test_first_prompt_of_each_hour_triggers_the_coach_once(self):
self.assertIn("kvb-coach", self._run("2026-11-03T14"))
self.assertEqual(self._run("2026-11-03T14"), "")
self.assertIn("kvb-coach", self._run("2026-11-03T15"))
self.assertEqual(sorted(p.name for p in self.hours.iterdir()), ["2026-11-03T15"])
if __name__ == "__main__":
unittest.main()
+1
View File
@@ -148,6 +148,7 @@ class TestSettingsRegistration(unittest.TestCase):
data = self._patch()
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-coach/hooks/reminder.sh"), 1)
self.assertNotIn("tune/hooks/reminder.sh", entries)
gate = json.dumps(data["hooks"]["PreToolUse"])
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)