Self-hosted ops dashboard fed by markdown briefs in your project folders
War Room Starter
A self-hosted operations dashboard that pulls from markdown briefs in your project folders. One static HTML file, one Python scanner, no server, no database, no SaaS.
What this is
You keep a markdown file inside each project folder โ your "canonical brief." A small Python scanner walks those folders, reads the briefs, and bakes the data into war-room.html. You open the HTML in any browser and see one tab per project: file activity, last-touched timestamps, and the brief itself rendered live.
That's the whole loop. Update a brief, scanner picks it up on the next pass, dashboard reflects it.
Why
If you run multiple side projects and care about not losing context between sessions, the brief becomes the source of truth. The dashboard is just a view over the briefs โ no separate state to drift.
Requirements
- Python 3.9 or newer
- Any modern browser
Quick start
git clone https://github.com/YOUR_USERNAME/war-room-starter.git
cd war-room-starter
cp config.example.json config.json
Then on Windows, double-click start-watcher.bat. On macOS or Linux:
./start-watcher.sh
Open war-room.html in your browser. You'll see three example projects (Project A, B, C) populated from the briefs in example-projects/. The watcher rescans every 30 seconds; hit the REFRESH button in the dashboard to re-read the file.
Add your own project
- Drop a brief into your project folder. Anywhere on disk. Name the file whatever you want โ
PROJECT_BRIEF.mdis the default. Use the example briefs inexample-projects/as a template.
- Edit
config.json.
projects_root to the folder that contains your project folders, e.g. "~/code" or "C:\\Projects". Use \\ (double backslash) for Windows paths in JSON, or just forward slashes โ Python handles both.
- Add an entry under projects:
{
"name": "my-thing",
"label": "MY THING",
"brieffilename": "PROJECTBRIEF.md",
"tagline": "One-line description shown above the brief"
}
- name must match the folder name on disk under projects_root.
- Refresh. If the watcher is running, it picks up changes on the next scan. Otherwise re-run
refresh-once. A new tab appears for your project with a live card and the rendered brief.
Workflow diagrams in your briefs
Add a fenced code block tagged mermaid and it renders as a diagram in the project tab.
markdown</code></pre>mermaid
flowchart LR
A[Source] --> B[Process] --> C[Output]
<pre><code class="lang-"></code></pre>
See the example briefs for working syntax. Mermaid syntax docs.
Auto-start on login
So the watcher is running every time you start your machine.
Windows
- Press
Win + R, type shell:startup, press Enter. A folder opens.
- Right-click in that folder โ New โ Shortcut.
- Browse to
start-watcher.bat, finish the wizard.
- Right-click the shortcut you just made โ Properties โ set Run to
Minimized.
It boots a background console every login.
macOS
# In war-room-starter/
mkdir -p ~/Library/LaunchAgents
cat > ~/Library/LaunchAgents/com.warroom.watcher.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.warroom.watcher</string>
<key>ProgramArguments</key>
<array>
<string>$(pwd)/start-watcher.sh</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>WorkingDirectory</key><string>$(pwd)</string>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.warroom.watcher.plist
To stop: launchctl unload ~/Library/LaunchAgents/com.warroom.watcher.plist.
Linux (systemd user service)
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/war-room.service <<EOF
[Unit]
Description=War Room watcher
[Service] ExecStart=$(pwd)/start-watcher.sh WorkingDirectory=$(pwd) Restart=always
[Install] WantedBy=default.target EOF
systemctl --user daemon-reload systemctl --user enable --now war-room.service
How it works under the hood
โโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your project folders โ
โ /code/my-thing/ โ
โ PROJECT_BRIEF.md โโโผโโโ
โ /code/other/ โ โ
โ PROJECT_BRIEF.md โโโผโโโค
โโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โผ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ scanner.py โ โโโโถ โ war-room.html โ
โ (every 30s) โ โ โข baked-in JSON payload โ
โโโโโโโโโโโโโโโโโโโ โ โข marked.js renders briefs โ
โ โข mermaid renders diagrams โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
your browser
The scanner only modifies the block between two HTML comments:
<!-- WARROOMDATA_BEGIN -->
<!-- WARROOMDATA_END -->
Everything else in war-room.html is left alone, so your local edits to layout, CSS, or static content survive scans.
Configuration reference
| Field | Default | Notes | | --- | --- | --- | | dashboard_title | "MY WAR ROOM" | Shown in the topbar and as the page title. | | projects_root | "./example-projects" | Absolute path or relative to scanner.py. Where your project folders live. | | watchintervalsecs | 30 | How often the watcher rescans. | | maxbriefbytes | 1000000 | Briefs larger than this are skipped (with an error in the dashboard). | | ignoredirs | see config | Folders the file-counter skips inside each project (nodemodules, .git, etc.). | | projects[] | [] | One entry per project tab. | | projects[].name | required | Folder name under projects_root. | | projects[].label | name | Display name on the tab. | | projects[].brief_filename | required | Markdown file inside the project folder. | | projects[].tagline | "" | Optional one-liner shown above the brief. |
Customizing the look
war-room.html is one self-contained file. Edit the <style> block to change colors, fonts, spacing. The terminal aesthetic (orange + green + black, Courier New) is in there. CSS variables would be a clean refactor if you want to theme it.
Security
This is a single static HTML file you open from file://. Nothing is sent anywhere. Two CDN dependencies are loaded for rendering: marked for markdown and mermaid for diagrams, both from cdnjs. If you're offline they degrade gracefully โ markdown renders as raw text, mermaid blocks render as code.
Don't put secrets in your briefs. API keys, passwords, customer data. The briefs render verbatim into the HTML.
Troubleshooting
The dashboard says "no scan data." The watcher hasn't run yet, or it ran but couldn't find the markers. Check that you ran start-watcher (or refresh-once) and that there are no Python errors in its terminal.
A project tab shows "Brief not found." Double-check the name in config.json matches the folder name exactly, and brief_filename matches the markdown file name. Capitalization matters.
The dashboard looks stale. Hit the REFRESH button in the topbar โ it just reloads the HTML from disk. Browsers don't auto-refresh file:// pages.
Mermaid diagrams aren't rendering. Make sure your fenced code block is tagged mermaid (lowercase) and the syntax is valid. Open the browser console for errors.
The dashboard format breaks after a scan. Open scanner.py's output โ it should print [scanner] HH:MM:SS scanned N projects, M briefs -> war-room.html on success. If it errors out, the markers may be missing or the JSON injection failed.
License
MIT. See LICENSE.
Credit
Built solo, in public. If you fork it, let me know what you build with it.