Files
2026-07-13 12:35:03 +08:00

84 lines
3.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# ArcKit Stale Artifact Scanner
#
# One-shot scan wired into the plugin's `monitors` manifest key. Fires at
# session start (when the plugin is enabled in an ArcKit repo), emits one
# stdout line per stale artifact, and exits.
#
# Stale criteria:
# 1. Document Control "Next Review Date" is in the past
# 2. Status is DRAFT and the file hasn't been touched in >30 days
#
# Output format is a single notification line per artifact, readable as
# an additionalContext signal by Claude Code. Silent exit if the cwd is
# not an ArcKit project (no projects/ directory).
#
# Thresholds match the STALE-DRAFT / REVIEW-OVERDUE rules in
# /arckit:health (graph-inject.mjs formatHealth) so the monitor is a
# real-time push of the same signal the slash command reports on demand.
set -u
cwd="$(pwd)"
if [[ ! -d "$cwd/projects" ]]; then
exit 0
fi
today="$(date +%Y-%m-%d)"
stale_count=0
max_report=10
# Resolve a GNU-style date threshold for draft staleness (30 days ago).
# Falls back silently if `date -d` unsupported.
draft_threshold=""
if date -d "30 days ago" +%Y-%m-%d >/dev/null 2>&1; then
draft_threshold="$(date -d "30 days ago" +%Y-%m-%d)"
fi
# Iterate ARC-*.md files under projects/, ignoring symlinks and node_modules
while IFS= read -r -d '' file; do
[[ $stale_count -ge $max_report ]] && break
# Pull Document Control table fields. Anchor Status to a row whose label is
# exactly "Status" (with optional **markdown bold**) so we don't match
# entity-attribute tables further down the file.
next_review="$(grep -m1 -i "Next Review Date" "$file" 2>/dev/null \
| sed -E 's/.*\| *([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/')"
status="$(grep -m1 -iE '^\| *\*{0,2} *Status *\*{0,2} *\|' "$file" 2>/dev/null \
| grep -oiE '(DRAFT|IN_REVIEW|APPROVED|PUBLISHED|SUPERSEDED|ARCHIVED)' \
| head -1 \
| tr '[:lower:]' '[:upper:]')"
last_modified="$(grep -m1 -i "Last Modified" "$file" 2>/dev/null \
| sed -E 's/.*\| *([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/')"
rel="${file#$cwd/}"
# Overdue review
if [[ -n "$next_review" && "$next_review" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
if [[ "$next_review" < "$today" ]]; then
echo "[ArcKit monitor] STALE: $rel — review overdue since $next_review"
stale_count=$((stale_count + 1))
continue
fi
fi
# Long-running draft
if [[ "$status" == "DRAFT" && -n "$draft_threshold" \
&& -n "$last_modified" && "$last_modified" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
if [[ "$last_modified" < "$draft_threshold" ]]; then
echo "[ArcKit monitor] STALE: $rel — DRAFT unchanged since $last_modified"
stale_count=$((stale_count + 1))
fi
fi
done < <(find "$cwd/projects" -type f -name "ARC-*.md" -not -path "*/node_modules/*" -print0 2>/dev/null)
if [[ $stale_count -eq 0 ]]; then
exit 0
fi
if [[ $stale_count -ge $max_report ]]; then
echo "[ArcKit monitor] (reporting first $max_report; run /arckit:health for the full list)"
else
echo "[ArcKit monitor] Found $stale_count stale artifact(s). Run /arckit:health for details."
fi