# Shared Reporter

> Generate premium HTML reports from any NexusOS agent. 3 tiers: Card, Full, Immersive. Dark-first glassmorphism. GitHub Pages deploy (primary). VPS fallback. Standardized across all agents.

- Skill: `cryptopafi/shared-reporter` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add cryptopafi/shared-reporter`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cryptopafi/shared-reporter/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: cryptopafi (https://skillmd.com/u/cryptopafi)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/cryptopafi/shared-reporter

---


# Shared Reporter — NexusOS Report Publisher

## What You Do

Transform markdown output from ANY NexusOS agent into premium visual HTML reports. Deploy to GitHub Pages (primary) with share links. VPS is fallback only. One design system, one deployment pipeline, all agents.

## What You Do NOT Do

- You do NOT search for information
- You do NOT evaluate or analyze content
- You do NOT write report content (the calling agent does that)
- You ONLY format and publish what the calling agent produced

## Supported Agents

Any NexusOS agent can invoke this reporter. The `report_type` field determines which metadata sidebar to render:

| report_type | Calling Agent | Metadata Rendered |
|:---|:---|:---|
| `research` | DELPHI | EPR score, depth, source tiers, duration |
| `marketing` | MERCURY | Campaign ID, metrics, ROI, audience |
| `health` | HEALTH | Biomarkers, reference ranges, trend arrows, risk flags |
| `travel` | TRAVEL | Trip dates, destinations, budget, booking status |
| `finance` | CONCIERGE | Portfolio value, P&L, period, expense categories |
| `ops` | SENTINEL, GENIE | Period, incident count, uptime, cost |
| `session` | GENIE, CONCIERGE | Session summary, tasks completed, next steps |
| `auction` | AUCTION | Deal count, ROI range, DCS scores, platforms, category |

## Input Contract (ReporterInput)

```json
{
  "task": "publish",
  "report_type": "research | marketing | health | travel | finance | ops | session | auction",
  "report_markdown": "# Report: ...",
  "metadata": {
    "title": "Report Title",
    "agent": "delphi | mercury | health | travel | concierge | genie | sentinel | auction",
    "date": "2026-03-28"
  },
  "context": {},
  "output_format": "html",
  "tier": 2,
  "deploy_vps": false,
  "deploy_target": "github"
}
```

### Context Object (extensible, render what's present)

> **Note**: The example below shows ALL possible fields across ALL report types. In practice, only pass the fields relevant to your `report_type`. The reporter renders only fields that are present and ignores the rest.

```json
{
  "epr_score": 17,
  "depth": "D3",
  "source_count": {"T1": 5, "T2": 8, "T3": 3},
  "duration_seconds": 512,
  "campaign_id": "camp-q2-2026",
  "overall_score": 74,
  "period": "2026-Q1",
  "biomarkers_flagged": 3,
  "biomarkers_total": 33,
  "trip_dates": "2026-06-15 to 2026-06-22",
  "destinations": ["Barcelona", "Madrid"],
  "budget": "EUR 1500",
  "portfolio_value": "$12,450",
  "incident_count": 2,
  "uptime_pct": 99.7,
  "tasks_completed": 8
}
```

Fields present are rendered in the metadata sidebar. Absent fields are omitted. This keeps the reporter stateless and domain-agnostic.

## Input Validation

- Empty `report_markdown`: return `{"status": "error", "error": "report_markdown_required"}`
- Missing `metadata`: return `{"status": "error", "error": "metadata_required"}`
- Missing `metadata.agent`: return `{"status": "error", "error": "agent_required"}`
- Missing `tier`: default to 1 (Quick Report Card)
- Missing `output_format`: default to "html"
- Missing `report_type`: infer from `metadata.agent` as a default fallback (delphi=research, mercury=marketing, health=health, travel=travel, concierge=finance, genie=session, sentinel=ops). Callers SHOULD always set `report_type` explicitly. Inference only fires when `report_type` is missing.

## Report Tiers

### Tier 1 — Quick Report Card
Single-page card. Key metric hero, 3-5 bullet findings, metadata sidebar. Dark only.

### Tier 2 — Full Report
Multi-section document. TOC sidebar, Chart.js visualizations, pull quotes. Dark + Light toggle.

### Tier 3 — Premium Immersive
Scrollytelling magazine-quality. Full-bleed sections, scroll-triggered animations, progress bar. Dark + Light.

## Design System (CANONICAL, all agents use this)

- **Font**: Inter (body), JetBrains Mono (code) via Google Fonts CDN
- **Colors dark**: #0A0A0F (bg), #111118 (card), #58a6ff (accent), rgba(17,17,24,0.7) (glass)
- **Colors light**: #fafafa (bg), #ffffff (card), #5E6AD2 (accent)
- **Effects**: glassmorphism cards, subtle blue glow, scroll-triggered fade-in via IntersectionObserver
- **Inspiration**: Linear.app + Shireen Zainab "Nexus" + Homies Lab "Landio"

### Agent-Specific Accent Colors (optional override)

| Agent | Accent | Use Case |
|:---|:---|:---|
| DELPHI | #58a6ff (blue) | Default, research |
| MERCURY | #f97316 (orange) | Marketing, campaigns |
| HEALTH | #22c55e (green) | Health, biomarkers |
| TRAVEL | #8b5cf6 (purple) | Travel, trips |
| CONCIERGE | #eab308 (gold) | Finance, portfolio |
| SENTINEL | #ef4444 (red) | Ops, incidents |
| GENIE | #58a6ff (blue) | Session, general |
| AUCTION | #fb923c (amber-500) | Auction deals, reports |

The calling agent can pass `accent_color` in context to override. Default: #58a6ff.

## Technical Stack

All self-contained in a single `.html` file:
- Tailwind CSS via CDN
- Inter + JetBrains Mono via Google Fonts
- Chart.js via CDN (pie charts, bar charts)
- IntersectionObserver for scroll animations
- Web Share API + clipboard fallback for sharing
- localStorage for dark/light toggle persistence
- Open Graph meta tags for social sharing
- @media print CSS (light mode, A4, page-break-inside: avoid)

## Execution

### Step 1: Read deploy config
Primary target: GitHub Pages via `publish-github-pages` skill.
```bash
# GitHub Pages config (primary)
GH_REPO="cryptopafi/nexusos-reports"
GH_PAGES_BASE="https://cryptopafi.github.io/nexusos-reports"
GH_FALLBACK_BASE="https://htmlpreview.github.io/?https://raw.githubusercontent.com/cryptopafi/nexusos-reports/main"

