Install
$ agentstack add skill-gerodp-hermes-productivity-skills-weekly-report ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Weekly Report
Produce one weekly report that combines six sources, gathered in a single deterministic step:
- Projects & time — time worked per project, with the tasks under each
(from superproductivity-report).
- Notes this week — Obsidian notes modified or created this week, each with
2–3 key bullet points (from obsidian-notes; the bullets are written by the agent from each note's body).
- Emails sent — the emails you sent this week (from the
emailopsskill via
emailops-cli): date, recipients, subject, snippet.
- Calendar — events on your calendar this week (from the
calendarskill):
day, time, summary, location.
- Commits — git commits you authored this week per repo under
GIT_REPOS_DIR
(from git-report): repo, count, subject/date, and line churn.
- Visits vs last week — page visits this week compared with the previous
week (from umami-report).
This skill orchestrates the others: gather runs them with --json in parallel (wall time ≈ the slowest source, not the sum) and assembles one bundle (including each note's body, so the bullets can be written in one pass). The agent then renders the Markdown report. Each source is independent and degrades gracefully — if one isn't configured, that section is skipped with a short note and the rest still renders. Zero dependencies (Python stdlib only); local-time, Monday-start weeks.
Config note: the orchestrator loads ~/.hermes/.env itself and passes the values down to the child skills (secrets via the child environment, never on the command line). This is needed because env vars Hermes injects for a skill don't always reach a nested subprocess. Override the dotenv path with --env-file.
Script path: ~/.hermes/skills/reports/weekly-report/scripts/weekly_report.py
When to Use
- "Make my weekly report." →
gather --period this-week, then render. - "Weekly review for last week." →
gather --period last-week. - "Report for a custom range." →
gather --start … --end ….
Prerequisites
Python 3.8+ (stdlib only). No new configuration — it reuses what the source skills already read, from ~/.hermes/.env / PATH:
SP_DATA_FILE→ projects & time sectionOBSIDIAN_VAULT→ notes sectionUMAMI_URL,UMAMI_USERNAME,UMAMI_PASSWORD→ visits sectionemailops-clionPATH(with a configured account) → emails-sent section- the
calendarskill (authenticated google-workspace) → calendar section.
CALENDAR_EXCLUDE (optional) — comma-separated substrings; events whose summary contains any (case-insensitive) are dropped from the calendar section and the meetings count (e.g. Psicóloga,Cita médica,Cumple). --exclude-events overrides per-run.
GIT_REPOS_DIR(folder of git repos;GIT_AUTHORoptional, defaults to your
git config user.email; GIT_EXCLUDE optional, comma-separated repos to skip, e.g. obsidian_vault) → commits section (scans all branches)
OBSIDIAN_EXCLUDE(optional) — comma-separated Obsidian folder prefixes to
drop from the notes section (e.g. Notion/Personal,Archive). Matches whole path components; --exclude overrides it per-run. Read directly from ~/.hermes/.env (undeclared, so you're never prompted).
Whatever is configured is included; missing sources are skipped. You normally don't need check — gather is self-describing (its errors[] array lists every skipped source). Reach for check only to diagnose why a source is missing. Skipping the pre-check saves a few seconds per report.
Commands
SCRIPT=~/.hermes/skills/reports/weekly-report/scripts/weekly_report.py
check — which source skills are ready (optional diagnostic)
python3 $SCRIPT check
Probes each source (in parallel) and prints ✓/✗ per source. Optional — you don't need it before generating a report (gather already reports skipped sources in errors[]). Use it to diagnose why a source isn't showing up.
gather — collect the weekly bundle (this is what the agent consumes)
python3 $SCRIPT gather # this week, JSON bundle (default)
python3 $SCRIPT gather --period last-week
python3 $SCRIPT gather --days 7
python3 $SCRIPT gather --start 2026-06-01 --end 2026-06-07
python3 $SCRIPT gather --by created # notes filtered by creation date
python3 $SCRIPT gather --text # human-readable skeleton, not JSON
Default output is the JSON bundle. Shape:
{
"range": {"start": "...", "end": "...", "label": "this-week"},
"projects": {"projects": [{"project","ms","hours"}], "tasks": [{"title","project","ms","hours"}], ...} | {"error": "..."},
"notes": {"count": N, "by": "modified", "notes": [{"rel","name","modified","created","size","body","truncated"}], "dropped": K?, "excluded": M?} | {"error": "..."},
"emails": {"count": N, "mailbox": "sent", "emails": [{"id","date","to":[...],"subject","snippet"}], "possibly_truncated": true?} | {"error": "..."},
"calendar": {"count": N, "events": [{"summary","day","start_local","end_local","all_day","location"}], "excluded": M?} | {"error": "..."},
"git": {"total": N, "repos": [{"repo","count","insertions","deletions","commits":[{"short","date","subject"}]}], ...} | {"error": "..."},
"visits": {"sites": [{"name","visitors","visitors_prev","visits","visits_prev","pageviews","pageviews_prev","bounce_pct","avg_duration","avg_duration_prev"}], ...} | {"error": "..."},
"errors": ["visits: missing environment variable(s): UMAMI_URL"]
}
| Flag | Meaning | | --- | --- | | --period | today, this-week (default), last-week, this-month, last-month, this-year, last-year | | --days N | the last N days, inclusive of today | | --start / --end | explicit YYYY-MM-DD range (both required, end inclusive) | | --by | modified (default) or created — which note timestamp the window filters on | | --max-notes | max notes to pull bodies for (default 40; any overflow is reported in notes.dropped, never silently dropped) | | --note-chars | truncate each (whitespace-compacted) note body to N chars (default 1200, tuned for fast summarisation; raise for fuller bodies, 0 = no limit) | | --max-emails | max recent sent emails to scan for the window (default 200; if the cap is hit, emails.possibly_truncated is set) | | --exclude | comma-separated Obsidian folder(s) to drop from the notes section (overrides $OBSIDIAN_EXCLUDE; reported in notes.excluded) | | --exclude-events | comma-separated substrings; matching calendar events are dropped (overrides $CALENDAR_EXCLUDE; reported in calendar.excluded) | | --env-file | dotenv to read config from (default ~/.hermes/.env) | | --text | print a readable skeleton (tables + note/email lists) instead of JSON |
Precedence: --start/--end > --days > --period. The visits section uses Umami's built-in comparison to the previous equal-length period (so a this-week window compares against last week).
How to produce the report (agent guidance)
- Gather: go straight to
gather --period this-weekand parse the JSON
bundle — do not run check first (it re-probes every source and just adds latency). The bundle's errors[] already tells you which sources were skipped, so you can report availability from the gather output alone. Only fall back to check if the user asks why a section is missing.
- Render a Markdown report with these sections, in order:
- ## Projects & time — group
projects[]with theirtasks[](match on the
task's project field): per project show total hours, then a bullet per task with its hours. End with a TOTAL.
- ## Notes this week — for each note in
notes.notes, a sub-heading with its
title (name) and dates (modified / created), followed by 2–3 short bullet points (≤ ~12 words each) that summarise the note's body. Read each body — don't summarise from the title. Speed: the bodies are already in the bundle — work in one pass and do not re-open the notes or call any skill again. For a note whose body is empty or trivial, use a single line instead of inventing bullets. If truncated is true, note it's a partial read. If notes.dropped > 0, mention how many notes weren't included. notes.excluded (if present) counts notes filtered out by OBSIDIAN_EXCLUDE — those are intentionally hidden, so don't try to recover them.
- ## Emails sent — list each email in
emails.emails: itsdate,
recipients (to), and subject (the snippet adds context). Group or count them however reads best. If emails.possibly_truncated is set, note the list may be incomplete (raise --max-emails).
- ## Calendar — list events in
calendar.events, grouped byday: show
start_local–end_local (or "all-day"), summary, and location. Note the window is local-time and (for this-week) capped at today, so it reflects the week so far.
- ## Commits — for each repo in
git.repos(already sorted by commit
count), show the repo, its commit count and churn (insertions/ deletions), then a bullet per commit with its date and subject. End with git.total across all repos. Keep it terse — don't re-run git.
- ## Visits vs last week — per site in
visits.sites, show
visitors/visits/pageviews with a ▲/▼ delta vs the *_prev value (last week), plus bounce_pct and avg_duration (seconds → e.g. "1m 35s"), and a TOTAL row.
- For any section that is an
{"error": …}, render a brief
"unavailable: " line instead of the section body. The errors[] array lists every skipped source.
- Show the report, then offer to save it to Obsidian. If the user agrees,
pipe the Markdown to the obsidian-notes skill:
``bash printf '%s' "$REPORT_MARKDOWN" | \ python3 ~/.hermes/skills/notes/obsidian-notes/scripts/obsidian_notes.py \ save --title "Weekly report 2026-06-22 — 2026-06-26" --folder Reports --tag weekly ``
Don't save unless asked — show first, save on confirmation.
Optional: a shareable HTML page (collapsible sections)
Produces a single self-contained, full-width .html file with a highlights card band — total hours, a projects-by-time pie chart, meetings (count + total time + the 3 longest), top repos by commits, and site visits — above a 3-per-row grid of collapsible sections (Projects & time · Calendar · Git activity / Notes · Emails · Visits), expanded by default. Projects and Git rows expand to their tasks/commits (the list isn't repeated); Visits shows per-site bounce rate and average visit duration. Dark theme, no dependencies — open or email the file. Error sections degrade to "unavailable".
Use the html subcommand — one command, one process:
python3 ~/.hermes/skills/reports/weekly-report/scripts/weekly_report.py \
html --period this-week -o ~/weekly-report.html
It gathers, renders, and writes the file itself, then prints the path (default ~/weekly-report.html). Do this instead of piping gather | render_html.py: the pipe is a compound shell command (variable assignment + pipe + a file write) that trips Hermes' per-command approval gate and can stall the pipeline — the single html subcommand avoids all of that. Writing the file still needs your one-time approval; approve it and it completes.
render_html.py remains available for rendering a saved bundle directly (render_html.py bundle.json -o report.html, or bundle on stdin). This is the deterministic render (no LLM) — a quick shareable artifact; use the agent-written Markdown (steps 1–2) when you want the prose bullet summaries.
Pitfalls
- Partial reports are normal: if
SP_DATA_FILE/OBSIDIAN_VAULT/ Umami
creds aren't all set, the report still renders with the available sections. Run check to see which are live.
- Note timestamps: "modified/created this week" come from the filesystem;
syncing a vault can reset them (documented in the obsidian-notes skill). Use --by created if creation dates are the more meaningful axis for you.
- Body truncation: long notes are cut to
--note-chars(default 2000) and
flagged truncated — raise it for fuller summaries, at the cost of a bigger bundle.
- Stale SP data: a Super Productivity backup export is a point-in-time
snapshot; use a live sync-data.json for up-to-the-minute hours.
--start/--endmust be given together (YYYY-MM-DD), end inclusive.
Verification
python3 ~/.hermes/skills/reports/weekly-report/scripts/weekly_report.py check
# → ✓/✗ per source skill.
python3 ~/.hermes/skills/reports/weekly-report/scripts/weekly_report.py gather --text
# → readable skeleton: projects table, note list, visits table.
Offline unit tests (children stubbed — no subprocess, no network):
python3 ~/.hermes/skills/reports/weekly-report/scripts/test_weekly_report.py
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: gerodp
- Source: gerodp/hermes-productivity-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.