whats-cc-doing 0.2.0__tar.gz

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 (42) hide show
  1. whats_cc_doing-0.2.0/.gitignore +11 -0
  2. whats_cc_doing-0.2.0/CHANGELOG.md +131 -0
  3. whats_cc_doing-0.2.0/LICENSE +21 -0
  4. whats_cc_doing-0.2.0/PKG-INFO +251 -0
  5. whats_cc_doing-0.2.0/README.md +224 -0
  6. whats_cc_doing-0.2.0/pyproject.toml +66 -0
  7. whats_cc_doing-0.2.0/src/whats_cc_doing/__init__.py +10 -0
  8. whats_cc_doing-0.2.0/src/whats_cc_doing/__main__.py +11 -0
  9. whats_cc_doing-0.2.0/src/whats_cc_doing/cli.py +777 -0
  10. whats_cc_doing-0.2.0/src/whats_cc_doing/config.py +192 -0
  11. whats_cc_doing-0.2.0/src/whats_cc_doing/dash.py +325 -0
  12. whats_cc_doing-0.2.0/src/whats_cc_doing/drift.py +154 -0
  13. whats_cc_doing-0.2.0/src/whats_cc_doing/harness.py +567 -0
  14. whats_cc_doing-0.2.0/src/whats_cc_doing/registry.py +109 -0
  15. whats_cc_doing-0.2.0/src/whats_cc_doing/render.py +359 -0
  16. whats_cc_doing-0.2.0/src/whats_cc_doing/serve.py +323 -0
  17. whats_cc_doing-0.2.0/src/whats_cc_doing/signals.py +689 -0
  18. whats_cc_doing-0.2.0/src/whats_cc_doing/status_json.py +68 -0
  19. whats_cc_doing-0.2.0/src/whats_cc_doing/templates/nudge-message.md +28 -0
  20. whats_cc_doing-0.2.0/src/whats_cc_doing/tui.py +583 -0
  21. whats_cc_doing-0.2.0/src/whats_cc_doing/util.py +39 -0
  22. whats_cc_doing-0.2.0/src/whats_cc_doing/verdict.py +65 -0
  23. whats_cc_doing-0.2.0/src/whats_cc_doing/watchdog.py +741 -0
  24. whats_cc_doing-0.2.0/templates/ccdoing.example.yaml +147 -0
  25. whats_cc_doing-0.2.0/templates/ccdoing.service +31 -0
  26. whats_cc_doing-0.2.0/templates/cron.txt +8 -0
  27. whats_cc_doing-0.2.0/templates/nudge-message.md +28 -0
  28. whats_cc_doing-0.2.0/tests/__init__.py +0 -0
  29. whats_cc_doing-0.2.0/tests/conftest.py +125 -0
  30. whats_cc_doing-0.2.0/tests/test_config_and_cli.py +189 -0
  31. whats_cc_doing-0.2.0/tests/test_dash.py +235 -0
  32. whats_cc_doing-0.2.0/tests/test_drift.py +262 -0
  33. whats_cc_doing-0.2.0/tests/test_harness.py +123 -0
  34. whats_cc_doing-0.2.0/tests/test_harness_discovery.py +240 -0
  35. whats_cc_doing-0.2.0/tests/test_publish_fixes.py +262 -0
  36. whats_cc_doing-0.2.0/tests/test_render_ui.py +229 -0
  37. whats_cc_doing-0.2.0/tests/test_review_fixes.py +447 -0
  38. whats_cc_doing-0.2.0/tests/test_signals.py +269 -0
  39. whats_cc_doing-0.2.0/tests/test_tui_keys.py +81 -0
  40. whats_cc_doing-0.2.0/tests/test_verdict_and_render.py +120 -0
  41. whats_cc_doing-0.2.0/tests/test_view_serve_registry.py +325 -0
  42. whats_cc_doing-0.2.0/tests/test_watchdog.py +489 -0
