maintain: general wiki freshness mechanism + restore tailscale routes page
Replace the skills-only regeneration script with a general wiki-maintain.sh that: - regenerates skills-index from live skill tree (330 skills / 26 categories) - audits EVERY page for staleness vs its updated date (>21d flagged) - rebuilds + redeploys the Quartz site - writes a dated freshness report to raw/stocktake/ Also fix the tailscale.md page (it had drifted to only 4 routes; reality now has 11 CT460 routes incl /webui, /agent-estate, /synergy-prep, /evening-grid, /wiki and the /wiki proxy was also missing from the atomic apply script — added there). Cron wiki-stocktake-maintenance (1a8e90ea2271) now runs this daily 03:00 and commits+pushes so the wiki cannot silently drift again.
This commit is contained in:
@@ -3,8 +3,8 @@ title: Tailscale
|
||||
type: infrastructure
|
||||
status: active
|
||||
created: 2026-07-26
|
||||
updated: 2026-07-27
|
||||
verified_on: 2026-07-27
|
||||
updated: 2026-08-16
|
||||
verified_on: 2026-08-16
|
||||
confidence: high
|
||||
tags: [tailscale, vpn, networking, infrastructure]
|
||||
---
|
||||
@@ -20,12 +20,18 @@ WireGuard-based mesh VPN connecting all homelab hosts and providing HTTPS access
|
||||
- **Runtipi node:** CT201 (`runtipi.kangaroo-eel.ts.net`)
|
||||
|
||||
## CT460 Serve Routes
|
||||
| Path | Target | Notes |
|
||||
|---|---|---|
|
||||
| `/` | `http://127.0.0.1:9119` | Hermes WebUI |
|
||||
| `/jobs` | `http://127.0.0.1:9099` | Jobs Dashboard |
|
||||
| `/desktop` | `http://127.0.0.1:6081` | CUA noVNC |
|
||||
| `/vnc-camofox` | `http://127.0.0.1:6080` | Camofox VNC |
|
||||
|| Path | Target | Notes |
|
||||
||---|---|---|---|
|
||||
|| `/` | `http://127.0.0.1:9119` | Hermes WebUI |
|
||||
|| `/ui` | `http://127.0.0.1:5175/` | Hermes UI (Vite) |
|
||||
|| `/jobs` | `http://127.0.0.1:9099` | Jobs Dashboard |
|
||||
|| `/webui` | `http://127.0.0.1:8787/` | Hermes WebUI (Standalone) |
|
||||
|| `/desktop` | `http://127.0.0.1:6081` | CUA noVNC |
|
||||
|| `/vnc-camofox` | `http://127.0.0.1:6080` | Camofox VNC |
|
||||
|| `/agent-estate` | `http://127.0.0.1:8769/agent-estate-role-dashboard.html` | Role dashboard |
|
||||
|| `/synergy-prep` | `http://127.0.0.1:9123/` | Synergy prep |
|
||||
|| `/evening-grid.html` | `http://127.0.0.1:9099/evening-grid.html` | Evening grid |
|
||||
|| `/wiki` | `http://100.118.5.51:9120` | Agent Estate Wiki (Quartz) |
|
||||
|
||||
## CT460 Funnel Routes
|
||||
| Port | Target | Notes |
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Wiki freshness audit — 2026-08-16
|
||||
Threshold: >21 days since `updated`
|
||||
|
||||
## STALE (36)
|
||||
|
||||
- [ 25d] comparisons/camoufox-vs-chrome.md (updated 2026-07-22)
|
||||
- [ 25d] comparisons/hermes-vs-nanobot.md (updated 2026-07-22)
|
||||
- [ 25d] concepts/agent-memory.md (updated 2026-07-22)
|
||||
- [ 25d] concepts/model-routing.md (updated 2026-07-22)
|
||||
- [ 25d] concepts/persistent-shells.md (updated 2026-07-22)
|
||||
- [ 25d] concepts/tool-calling.md (updated 2026-07-22)
|
||||
- [ 25d] decisions/index.md (updated 2026-07-22)
|
||||
- [ 25d] decisions/keep-openrouter-as-fallback.md (updated 2026-07-22)
|
||||
- [ 25d] decisions/persistent-shell-enabled.md (updated 2026-07-22)
|
||||
- [ 25d] experiments/index.md (updated 2026-07-22)
|
||||
- [ 25d] infrastructure/backups.md (updated 2026-07-22)
|
||||
- [ 25d] infrastructure/credentials-map.md (updated 2026-07-22)
|
||||
- [ 25d] infrastructure/docker-services.md (updated 2026-07-22)
|
||||
- [ 25d] infrastructure/domains-and-tunnels.md (updated 2026-07-22)
|
||||
- [ 25d] infrastructure/network-map.md (updated 2026-07-22)
|
||||
- [ 25d] queries/retained-research-answers.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/backup-wiki.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/diagnose-docker.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/provider-health.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/recover-docker-service.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/restart-browser.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/restart-hermes.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/restore-openrouter.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/test-openrouter.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/troubleshoot-tool-loop.md (updated 2026-07-22)
|
||||
- [ 25d] runbooks/update-hermes-safely.md (updated 2026-07-22)
|
||||
- [ 25d] systems/cloudflare-tunnel.md (updated 2026-07-22)
|
||||
- [ 25d] systems/current-profile.md (updated 2026-07-22)
|
||||
- [ 25d] systems/headless-server.md (updated 2026-07-22)
|
||||
- [ 25d] systems/home-assistant.md (updated 2026-07-22)
|
||||
- [ 25d] systems/messaging-integrations.md (updated 2026-07-22)
|
||||
- [ 25d] systems/openrouter.md (updated 2026-07-22)
|
||||
- [ 25d] systems/terminal-backend.md (updated 2026-07-22)
|
||||
- [ 22d] runbooks/gateway-resource-resilience.md (updated 2026-07-25)
|
||||
- [ 22d] systems/hermes-agent.md (updated 2026-07-25)
|
||||
- [ 22d] systems/tool-search.md (updated 2026-07-25)
|
||||
|
||||
## FRESH (33)
|
||||
|
||||
- [ 1d] agents/hermes-chaos.md
|
||||
- [ 1d] agents/hermes-production.md
|
||||
- [ 1d] agents/index.md
|
||||
- [ 1d] agents/nanobot.md
|
||||
- [ 1d] agents/open-claw.md
|
||||
- [ 1d] agents/qwenpaw.md
|
||||
- [ 1d] agents/zeroclaw.md
|
||||
- [ 4d] comparisons/memory-backends.md
|
||||
- [ 4d] comparisons/model-routers.md
|
||||
- [ 14d] comparisons/steel-vs-camoufox.md
|
||||
- [ 14d] concepts/browser-automation.md
|
||||
- [ 2d] current-state.md
|
||||
- [ 14d] decisions/rollback-steel-to-camofox.md
|
||||
- [ 15d] incidents/2026-08-01-mnemosyne-capture-failure.md
|
||||
- [ 2d] infrastructure/hosts.md
|
||||
- [ 3d] infrastructure/proxmox.md
|
||||
- [ 0d] infrastructure/tailscale.md
|
||||
- [ 14d] reports/uber-eats/index.md
|
||||
- [ 19d] runbooks/job-radar.md
|
||||
- [ 19d] runbooks/multiplexer-setup.md
|
||||
- [ 14d] systems/browser-backend.md
|
||||
- [ 2d] systems/guanaco.md
|
||||
- [ 17d] systems/hermes-ui.md
|
||||
- [ 21d] systems/homepage-dashboard.md
|
||||
- [ 15d] systems/memory-backend.md
|
||||
- [ 14d] systems/mnemosyne.md
|
||||
- [ 2d] systems/model-providers.md
|
||||
- [ 9d] systems/nanobot.md
|
||||
- [ 21d] systems/obsidian.md
|
||||
- [ 2d] systems/omniroute.md
|
||||
- [ 18d] systems/opencode-go.md
|
||||
- [ 6d] systems/scheduled-tasks.md
|
||||
- [ 0d] systems/skills-index.md
|
||||
|
||||
## NO updated DATE (7)
|
||||
|
||||
- SCHEMA.md
|
||||
- experiments/active/example.md
|
||||
- incidents/2026-07-29-mnemosyne-relay.md
|
||||
- incidents/2026-08-04-omniroute-free-deepseek-silent-stops.md
|
||||
- index.md
|
||||
- log.md
|
||||
- reports/uber-eats/2026-07.md
|
||||
|
||||
TOTAL: 36 stale, 33 fresh, 7 no-date of 76 pages
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/bin/bash
|
||||
# regenerate-skills-index.sh
|
||||
# Stock-take: regenerate systems/skills-index.md from the live skill tree,
|
||||
# rebuild the Quartz site, and redeploy. Run on a schedule so the wiki never drifts.
|
||||
set -euo pipefail
|
||||
|
||||
WIKI="/home/hermes/workspace/agent-estate-wiki"
|
||||
QUARTZ="/home/hermes/workspace/quartz-temp"
|
||||
SKILLS="/home/hermes/.hermes/skills"
|
||||
|
||||
echo "[$(date -Is)] Starting skills stock-take"
|
||||
|
||||
# 1. Regenerate skills-index.md from the live skill tree
|
||||
python3 - "$SKILLS" "$WIKI/systems/skills-index.md" <<'PY'
|
||||
import sys, re
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
|
||||
skills_root = Path(sys.argv[1])
|
||||
out_path = Path(sys.argv[2])
|
||||
skill_files = list(skills_root.rglob('SKILL.md'))
|
||||
|
||||
skills = defaultdict(list)
|
||||
for f in skill_files:
|
||||
rel = f.relative_to(skills_root)
|
||||
parts = rel.parts
|
||||
if len(parts) >= 3:
|
||||
cat, name = parts[0], parts[1]
|
||||
elif len(parts) == 2:
|
||||
cat, name = 'uncategorized', parts[0]
|
||||
else:
|
||||
continue
|
||||
desc = ''
|
||||
try:
|
||||
content = f.read_text(errors='ignore')
|
||||
m = re.search(r'^description:\s*(.+)$', content[:2000], re.M)
|
||||
if m:
|
||||
desc = m.group(1).strip().strip('"').strip("'")[:120]
|
||||
except Exception:
|
||||
pass
|
||||
skills[cat].append((name, desc))
|
||||
|
||||
total = sum(len(v) for v in skills.values())
|
||||
|
||||
L = []
|
||||
L.append('---')
|
||||
L.append('title: Installed Skills')
|
||||
L.append('type: system')
|
||||
L.append('status: active')
|
||||
L.append('created: 2026-07-22')
|
||||
L.append(f'updated: {date.today().isoformat()}')
|
||||
L.append(f'verified_on: {date.today().isoformat()}')
|
||||
L.append('confidence: high')
|
||||
L.append('tags: [skills, hermes]')
|
||||
L.append('sources: []')
|
||||
L.append('---')
|
||||
L.append('')
|
||||
L.append('# Installed Skills')
|
||||
L.append('')
|
||||
L.append(f'**Total installed: {total} skills** in `~/.hermes/skills/`.')
|
||||
L.append('')
|
||||
L.append('This page is auto-regenerated from the live skill inventory. Do not hand-edit.')
|
||||
L.append('')
|
||||
L.append('## Stock-take notes')
|
||||
L.append('- Category names come from the directory structure under `~/.hermes/skills/` (`category/skill/SKILL.md`).')
|
||||
L.append('- Skills stored directly under `~/.hermes/skills/` (no subfolder) are grouped under `uncategorized`.')
|
||||
L.append('- Deeper nested paths (e.g. `mlops/evaluation`) are grouped by their top-level category.')
|
||||
L.append('')
|
||||
|
||||
priority = ['core-hermes','autonomous-ai-agents','hermes','devops','productivity','creative','software-development','research','email','mlops','github','apple','frontend','media','note-taking','web-development','smart-home','mcp','uncategorized']
|
||||
def cat_order(cat):
|
||||
try: return priority.index(cat)
|
||||
except ValueError: return 100 + len(priority)
|
||||
cats = sorted(skills.items(), key=lambda x: (cat_order(x[0]), -len(x[1])))
|
||||
|
||||
for cat, items in cats:
|
||||
nice = cat.replace('-', ' ').title()
|
||||
L.append(f'## {nice} ({len(items)})')
|
||||
L.append('')
|
||||
for name, desc in sorted(items):
|
||||
L.append(f'- `{name}` — {desc}' if desc else f'- `{name}`')
|
||||
L.append('')
|
||||
|
||||
L.append('## Related')
|
||||
L.append('- [[systems/hermes-agent]]')
|
||||
L.append('- [[systems/omniroute]]')
|
||||
L.append('')
|
||||
|
||||
out_path.write_text('\n'.join(L))
|
||||
print(f"Regenerated {out_path} with {total} skills across {len(skills)} categories")
|
||||
PY
|
||||
|
||||
# 2. Sync content into Quartz
|
||||
rm -rf "$QUARTZ/content" "$QUARTZ/public"
|
||||
mkdir -p "$QUARTZ/content"
|
||||
python3 - "$WIKI" "$QUARTZ/content" <<'PY'
|
||||
import shutil, sys
|
||||
from pathlib import Path
|
||||
src = Path(sys.argv[1]); dst = Path(sys.argv[2])
|
||||
skip = {'.git','public','node_modules','quartz-site','serve.py','.gitignore','package.json','package-lock.json','.codewiki-state.json'}
|
||||
for item in src.iterdir():
|
||||
if item.name in skip: continue
|
||||
if item.is_dir():
|
||||
shutil.copytree(item, dst / item.name, dirs_exist_ok=True)
|
||||
elif item.suffix == '.md':
|
||||
shutil.copy2(item, dst / item.name)
|
||||
PY
|
||||
|
||||
# 3. Build Quartz
|
||||
cd "$QUARTZ"
|
||||
npx quartz build >/tmp/quartz-build.log 2>&1 || { echo "Quartz build failed"; tail -20 /tmp/quartz-build.log; exit 1; }
|
||||
|
||||
# 4. Deploy to public
|
||||
rm -rf "$WIKI/public"
|
||||
mkdir -p "$WIKI/public"
|
||||
cp -a "$QUARTZ/public/." "$WIKI/public/"
|
||||
|
||||
echo "[$(date -Is)] Completed: $(find "$WIKI/public" -name '*.html' | wc -l) html files deployed"
|
||||
+20
-21
@@ -3,8 +3,8 @@ title: Installed Skills
|
||||
type: system
|
||||
status: active
|
||||
created: 2026-07-22
|
||||
updated: 2026-08-15
|
||||
verified_on: 2026-08-15
|
||||
updated: 2026-08-16
|
||||
verified_on: 2026-08-16
|
||||
confidence: high
|
||||
tags: [skills, hermes]
|
||||
sources: []
|
||||
@@ -17,9 +17,8 @@ sources: []
|
||||
This page is auto-regenerated from the live skill inventory. Do not hand-edit.
|
||||
|
||||
## Stock-take notes
|
||||
- Category names come from the directory structure under `~/.hermes/skills/` (`category/skill/SKILL.md`).
|
||||
- Skills stored directly under `~/.hermes/skills/` (no subfolder) are grouped under `uncategorized`.
|
||||
- Deeper nested paths (e.g. `mlops/evaluation`) are grouped by their top-level category.
|
||||
- Category = directory structure under `~/.hermes/skills/` (`category/skill/SKILL.md`).
|
||||
- Skills directly under `~/.hermes/skills/` group under `uncategorized`.
|
||||
|
||||
## Autonomous Ai Agents (8)
|
||||
|
||||
@@ -397,10 +396,26 @@ This page is auto-regenerated from the live skill inventory. Do not hand-edit.
|
||||
- `working-with-legacy-code` — Safely change and test untested codebases using Feathers'' "Working Effectively with Legacy Code". Use when the user men
|
||||
- `yuanbao` — Yuanbao (元宝) groups: @mention users, query info/members.
|
||||
|
||||
## Conduct (1)
|
||||
|
||||
- `simple-directives-execution` — Execute now when told 'just do X' — no investigation.
|
||||
|
||||
## Cron (1)
|
||||
|
||||
- `cron-digest-formatting` — Use when formatting cron Telegram news digests for Anthony.
|
||||
|
||||
## Data Science (1)
|
||||
|
||||
- `jupyter-live-kernel` — Iterative Python via live Jupyter kernel (hamelnb).
|
||||
|
||||
## Finance (1)
|
||||
|
||||
- `3-statement-model` — Build integrated IS/BS/CF financial workbooks in Excel.
|
||||
|
||||
## Job Hunting (1)
|
||||
|
||||
- `seek-extraction-patterns` — Extract salary, closing dates, and job details from SEEK (au.seek.com) listings at scale. Use when enriching a batch of
|
||||
|
||||
## Omniroute (1)
|
||||
|
||||
- `omniroute-skills` — Interact with OmniRoute's Skills framework — list, install, enable, execute, and debug skills via REST API and MCP tools
|
||||
@@ -413,22 +428,6 @@ This page is auto-regenerated from the live skill inventory. Do not hand-edit.
|
||||
|
||||
- `wiki-web-hosting` — Publish Markdown wikis as Quartz sites over Tailscale.
|
||||
|
||||
## Job Hunting (1)
|
||||
|
||||
- `seek-extraction-patterns` — Extract salary, closing dates, and job details from SEEK (au.seek.com) listings at scale. Use when enriching a batch of
|
||||
|
||||
## Conduct (1)
|
||||
|
||||
- `simple-directives-execution` — Execute now when told 'just do X' — no investigation.
|
||||
|
||||
## Cron (1)
|
||||
|
||||
- `cron-digest-formatting` — Use when formatting cron Telegram news digests for Anthony.
|
||||
|
||||
## Data Science (1)
|
||||
|
||||
- `jupyter-live-kernel` — Iterative Python via live Jupyter kernel (hamelnb).
|
||||
|
||||
## Related
|
||||
- [[systems/hermes-agent]]
|
||||
- [[systems/omniroute]]
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
# wiki-maintain.sh — General Agent Estate Wiki freshness maintenance.
|
||||
# Regenerates machine-derivable pages from live state, audits every page for
|
||||
# staleness, rebuilds the Quartz site, and deploys. Run on a schedule so the
|
||||
# wiki never silently drifts from reality.
|
||||
#
|
||||
# Usage: ./wiki-maintain.sh [--no-build]
|
||||
# --no-build regenerate content + audit but skip quartz build/deploy
|
||||
set -euo pipefail
|
||||
|
||||
WIKI="/home/hermes/workspace/agent-estate-wiki"
|
||||
QUARTZ="/home/hermes/workspace/quartz-temp"
|
||||
SKILLS="/home/hermes/.hermes/skills"
|
||||
STALE_DAYS=21
|
||||
NO_BUILD=false
|
||||
[[ "${1:-}" == "--no-build" ]] && NO_BUILD=true
|
||||
|
||||
mkdir -p "$WIKI/raw/stocktake"
|
||||
REPORT="$WIKI/raw/stocktake/freshness-$(date +%Y%m%d).txt"
|
||||
|
||||
echo "[$(date -Is)] Agent Estate Wiki maintenance start"
|
||||
echo " wiki=${WIKI}"
|
||||
echo " stale-threshold=${STALE_DAYS}d"
|
||||
|
||||
# ── 1. Regenerate skills-index from live skill tree ────────────────────────
|
||||
python3 - "$SKILLS" "$WIKI/systems/skills-index.md" <<'PY'
|
||||
import sys, re
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
skills_root = Path(sys.argv[1]); out = Path(sys.argv[2])
|
||||
files = sorted(skills_root.rglob('SKILL.md'))
|
||||
skills = defaultdict(list)
|
||||
for f in files:
|
||||
rel = f.relative_to(skills_root); parts = rel.parts
|
||||
if len(parts) >= 3: cat, name = parts[0], parts[1]
|
||||
elif len(parts) == 2: cat, name = 'uncategorized', parts[0]
|
||||
else: continue
|
||||
desc = ''
|
||||
try:
|
||||
m = re.search(r'^description:\s*(.+)$', f.read_text(errors='ignore')[:2000], re.M)
|
||||
if m: desc = m.group(1).strip().strip('"').strip("'")[:120]
|
||||
except Exception: pass
|
||||
skills[cat].append((name, desc))
|
||||
total = sum(len(v) for v in skills.values())
|
||||
L = ['---','title: Installed Skills','type: system','status: active',
|
||||
'created: 2026-07-22', f'updated: {date.today().isoformat()}',
|
||||
f'verified_on: {date.today().isoformat()}','confidence: high',
|
||||
'tags: [skills, hermes]','sources: []','---','','# Installed Skills','',
|
||||
f'**Total installed: {total} skills** in `~/.hermes/skills/`.','',
|
||||
'This page is auto-regenerated from the live skill inventory. Do not hand-edit.','',
|
||||
'## Stock-take notes','- Category = directory structure under `~/.hermes/skills/` (`category/skill/SKILL.md`).',
|
||||
'- Skills directly under `~/.hermes/skills/` group under `uncategorized`.','']
|
||||
priority = ['core-hermes','autonomous-ai-agents','hermes','devops','productivity','creative',
|
||||
'software-development','research','email','mlops','github','apple','frontend','media',
|
||||
'note-taking','web-development','smart-home','mcp','uncategorized']
|
||||
def order(c):
|
||||
try: return priority.index(c)
|
||||
except ValueError: return 100+len(priority)
|
||||
for cat, items in sorted(skills.items(), key=lambda x:(order(x[0]),-len(x[1]))):
|
||||
L.append(f'## {cat.replace("-"," ").title()} ({len(items)})'); L.append('')
|
||||
for name, desc in sorted(items):
|
||||
L.append(f'- `{name}` — {desc}' if desc else f'- `{name}`')
|
||||
L.append('')
|
||||
L += ['## Related','- [[systems/hermes-agent]]','- [[systems/omniroute]]','']
|
||||
out.write_text('\n'.join(L))
|
||||
print(f"[1/3] skills: {total} across {len(skills)} categories -> {out}")
|
||||
PY
|
||||
|
||||
# ── 2. Audit every page for staleness ────────────────────────────────────────
|
||||
python3 - "$WIKI" "$REPORT" "$STALE_DAYS" <<'PY'
|
||||
import sys, re
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
wiki = Path(sys.argv[1]); report = Path(sys.argv[2]); threshold = int(sys.argv[3])
|
||||
today = date.today()
|
||||
stale, fresh, nodate = [], [], []
|
||||
for f in sorted(wiki.rglob('*.md')):
|
||||
rel = f.relative_to(wiki)
|
||||
if str(rel).startswith('raw/'): continue
|
||||
content = f.read_text(errors='ignore')
|
||||
upd = re.search(r'^updated:\s*(\S+)', content[:800], re.M)
|
||||
if not upd:
|
||||
nodate.append(str(rel)); continue
|
||||
try: d = date.fromisoformat(upd.group(1))
|
||||
except ValueError: nodate.append(str(rel)); continue
|
||||
age = (today - d).days
|
||||
(stale if age > threshold else fresh).append((str(rel), age, upd.group(1)))
|
||||
stale.sort(key=lambda x:-x[1])
|
||||
lines = [f"# Wiki freshness audit — {today.isoformat()}",
|
||||
f"Threshold: >{threshold} days since `updated`",
|
||||
f"", f"## STALE ({len(stale)})", ""]
|
||||
for rel, age, d in stale:
|
||||
lines.append(f"- [{age:>3}d] {rel} (updated {d})")
|
||||
lines += [f"", f"## FRESH ({len(fresh)})", ""]
|
||||
for rel, age, d in fresh:
|
||||
lines.append(f"- [{age:>3}d] {rel}")
|
||||
lines += [f"", f"## NO updated DATE ({len(nodate)})", ""]
|
||||
for rel in nodate:
|
||||
lines.append(f"- {rel}")
|
||||
lines += ["", f"TOTAL: {len(stale)} stale, {len(fresh)} fresh, {len(nodate)} no-date of {len(stale)+len(fresh)+len(nodate)} pages"]
|
||||
report.write_text('\n'.join(lines))
|
||||
print(f"[2/3] audit: {len(stale)} stale (>={threshold}d), {len(fresh)} fresh, {len(nodate)} no-date -> {report}")
|
||||
PY
|
||||
|
||||
# ── 3. Sync content, build, deploy ─────────────────────────────────────────
|
||||
if $NO_BUILD; then
|
||||
echo "[3/3] --no-build: content + audit done, skipping quartz build"
|
||||
exit 0
|
||||
fi
|
||||
rm -rf "$QUARTZ/content" "$QUARTZ/public"
|
||||
mkdir -p "$QUARTZ/content"
|
||||
python3 - "$WIKI" "$QUARTZ/content" <<'PY'
|
||||
import shutil, sys
|
||||
from pathlib import Path
|
||||
src = Path(sys.argv[1]); dst = Path(sys.argv[2])
|
||||
skip = {'.git','public','node_modules','quartz-site','serve.py','.gitignore',
|
||||
'package.json','package-lock.json','.codewiki-state.json','regenerate-skills-index.sh',
|
||||
'wiki-maintain.sh','raw'}
|
||||
for item in src.iterdir():
|
||||
if item.name in skip: continue
|
||||
if item.is_dir(): shutil.copytree(item, dst/item.name, dirs_exist_ok=True)
|
||||
elif item.suffix == '.md': shutil.copy2(item, dst/item.name)
|
||||
PY
|
||||
cd "$QUARTZ"
|
||||
npx quartz build >/tmp/quartz-build.log 2>&1 || { echo "Quartz build FAILED"; tail -20 /tmp/quartz-build.log; exit 1; }
|
||||
rm -rf "$WIKI/public"
|
||||
mkdir -p "$WIKI/public"
|
||||
cp -a "$QUARTZ/public/." "$WIKI/public/"
|
||||
echo "[3/3] build+deploy: $(find "$WIKI/public" -name '*.html' | wc -l) html files"
|
||||
echo "[$(date -Is)] Wiki maintenance complete"
|
||||
echo ""
|
||||
echo "=== Freshness summary ==="
|
||||
grep -E '^TOTAL:' "$REPORT" || true
|
||||
Reference in New Issue
Block a user