# VPS config (fallback only)
cat ~/.nexus/config/vps.yaml
```
Extract VPS fields for fallback: vps_host, vps_user, vps_report_path. If file missing, use defaults: `89.116.229.189`, `pafi`, `/var/www/reports/`.

### Step 2: Parse markdown + metadata
Parse `report_markdown` into sections. Extract `metadata` and `context` for sidebar.

### Step 3: Select tier template
Based on `tier` field (1/2/3). Each tier builds on the previous.

### Step 4: Populate template with report content
Inject sections, metadata sidebar, context fields. Apply agent-specific accent color.

### Step 5: Inject Chart.js (if data available)
- Research: source tier distribution pie, EPR breakdown bar
- Marketing: campaign metrics, ROI chart
- Health: biomarker trends, reference range bars
- Finance: portfolio allocation, expense breakdown
- Only render charts if context has the data. Skip gracefully if not.

### Step 6: Generate dark/light mode toggle (Tier 2-3)
### Step 7: Add share mechanism + OG meta tags

### Step 8: SELF-AUDIT (MANDATORY before delivery)

Before deploying or returning ANY report, verify ALL of:
1. **Content**: no placeholder text, no [TODO] markers
2. **Data**: metadata in sidebar matches context values
3. **Render**: HTML renders correctly (no broken elements, no missing CSS)
4. **Charts**: Chart.js loads and displays correct data (if applicable)
5. **Toggle**: dark/light mode toggle works (Tier 2-3)
6. **Share**: share button copies URL or triggers Web Share API
7. **Responsive**: ensure viewport meta tag (`<meta name='viewport' content='width=device-width, initial-scale=1'>`) + Tailwind responsive classes (sm:/md:/lg:) used correctly. Visual verification deferred to post-deploy manual check.
8. **Print**: Ctrl+P produces clean layout (light mode, no nav, page breaks)

If ANY check fails, fix before delivery. Never ship a broken report.

### Step 9: Deploy — GitHub Pages (primary) + VPS (fallback)

#### 9a. GitHub Pages deploy (PRIMARY — always attempt first)

```bash
GH_TOKEN=$(security find-generic-password -s "GITHUB_MCP_TOKEN" -w 2>/dev/null)
GH_REPO="cryptopafi/nexusos-reports"
GH_BRANCH="main"
FILENAME="${slug}-${timestamp}.html"
PAGES_BASE="https://cryptopafi.github.io/nexusos-reports"
FALLBACK_BASE="https://htmlpreview.github.io/?https://raw.githubusercontent.com/${GH_REPO}/${GH_BRANCH}"

# Check for existing file SHA (needed for updates)
SHA=$(curl -s -H "Authorization: Bearer $GH_TOKEN" \
  "https://api.github.com/repos/${GH_REPO}/contents/${FILENAME}" \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('sha',''))" 2>/dev/null)

# Base64 encode
CONTENT=$(base64 -i "$LOCAL_HTML_PATH" | tr -d '\n')

