Compare current progress against goals with quantified status, velocity, trend, and a concrete next action per goal
Install
npx skillscat add atrium-hermes/atrium-lighthouse/goal-tracker Install via the SkillsCat registry.
${var} — Specific goal title or slug to focus on. If empty, tracks all goals in MEMORY.md.
Read memory/MEMORY.md (for the goal list) and memory/goal-state.json (prior-run snapshot, if it exists).
Inputs
Primary goal source: memory/MEMORY.md section titled ## Goals. If absent, fall back to ## Next Priorities. If both are missing or empty, send ./notify "Goal Tracker — NO_GOALS (add a '## Goals' section to memory/MEMORY.md)" and exit.
Evidence sources (use every source that responds; record each in the source-status footer):
memory/logs/*.md— last 30 days. Case-insensitive whole-word match against keywords parsed from each goal title.git log --since="30 days ago" --pretty=format:"%ad|%s" --date=short— commit subjects.gh pr list --state=all --search "updated:>=$(date -d '30 days ago' +%F)" --json number,title,state,updatedAt,url— recent PRs.gh issue list --state=all --search "updated:>=$(date -d '30 days ago' +%F)" --json number,title,state,updatedAt,url— recent issues.memory/cron-state.json— skill health; relevant when a goal depends on a skill running (e.g., "run first digest").
If ${var} is set, filter to the matching goal after loading.
Steps
1. Parse goals and prior state
For each goal entry, derive:
id— slugified title (stable across runs)title— original textkeywords— title minus stopwords (also include obvious aliases, e.g. "digest" ↔ "rss-digest")due/target— parse if present in the bullet, else null
If memory/goal-state.json exists, load {goal_id: {status, activity_count_14d, last_activity_date, run_at}} for trend comparison.
2. Gather evidence per goal
Across all responsive sources, compute:
activity_count_14d— distinct matching entries in last 14 daysactivity_count_30d— same, 30-day windowlast_activity_date— most recent matching evidence (any source); null if nonedays_since_last_activity— today minuslast_activity_datecompletion_signal— true if a log/commit/PR entry pairs the goal's keywords with phrases like "completed", "done", "shipped", "launched", "closed", "merged" (goal-specific PRs only)blocker_signal— true if a log entry in the last 14 days pairs keywords with "blocked", "waiting on", "stuck on"; capture the blocker phrase
Dedupe evidence by (source, date, ref) so a log mentioning a PR doesn't double-count.
3. Assign status (apply rules in order — first match wins)
| Status | Rule |
|---|---|
| DONE | completion_signal is true, OR the goal is already marked complete in MEMORY.md |
| BLOCKED | blocker_signal is true within the last 14 days |
| ON TRACK | activity_count_14d >= 2 AND days_since_last_activity <= 7 |
| NEEDS ATTENTION | activity_count_14d == 1 OR days_since_last_activity between 8 and 14 inclusive |
| AT RISK | activity_count_14d == 0 AND (days_since_last_activity > 14 OR no activity ever) |
4. Compute trend vs prior snapshot
improving— status moved up the ladder (AT RISK → NEEDS ATTENTION → ON TRACK → DONE) ORactivity_count_14drose by ≥50%flat— same status ANDactivity_count_14dwithin ±25%degrading— status moved down ORactivity_count_14dfell by ≥50%new— no prior record
5. Propose one concrete action per non-DONE goal
Pick the single highest-leverage next step for each goal. Rules:
- AT RISK with
days_since_last_activity > 21→ name a specific Aeon skill to enable, a concrete commit, or a file to create (e.g., "Enablerss-digestin aeon.yml to produce the weekly digest evidence"). - BLOCKED → name the blocker and one unblock step.
- NEEDS ATTENTION → name the smallest next deliverable.
- ON TRACK → omit action line entirely.
Use one action verb. ≤15 words. No vague "continue monitoring" advice. No action = skip the line, don't fill with filler.
6. Format the report
*Goal Tracker — ${today}*
Summary: N goals — X at risk, Y needs attention, Z on track, W blocked, V done (overall ↑ improving / → flat / ↓ degrading)
AT RISK (sorted by days_since_last_activity, descending)
• <goal title> — 18d idle, 0 activity/14d (was NEEDS ATTENTION ↓)
→ Action: <one-verb next step>
NEEDS ATTENTION
• <goal title> — 9d idle, 1 activity/14d (new)
→ Action: <one-verb next step>
BLOCKED
• <goal title> — waiting on <blocker> since <date>
→ Action: <unblock step>
ON TRACK
• <goal title> — 3d idle, 5 activity/14d (↑ improving)
DONE
• <goal title> — completed <date>
Sources: logs=ok, git=ok, gh_pr=ok, gh_issue=ok, cron-state=okOmit any status section that has zero goals.
7. Update MEMORY.md safely
- Move DONE goals to a
## Completed Goalssection with completion date. Never delete goals silently. - Annotate BLOCKED goals inline with the blocker note, but keep them in the active list.
- Do not reorder, rephrase, or rewrite the user's goal text.
- Only write MEMORY.md if at least one goal's status changed since the last run. Otherwise leave the file untouched.
8. Persist state
Write memory/goal-state.json (create if missing):
{
"run_at": "YYYY-MM-DDTHH:MM:SSZ",
"goals": {
"<goal-id>": {
"status": "AT_RISK",
"activity_count_14d": 0,
"last_activity_date": "YYYY-MM-DD"
}
}
}9. Notify and log
Send the full formatted report via ./notify.
Append to memory/logs/${today}.md:
### goal-tracker
- Tracked: N goals (scope: ${var or "all"})
- Status: X at risk, Y needs attention, Z on track, W blocked, V done
- Trend: <notable shifts vs prior run, or "no prior snapshot">
- Actions proposed: <count>
- Sources: logs=ok, git=ok, gh_pr=ok, gh_issue=ok, cron-state=okSandbox note
This skill uses gh CLI and local file reads — both work inside the GitHub Actions sandbox. If gh pr list or gh issue list fails, record gh_pr=fail / gh_issue=fail in the source-status footer and proceed with logs + git evidence only. Do not abort the run on a single-source failure — the whole point of multiple sources is graceful degradation.
Constraints
- Never mark a goal DONE without a concrete completion signal. Prefer false negatives (leaving a finished goal as ON TRACK) over false positives (declaring a goal done prematurely).
- Do not invent, add, reorder, or rephrase goals in MEMORY.md. This skill reads and annotates — it never authors.
- Do not change the skill's tags or var semantics.
- If MEMORY.md has zero goals, exit with NO_GOALS and tell the user exactly which section to add.