From 5fe40f4a6352ce263d0fa9fc954adbaf6bfec051 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Wed, 4 Mar 2026 16:17:12 -0800 Subject: [PATCH] docs: add sponsorship playbook and monthly metrics automation --- .github/workflows/monthly-metrics.yml | 185 +++++++++++++++++++++++ README.md | 5 +- SPONSORING.md | 43 ++++++ docs/business/metrics-and-sponsorship.md | 2 + docs/business/social-launch-copy.md | 62 ++++++++ 5 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/monthly-metrics.yml create mode 100644 SPONSORING.md create mode 100644 docs/business/social-launch-copy.md diff --git a/.github/workflows/monthly-metrics.yml b/.github/workflows/monthly-metrics.yml new file mode 100644 index 00000000..3221f321 --- /dev/null +++ b/.github/workflows/monthly-metrics.yml @@ -0,0 +1,185 @@ +name: Monthly Metrics Snapshot + +on: + schedule: + - cron: '0 14 1 * *' # Monthly on the 1st at 14:00 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + snapshot: + name: Update metrics issue + runs-on: ubuntu-latest + steps: + - name: Update monthly metrics issue + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const title = "Monthly Metrics Snapshot"; + const label = "metrics-snapshot"; + const monthKey = new Date().toISOString().slice(0, 7); + + function parseLastPage(linkHeader) { + if (!linkHeader) return null; + const match = linkHeader.match(/&page=(\d+)>; rel="last"/); + return match ? Number(match[1]) : null; + } + + function fmt(value) { + if (value === null || value === undefined) return "n/a"; + return Number(value).toLocaleString("en-US"); + } + + async function getNpmDownloads(range, pkg) { + try { + const res = await fetch(`https://api.npmjs.org/downloads/point/${range}/${pkg}`); + if (!res.ok) return null; + const data = await res.json(); + return data.downloads ?? null; + } catch { + return null; + } + } + + async function getContributorsCount() { + try { + const resp = await github.rest.repos.listContributors({ + owner, + repo, + per_page: 1, + anon: "false" + }); + return parseLastPage(resp.headers.link) ?? resp.data.length; + } catch { + return null; + } + } + + async function getReleasesCount() { + try { + const resp = await github.rest.repos.listReleases({ + owner, + repo, + per_page: 1 + }); + return parseLastPage(resp.headers.link) ?? resp.data.length; + } catch { + return null; + } + } + + async function getTraffic(metric) { + try { + const route = metric === "clones" + ? "GET /repos/{owner}/{repo}/traffic/clones" + : "GET /repos/{owner}/{repo}/traffic/views"; + const resp = await github.request(route, { owner, repo }); + return resp.data?.count ?? null; + } catch { + return null; + } + } + + const [ + mainWeek, + shieldWeek, + mainMonth, + shieldMonth, + repoData, + contributors, + releases, + views14d, + clones14d + ] = await Promise.all([ + getNpmDownloads("last-week", "ecc-universal"), + getNpmDownloads("last-week", "ecc-agentshield"), + getNpmDownloads("last-month", "ecc-universal"), + getNpmDownloads("last-month", "ecc-agentshield"), + github.rest.repos.get({ owner, repo }), + getContributorsCount(), + getReleasesCount(), + getTraffic("views"), + getTraffic("clones") + ]); + + const stars = repoData.data.stargazers_count; + const forks = repoData.data.forks_count; + + const tableHeader = [ + "| Month (UTC) | ecc-universal (week) | ecc-agentshield (week) | ecc-universal (30d) | ecc-agentshield (30d) | Stars | Forks | Contributors | GitHub App installs (manual) | Views (14d) | Clones (14d) | Releases |", + "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|" + ].join("\n"); + + const row = `| ${monthKey} | ${fmt(mainWeek)} | ${fmt(shieldWeek)} | ${fmt(mainMonth)} | ${fmt(shieldMonth)} | ${fmt(stars)} | ${fmt(forks)} | ${fmt(contributors)} | n/a | ${fmt(views14d)} | ${fmt(clones14d)} | ${fmt(releases)} |`; + + const intro = [ + "# Monthly Metrics Snapshot", + "", + "Automated monthly snapshot for sponsor/partner reporting.", + "", + "- `GitHub App installs (manual)` is intentionally manual until a stable public API path is available.", + "- Traffic metrics are 14-day rolling windows from the GitHub traffic API and can show `n/a` if unavailable.", + "", + tableHeader + ].join("\n"); + + try { + await github.rest.issues.getLabel({ owner, repo, name: label }); + } catch (error) { + if (error.status === 404) { + await github.rest.issues.createLabel({ + owner, + repo, + name: label, + color: "0e8a16", + description: "Automated monthly project metrics snapshots" + }); + } else { + throw error; + } + } + + const issuesResp = await github.rest.issues.listForRepo({ + owner, + repo, + state: "open", + labels: label, + per_page: 100 + }); + + let issue = issuesResp.data.find((item) => item.title === title); + + if (!issue) { + const created = await github.rest.issues.create({ + owner, + repo, + title, + labels: [label], + body: `${intro}\n${row}\n` + }); + console.log(`Created issue #${created.data.number}`); + return; + } + + const currentBody = issue.body || ""; + if (currentBody.includes(`| ${monthKey} |`)) { + console.log(`Issue #${issue.number} already has snapshot row for ${monthKey}`); + return; + } + + const body = currentBody.includes("| Month (UTC) |") + ? `${currentBody.trimEnd()}\n${row}\n` + : `${intro}\n${row}\n`; + + await github.rest.issues.update({ + owner, + repo, + issue_number: issue.number, + body + }); + console.log(`Updated issue #${issue.number}`); diff --git a/README.md b/README.md index 9e9f5877..6c7e74b7 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,15 @@ Use these live signals when presenting ECC to sponsors, platforms, or ecosystem - **Main package installs:** [`ecc-universal` on npm](https://www.npmjs.com/package/ecc-universal) - **Security companion installs:** [`ecc-agentshield` on npm](https://www.npmjs.com/package/ecc-agentshield) - **GitHub App distribution:** [ECC Tools marketplace listing](https://github.com/marketplace/ecc-tools) +- **Automated monthly metrics issue:** powered by `.github/workflows/monthly-metrics.yml` - **Repo adoption signal:** stars/forks/contributors badges at the top of this README Download counts for Claude Code plugin installs are not currently exposed as a public API. For partner reporting, combine npm metrics with GitHub App installs and repository traffic/fork growth. For a sponsor-call metrics checklist and command snippets, see [`docs/business/metrics-and-sponsorship.md`](docs/business/metrics-and-sponsorship.md). +[**Sponsor ECC**](https://github.com/sponsors/affaan-m) | [Sponsor Tiers](SPONSORS.md) | [Sponsorship Program](SPONSORING.md) + --- ## The Guides @@ -1217,7 +1220,7 @@ These configs work for my workflow. You should: This project is free and open source. Sponsors help keep it maintained and growing. -[**Become a Sponsor**](https://github.com/sponsors/affaan-m) | [Sponsor Tiers](SPONSORS.md) +[**Become a Sponsor**](https://github.com/sponsors/affaan-m) | [Sponsor Tiers](SPONSORS.md) | [Sponsorship Program](SPONSORING.md) --- diff --git a/SPONSORING.md b/SPONSORING.md new file mode 100644 index 00000000..720616a4 --- /dev/null +++ b/SPONSORING.md @@ -0,0 +1,43 @@ +# Sponsoring ECC + +ECC is maintained as an open-source agent harness performance system across Claude Code, Cursor, OpenCode, and Codex app/CLI. + +## Why Sponsor + +Sponsorship directly funds: + +- Faster bug-fix and release cycles +- Cross-platform parity work across harnesses +- Public docs, skills, and reliability tooling that remain free for the community + +## Sponsorship Tiers + +These are practical starting points and can be adjusted for partnership scope. + +| Tier | Price | Best For | Includes | +|------|-------|----------|----------| +| Pilot Partner | $200/mo | First sponsor engagement | Monthly metrics update, roadmap preview, prioritized maintainer feedback | +| Growth Partner | $500/mo | Teams actively adopting ECC | Pilot benefits + monthly office-hours sync + workflow integration guidance | +| Strategic Partner | $1,000+/mo | Platform/ecosystem partnerships | Growth benefits + coordinated launch support + deeper maintainer collaboration | + +## Sponsor Reporting + +Metrics shared monthly can include: + +- npm downloads (`ecc-universal`, `ecc-agentshield`) +- Repository adoption (stars, forks, contributors) +- GitHub App install trend +- Release cadence and reliability milestones + +For exact command snippets and a repeatable pull process, see [`docs/business/metrics-and-sponsorship.md`](docs/business/metrics-and-sponsorship.md). + +## Expectations and Scope + +- Sponsorship supports maintenance and acceleration; it does not transfer project ownership. +- Feature requests are prioritized based on sponsor tier, ecosystem impact, and maintenance risk. +- Security and reliability fixes take precedence over net-new features. + +## Sponsor Here + +- GitHub Sponsors: [https://github.com/sponsors/affaan-m](https://github.com/sponsors/affaan-m) +- Project site: [https://ecc.tools](https://ecc.tools) diff --git a/docs/business/metrics-and-sponsorship.md b/docs/business/metrics-and-sponsorship.md index 540fd473..9d884e75 100644 --- a/docs/business/metrics-and-sponsorship.md +++ b/docs/business/metrics-and-sponsorship.md @@ -70,3 +70,5 @@ Use this on calls: > We track adoption through npm distribution, GitHub App installs, and repository growth. > Claude plugin installs are structurally undercounted publicly, so we use a blended metrics model. > The project supports Claude Code, Cursor, OpenCode, and Codex app/CLI with production-grade hook reliability and a large passing test suite. + +For launch-ready social copy snippets, see [`social-launch-copy.md`](./social-launch-copy.md). diff --git a/docs/business/social-launch-copy.md b/docs/business/social-launch-copy.md new file mode 100644 index 00000000..a7429756 --- /dev/null +++ b/docs/business/social-launch-copy.md @@ -0,0 +1,62 @@ +# Social Launch Copy (X + LinkedIn) + +Use these templates as launch-ready starting points. Replace placeholders before posting. + +## X Post: Release Announcement + +```text +ECC v1.8.0 is live. + +We moved from “config pack” to an agent harness performance system: +- hook reliability fixes +- new harness commands +- cross-tool parity (Claude Code, Cursor, OpenCode, Codex) + +Start here: +``` + +## X Post: Proof + Metrics + +```text +If you evaluate agent tooling, use blended distribution metrics: +- npm installs (`ecc-universal`, `ecc-agentshield`) +- GitHub App installs +- repo adoption (stars/forks/contributors) + +We now track this monthly in-repo for sponsor transparency. +``` + +## X Quote Tweet: Eval Skills Article + +```text +Strong point on eval discipline. + +In ECC we turned this into production checks via: +- /harness-audit +- /quality-gate +- Stop-phase session summaries + +This is where harness performance compounds over time. +``` + +## X Quote Tweet: Plankton / deslop workflow + +```text +This workflow direction is right: optimize the harness, not just prompts. + +Our v1.8.0 focus was reliability + parity + measurable quality gates across toolchains. +``` + +## LinkedIn Post: Partner-Friendly Summary + +```text +We shipped ECC v1.8.0 with one objective: improve agent harness performance in production. + +Highlights: +- more reliable hook lifecycle behavior +- new harness-level quality commands +- parity across Claude Code, Cursor, OpenCode, and Codex +- stronger sponsor-facing metrics tracking + +If your team runs AI coding agents daily, this is designed for operational use. +```