From cfd518d4519104f9eab841ed952fd7ed2084f018 Mon Sep 17 00:00:00 2001 From: Malin Date: Mon, 24 Aug 2026 07:34:15 +0200 Subject: [PATCH] feat: add sitrep-panel skill Live local HTML progress report (status board, running narrative, screenshots, newest-first log) served on localhost via a stdlib-only Python server, so a human can watch a long/delegated agent task without reading the raw transcript. Adapted from dbl8005/sitrep-panel (MIT), evaluated and drafted via Codex per this fleet's standard skill-candidate review process. Bundled server and HTML template copied unmodified from upstream. Co-Authored-By: Claude Sonnet 5 --- README.md | 6 + skills/sitrep-panel/SKILL.md | 176 ++++++++ .../sitrep-panel/support/assets/template.html | 413 ++++++++++++++++++ skills/sitrep-panel/support/scripts/serve.py | 49 +++ 4 files changed, 644 insertions(+) create mode 100644 skills/sitrep-panel/SKILL.md create mode 100644 skills/sitrep-panel/support/assets/template.html create mode 100755 skills/sitrep-panel/support/scripts/serve.py diff --git a/README.md b/README.md index e21c405..eb85217 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,11 @@ than assuming the delegate can fetch it itself. - `obsidian-vault-memory` -- use an Obsidian vault as durable cross-session memory: low-conflict session capture, canonical-note promotion, provenance links, queryable Bases, and Syncthing-safe write boundaries. +- `sitrep-panel` -- keep a live local HTML report (status board, running + narrative, screenshots, newest-first log) updated during a long or + delegated task, served on localhost via a zero-dependency stdlib Python + server, so a human can watch progress in a browser tab instead of reading + the transcript. ## Provenance @@ -136,6 +141,7 @@ each skill's frontmatter: - [citeworthyio/seo-agent](https://github.com/citeworthyio/seo-agent) (MIT) - [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) (MIT) - [kepano/obsidian-skills](https://github.com/kepano/obsidian-skills) (MIT) +- [dbl8005/sitrep-panel](https://github.com/dbl8005/sitrep-panel) (MIT) ## Vetting external skills diff --git a/skills/sitrep-panel/SKILL.md b/skills/sitrep-panel/SKILL.md new file mode 100644 index 0000000..534f498 --- /dev/null +++ b/skills/sitrep-panel/SKILL.md @@ -0,0 +1,176 @@ +--- +name: sitrep-panel +description: Use when a human wants live browser visibility into a long-running or delegated agent task — asks for a sitrep panel, live status page, progress report, running narrative, screenshots, or a watchable local dashboard. Covers scaffolding, serving, resuming, updating, archiving, and stopping a private localhost report across unrelated project types and agent CLIs. +license: MIT +--- + +# Sitrep Panel + +Keep a live local HTML report updated while work proceeds: a four-column +status board, a standing explanation of what is happening now, screenshots, +and a newest-first log. The browser polls a tiny timestamp file and reloads +when it changes. Everything stays on the machine; there is no account, +publish step, dependency install, build process, or external request. + +Provenance: adapted from +[`dbl8005/sitrep-panel`](https://github.com/dbl8005/sitrep-panel), released +under the MIT license. The bundled server and HTML template are copied +unmodified from upstream. The workflow below preserves upstream's scaffold, +serve, resume, archive, and update protocol while adapting invocation and +background-process guidance for this fleet's mixed agents and CLI delegates. + +This is scratch visibility, not durable project documentation. Keep one +active report at `.sitrep-panel/report/` per project so its URL remains stable +through a long session. + +## Commands + +Interpret `sitrep-panel [start | open | stop | archive]`; default to `start`. + +- `start`: scaffold if necessary, start or reuse the server, and print the URL. +- `open`: print the active report's URL, starting its server if needed; do not + alter report content. +- `archive`: move the active report to + `.sitrep-panel/archive/-/`, then scaffold a fresh + report. Use this only for genuinely unrelated work. +- `stop`: stop the server process that was launched for this report. Preserve + all report content. + +Do not assume slash-command support. These are workflow actions a session +agent or CLI delegate can carry out with its own filesystem and background +execution tools. + +## Scaffold or resume + +Resolve `` to the directory containing this `SKILL.md`, and +`` to the project root. On `start`, if +`/.sitrep-panel/report/` does not exist: + +1. Create `/.sitrep-panel/report/screenshots/`. +2. Copy `/support/assets/template.html` to + `/.sitrep-panel/report/index.html`. +3. Replace `{{TITLE}}` with a short task name and `{{SUBTITLE}}` with one line + saying what the report tracks and when it started. HTML-escape inserted + text. +4. Write `/.sitrep-panel/report/meta.json` with a current UTC time: + + ```json + {"updated_at": "2026-08-24T12:34:56Z"} + ``` + +5. In a Git repository, check whether `.sitrep-panel/` is already ignored + (`git check-ignore -q .sitrep-panel/` is suitable once it exists). If not, + append `.sitrep-panel/` to the project's `.gitignore` and tell the user. + Skip this only when the user explicitly wants the report committed. + +If the report already exists, resume it without overwriting content. Archive +first only when the new work is unrelated. Treat any existing `.gitignore` +changes as project changes: preserve them and append only the missing rule. + +## Serve on localhost + +First read `.sitrep-panel/report/.server.json` if present. If it contains a +port and a GET to `http://127.0.0.1:/meta.json` succeeds, reuse that +server and do not start a duplicate. + +Otherwise launch the bundled blocking server with the current agent's +detached/background mechanism: + +```bash +python3 /support/scripts/serve.py /.sitrep-panel/report +``` + +Capture its single startup line: + +```text +SERVING http://localhost:/ +``` + +It binds only to `127.0.0.1`, tries port 8934, and lets the OS select a free +port if 8934 is occupied. Immediately record the selected port and UTC start +time in `.sitrep-panel/report/.server.json`: + +```json +{"port": 8934, "started_at": "2026-08-24T12:34:56Z"} +``` + +Also retain the background process/session handle when the execution surface +provides one; use that exact handle for `stop`. Never stop a server with a +broad `pkill python`, `killall`, or an unverified pattern. After stopping, +verify the recorded endpoint no longer answers and remove only the stale +`.server.json` file. A stale file is harmless: `start` and `open` must probe +the endpoint before trusting it. + +After every `start` or `open`, show `http://localhost:/` prominently. +The URL is the result, not an implementation detail. + +## Update throughout the task + +A panel written once at startup is useless. Update it after each meaningful +write, decision, state transition, screenshot, or verification result. Skip +noise such as routine file reads. + +For each update, edit `index.html` and then perform these operations: + +1. Replace the contents of `#current-work-body` with plain-language prose + explaining what is happening now and why. This block describes the + present, not history. +2. Prepend one entry directly inside `#entries`, before older entries. Use + the exact `.entry`, `.entry-rail`, `.entry-dot`, `.entry-time`, + `.entry-title`, and `.entry-body` structure in the template comment; the + timeline styling depends on it. State what happened, what was decided and + why, and what was verified. +3. Add or move task cards among `#board-todo`, `#board-progress`, + `#board-done`, and `#board-blocked`. A card is + `
Step name
`. Move the element between + containers; do not invent status classes. +4. Save useful screenshots under `report/screenshots/` and reference them + with relative paths such as + `Settings after update`. + Put them in `#shots-body`, a relevant log entry, or both. Use meaningful + filenames and alt text. +5. Rewrite `meta.json` with a new ISO-8601 UTC `updated_at` value **last**. + The page polls it every two seconds; touching it before `index.html` is + fully written can trigger a stale or partial reload. + +Preserve valid HTML and HTML-escape task text, tracker titles, paths, and log +content. Do not replace the whole report when a targeted block update will +do; the additive protocol leaves useful partial history if an agent stops. + +At completion, move the final card to Done, add the verification result to +the log, and replace the current-work block with a concise completion summary +or an honest blocked state. Touch `meta.json` last as usual. + +## Use real tracker data or a manual board + +If a working issue-tracker connection is already available and real tickets +map to this task, use their real key, title, link, and status. Refresh that +mapping as work advances. Map tracker states conservatively into To do, In +progress, Done, or Blocked. + +Otherwise maintain a plain checklist of actual work. Never fabricate tickets +to make the board look populated. If several trackers are plausible and the +choice changes what the board represents, ask which one to use. + +## When to run this + +- A user explicitly asks for a sitrep panel, live status page, progress + dashboard, screenshots-as-you-go, or a browser view of agent work. +- A long-running delegated or background task needs passive human visibility + without repeated transcript/status queries. +- A multi-stage operational or development session benefits from a stable, + private localhost view shared across session agents and CLI delegates. + +## What NOT to do + +- Do not start this automatically for every small task; maintaining it has a + real update cost and is valuable mainly for long or explicitly watched work. +- Do not expose the server beyond loopback, tunnel it, upload the report, or + add external assets without explicit authorization. This skill is local and + private by design. +- Do not present it as a durable audit log. Archive preserves a snapshot, but + `.sitrep-panel/` is normally ignored scratch state. +- Do not fabricate status, tracker tickets, screenshots, verification, or + narrative. The panel must reflect work that actually happened. +- Do not forget the final `meta.json` write after a content update, and do not + bury the localhost URL after `start` or `open`. diff --git a/skills/sitrep-panel/support/assets/template.html b/skills/sitrep-panel/support/assets/template.html new file mode 100644 index 0000000..c59792e --- /dev/null +++ b/skills/sitrep-panel/support/assets/template.html @@ -0,0 +1,413 @@ + + + + + + +{{TITLE}} — sitrep-panel + + + +
+
+ + sitrep-panel +
+ +
+ +
+

{{TITLE}}

+

{{SUBTITLE}}

+ just started +
+ + + +
+
+

Status board

+

Where each part of the task stands right now.

+
+
+
To do 0
+
+
+
+
In progress 0
+
+
+
+
Done 0
+
+
+
+
Blocked 0
+
+
+
+ +
+ +
+

What's happening now

+

What's actually happening, in plain language — not just a status word.

+
+

Nothing yet — this fills in once work starts.

+
+
+ +
+

Screenshots

+

Visual proof, dropped in as it happens.

+
+
+ +
+

Log (newest first)

+

Everything that's happened, in order.

+
+ +
+
+
+ + + + diff --git a/skills/sitrep-panel/support/scripts/serve.py b/skills/sitrep-panel/support/scripts/serve.py new file mode 100755 index 0000000..c15c3d2 --- /dev/null +++ b/skills/sitrep-panel/support/scripts/serve.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Serve a sitrep-panel report directory on a free localhost port. + +Usage: python3 serve.py [preferred_port] + +Prints exactly one line to stdout on success: `SERVING http://localhost:/` +then blocks, running the HTTP server — launch this with the caller's +background/detached mechanism (e.g. Claude Code's Bash tool with +run_in_background: true). Stdlib only, no dependencies. +""" + +from __future__ import annotations + +import functools +import http.server +import sys + + +class QuietHandler(http.server.SimpleHTTPRequestHandler): + """SimpleHTTPRequestHandler that suppresses per-request access logging, + so stdout stays to the one clean SERVING line callers parse.""" + + def log_message(self, format: str, *args: object) -> None: # noqa: A002 + pass + + +def main() -> None: + if len(sys.argv) < 2: + print("usage: serve.py [preferred_port]", file=sys.stderr) + sys.exit(1) + + directory = sys.argv[1] + preferred_port = int(sys.argv[2]) if len(sys.argv) > 2 else 8934 + handler = functools.partial(QuietHandler, directory=directory) + + try: + httpd = http.server.ThreadingHTTPServer(("127.0.0.1", preferred_port), handler) + except OSError: + # Preferred port is taken — let the OS assign a free one rather than + # guessing and racing another process for it. + httpd = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler) + + port = httpd.server_address[1] + print(f"SERVING http://localhost:{port}/", flush=True) + httpd.serve_forever() + + +if __name__ == "__main__": + main()