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.
- whats_cc_doing-0.2.0/.gitignore +11 -0
- whats_cc_doing-0.2.0/CHANGELOG.md +131 -0
- whats_cc_doing-0.2.0/LICENSE +21 -0
- whats_cc_doing-0.2.0/PKG-INFO +251 -0
- whats_cc_doing-0.2.0/README.md +224 -0
- whats_cc_doing-0.2.0/pyproject.toml +66 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/__init__.py +10 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/__main__.py +11 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/cli.py +777 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/config.py +192 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/dash.py +325 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/drift.py +154 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/harness.py +567 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/registry.py +109 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/render.py +359 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/serve.py +323 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/signals.py +689 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/status_json.py +68 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/templates/nudge-message.md +28 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/tui.py +583 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/util.py +39 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/verdict.py +65 -0
- whats_cc_doing-0.2.0/src/whats_cc_doing/watchdog.py +741 -0
- whats_cc_doing-0.2.0/templates/ccdoing.example.yaml +147 -0
- whats_cc_doing-0.2.0/templates/ccdoing.service +31 -0
- whats_cc_doing-0.2.0/templates/cron.txt +8 -0
- whats_cc_doing-0.2.0/templates/nudge-message.md +28 -0
- whats_cc_doing-0.2.0/tests/__init__.py +0 -0
- whats_cc_doing-0.2.0/tests/conftest.py +125 -0
- whats_cc_doing-0.2.0/tests/test_config_and_cli.py +189 -0
- whats_cc_doing-0.2.0/tests/test_dash.py +235 -0
- whats_cc_doing-0.2.0/tests/test_drift.py +262 -0
- whats_cc_doing-0.2.0/tests/test_harness.py +123 -0
- whats_cc_doing-0.2.0/tests/test_harness_discovery.py +240 -0
- whats_cc_doing-0.2.0/tests/test_publish_fixes.py +262 -0
- whats_cc_doing-0.2.0/tests/test_render_ui.py +229 -0
- whats_cc_doing-0.2.0/tests/test_review_fixes.py +447 -0
- whats_cc_doing-0.2.0/tests/test_signals.py +269 -0
- whats_cc_doing-0.2.0/tests/test_tui_keys.py +81 -0
- whats_cc_doing-0.2.0/tests/test_verdict_and_render.py +120 -0
- whats_cc_doing-0.2.0/tests/test_view_serve_registry.py +325 -0
- whats_cc_doing-0.2.0/tests/test_watchdog.py +489 -0
|
@@ -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
|
+
[](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
|
+

|
|
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
|
+

|
|
209
|
+
|
|
210
|
+
The `s` split mode, three projects at once:
|
|
211
|
+
|
|
212
|
+

|
|
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
|
+
[](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
|
+

|
|
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
|
+

|
|
182
|
+
|
|
183
|
+
The `s` split mode, three projects at once:
|
|
184
|
+
|
|
185
|
+

|
|
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)
|