@rm0nroe/coach-claw 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +311 -0
  3. package/coach/README.md +99 -0
  4. package/coach/bin/aggregate_facets.py +274 -0
  5. package/coach/bin/analyze.py +678 -0
  6. package/coach/bin/bank.py +247 -0
  7. package/coach/bin/banner_themes.py +645 -0
  8. package/coach/bin/coach_paths.py +33 -0
  9. package/coach/bin/coexistence_check.py +129 -0
  10. package/coach/bin/configure.py +245 -0
  11. package/coach/bin/cron_check.py +81 -0
  12. package/coach/bin/default_statusline.py +135 -0
  13. package/coach/bin/doctor.py +663 -0
  14. package/coach/bin/insights-llm.sh +264 -0
  15. package/coach/bin/insights.sh +163 -0
  16. package/coach/bin/insights_window.py +111 -0
  17. package/coach/bin/marker_io.py +154 -0
  18. package/coach/bin/merge.py +671 -0
  19. package/coach/bin/redact.py +86 -0
  20. package/coach/bin/render_env.py +148 -0
  21. package/coach/bin/reward_hints.py +87 -0
  22. package/coach/bin/run-insights.sh +20 -0
  23. package/coach/bin/run_with_lock.py +85 -0
  24. package/coach/bin/scoring.py +260 -0
  25. package/coach/bin/skill_inventory.py +215 -0
  26. package/coach/bin/stats.py +459 -0
  27. package/coach/bin/status.py +293 -0
  28. package/coach/bin/statusline_self_patch.py +205 -0
  29. package/coach/bin/statusline_variants.py +146 -0
  30. package/coach/bin/statusline_wrap.py +244 -0
  31. package/coach/bin/statusline_wrap_action.py +460 -0
  32. package/coach/bin/switch_to_plugin.py +256 -0
  33. package/coach/bin/themes.py +256 -0
  34. package/coach/bin/user_config.py +176 -0
  35. package/coach/bin/xp_accounting.py +98 -0
  36. package/coach/changelog.md +4 -0
  37. package/coach/default-statusline-command.sh +19 -0
  38. package/coach/default-statusline-wrap-command.sh +15 -0
  39. package/coach/profile.yaml +37 -0
  40. package/coach/tests/conftest.py +13 -0
  41. package/coach/tests/test_aggregate_facets.py +379 -0
  42. package/coach/tests/test_analyze_aggregate.py +153 -0
  43. package/coach/tests/test_analyze_redaction.py +105 -0
  44. package/coach/tests/test_analyze_strengths.py +165 -0
  45. package/coach/tests/test_bank_atomic_write.py +61 -0
  46. package/coach/tests/test_bank_concurrency.py +126 -0
  47. package/coach/tests/test_banner_themes.py +981 -0
  48. package/coach/tests/test_celebrate_dedup.py +409 -0
  49. package/coach/tests/test_coach_paths.py +50 -0
  50. package/coach/tests/test_coexistence_check.py +128 -0
  51. package/coach/tests/test_configure.py +258 -0
  52. package/coach/tests/test_cron_check.py +118 -0
  53. package/coach/tests/test_cron_nudge_hook.py +134 -0
  54. package/coach/tests/test_detection_parity.py +105 -0
  55. package/coach/tests/test_doctor.py +595 -0
  56. package/coach/tests/test_hook_bespoke_dispatch.py +288 -0
  57. package/coach/tests/test_hook_module_resolution.py +116 -0
  58. package/coach/tests/test_hook_relevance.py +996 -0
  59. package/coach/tests/test_hook_render_env.py +364 -0
  60. package/coach/tests/test_hook_session_id_guard.py +160 -0
  61. package/coach/tests/test_insights_llm.py +759 -0
  62. package/coach/tests/test_insights_llm_venv_path.py +109 -0
  63. package/coach/tests/test_insights_window.py +237 -0
  64. package/coach/tests/test_install.py +1150 -0
  65. package/coach/tests/test_install_pyyaml_fallback.py +142 -0
  66. package/coach/tests/test_marker_consumption.py +167 -0
  67. package/coach/tests/test_marker_writer_locking.py +305 -0
  68. package/coach/tests/test_merge.py +413 -0
  69. package/coach/tests/test_no_broken_mktemp.py +90 -0
  70. package/coach/tests/test_render_env.py +137 -0
  71. package/coach/tests/test_render_env_glyphs.py +119 -0
  72. package/coach/tests/test_reward_hints.py +59 -0
  73. package/coach/tests/test_scoring.py +147 -0
  74. package/coach/tests/test_session_start_weekly_trigger.py +92 -0
  75. package/coach/tests/test_skill_inventory.py +368 -0
  76. package/coach/tests/test_stats_hybrid.py +142 -0
  77. package/coach/tests/test_status_accounting.py +41 -0
  78. package/coach/tests/test_statusline_failsafe.py +70 -0
  79. package/coach/tests/test_statusline_self_patch.py +261 -0
  80. package/coach/tests/test_statusline_variants.py +110 -0
  81. package/coach/tests/test_statusline_wrap.py +196 -0
  82. package/coach/tests/test_statusline_wrap_action.py +408 -0
  83. package/coach/tests/test_switch_to_plugin.py +360 -0
  84. package/coach/tests/test_themes.py +104 -0
  85. package/coach/tests/test_user_config.py +160 -0
  86. package/coach/tests/test_wrap_announce_hook.py +130 -0
  87. package/coach/tests/test_xp_accounting.py +55 -0
  88. package/hooks/coach-session-start.py +536 -0
  89. package/hooks/coach-user-prompt.py +2288 -0
  90. package/install-launchd.sh +102 -0
  91. package/install.sh +597 -0
  92. package/launchd/com.local.claude-coach.plist.template +34 -0
  93. package/launchd/run-insights.sh +20 -0
  94. package/npm/coach-claw.js +259 -0
  95. package/package.json +52 -0
  96. package/requirements.txt +11 -0
  97. package/settings-snippet.json +31 -0
  98. package/skills/coach/SKILL.md +107 -0
  99. package/skills/coach-insights/SKILL.md +78 -0
  100. package/skills/config/SKILL.md +149 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryan Monroe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,311 @@