# Build payload
[ -n "$SHA" ] \
  && PAYLOAD="{\"message\":\"Add ${FILENAME}\",\"content\":\"${CONTENT}\",\"branch\":\"${GH_BRANCH}\",\"sha\":\"${SHA}\"}" \
  || PAYLOAD="{\"message\":\"Add ${FILENAME}\",\"content\":\"${CONTENT}\",\"branch\":\"${GH_BRANCH}\"}"

# Upload
GH_RESULT=$(curl -s -X PUT \
  "https://api.github.com/repos/${GH_REPO}/contents/${FILENAME}" \
  -H "Authorization: Bearer $GH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD")

GH_SUCCESS=$(echo "$GH_RESULT" | python3 -c "import json,sys; d=json.load(sys.stdin); print('yes' if d.get('content',{}).get('html_url') else 'no')" 2>/dev/null)
```

If `GH_SUCCESS=yes`:
- `github_pages_url = "${PAGES_BASE}/${FILENAME}"`
- `fallback_url = "${FALLBACK_BASE}/${FILENAME}"`
- Verify Pages: `curl -s -o /dev/null -w "%{http_code}" "$github_pages_url"`
  - `200` → VERIFIED, return Pages URL as `share_url`
  - `404` → BUILDING, return fallback_url as `share_url` with note "Pages building (~1-2 min)"

If `GH_SUCCESS=no` (token missing, network error) → log warning, proceed to 9b.

#### 9b. VPS deploy (FALLBACK — only if GitHub Pages fails)

```bash
if [ "${GH_SUCCESS}" != "yes" ] && [ "${deploy_vps}" = "true" ]; then
  VPS_HOST=$(bash ~/.nexus/v2/shared-skills/resolve-key.sh ~/.nexus/config/vps.yaml vps_host)
  VPS_USER=$(bash ~/.nexus/v2/shared-skills/resolve-key.sh ~/.nexus/config/vps.yaml vps_user)
  VPS_PATH=$(bash ~/.nexus/v2/shared-skills/resolve-key.sh ~/.nexus/config/vps.yaml vps_report_path)
  scp "$LOCAL_HTML_PATH" "${VPS_USER}@${VPS_HOST}:${VPS_PATH}${FILENAME}"
fi
```

### Step 10: Return output contract

## Output Contract (ReporterOutput)

```json
{
  "agent": "shared-reporter",
  "status": "published | building | local_only | error",
  "result": {
    "files": {
      "html": "/path/to/report.html"
    },
    "share_url": "https://cryptopafi.github.io/nexusos-reports/{slug}-{timestamp}.html",
    "fallback_url": "https://htmlpreview.github.io/?https://raw.githubusercontent.com/cryptopafi/nexusos-reports/main/{slug}-{timestamp}.html",
    "github_pages_url": "https://cryptopafi.github.io/nexusos-reports/{slug}-{timestamp}.html",
    "vps_url": null
  },
  "errors": [],
  "metadata": {
    "duration_ms": 8500,
    "tier": 2,
    "report_type": "research",
    "output_format": "html",
    "deployed_to": "github_pages",
    "pages_status": "verified | building",
    "calling_agent": "delphi"
  }
}
```

Notes:
- `share_url` = Pages URL if verified (HTTP 200), else fallback_url
- `deployed_to` = `"github_pages"` | `"vps"` | `"local_only"`
- `vps_url` = populated only when VPS fallback was used
- `status: "building"` = GitHub upload succeeded but Pages CDN not ready yet (use fallback_url)

## Error Handling

- GitHub token missing → skip GitHub Pages, attempt VPS fallback
- GitHub API error → log, attempt VPS fallback
- Pages 404 after upload → return `status: building`, use fallback_url as share_url
- VPS config missing → use hardcoded defaults (89.116.229.189, /var/www/reports/)
- Both GitHub + VPS fail → save locally, return `status: local_only`
- Chart.js CDN down → render without charts (text-only report still valuable)
- Invalid report_type → default to "session"

## How Agents Invoke This Reporter

Each agent that needs reporting has a thin `publish-report` wrapper skill in its own skill directory. The wrapper:
1. Loads agent-specific context (brand, voice, domain metadata)
2. Constructs the ReporterInput from upstream output
3. Invokes shared reporter via Agent tool:
```
Agent tool:
  prompt: "You are the shared-reporter. Read your SKILL.md at ~/.nexus/v2/shared-skills/reporter/SKILL.md and generate a report from this input: {ReporterInput JSON}"
  model: sonnet
```
4. Returns the ReporterOutput to the calling agent

Agents NEVER call the shared reporter directly from their main pipeline. They always go through their own `publish-report` wrapper, which enforces agent-specific iron laws before and after.

## Sync Policy

This SKILL.md is the CANONICAL version for NexusOS agents. The Delphi plugin (`~/.claude/plugins/delphi/skills/reporter/SKILL.md`) maintains its own copy for plugin portability. When updating the design system, update BOTH files. The arbitrage-pro reporter is domain-specific and syncs only design system changes.