@@ -0,0 +1,11 @@
1
+ .work/
2
+ __pycache__/
3
+ *.pyc
4
+ .venv/
5
+ reports/
6
+ dist/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .ccdoing/
10
+ ccdoing.yaml.bak
11
+ .claude/
@@ -0,0 +1,131 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is
4
+ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
+ this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.2.0] - 2026-08-30
8
+
9
+ Second live-install round: fixed the TUI arrow keys (escape sequences
10
+ were split by buffered stdin reads - raw-fd reads + a pure decoder now;
11
+ j/k work too), renamed the side-by-side web feature to Multi-view
12
+ (`/multi`), added daemonized serving (`ccdoing serve --all --daemon`,
13
+ `serve stop`, `serve status`, pidfile-tracked with restart semantics and
14
+ prominent URLs), a `python -m whats_cc_doing` entry point, a setup-skill
15
+ phase that offers to start the web server, new `/ccdoing:serve` and
16
+ `/ccdoing:help` skills, and a skill-level CLI-vs-plugin version check
17
+ (a stale installed build bit the second live test).
18
+
19
+ Publish-readiness pass from an independent code review: `serve --daemon`
20
+ forwards the resolved `--config` to its respawned child (it previously
21
+ resolved config from the child's cwd); escalation-tier retry semantics
22
+ are a structured `retryable` flag on ActionResult instead of matching
23
+ detail-string wording (a transient nudge launch failure no longer
24
+ consumes the tier for the whole quiet episode); quiet duration persists
25
+ correctly across ticks; drift bookkeeping keys can no longer collide for
26
+ duplicate type:label signal pairs; concurrent ticks write unique temp
27
+ files; cron/systemd install output quotes spaced paths; sdist excludes
28
+ keep dev files, lockfiles, and the author's local config out of the
29
+ published tarball; README links work on PyPI; the setup skill installs
30
+ the CLI from the plugin root first (works before and after PyPI
31
+ publication).
32
+
33
+ ## [0.1.0] - 2026-08-29
34
+
35
+ E2E dry-run polish (pre-release): manifests corrected to the nudge-only
36
+ design (no more "resume-first" phrasing anywhere), local-checkout install
37
+ path documented, `init` suggests a masquerade-proof `min_items` for
38
+ json_headline, unambiguous notify dry-run wording, doctor tick labels,
39
+ and relative-path/pgrep-self-match cautions in the skills and example
40
+ config.
41
+
42
+ First release. Extracted and generalized from a larger private
43
+ project's internal status page, then hardened by a five-agent review
44
+ round (adversarial code review, real-project field test, release audit)
45
+ before ever being published - see BUILD-LOG.md for that story.
46
+
47
+ ### Added
48
+
49
+ - Persistent notify targets (2026-08-30, from the first live install
50
+ test): notification URLs now live in `.ccdoing/notify.urls` (one
51
+ apprise URL per line, `#` comments), read on every tick so
52
+ cron/systemd watchdogs need no environment plumbing;
53
+ `$CCDOING_NOTIFY_URLS` overrides the file when set. `ccdoing init`
54
+ scaffolds the file and suggests `.gitignore` coverage;
55
+ `test-escalation --tier notify` prints the resolved source, target
56
+ URLs, and ntfy subscribe links. The setup skill now shows the
57
+ topic/subscribe link BEFORE any test notification and prints the full
58
+ nudge message before asking for approval, offers to start
59
+ not-yet-running services before the verification tick, and offers
60
+ "browser notifications" as a transport.
61
+ - Browser notifications (2026-08-30): opt-in, pure viewer-side toggle on
62
+ the status page and the dashboard - fires on verdict transitions
63
+ (into QUIET/DOWN/STUCK and on recovery to ACTIVE) while a page is
64
+ open; per-project last-seen state in localStorage, feature-detected
65
+ (hidden on `file://`).
66
+ - All-projects dashboard (2026-08-30): `ccdoing view` outside a project
67
+ (or `--dash`) lists every registered project with recent activity -
68
+ title, colored verdict, "Last signal: X ago", `[stale]` when the
69
+ generator stopped - with arrow/Enter navigation into any project's
70
+ live view and back, a live day-range control (`d`, default 4 days),
71
+ and split panes on wide terminals; `ccdoing serve --all` is the web
72
+ twin - cards linking to each project's real page at `/p/<name>/`, a
73
+ client-side "active within N days" filter, and `/multi` (Multi-view) for 2-4
74
+ status pages side by side. Registered names only; per-project output
75
+ dirs only; no-store everywhere.
76
+ - Ten passive signal types (git, process, file_mtime, http, log_tail,
77
+ jsonl_log, claude_session, json_headline, ci, command) with a
78
+ never-raise contract. `json_headline` (added 2026-08-30, straight from
79
+ a staleness hit in the original internal version) reads headline metrics from
80
+ result JSONs by glob pattern - newest full run wins, `min_items` keeps
81
+ partial saves from masquerading as the battery.
82
+ - Config-drift detection: per-signal `state` (ok / no-match / stale) in
83
+ status.json and on the page, a `maintenance` summary, `ccdoing doctor
84
+ --drift` (inventory re-diff + dead-signal report; `--quiet` one-liner
85
+ wired into the session-start hook), and the `/ccdoing:tune` skill that
86
+ turns findings into approved config deltas.
87
+ - The harness adapter: semantic classification of Claude Code sessions
88
+ (WORKING / WAITING_ON / DEAD_WAIT / ABANDONED / IDLE) from on-disk
89
+ artifacts - timestamps, types, and ids only, never transcript content.
90
+ - ACTIVE / QUIET / DOWN / STUCK verdict with cause attribution;
91
+ status.json as the machine interface beside the auto-refreshing
92
+ status.html.
93
+ - Watchdog escalation ladder (log -> notify via apprise -> nudge), with
94
+ rails enforced in code: cooldown, daily cap, single-flight lock,
95
+ fenced-untrusted evidence bundles, hostile-session-id rejection.
96
+ Tier 3 is a nudge, never a resume: one informational cross-session
97
+ message (user-approved template) into a session that is provably
98
+ parked on dead work AND provably still running - the session decides;
99
+ finished sessions sitting open overnight are healthy and left alone.
100
+ Before any nudge, an idle probe (a pure notify_when_idle
101
+ subscription, zero cost to the watched session) checks whether the
102
+ session is simply finished - idle means healthy, stand down, budget
103
+ untouched.
104
+ (Redesigned 2026-08-30 from the original resume-first approach after
105
+ Nick's feedback; the healthchecks.io relay was dropped the same day -
106
+ the page now self-detects a stopped generator with a stale banner, and
107
+ systemd Restart=/cron re-entry recover the loop itself.)
108
+ - Page UI (2026-08-30): configurable `title:` (setup picks it; plain
109
+ "What's CC Doing" fallback), local human-readable times by default
110
+ (`timezone:` option), ages like "19h 8m", an at-a-glance activity
111
+ table of primary signals (ACTIVE green / inactive grey, drift-marked)
112
+ with the full weight-sorted table behind an All-signals expander.
113
+ - Session discovery by recorded working directory (2026-08-30): sessions
114
+ launched in a parent directory that work on this project are found;
115
+ session names (/rename), pid-reuse-safe process liveness, and active
116
+ subagent labels shown from dedicated metadata stores. No todo/progress
117
+ bars - Claude Code persists no todo store to disk, so none is faked.
118
+ - Terminal + serving story (2026-08-30): `ccdoing view` (ANSI live
119
+ viewer for ssh/headless, --fresh/--once), `ccdoing serve` (localhost
120
+ static server, Cache-Control: no-store on every response), and a
121
+ multi-project registry (`ccdoing projects`, `view --project NAME`).
122
+ - CLI: init (with --write-nudge-message), tick, run, status, view,
123
+ serve, projects, doctor (--arm-check), test-escalation, install
124
+ (systemd/cron with absolute paths).
125
+ - Claude Code plugin: setup / status / live skills, SessionStart
126
+ arm-check hook, self-hosting marketplace manifest.
127
+ - Privacy defaults: process command lines redacted on rendered pages;
128
+ path-scoped process patterns generated by init.
129
+ - CI: 3.11-3.13 test matrix. (The self-monitoring GitHub Pages
130
+ workflow was removed 2026-08-30 - a live-published status page makes
131
+ no sense for installers; a committed example + screenshot replace it.)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nick Rotundo
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.
@@ -0,0 +1,251 @@
1
+ Metadata-Version: 2.5
2
+ Name: whats-cc-doing
3
+ Version: 0.2.0
4
+ Summary: Passive status page + watchdog for Claude Code sessions: see what Claude is doing from observed side effects (git, processes, task files), detect stalls, and deliver the diagnosis into a provably stuck session as an informational nudge - never a resume or restart.
5
+ Project-URL: Homepage, https://github.com/nickjrotundo/whats-cc-doing
6
+ Project-URL: Repository, https://github.com/nickjrotundo/whats-cc-doing
7
+ Project-URL: Issues, https://github.com/nickjrotundo/whats-cc-doing/issues
8
+ Project-URL: Changelog, https://github.com/nickjrotundo/whats-cc-doing/blob/main/CHANGELOG.md
9
+ Author: Nick Rotundo
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,claude-code,monitoring,status-page,watchdog
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Classifier: Topic :: System :: Monitoring
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: apprise>=1.7
23
+ Requires-Dist: pyyaml>=6.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # What's CC Doing
29
+
30
+ [![tests](https://github.com/nickjrotundo/whats-cc-doing/actions/workflows/tests.yml/badge.svg)](https://github.com/nickjrotundo/whats-cc-doing/actions/workflows/tests.yml)
31
+
32
+ **A passive status page + watchdog for Claude Code sessions. Nothing
33
+ self-reports, so nothing can lie about being alive.**
34
+
35
+ `ccdoing` regenerates a status page (HTML for you, JSON for agents) from
36
+ *observed side effects* - git commits, the process table, file mtimes,
37
+ Claude Code's own on-disk session artifacts - and renders one verdict:
38
+
39
+ | Verdict | Meaning |
40
+ |---|---|
41
+ | `ACTIVE` | a primary signal moved inside the window |
42
+ | `QUIET` | nothing has moved - possibly stalled (with per-signal ages) |
43
+ | `DOWN` | a health check is failing |
44
+ | `STUCK` | a session appears parked on dead work - with the evidence attached |
45
+
46
+ When QUIET persists, an escalation ladder takes over: log, then notify,
47
+ then - if you opt in - a **nudge**: one informational message into a
48
+ session provably parked on dead work, ending with "ignore this if
49
+ you're fine." The watchdog never restarts or resumes anything - only
50
+ the session knows whether it still has work, so the session decides. A
51
+ finished session sitting open overnight is healthy and is left alone.
52
+
53
+ ## Why this exists
54
+
55
+ A long-running Claude Code subagent was off doing testing, and the TUI
56
+ gave no visual feedback about whether it was working or hung. It *was*
57
+ working - but the only way to see that was activity signals the TUI
58
+ doesn't show: fresh task output files, transcript growth, processes in
59
+ the table. This page began as visual proof of background work; the
60
+ watchdog came later, after a session-level watchdog lapsed at handoff
61
+ and a human manually checking `git log` caught an agent waiting forever
62
+ on a notification that could never arrive.
63
+
64
+ Two lessons became design decisions: any arming step someone must
65
+ remember will eventually be skipped (so the loop is OS-level
66
+ systemd/cron, re-checked at session start), and self-reported liveness
67
+ is worthless for stuck agents (so nothing here is instrumented or
68
+ subscribed - side effects don't lie). Full origin story:
69
+ [BUILD-LOG.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/BUILD-LOG.md).
70
+
71
+ ## The harness adapter (the interesting part)
72
+
73
+ The `claude_session` signal classifies every Claude Code session of a
74
+ project semantically, from artifacts Claude Code already writes:
75
+
76
+ - `WORKING` - transcript actively growing
77
+ - `WAITING_ON` - parked on background task(s) still producing output, or
78
+ whose output file a process still holds open (legitimate; not flagged)
79
+ - `DEAD_WAIT` - parked on task(s) whose output stopped moving past
80
+ threshold, with no sign of a live producer -> verdict `STUCK`, with the
81
+ evidence attached
82
+ - `ABANDONED` - would be DEAD_WAIT, but the session has been inactive
83
+ past `stuck_max_age_minutes` (default 120) - informational only; a
84
+ long-dead session is never called STUCK or nudged
85
+ - `IDLE` - nothing pending
86
+
87
+ `DEAD_WAIT` is evidence-based inference, not proof - the "waiting on a
88
+ notification that can never arrive" case heartbeat monitors structurally
89
+ cannot see. Sessions are matched by where they *work* (their recorded
90
+ working directory), not just where they started, and the page shows
91
+ session names, process liveness, and active subagent labels when
92
+ available - never transcript content. Mechanics and tradeoffs:
93
+ [DESIGN.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/DESIGN.md);
94
+ honest limits (activity is not progress):
95
+ [ANALYSIS.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/ANALYSIS.md).
96
+
97
+ ## Install & use (Claude Code)
98
+
99
+ Claude Code is the supported way to use this tool:
100
+
101
+ ```
102
+ /plugin marketplace add nickjrotundo/whats-cc-doing
103
+ /plugin install ccdoing@whats-cc-doing
104
+ ```
105
+
106
+ Then run the **setup** skill (`/ccdoing:setup`) in your project: Claude
107
+ inventories it, proposes signals, asks four questions, writes
108
+ `ccdoing.yaml`, installs the watchdog loop, fires a test notification
109
+ so you see the alert channel work *now* rather than at 3am, and - if
110
+ you enable tier 3 - shows you the complete nudge message for approval
111
+ first. Day to day: `/ccdoing:status`, `/ccdoing:serve`, `/ccdoing:live`,
112
+ `/ccdoing:help`.
113
+
114
+ The CLI underneath - install from PyPI (`uv tool install
115
+ whats-cc-doing`), from the repo (`uv tool install
116
+ git+https://github.com/nickjrotundo/whats-cc-doing`), or from a local
117
+ checkout (`uv pip install /path/to/whats-cc-doing` in a venv):
118
+
119
+ ```
120
+ ccdoing init # inventory this project, write ccdoing.yaml
121
+ ccdoing tick # one cycle: collect, render, escalate (cron-safe)
122
+ ccdoing run # the same, in a loop
123
+ ccdoing status [--fresh] # print the verdict JSON to stdout
124
+ ccdoing view [--fresh] # live terminal viewer (ssh/headless friendly)
125
+ ccdoing serve --all --daemon # background web server for the dashboard (prints link)
126
+ ccdoing serve stop|status # stop the web server / is it running + where
127
+ ccdoing projects # every registered project + last verdict
128
+ ccdoing doctor # env/config checks; --drift for config rot
129
+ ccdoing test-escalation --tier log|notify|nudge # prove the ladder works
130
+ ccdoing install [--mode cron] # print install units
131
+ ```
132
+
133
+ The CLI runs without Claude Code (generic signals work on any repo or
134
+ long-running job), but that path is untested and unsupported - you're
135
+ on your own, but feel free to try.
136
+
137
+ ## The escalation ladder
138
+
139
+ ```yaml
140
+ watchdog:
141
+ escalation:
142
+ - { after_quiet_minutes: 15, action: log }
143
+ - { after_quiet_minutes: 30, action: notify } # apprise -> anywhere
144
+ - { after_quiet_minutes: 45, action: nudge,
145
+ cooldown_minutes: 60, max_per_day: 3 }
146
+ ```
147
+
148
+ Notify targets persist in **`.ccdoing/notify.urls`** - one
149
+ [apprise](https://github.com/caronc/apprise) URL per line, read on
150
+ every tick, so a cron/systemd watchdog needs no environment plumbing
151
+ (`$CCDOING_NOTIFY_URLS` overrides it for one-off tests; keep the file
152
+ gitignored - topics and webhooks are effectively secrets). Prefer no
153
+ external service? The status page and dashboard carry an **"enable
154
+ browser notifications"** toggle - viewer-side, fires on verdict
155
+ transitions, works over `ccdoing serve` while a page is open.
156
+
157
+ Tier 3 is a **nudge, never a resume**: only for a `DEAD_WAIT` session
158
+ whose process is verifiably alive and which a zero-cost [idle
159
+ probe](https://code.claude.com/docs/en/cross-session-messaging) did not
160
+ report idle (idle means finished - the probe stands the watchdog down).
161
+ One courier delivers your pre-approved message into the running
162
+ session, which decides for itself. No `--resume`, no fresh sessions;
163
+ cooldown, daily cap, and refuse-while-running are enforced in code.
164
+
165
+ Who watches the watchdog? The page itself: it shows a red "generator
166
+ appears down" banner when it misses its own refresh, and systemd
167
+ `Restart=`/cron re-entry are the loop's recovery.
168
+
169
+ ## Outputs
170
+
171
+ - `reports/status/status.html` - dark, dependency-free, auto-refreshing.
172
+ Default view is the at-a-glance **activity table** (green ACTIVE /
173
+ grey inactive per primary signal, drift-marked when misconfigured);
174
+ full detail sits behind an "All signals" expander.
175
+ - `reports/status/status.json` - **the interface.** Agents and scripts
176
+ read this; nothing should ever scrape the HTML.
177
+
178
+ ![example status page](https://raw.githubusercontent.com/nickjrotundo/whats-cc-doing/main/docs/status-example.png)
179
+
180
+ Real files in [examples/](https://github.com/nickjrotundo/whats-cc-doing/tree/main/examples) -
181
+ this repository monitoring itself.
182
+
183
+ ### Viewing the page (headless / remote / WSL2)
184
+
185
+ 1. **`ccdoing serve --all --daemon`** - background server for the
186
+ all-projects dashboard; prints the localhost URL. `serve stop` /
187
+ `serve status` manage it; starting again restarts. WSL2: localhost
188
+ forwarding to the Windows browser usually works, but not always - if
189
+ it doesn't, open the URL in a Linux browser (e.g. WSLg-launched
190
+ Chrome).
191
+ 2. **`ccdoing view`** - live terminal viewer over ssh (`--fresh` to
192
+ generate as it views, `--once` for scripts).
193
+ 3. **Open the file** - `file://` works fully in Chromium-family
194
+ browsers - or serve `reports/status/` from your app's own static
195
+ mount with caching disabled (a cached status page is a lying one).
196
+
197
+ Many projects on one machine? `init` registers each; `ccdoing projects`
198
+ lists them with last verdicts (`--unregister NAME` to remove); each runs
199
+ its own independent loop - one registry, one viewer, no per-project
200
+ ports. And there is an **all-projects dashboard**, terminal and web:
201
+
202
+ - **TUI**: `ccdoing view --dash` (the default outside any configured
203
+ project) - title, colored verdict, "Last signal: 12m ago" per
204
+ project; arrows + Enter open one, `b` comes back, `d` sets the
205
+ activity window, `s` splits side-by-side on wide terminals, `[stale]`
206
+ marks dead generators.
207
+
208
+ ![TUI dashboard: all projects with verdicts and last-signal ages](https://raw.githubusercontent.com/nickjrotundo/whats-cc-doing/main/docs/tui-dashboard.png)
209
+
210
+ The `s` split mode, three projects at once:
211
+
212
+ ![TUI multi-view: three project panes with per-signal states](https://raw.githubusercontent.com/nickjrotundo/whats-cc-doing/main/docs/tui-multiview.png)
213
+
214
+ - **Web**: `ccdoing serve --all` - card-per-project dashboard with a
215
+ live "active within N days" filter, click-through to each live status
216
+ page, and a Multi-view grid of 2-4 pages at `/multi`. Localhost-only
217
+ by default; every response is `Cache-Control: no-store`.
218
+
219
+ Privacy note before publishing a page anywhere public: `process`
220
+ signals render command lines, redacted by default (`PID basename
221
+ (+N args)`); the `command` signal's stdout renders too - treat
222
+ `ccdoing.yaml` as config-as-code.
223
+
224
+ ## Keeping the page honest as the project changes
225
+
226
+ The original internal status page hardcoded two `/tmp` eval-JSON paths
227
+ and went stale the day after shipping. ccdoing is built to notice that
228
+ class of rot: `json_headline` signals use glob patterns + a `min_items`
229
+ shape filter (newest matching file wins); every path-shaped signal
230
+ reports `no-match`/`stale` drift states on the page and in status.json;
231
+ `ccdoing doctor --drift` re-diffs the setup-time inventory (the plugin
232
+ surfaces a one-line notice at session start); and **`/ccdoing:tune`**
233
+ turns findings into config *deltas* for approval - never rewriting
234
+ values you tuned.
235
+
236
+ ## Supported platforms
237
+
238
+ | Platform | Status |
239
+ |---|---|
240
+ | Linux | supported (developed and tested here) |
241
+ | WSL2 | supported (`systemctl --user` varies - setup verifies, cron fallback) |
242
+ | macOS | best-effort: generic signals work; Claude task outputs may live under `$TMPDIR` rather than `/tmp/claude-*` - set `task_root_glob` on the `claude_session` signal; use cron (no launchd template yet) |
243
+ | Windows (native) | unsupported (`pgrep`, POSIX paths, systemd/cron assumptions) |
244
+
245
+ ## Docs & support
246
+
247
+ - [DESIGN.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/DESIGN.md) - architecture, harness mechanics, decision log
248
+ - [ANALYSIS.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/ANALYSIS.md) - known gaps, limits, v0.2 roadmap
249
+ - [BUILD-LOG.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/BUILD-LOG.md) - origin story + how this was built (an AI-driven build, documented honestly)
250
+ - [CHANGELOG.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/CHANGELOG.md) - release history
251
+ - Questions/bugs: [GitHub Issues](https://github.com/nickjrotundo/whats-cc-doing/issues)
@@ -0,0 +1,224 @@
1
+ # What's CC Doing
2
+
3
+ [![tests](https://github.com/nickjrotundo/whats-cc-doing/actions/workflows/tests.yml/badge.svg)](https://github.com/nickjrotundo/whats-cc-doing/actions/workflows/tests.yml)
4
+
5
+ **A passive status page + watchdog for Claude Code sessions. Nothing
6
+ self-reports, so nothing can lie about being alive.**
7
+
8
+ `ccdoing` regenerates a status page (HTML for you, JSON for agents) from
9
+ *observed side effects* - git commits, the process table, file mtimes,
10
+ Claude Code's own on-disk session artifacts - and renders one verdict:
11
+
12
+ | Verdict | Meaning |
13
+ |---|---|
14
+ | `ACTIVE` | a primary signal moved inside the window |
15
+ | `QUIET` | nothing has moved - possibly stalled (with per-signal ages) |
16
+ | `DOWN` | a health check is failing |
17
+ | `STUCK` | a session appears parked on dead work - with the evidence attached |
18
+
19
+ When QUIET persists, an escalation ladder takes over: log, then notify,
20
+ then - if you opt in - a **nudge**: one informational message into a
21
+ session provably parked on dead work, ending with "ignore this if
22
+ you're fine." The watchdog never restarts or resumes anything - only
23
+ the session knows whether it still has work, so the session decides. A
24
+ finished session sitting open overnight is healthy and is left alone.
25
+
26
+ ## Why this exists
27
+
28
+ A long-running Claude Code subagent was off doing testing, and the TUI
29
+ gave no visual feedback about whether it was working or hung. It *was*
30
+ working - but the only way to see that was activity signals the TUI
31
+ doesn't show: fresh task output files, transcript growth, processes in
32
+ the table. This page began as visual proof of background work; the
33
+ watchdog came later, after a session-level watchdog lapsed at handoff
34
+ and a human manually checking `git log` caught an agent waiting forever
35
+ on a notification that could never arrive.
36
+
37
+ Two lessons became design decisions: any arming step someone must
38
+ remember will eventually be skipped (so the loop is OS-level
39
+ systemd/cron, re-checked at session start), and self-reported liveness
40
+ is worthless for stuck agents (so nothing here is instrumented or
41
+ subscribed - side effects don't lie). Full origin story:
42
+ [BUILD-LOG.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/BUILD-LOG.md).
43
+
44
+ ## The harness adapter (the interesting part)
45
+
46
+ The `claude_session` signal classifies every Claude Code session of a
47
+ project semantically, from artifacts Claude Code already writes:
48
+
49
+ - `WORKING` - transcript actively growing
50
+ - `WAITING_ON` - parked on background task(s) still producing output, or
51
+ whose output file a process still holds open (legitimate; not flagged)
52
+ - `DEAD_WAIT` - parked on task(s) whose output stopped moving past
53
+ threshold, with no sign of a live producer -> verdict `STUCK`, with the
54
+ evidence attached
55
+ - `ABANDONED` - would be DEAD_WAIT, but the session has been inactive
56
+ past `stuck_max_age_minutes` (default 120) - informational only; a
57
+ long-dead session is never called STUCK or nudged
58
+ - `IDLE` - nothing pending
59
+
60
+ `DEAD_WAIT` is evidence-based inference, not proof - the "waiting on a
61
+ notification that can never arrive" case heartbeat monitors structurally
62
+ cannot see. Sessions are matched by where they *work* (their recorded
63
+ working directory), not just where they started, and the page shows
64
+ session names, process liveness, and active subagent labels when
65
+ available - never transcript content. Mechanics and tradeoffs:
66
+ [DESIGN.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/DESIGN.md);
67
+ honest limits (activity is not progress):
68
+ [ANALYSIS.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/ANALYSIS.md).
69
+
70
+ ## Install & use (Claude Code)
71
+
72
+ Claude Code is the supported way to use this tool:
73
+
74
+ ```
75
+ /plugin marketplace add nickjrotundo/whats-cc-doing
76
+ /plugin install ccdoing@whats-cc-doing
77
+ ```
78
+
79
+ Then run the **setup** skill (`/ccdoing:setup`) in your project: Claude
80
+ inventories it, proposes signals, asks four questions, writes
81
+ `ccdoing.yaml`, installs the watchdog loop, fires a test notification
82
+ so you see the alert channel work *now* rather than at 3am, and - if
83
+ you enable tier 3 - shows you the complete nudge message for approval
84
+ first. Day to day: `/ccdoing:status`, `/ccdoing:serve`, `/ccdoing:live`,
85
+ `/ccdoing:help`.
86
+
87
+ The CLI underneath - install from PyPI (`uv tool install
88
+ whats-cc-doing`), from the repo (`uv tool install
89
+ git+https://github.com/nickjrotundo/whats-cc-doing`), or from a local
90
+ checkout (`uv pip install /path/to/whats-cc-doing` in a venv):
91
+
92
+ ```
93
+ ccdoing init # inventory this project, write ccdoing.yaml
94
+ ccdoing tick # one cycle: collect, render, escalate (cron-safe)
95
+ ccdoing run # the same, in a loop
96
+ ccdoing status [--fresh] # print the verdict JSON to stdout
97
+ ccdoing view [--fresh] # live terminal viewer (ssh/headless friendly)
98
+ ccdoing serve --all --daemon # background web server for the dashboard (prints link)
99
+ ccdoing serve stop|status # stop the web server / is it running + where
100
+ ccdoing projects # every registered project + last verdict
101
+ ccdoing doctor # env/config checks; --drift for config rot
102
+ ccdoing test-escalation --tier log|notify|nudge # prove the ladder works
103
+ ccdoing install [--mode cron] # print install units
104
+ ```
105
+
106
+ The CLI runs without Claude Code (generic signals work on any repo or
107
+ long-running job), but that path is untested and unsupported - you're
108
+ on your own, but feel free to try.
109
+
110
+ ## The escalation ladder
111
+
112
+ ```yaml
113
+ watchdog:
114
+ escalation:
115
+ - { after_quiet_minutes: 15, action: log }
116
+ - { after_quiet_minutes: 30, action: notify } # apprise -> anywhere
117
+ - { after_quiet_minutes: 45, action: nudge,
118
+ cooldown_minutes: 60, max_per_day: 3 }
119
+ ```
120
+
121
+ Notify targets persist in **`.ccdoing/notify.urls`** - one
122
+ [apprise](https://github.com/caronc/apprise) URL per line, read on
123
+ every tick, so a cron/systemd watchdog needs no environment plumbing
124
+ (`$CCDOING_NOTIFY_URLS` overrides it for one-off tests; keep the file
125
+ gitignored - topics and webhooks are effectively secrets). Prefer no
126
+ external service? The status page and dashboard carry an **"enable
127
+ browser notifications"** toggle - viewer-side, fires on verdict
128
+ transitions, works over `ccdoing serve` while a page is open.
129
+
130
+ Tier 3 is a **nudge, never a resume**: only for a `DEAD_WAIT` session
131
+ whose process is verifiably alive and which a zero-cost [idle
132
+ probe](https://code.claude.com/docs/en/cross-session-messaging) did not
133
+ report idle (idle means finished - the probe stands the watchdog down).
134
+ One courier delivers your pre-approved message into the running
135
+ session, which decides for itself. No `--resume`, no fresh sessions;
136
+ cooldown, daily cap, and refuse-while-running are enforced in code.
137
+
138
+ Who watches the watchdog? The page itself: it shows a red "generator
139
+ appears down" banner when it misses its own refresh, and systemd
140
+ `Restart=`/cron re-entry are the loop's recovery.
141
+
142
+ ## Outputs
143
+
144
+ - `reports/status/status.html` - dark, dependency-free, auto-refreshing.
145
+ Default view is the at-a-glance **activity table** (green ACTIVE /
146
+ grey inactive per primary signal, drift-marked when misconfigured);
147
+ full detail sits behind an "All signals" expander.
148
+ - `reports/status/status.json` - **the interface.** Agents and scripts
149
+ read this; nothing should ever scrape the HTML.
150
+
151
+ ![example status page](https://raw.githubusercontent.com/nickjrotundo/whats-cc-doing/main/docs/status-example.png)
152
+
153
+ Real files in [examples/](https://github.com/nickjrotundo/whats-cc-doing/tree/main/examples) -
154
+ this repository monitoring itself.
155
+
156
+ ### Viewing the page (headless / remote / WSL2)
157
+
158
+ 1. **`ccdoing serve --all --daemon`** - background server for the
159
+ all-projects dashboard; prints the localhost URL. `serve stop` /
160
+ `serve status` manage it; starting again restarts. WSL2: localhost
161
+ forwarding to the Windows browser usually works, but not always - if
162
+ it doesn't, open the URL in a Linux browser (e.g. WSLg-launched
163
+ Chrome).
164
+ 2. **`ccdoing view`** - live terminal viewer over ssh (`--fresh` to
165
+ generate as it views, `--once` for scripts).
166
+ 3. **Open the file** - `file://` works fully in Chromium-family
167
+ browsers - or serve `reports/status/` from your app's own static
168
+ mount with caching disabled (a cached status page is a lying one).
169
+
170
+ Many projects on one machine? `init` registers each; `ccdoing projects`
171
+ lists them with last verdicts (`--unregister NAME` to remove); each runs
172
+ its own independent loop - one registry, one viewer, no per-project
173
+ ports. And there is an **all-projects dashboard**, terminal and web:
174
+
175
+ - **TUI**: `ccdoing view --dash` (the default outside any configured
176
+ project) - title, colored verdict, "Last signal: 12m ago" per
177
+ project; arrows + Enter open one, `b` comes back, `d` sets the
178
+ activity window, `s` splits side-by-side on wide terminals, `[stale]`
179
+ marks dead generators.
180
+
181
+ ![TUI dashboard: all projects with verdicts and last-signal ages](https://raw.githubusercontent.com/nickjrotundo/whats-cc-doing/main/docs/tui-dashboard.png)
182
+
183
+ The `s` split mode, three projects at once:
184
+
185
+ ![TUI multi-view: three project panes with per-signal states](https://raw.githubusercontent.com/nickjrotundo/whats-cc-doing/main/docs/tui-multiview.png)
186
+
187
+ - **Web**: `ccdoing serve --all` - card-per-project dashboard with a
188
+ live "active within N days" filter, click-through to each live status
189
+ page, and a Multi-view grid of 2-4 pages at `/multi`. Localhost-only
190
+ by default; every response is `Cache-Control: no-store`.
191
+
192
+ Privacy note before publishing a page anywhere public: `process`
193
+ signals render command lines, redacted by default (`PID basename
194
+ (+N args)`); the `command` signal's stdout renders too - treat
195
+ `ccdoing.yaml` as config-as-code.
196
+
197
+ ## Keeping the page honest as the project changes
198
+
199
+ The original internal status page hardcoded two `/tmp` eval-JSON paths
200
+ and went stale the day after shipping. ccdoing is built to notice that
201
+ class of rot: `json_headline` signals use glob patterns + a `min_items`
202
+ shape filter (newest matching file wins); every path-shaped signal
203
+ reports `no-match`/`stale` drift states on the page and in status.json;
204
+ `ccdoing doctor --drift` re-diffs the setup-time inventory (the plugin
205
+ surfaces a one-line notice at session start); and **`/ccdoing:tune`**
206
+ turns findings into config *deltas* for approval - never rewriting
207
+ values you tuned.
208
+
209
+ ## Supported platforms
210
+
211
+ | Platform | Status |
212
+ |---|---|
213
+ | Linux | supported (developed and tested here) |
214
+ | WSL2 | supported (`systemctl --user` varies - setup verifies, cron fallback) |
215
+ | macOS | best-effort: generic signals work; Claude task outputs may live under `$TMPDIR` rather than `/tmp/claude-*` - set `task_root_glob` on the `claude_session` signal; use cron (no launchd template yet) |
216
+ | Windows (native) | unsupported (`pgrep`, POSIX paths, systemd/cron assumptions) |
217
+
218
+ ## Docs & support
219
+
220
+ - [DESIGN.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/DESIGN.md) - architecture, harness mechanics, decision log
221
+ - [ANALYSIS.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/ANALYSIS.md) - known gaps, limits, v0.2 roadmap
222
+ - [BUILD-LOG.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/BUILD-LOG.md) - origin story + how this was built (an AI-driven build, documented honestly)
223
+ - [CHANGELOG.md](https://github.com/nickjrotundo/whats-cc-doing/blob/main/CHANGELOG.md) - release history
224
+ - Questions/bugs: [GitHub Issues](https://github.com/nickjrotundo/whats-cc-doing/issues)