1
+ # Coach Claw
2
+
3
+ > [**rm0nroe.github.io/coach-claw →**](https://rm0nroe.github.io/coach-claw/)  ·  live demo with the scroll-pinned tip walkthrough
4
+
5
+ A self-evolving coaching layer for [Claude Code](https://claude.com/claude-code). It learns
6
+ from your actual session transcripts and nudges your habits — both the ones to break and
7
+ the ones to keep going — with XP-rewarded ambient tips inside Claude Code.
8
+
9
+ A daily background pass (the deterministic Coach insights cron) detects concrete behavior
10
+ patterns from your transcripts. The `SessionStart` hook injects the watch-list into Claude's
11
+ context. The `UserPromptSubmit` hook runs a deterministic scheduler — picks a tip, pre-computes
12
+ its reward attribution, asks Claude to render it. Follow the tip → next turn shows `✅ Tip cleared`.
13
+ Five clean Coach insights runs in a row → the weakness graduates and retires. Lifetime XP
14
+ crosses a threshold → 🎉 Level up.
15
+
16
+ **Cron path: local-only, zero network.** Transcripts and the profile live under `~/.claude/`.
17
+ The scheduled cron pass is pure Python — no LLM call, no token cost. **Weekly path + on-demand
18
+ `/coach-insights`** triggers Claude Code's built-in `/insights` once per 7 days (via `claude -p`)
19
+ to refresh structured `facets/*.json` sidecar data. Coach itself does not independently upload
20
+ transcripts; the nested `/insights` refresh is an Anthropic-side Claude Code operation that
21
+ runs inside the user's existing authenticated session and writes only to local sidecar files.
22
+ Coach reads those local sidecars. The LLM call's content output is discarded — only the sidecar
23
+ refresh matters. Detections come from deterministic Python aggregation of stable enum keys.
24
+
25
+ ---
26
+
27
+ ## Install
28
+
29
+ Coach Claw ships two parallel distributions. Pick whichever you prefer
30
+ — they share the same `~/.claude/coach/` state, so you can use either
31
+ or both:
32
+
33
+ | Path | Best for | Command |
34
+ |---|---|---|
35
+ | **npm CLI** (canonical) | Provider-agnostic + OS-side cron + statusLine | `npx @rm0nroe/coach-claw@latest install` |
36
+ | **Claude Code plugin** | Marketplace install, no terminal needed | `/plugin marketplace add rm0nroe/coach-claw-plugin-marketplace` then `/plugin install coach-claw@coach-claw-plugins` |
37
+
38
+ The npm CLI is the canonical path — it manages launchd/cron registration,
39
+ patches the main statusline, and works regardless of which AI coding
40
+ assistant you're using day-to-day. The plugin is a Claude-Code-native
41
+ alternative that self-installs hooks and statusline; it nudges you to
42
+ the npm CLI for OS-level scheduling that the plugin model can't
43
+ register on its own. When both are installed, the plugin defers to the
44
+ CLI by default (run `/coach-claw:switch` to flip control).
45
+
46
+ ### Prerequisites (both paths)
47
+
48
+ - macOS 11+ or Linux
49
+ - `bash`, `git`, Python 3.8+ (system or Homebrew)
50
+ - [Claude Code](https://claude.com/claude-code) installed and run at least once
51
+ - PyYAML — the npm installer auto-installs it; the plugin path
52
+ provisions a per-plugin venv on first SessionStart. See
53
+ [Troubleshooting](#troubleshooting) if either path's PyYAML setup fails.
54
+
55
+ ### Install via npm CLI
56
+
57
+ 1. **Check your machine**
58
+
59
+ ```bash
60
+ npx @rm0nroe/coach-claw@latest doctor
61
+ ```
62
+
63
+ 2. **Run the installer**
64
+
65
+ ```bash
66
+ npx @rm0nroe/coach-claw@latest install --seed
67
+ ```
68
+
69
+ `--seed` is optional but recommended — it pre-populates your profile
70
+ from the last 7 days of transcripts so your first session isn't empty.
71
+ See [Install options](#install-options) for the full flag list.
72
+
73
+ 3. **Register the daily insights cron**
74
+
75
+ - **macOS**:
76
+ ```bash
77
+ npx @rm0nroe/coach-claw@latest launchd
78
+ ```
79
+ - **Linux** — `crontab -e`, then add (runs daily at 04:00 local):
80
+ ```
81
+ 0 4 * * * $HOME/.claude/coach/bin/insights.sh 1d >> /tmp/claude-coach.log 2>&1
82
+ ```
83
+
84
+ 4. **Verify**
85
+
86
+ ```bash
87
+ npx @rm0nroe/coach-claw@latest doctor
88
+ ```
89
+
90
+ Open a fresh Claude Code session and run:
91
+
92
+ ```
93
+ /coach status
94
+ ```
95
+
96
+ You should see your level, ELO, and (if `--seed` was passed) a
97
+ tracked weakness or two.
98
+
99
+ 5. **(Optional) Customize the look**
100
+
101
+ ```bash
102
+ npx @rm0nroe/coach-claw@latest config wizard
103
+ ```
104
+
105
+ Interactive picker for statusline variant + theme. Or
106
+ non-interactive:
107
+
108
+ ```bash
109
+ npx @rm0nroe/coach-claw@latest config set --theme ocean --statusline pips
110
+ ```
111
+
112
+ Same backing file as the `/config` slash command inside Claude
113
+ Code — `~/.claude/coach/.user_config.json`. Run either surface
114
+ any time, they don't conflict.
115
+
116
+ ### Install options
117
+
118
+ | Flag | Effect |
119
+ |---|---|
120
+ | `--seed` (alias `--bootstrap`) | After install, run a 7-day insights pass against your existing transcripts so the profile isn't empty on day one. |
121
+ | `--no-seed` | Skip seeding explicitly (forward-compatible with a future TTY-gated seed prompt). Mutually exclusive with `--seed`. |
122
+ | `--fresh` | Skip recovery from a prior `/coach uninstall`. Forces a true fresh install (drops any `coach.bak.<ts>/` siblings). |
123
+ | `--no-prune-backups` | Keep ALL `coach.bak.<ts>/`, `settings.json.bak.<ts>`, and `hooks/*.bak.<ts>`. Default trims to the 3 most recent of each kind. |
124
+ | `-h`, `--help` | Print full usage and exit. |
125
+
126
+ ### Install via Claude Code plugin
127
+
128
+ If you'd rather install from inside Claude Code, add the marketplace
129
+ once and then install the plugin:
130
+
131
+ ```
132
+ /plugin marketplace add rm0nroe/coach-claw-plugin-marketplace
133
+ /plugin install coach-claw@coach-claw-plugins
134
+ ```
135
+
136
+ After install, restart Claude Code (or open a new session) so the
137
+ plugin's SessionStart hook can:
138
+
139
+ - bootstrap a per-plugin Python venv at `${CLAUDE_PLUGIN_DATA}/venv/`
140
+ (PyYAML lands there automatically)
141
+ - self-install the main statusline into `~/.claude/settings.json`
142
+
143
+ The plugin namespaces all skills under `/coach-claw:`:
144
+
145
+ | npm CLI command | Plugin equivalent |
146
+ |---|---|
147
+ | `/coach status` | `/coach-claw:coach status` |
148
+ | `/coach-insights` | `/coach-claw:coach-insights` |
149
+ | `/config` | `/coach-claw:config` |
150
+ | _(none — npm-only)_ | `/coach-claw:switch` (flip control to plugin if both installed) |
151
+ | _(none — npm-only)_ | `/coach-claw:doctor` (diagnose plugin state; `--remove-statusline`, `--wrap-statusline`, `--unwrap-statusline` actions) |
152
+
153
+ When Claude Code starts a session and finds a custom statusline, the
154
+ plugin auto-wraps it: your existing command keeps rendering, and the
155
+ Coach segment appends to it. Original is preserved in
156
+ `~/.claude/coach/.statusline-wrap.json`. Run
157
+ `/coach-claw:doctor --unwrap-statusline` to revert (sticky — the
158
+ plugin won't auto-rewrap). The CLI installer (`npx coach-claw install`)
159
+ does the same wrap on a fresh box.
160
+
161
+ The plugin **does not** register the daily insights cron. The first
162
+ time you start a session under a plugin-only install, you'll see a
163
+ one-time banner suggesting `npx @rm0nroe/coach-claw@latest launchd`.
164
+ The cron stays an npm-CLI feature because the plugin model has no
165
+ equivalent for OS-level scheduling.
166
+
167
+ ### Troubleshooting
168
+
169
+ **`ERROR: could not install PyYAML automatically` on macOS**
170
+
171
+ Homebrew Python 3.12+ blocks `pip install --user` per PEP 668. The
172
+ installer auto-retries with `pip … --break-system-packages` (safe for
173
+ a per-user library install — the marker exists to protect *system*
174
+ site-packages, which `--user` doesn't touch). If both attempts fail,
175
+ install PyYAML manually with one of:
176
+
177
+ ```bash
178
+ python3 -m pip install --user --break-system-packages pyyaml
179
+ # or, if you'd rather isolate it:
180
+ python3 -m venv ~/.coach-venv && ~/.coach-venv/bin/pip install pyyaml
181
+ # then re-run install.sh with that venv's python3 on PATH:
182
+ PATH=~/.coach-venv/bin:$PATH ./install.sh
183
+ ```
184
+
185
+ Then re-run `./install.sh` (the first form) or use the `PATH=…` prefix
186
+ shown above (the venv form).
187
+
188
+ **`python3 not found in PATH`**
189
+
190
+ Install Python 3.8+ via your OS package manager (`brew install python`
191
+ on macOS) and re-run.
192
+
193
+ **More**: see
194
+ [`artifacts/infrastructure.md`](./artifacts/infrastructure.md).
195
+
196
+ ---
197
+
198
+ ## Use
199
+
200
+ Inside Claude Code (slash commands):
201
+
202
+ ```
203
+ /coach status level, XP, weakness streaks, last Coach insights run
204
+ /coach off | on toggle without uninstalling
205
+ /coach uninstall mv (not rm) to ~/.claude/coach.bak.<ts>
206
+ /coach-insights manual LLM-driven pass — refreshes /insights facets
207
+ sidecars, aggregates stable enum keys, merges
208
+ into profile by default. Pass --dry-run to print
209
+ the detections JSON without merging. (Also fires
210
+ automatically once / 7d on session start.)
211
+ /config statusline variant + theme + ELO range
212
+ (4 variants × 12 themes — see /config preview)
213
+ ```
214
+
215
+ From the terminal (same backing file as `/config`):
216
+
217
+ ```
218
+ npx @rm0nroe/coach-claw@latest config preview # see all combos
219
+ npx @rm0nroe/coach-claw@latest config wizard # interactive picker
220
+ npx @rm0nroe/coach-claw@latest config set --theme ocean # scriptable
221
+ npx @rm0nroe/coach-claw@latest config set --statusline pips
222
+ npx @rm0nroe/coach-claw@latest config set --elo 1200 2800
223
+ ```
224
+
225
+ Per-session kill: `COACH_DISABLE=1 claude`.
226
+
227
+ ---
228
+
229
+ ## Cost
230
+
231
+ | | Input tokens / session | Notes |
232
+ |---|---:|---|
233
+ | `SessionStart` block | ~2,400 | once per session, prompt-cacheable |
234
+ | Tip when fired | ~530 | 1–3 / session, probability + cooldown gated |
235
+ | Scheduled cron insights pass | **0** | pure Python, runs daily at 04:00 |
236
+ | Weekly LLM-driven trigger | one Claude session / 7 days | spawns `claude -p "/insights"` for the side effect of refreshing sidecars; output discarded |
237
+ | On-demand `/coach-insights` | one Claude session per `--force` invocation | same wrapper as the weekly trigger |
238
+ | Statusline + `/coach status` | **0** | pure Python |
239
+
240
+ ≈ $0.03–$0.10/day on Sonnet, $0.15–$0.50/day on Opus at 5 sessions/day.
241
+ Weekly LLM trigger adds ~$0.01–$0.03/week (~$0.50/year) — we don't pay
242
+ for output tokens we don't read.
243
+
244
+ ---
245
+
246
+ ## Privacy
247
+
248
+ **Cron path (the deterministic daily insights pass):** `coach/bin/redact.py` runs before any
249
+ transcript byte reaches `analyze.py`. Strips API keys (Anthropic, OpenAI, AWS, GitHub, Slack,
250
+ Stripe, HuggingFace, npm, Google), Bearer tokens, PEM blocks, JWTs, and long hex secrets.
251
+ Over-redacts by design — false positives are cheap, leaked credentials are not. `profile.yaml`
252
+ ends up with only pattern slugs, counts, and timestamps. Nothing leaves your machine.
253
+
254
+ **Weekly path + on-demand `/coach-insights`:** triggers Claude Code's built-in `/insights`
255
+ once per 7 days (via a `claude -p` subprocess). That's an Anthropic-side LLM step you're already
256
+ inside of by virtue of running Claude Code — Coach just runs it for the side effect of refreshing
257
+ `~/.claude/usage-data/facets/*.json`. We discard the CLI's content output and read only the
258
+ locally-written sidecar JSON Anthropic produces. Detections derive from stable enum keys
259
+ (`friction_counts.*`, `primary_success`) — deterministic Python aggregation, no slug translation,
260
+ no fuzzy matching. `profile.yaml` still ends up with only short summaries (≤120 chars, file
261
+ paths and extensions redacted); raw transcript content never lands in Coach state.
262
+
263
+ ---
264
+
265
+ ## Tunable
266
+
267
+ For display (statusline variant, level-name theme, ELO range): use
268
+ `/config`. Settings persist to `~/.claude/coach/.user_config.json`.
269
+ Tip behavior + math are still constants at the top of two files:
270
+
271
+ - `~/.claude/hooks/coach-user-prompt.py` — `TIP_FIRE_PROBABILITY`, cooldowns, label rotation
272
+ - `~/.claude/coach/bin/merge.py` — confidence math, debounce, decay, graduation thresholds
273
+
274
+ ---
275
+
276
+ ## Failsafe
277
+
278
+ Both hooks wrap `main()` in `try/except` and always `exit 0`. A coach bug cannot break a
279
+ Claude Code session.
280
+
281
+ ---
282
+
283
+ ## Tests
284
+
285
+ ```bash
286
+ cd ~/.claude/coach && python3 -m pytest tests/
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Docs
292
+
293
+ - [`artifacts/architecture.md`](./artifacts/architecture.md) — full loop, components,
294
+ data model, scheduler internals, design rationale
295
+ - [`artifacts/infrastructure.md`](./artifacts/infrastructure.md) — install, runbook,
296
+ Linux cron, launchd, troubleshooting, config reference
297
+ - [`BACKLOG.md`](./BACKLOG.md) — open work
298
+ - [`CONTRIBUTING.md`](./CONTRIBUTING.md) — test-first workflow, code style, PR expectations
299
+ - [`CLAUDE.md`](./CLAUDE.md) — guidance for Claude Code sessions on this codebase
300
+ - [`AGENTS.md`](./AGENTS.md) — project structure + build commands
301
+
302
+ ---
303
+
304
+ ## License
305
+
306
+ MIT — see [`LICENSE`](./LICENSE).
307
+
308
+ ## Acknowledgments
309
+
310
+ The lobster mark in `artifacts/social-preview.png` is adapted from
311
+ [Twemoji](https://github.com/jdecked/twemoji) (codepoint U+1F99E), CC-BY 4.0.
@@ -0,0 +1,99 @@
1
+ # Coach Claw — data directory
2
+
3
+ This directory is the live state of your coach: your tracked patterns,
4
+ lifetime XP, banked sessions, and one-line-per-run changelog. Everything
5
+ here is git-tracked (so profile mutations are rollback-able commits)
6
+ and stays local to your machine — Coach never independently uploads
7
+ `profile.yaml`, the changelog, the session ledger, or any redacted
8
+ example. (The weekly LLM path invokes `claude -p "/insights"` for the
9
+ side effect of refreshing local sidecars in `~/.claude/usage-data/`;
10
+ that goes through Claude Code, not Coach. See
11
+ `artifacts/infrastructure.md` § Security.)
12
+
13
+ ## Files
14
+
15
+ | File | Purpose |
16
+ |---|---|
17
+ | `profile.yaml` | Active + probationary + candidate entries, graduated patterns, neutral archives, skill hints, split XP accounting (`session_banked_xp`, `milestone_xp`, `graduation_xp`, `manual_adjustments`). Source of truth. |
18
+ | `changelog.md` | One line per `/coach-insights` run — what got added, promoted, retired. |
19
+ | `banked_sessions.json` | Per-session XP ledger, keyed by session UUID. Populated by `bank.py` at SessionStart. |
20
+ | `log.ndjson` | Bounded redacted `tip_fired` / `tip_completed` events. No transcript text, tool commands, examples, or generated tip prose. |
21
+ | `.disabled` | Flag file — if present, both hooks exit silently. Removed by `/coach on`. |
22
+ | `.lock` | `flock` file used by `merge.py` + `bank.py` for profile writes. |
23
+ | `.pending_*` | Markers the hook consumes on next turn (graduation, streak tick, regression, level-up). |
24
+ | `.tip_state.json` | Scheduler state — last-fire timestamps, pending completions, global cooldown. |
25
+ | `.level_state.json` | High-water-mark level index; never moves downward (prevents false re-celebration). |
26
+
27
+ ## Control
28
+
29
+ ```bash
30
+ /coach on # remove .disabled
31
+ /coach off # write .disabled (hooks silent until re-enabled)
32
+ /coach status # level, XP breakdown, per-pattern streaks, last /coach-insights run
33
+ /coach uninstall # move this data dir to ~/.claude/coach.bak.<ts>/
34
+ COACH_DISABLE=1 claude # one-shot bypass without writing state
35
+ ```
36
+
37
+ ## Backup hygiene
38
+
39
+ Re-running `npx @rm0nroe/coach-claw@latest install` or `./install.sh` snapshots
40
+ the existing coach dir to
41
+ `~/.claude/coach.bak.<ts>/` and leaves byte-different `settings.json` /
42
+ `hooks/*.py` snapshots beside their live counterparts. Byte-identical
43
+ re-installs do not leave behind redundant `.bak.<ts>` siblings, and the
44
+ installer prunes by default — keeping the 3 most recent of each kind
45
+ (`coach.bak.*`, `settings.json.bak.*`, `hooks/<hook>.bak.*`) so backups
46
+ don't accumulate over months of upgrades.
47
+
48
+ To opt out (e.g. holding a specific `.bak` for forensic recovery):
49
+
50
+ ```bash
51
+ ./install.sh --no-prune-backups
52
+ ```
53
+
54
+ Manual equivalent of the prune for a one-shot cleanup outside the
55
+ installer:
56
+
57
+ ```bash
58
+ ls -dt ~/.claude/coach.bak.* | tail -n +4 | xargs rm -rf
59
+ ls -t ~/.claude/settings.json.bak.* | tail -n +4 | xargs rm -f
60
+ ls -t ~/.claude/hooks/*.bak.* | tail -n +4 | xargs rm -f
61
+ ```
62
+
63
+ ## Reinstall & Uninstall Safety
64
+
65
+ Re-running `npx @rm0nroe/coach-claw@latest install` or `./install.sh` from the
66
+ bundle preserves coach state, including XP, tracked patterns, the session
67
+ ledger, cooldowns, pending markers, and the disabled flag. `/coach uninstall`
68
+ is reversible: it renames this directory to
69
+ `~/.claude/coach.bak.<timestamp>/` instead of deleting it, then removes the
70
+ coach hook entries from `settings.json`.
71
+
72
+ For a clean setup test, uninstall, run `npx @rm0nroe/coach-claw@latest install --seed`
73
+ or `./install.sh --seed`, verify the new install, then copy the saved progress
74
+ files from the `coach.bak.<timestamp>/` directory back into
75
+ `~/.claude/coach/`. For an exact restore, also copy hidden runtime state files
76
+ if present: `.level_state.json`, `.tip_state.json`, `.last_session_start`, and
77
+ `log.ndjson`.
78
+
79
+ ## Rollback
80
+
81
+ Every `/coach-insights` run is a commit in `~/.claude/coach/`:
82
+
83
+ ```bash
84
+ git -C ~/.claude/coach log --oneline | head
85
+ git -C ~/.claude/coach diff HEAD~1 -- profile.yaml # see what changed
86
+ git -C ~/.claude/coach checkout HEAD~1 -- profile.yaml # roll back one run
87
+ ```
88
+
89
+ Hand-edits are welcome — the merge logic preserves explicit fields. If
90
+ a tracked weakness is wrong, edit the nudge or delete the entry directly.
91
+
92
+ ## Tests
93
+
94
+ The parent install also copied `tests/` here. After you have `pytest`
95
+ installed (`python3 -m pip install --user pytest`):
96
+
97
+ ```bash
98
+ cd ~/.claude/coach && python3 -m pytest tests/
99
+ ```