release: 2.0.0 — the agent harness operating system

Graduate 2.0.0-rc.1 to stable. Bump version across package, plugin,
marketplace, OpenCode, agent metadata, VERSION, and all localized docs.
Add 2.0.0 release notes + README sections (en/zh/pt-BR/tr), CHANGELOG
entry, and the ECC community Discord bot (dependency-free gateway client
+ guild command registrar). Update copilot-support and release-surface
tests for the sponsored-review migration and the 2.0.0 surface.
This commit is contained in:
ECC Test
2026-06-09 21:20:33 -04:00
parent 3e30f1a56a
commit 29edd57708
26 changed files with 402 additions and 134 deletions

View File

@@ -6,7 +6,7 @@
"plugins": [
{
"name": "ecc",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"source": {
"source": "local",
"path": "./"

View File

@@ -12,7 +12,7 @@
"name": "ecc",
"source": "./",
"description": "Harness-native ECC operator layer - 64 agents, 261 skills, 84 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"author": {
"name": "Affaan Mustafa",
"email": "me@affaanmustafa.com"

View File

@@ -1,6 +1,6 @@
{
"name": "ecc",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"description": "Harness-native ECC plugin for engineering teams - 64 agents, 261 skills, 84 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
"author": {
"name": "Affaan Mustafa",

View File

@@ -1,6 +1,6 @@
{
"name": "ecc",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"description": "Harness-native ECC workflows for Codex: shared skills, production-ready MCP configs, and selective-install-aligned conventions for TDD, security scanning, code review, and autonomous development.",
"author": {
"name": "Affaan Mustafa",

View File

@@ -1,12 +1,12 @@
{
"name": "ecc-universal",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ecc-universal",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"license": "MIT",
"devDependencies": {
"@opencode-ai/plugin": "^1.4.3",

View File

@@ -1,6 +1,6 @@
{
"name": "ecc-universal",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"description": "ECC plugin for OpenCode - agents, commands, hooks, and skills",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@@ -453,7 +453,7 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
const contextBlock = [
"# ECC Context (preserve across compaction)",
"",
"## Active Plugin: ECC v2.0.0-rc.1",
"## Active Plugin: ECC v2.0.0",
"- Hooks: file.edited, tool.execute.before/after, session.created/idle/deleted, shell.env, compacting, permission.ask",
"- Tools: run-tests, check-coverage, security-audit, format-code, lint-check, git-summary, changed-files",
"- Agents: 13 specialized (planner, architect, tdd-guide, code-reviewer, security-reviewer, build-error-resolver, e2e-runner, refactor-cleaner, doc-updater, go-reviewer, go-build-resolver, database-reviewer, python-reviewer)",

View File

@@ -2,7 +2,7 @@
This is a **production-ready AI coding plugin** providing 64 specialized agents, 261 skills, 84 commands, and automated hook workflows for software development.
**Version:** 2.0.0-rc.1
**Version:** 2.0.0
## Core Principles

View File

@@ -1,5 +1,24 @@
# Changelog
## 2.0.0 - 2026-06-09
### Added
- Discord community launch: server + GitHub PR/issue/release feed, `release-announce.yml` workflow (announce + pin + Discussions cross-post), and a dependency-free community bot (`scripts/discord/ecc-bot.mjs`) with `/ecc`, `/help`, `/skill`, `/docs`, `/release`.
- `orch-*` orchestrator skill family and dynamic workflow team orchestration.
- `kubernetes-patterns` skill, worktree-lifecycle service, MCP inventory (`ecc.mcp.v1`), codex-worktree and opencode session adapters.
### Fixed
- Plugin hooks silently no-oped on Node 21+ (`require.main` undefined under `node -e`).
- Windows reliability: `CLAUDE_PLUGIN_ROOT` normalization, stdin prompt passing, symlink/chmod test guards.
- Session-end `$`-sequence corruption, project-detect boundary matching, install manifest gaps, corrupted legacy shim truncation.
### Changed
- Version graduated to 2.0.0 stable across package, plugin, marketplace, OpenCode, and agent metadata.
- Smaller default OpenCode install surface; `rules/zh` removed from the always-loaded default install.
## 2.0.0-rc.1 - 2026-04-28
### Highlights

View File

@@ -40,7 +40,7 @@ Not just configs. A complete system: skills, instincts, memory optimization, con
Works across **Codex**, **Claude Code**, **Cursor**, **OpenCode**, **Gemini**, **Zed**, **GitHub Copilot**, and other AI agent harnesses.
ECC v2.0.0-rc.1 adds the public Hermes operator story on top of that reusable layer: start with the [Hermes setup guide](docs/HERMES-SETUP.md), then review the [rc.1 release notes](docs/releases/2.0.0-rc.1/release-notes.md) and [cross-harness architecture](docs/architecture/cross-harness.md).
ECC v2.0.0 adds the public Hermes operator story on top of that reusable layer: start with the [Hermes setup guide](docs/HERMES-SETUP.md), then review the [2.0.0 release notes](docs/releases/2.0.0/release-notes.md) and [cross-harness architecture](docs/architecture/cross-harness.md).
---
@@ -127,6 +127,10 @@ This repo is the raw code only. The guides explain everything.
## What's New
### v2.0.0 — The Agent Harness Operating System (Jun 2026)
Stable graduation of the 2.0 line: 261 skills, the control-pane substrate (session adapters + MCP inventory), the worktree-lifecycle service, the `orch-*` orchestrator family, and the launch of the [ECC Discord community](https://discord.gg/36yGMHGFbR). Full notes: [docs/releases/2.0.0/release-notes.md](docs/releases/2.0.0/release-notes.md).
### v2.0.0-rc.1 — Surface Refresh, Operator Workflows, and ECC 2.0 Alpha (Apr 2026)
- **Dashboard GUI** — New Tkinter-based desktop application (`ecc_dashboard.py` or `npm run dashboard`) with dark/light theme toggle, font customization, and project logo in header and taskbar.
@@ -1578,10 +1582,9 @@ ECC provides **GitHub Copilot support** for VS Code via Copilot Chat's native in
| Component | File | Purpose |
|-----------|------|---------|
| Core instructions | `.github/copilot-instructions.md` | Always-loaded rules: coding style, security, testing, git workflow |
| VS Code settings | `.vscode/settings.json` | Per-task instruction files for code gen, test gen, review, and commit messages |
| VS Code settings | `.vscode/settings.json` | Per-task instruction files for code gen, test gen, and commit messages |
| Plan prompt | `.github/prompts/plan.prompt.md` | Phased implementation planning |
| TDD prompt | `.github/prompts/tdd.prompt.md` | Red-Green-Improve cycle |
| Code review prompt | `.github/prompts/code-review.prompt.md` | Quality and security review |
| Security review prompt | `.github/prompts/security-review.prompt.md` | Deep OWASP-aligned security analysis |
| Build fix prompt | `.github/prompts/build-fix.prompt.md` | Systematic build and CI error resolution |
| Refactor prompt | `.github/prompts/refactor.prompt.md` | Dead code cleanup and simplification |
@@ -1594,16 +1597,16 @@ The committed `.vscode/settings.json` enables `chat.promptFiles` so VS Code can
To use the workflow prompts in Copilot Chat:
1. Open the Copilot Chat panel in VS Code.
2. Click the **paperclip / attach** icon and select **Prompt...**, or type `/` and choose a prompt.
3. Select the prompt (e.g. `plan`, `tdd`, `code-review`).
3. Select the prompt (e.g. `plan`, `tdd`, `security-review`).
### How It Works
GitHub Copilot in VS Code reads two types of files automatically:
- **`.github/copilot-instructions.md`** — repository-level instructions, always injected into every Copilot Chat request. Contains ECC's core coding standards, security checklist, testing requirements, and git workflow.
- **`.github/prompts/*.prompt.md`** — reusable prompt files users invoke on demand. Each prompt walks Copilot through a specific ECC workflow (plan → TDD → review → ship).
- **`.github/prompts/*.prompt.md`** — reusable prompt files users invoke on demand. Each prompt walks Copilot through a specific ECC workflow such as planning, TDD, security review, build-fix, or refactor.
The **`.vscode/settings.json`** adds per-task instruction overlays so Copilot receives the right context depending on whether you are generating code, writing tests, reviewing a selection, or drafting a commit message.
The **`.vscode/settings.json`** adds per-task instruction overlays so Copilot receives the right context for code generation, test generation, and commit message drafting.
### Feature Coverage
@@ -1613,7 +1616,7 @@ The **`.vscode/settings.json`** adds per-task instruction overlays so Copilot re
| Security checklist | Always-on + `security-review` prompt |
| Testing / TDD | Always-on + `tdd` prompt |
| Implementation planning | `plan` prompt |
| Code review | `code-review` prompt |
| Code review | External PR review via CodeRabbit + Greptile |
| Build error resolution | `build-fix` prompt |
| Refactoring | `refactor` prompt |
| Commit message format | Per-task instruction in `settings.json` |
@@ -1633,7 +1636,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode | GitHub Copilot |
|---------|-----------------------|------------|-----------|----------|----------------|
| **Agents** | 64 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | N/A |
| **Commands** | 84 | Shared | Instruction-based | 35 | 6 prompts |
| **Commands** | 84 | Shared | Instruction-based | 35 | 5 prompts |
| **Skills** | 261 | Shared | 10 (native format) | 37 | Via instructions |
| **Hook Events** | 8 types | 15 types | None yet | 11 types | None |
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | N/A |
@@ -1644,7 +1647,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
| **Context File** | CLAUDE.md + AGENTS.md | AGENTS.md | AGENTS.md | AGENTS.md | copilot-instructions.md |
| **Secret Detection** | Hook-based | beforeSubmitPrompt hook | Sandbox-based | Hook-based | Instruction-based |
| **Auto-Format** | PostToolUse hook | afterFileEdit hook | N/A | file.edited hook | N/A |
| **Version** | Plugin | Plugin | Reference config | 2.0.0-rc.1 | Instruction layer |
| **Version** | Plugin | Plugin | Reference config | 2.0.0 | Instruction layer |
**Key architectural decisions:**
- **AGENTS.md** at root is the universal cross-tool file (read by Claude Code, Cursor, Codex, and OpenCode — GitHub Copilot uses `.github/copilot-instructions.md` instead)

View File

@@ -80,6 +80,10 @@
## 最新动态
### v2.0.0 — 智能体 Harness 操作系统2026年6月
2.0 主线稳定版261 个技能、control-pane 基底(会话适配器 + MCP 清单、worktree 生命周期服务,以及 [ECC Discord 社区](https://discord.gg/36yGMHGFbR)。
### v2.0.0-rc.1 — 表面同步、运营工作流与 ECC 2.0 Alpha2026年4月
- **公共表面已与真实仓库同步** —— 元数据、目录数量、插件清单以及安装文档现在都与实际开源表面保持一致。

View File

@@ -1 +1 @@
2.0.0-rc.1
2.0.0

View File

@@ -1,6 +1,6 @@
spec_version: "0.1.0"
name: ecc
version: 2.0.0-rc.1
version: 2.0.0
description: "Initial gitagent export surface for ECC's shared skill catalog, governance, and identity. Native agents, commands, and hooks remain authoritative in the repository while manifest coverage expands."
author: affaan-m
license: MIT

View File

@@ -703,7 +703,7 @@ Suggested payload:
"skippedModules": []
},
"source": {
"repoVersion": "2.0.0-rc.1",
"repoVersion": "2.0.0",
"repoCommit": "git-sha",
"manifestVersion": 1
},

View File

@@ -80,6 +80,10 @@ Este repositório contém apenas o código. Os guias explicam tudo.
## O Que Há de Novo
### v2.0.0 — O Sistema Operacional do Harness de Agentes (Jun 2026)
Graduação estável da linha 2.0: 261 skills, substrato de control-pane, inventário MCP, serviço de ciclo de vida de worktrees e a comunidade no [Discord](https://discord.gg/36yGMHGFbR).
### v2.0.0-rc.1 — Sincronização de Superfície, Fluxos Operacionais e ECC 2.0 Alpha (Abr 2026)
- **Superfície pública sincronizada com o repositório real** — metadados, contagens de catálogo, manifests de plugin e documentação de instalação agora refletem a superfície OSS que realmente é entregue.

View File

@@ -0,0 +1,43 @@
# ECC 2.0.0 — The Agent Harness Operating System
ECC 2.0.0 is the stable graduation of the 2.0 line: ECC as a cross-harness operating system for agentic work. Claude Code stays first-class; Codex, OpenCode, Cursor, Gemini, Zed, and terminal-only workflows share the same skills, rules, hooks, MCP conventions, release gates, and operator workflows.
## Highlights
- 261 public skills across coding, research, security, media, enterprise ops, and agent workflows.
- ECC 2.0 control-pane substrate: harness-neutral session adapters (`ecc.session.v1`) covering Claude Code, Codex, OpenCode, and dmux.
- MCP inventory (`ecc.mcp.v1`): one normalized view of MCP server configs across harnesses, with fragmentation and drift detection and secret redaction.
- Worktree-lifecycle service: deterministic conflict prediction and safe garbage collection for parallel agent worktrees.
- `orch-*` orchestrator skill family plus dynamic workflow team orchestration.
- Rollout-derived optimization pack: `parallel-execution-optimizer`, `benchmark-optimization-loop`, `data-throughput-accelerator`, `latency-critical-systems`, `recursive-decision-ledger`.
## Hardening since rc.1
Roughly thirty PRs of fixes landed between rc.1 and stable. The ones worth knowing about:
- **Plugin hooks were silently no-ops on Node 21+** (#2184). The hook runner depended on `require.main` under `node -e`, which newer Node leaves undefined — every plugin hook exited cleanly without running. If you are on Node 21 or newer, update now.
- Windows reliability: `CLAUDE_PLUGIN_ROOT` path normalization (#2139), prompts passed via stdin so the shell does not mangle them (#2174), broken-symlink and chmod test guards (#2171, #2176).
- Security: curl credentials kept out of argv (#2175), gateguard now gates force/path checkouts as destructive (#2158) with env knobs for routine-command gating (#2161), advisory intake hardening.
- Correctness: session-end summaries no longer corrupt `$`-sequences in user messages (#2180), project detection matches package keys on boundaries so `preact` no longer reads as `react` (#2181), install manifest packaging gaps closed (#2172), corrupted legacy command shims truncated safely (#2167).
- Slimmer defaults: smaller OpenCode install surface with gated hooks-runtime (#2140), `rules/zh` out of the always-loaded default install (#2170).
- New surfaces: `kubernetes-patterns` skill (#2178), worktree-lifecycle service (#2164), MCP inventory (#2146), codex-worktree and opencode session adapters (#2145), the `orch-*` family (#2153).
## Community launch
The ECC Discord is live: <https://discord.gg/36yGMHGFbR>
- Release news lands in #announcements, auto-posted and pinned by the release workflow shipped in this very release (#2201).
- A live PR and issue feed runs in #pr-and-issues.
- The ECC bot answers `/skill`, `/docs`, and `/release` lookups in-server.
- #feedback and #feature-requests are read directly by the maintainer and shape the roadmap.
## Install or upgrade
```
/plugin marketplace add https://github.com/affaan-m/ECC
/plugin install ecc
```
Existing installs: `/plugin update ecc`
Full changelog: <https://github.com/affaan-m/ECC/compare/v2.0.0-rc.1...v2.0.0>

View File

@@ -2,7 +2,7 @@
Bu, yazılım geliştirme için 28 özel agent, 116 skill, 59 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
**Sürüm:** 2.0.0-rc.1
**Sürüm:** 2.0.0
## Temel İlkeler

View File

@@ -79,6 +79,10 @@ Bu repository yalnızca ham kodu içerir. Rehberler her şeyi açıklıyor.
## Yenilikler
### v2.0.0 — Ajan Harness İşletim Sistemi (Haz 2026)
2.0 hattının kararlı sürümü: 261 skill, control-pane altyapısı, MCP envanteri, worktree yaşam döngüsü servisi ve [Discord topluluğu](https://discord.gg/36yGMHGFbR).
### v2.0.0-rc.1 — Surface Sync, Operatör İş Akışları ve ECC 2.0 Alpha (Nis 2026)
- **Public surface canlı repo ile senkronlandı** — metadata, katalog sayıları, plugin manifest'leri ve kurulum odaklı dokümanlar artık gerçek OSS yüzeyiyle eşleşiyor.

View File

@@ -2,7 +2,7 @@
这是一个**生产就绪的 AI 编码插件**,提供 64 个专业代理、261 项技能、84 条命令以及自动化钩子工作流,用于软件开发。
**版本:** 2.0.0-rc.1
**版本:** 2.0.0
## 核心原则

View File

@@ -81,6 +81,10 @@
## 最新动态
### v2.0.0 — 智能体 Harness 操作系统2026年6月
2.0 主线稳定版261 个技能、control-pane 基底(会话适配器 + MCP 清单、worktree 生命周期服务,以及 [ECC Discord 社区](https://discord.gg/36yGMHGFbR)。
### v2.0.0-rc.1 — 表面同步、运营工作流与 ECC 2.0 Alpha2026年4月
* **公共表面已与真实仓库同步** —— 元数据、目录数量、插件清单以及安装文档现在都与实际开源表面保持一致。
@@ -1256,7 +1260,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
| **上下文文件** | CLAUDE.md + AGENTS.md | AGENTS.md | AGENTS.md | AGENTS.md |
| **秘密检测** | 基于钩子 | beforeSubmitPrompt 钩子 | 基于沙箱 | 基于钩子 |
| **自动格式化** | PostToolUse 钩子 | afterFileEdit 钩子 | N/A | file.edited 钩子 |
| **版本** | 插件 | 插件 | 参考配置 | 2.0.0-rc.1 |
| **版本** | 插件 | 插件 | 参考配置 | 2.0.0 |
**关键架构决策:**

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ecc-universal",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ecc-universal",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "ecc-universal",
"version": "2.0.0-rc.1",
"version": "2.0.0",
"description": "Harness-native agent operating system for Codex, OpenCode, Cursor, Gemini, Claude Code, and terminal workflows - skills, hooks, rules, MCP conventions, and operator control-plane patterns",
"publishConfig": {
"access": "public"

222
scripts/discord/ecc-bot.mjs Normal file
View File

@@ -0,0 +1,222 @@
#!/usr/bin/env node
// ECC community Discord bot — dependency-free (Node 22+ native WebSocket).
// Slash commands: /ecc /help /skill /docs /release
//
// Env: DISCORD_BOT_TOKEN (required), DISCORD_APP_ID (required),
// ECC_REPO (path to local clone, default ~/GitHub/ECC/everything-claude-code),
// DISCORD_INVITE (optional, shown in /ecc)
//
// Crash-only design: any gateway close, error, or missed heartbeat ack exits
// the process; the launchd/pm2 supervisor restarts it with a fresh identify.
// Register commands first: node scripts/discord/register-commands.mjs
'use strict';
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
const TOKEN = process.env.DISCORD_BOT_TOKEN;
const APP_ID = process.env.DISCORD_APP_ID;
if (!TOKEN || !APP_ID) {
console.error('missing DISCORD_BOT_TOKEN / DISCORD_APP_ID');
process.exit(1);
}
const REPO = process.env.ECC_REPO || join(homedir(), 'GitHub/ECC/everything-claude-code');
const REPO_URL = 'https://github.com/affaan-m/ECC';
const INVITE = process.env.DISCORD_INVITE || '';
const API = 'https://discord.com/api/v10';
const log = (...a) => console.log(new Date().toISOString(), ...a);
// ---------- skill + docs lookup (local clone as the data source) ----------
function parseFrontmatter(text) {
const m = text.match(/^---\n([\s\S]*?)\n---/);
if (!m) return {};
const out = {};
for (const line of m[1].split('\n')) {
const kv = line.match(/^(\w[\w-]*):\s*(.+)$/);
if (kv) out[kv[1]] = kv[2].replace(/^["']|["']$/g, '');
}
return out;
}
function loadSkills() {
const dir = join(REPO, 'skills');
if (!existsSync(dir)) return [];
const skills = [];
for (const name of readdirSync(dir)) {
const md = join(dir, name, 'SKILL.md');
if (!existsSync(md)) continue;
try {
const fm = parseFrontmatter(readFileSync(md, 'utf8'));
skills.push({ name, description: fm.description || '(no description)' });
} catch { /* unreadable skill dirs are skipped, not fatal */ }
}
return skills;
}
function findSkill(query) {
const q = query.toLowerCase().trim().replace(/\s+/g, '-');
const skills = loadSkills();
const exact = skills.find(s => s.name === q);
const ranked = exact
? [exact, ...skills.filter(s => s !== exact && s.name.includes(q))]
: skills.filter(s => s.name.includes(q) || s.description.toLowerCase().includes(query.toLowerCase()));
return ranked.slice(0, 5);
}
function searchDocs(query) {
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
const hits = [];
const roots = ['docs', 'README.md'];
const walk = rel => {
const abs = join(REPO, rel);
if (!existsSync(abs)) return;
if (statSync(abs).isDirectory()) {
for (const f of readdirSync(abs)) walk(join(rel, f));
return;
}
if (!rel.endsWith('.md')) return;
const nameScore = terms.filter(t => rel.toLowerCase().includes(t)).length;
let score = nameScore * 3;
if (nameScore < terms.length) {
try {
const head = readFileSync(abs, 'utf8').slice(0, 4000).toLowerCase();
score += terms.filter(t => head.includes(t)).length;
} catch { /* skip unreadable */ }
}
if (score > 0) hits.push({ rel, score });
};
for (const r of roots) walk(r);
return hits.sort((a, b) => b.score - a.score).slice(0, 5);
}
// ---------- command handlers ----------
const HELP = [
'**ECC bot commands**',
'- `/ecc` — what ECC is + all the links',
'- `/skill name:<query>` — look up an ECC skill',
'- `/docs query:<terms>` — search the ECC docs',
'- `/release` — latest ECC release',
'- `/help` — this message',
].join('\n');
const handlers = {
ecc: () => [
'**Everything Claude Code (ECC)** — the agent harness performance system.',
'Skills, agents, rules, hooks, MCP conventions, and operator workflows that move across Claude Code, Codex, OpenCode, Cursor, Gemini, and Zed.',
'',
`- repo: ${REPO_URL}`,
'- site: https://ecc.tools',
`- install: \`/plugin marketplace add affaan-m/everything-claude-code\` then \`/plugin install ecc\``,
INVITE ? `- invite a friend: ${INVITE}` : '',
].filter(Boolean).join('\n'),
help: () => HELP,
skill: (options) => {
const query = options.find(o => o.name === 'name')?.value || '';
const found = findSkill(query);
if (!found.length) return `no skill matching \`${query}\` — browse all: ${REPO_URL}/tree/main/skills`;
const [top, ...rest] = found;
return [
`**${top.name}** — ${top.description}`,
`${REPO_URL}/tree/main/skills/${top.name}`,
rest.length ? `\nalso close: ${rest.map(s => `\`${s.name}\``).join(', ')}` : '',
].filter(Boolean).join('\n');
},
docs: (options) => {
const query = options.find(o => o.name === 'query')?.value || '';
const hits = searchDocs(query);
if (!hits.length) return `nothing found for \`${query}\` — try ${REPO_URL}/tree/main/docs`;
return [`**docs matching \`${query}\`:**`, ...hits.map(h => `- ${REPO_URL}/blob/main/${h.rel.replace(/\\/g, '/')}`)].join('\n');
},
release: async () => {
const res = await fetch('https://api.github.com/repos/affaan-m/ECC/releases/latest', {
headers: { 'User-Agent': 'ecc-discord-bot' },
});
if (!res.ok) return `couldn't reach GitHub (${res.status}) — ${REPO_URL}/releases`;
const r = await res.json();
return `**${r.name || r.tag_name}**\n${r.html_url}`;
},
};
async function respond(interaction) {
const name = interaction.data?.name;
const handler = handlers[name];
const url = `${API}/interactions/${interaction.id}/${interaction.token}/callback`;
if (!handler) {
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 4, data: { content: `unknown command \`${name}\`` } }),
});
return;
}
try {
const content = await handler(interaction.data?.options || []);
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 4, data: { content: String(content).slice(0, 1990) } }),
});
log('handled', `/${name}`);
} catch (err) {
log('handler error', name, err.message);
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 4, data: { content: 'something broke handling that — try again in a minute' } }),
}).catch(() => {});
}
}
// ---------- gateway (crash-only: exit on any failure, supervisor restarts) ----------
let seq = null;
let acked = true;
async function main() {
const gw = await fetch(`${API}/gateway/bot`, { headers: { Authorization: `Bot ${TOKEN}` } }).then(r => r.json());
if (!gw.url) { console.error('gateway discovery failed:', JSON.stringify(gw).slice(0, 200)); process.exit(1); }
const ws = new WebSocket(`${gw.url}?v=10&encoding=json`);
const send = payload => ws.send(JSON.stringify(payload));
const die = reason => { log('exiting:', reason); process.exit(1); };
ws.onmessage = ev => {
const msg = JSON.parse(ev.data);
if (msg.s) seq = msg.s;
switch (msg.op) {
case 10: { // HELLO
const interval = msg.d.heartbeat_interval;
setTimeout(() => {
send({ op: 1, d: seq });
setInterval(() => {
if (!acked) die('missed heartbeat ack');
acked = false;
send({ op: 1, d: seq });
}, interval);
}, interval * Math.random());
send({ op: 2, d: { token: TOKEN, intents: 1, properties: { os: 'darwin', browser: 'ecc-bot', device: 'ecc-bot' } } });
break;
}
case 11: acked = true; break; // HEARTBEAT_ACK
case 1: send({ op: 1, d: seq }); break; // server-requested heartbeat
case 7: die('server requested reconnect'); break;
case 9: die('invalid session'); break;
case 0:
if (msg.t === 'READY') log(`READY as ${msg.d.user.username}#${msg.d.user.discriminator}`);
if (msg.t === 'INTERACTION_CREATE' && msg.d.type === 2) respond(msg.d);
break;
default: break;
}
};
ws.onclose = ev => die(`gateway closed (${ev.code})`);
ws.onerror = () => die('gateway error');
}
main().catch(err => { console.error('fatal:', err.message); process.exit(1); });

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env node
// Registers the ECC bot's guild slash commands (bulk overwrite, instant).
// Env: DISCORD_BOT_TOKEN, DISCORD_APP_ID, DISCORD_GUILD_ID
'use strict';
const { DISCORD_BOT_TOKEN: TOKEN, DISCORD_APP_ID: APP_ID, DISCORD_GUILD_ID: GUILD } = process.env;
if (!TOKEN || !APP_ID || !GUILD) {
console.error('missing DISCORD_BOT_TOKEN / DISCORD_APP_ID / DISCORD_GUILD_ID');
process.exit(1);
}
const COMMANDS = [
{ name: 'ecc', description: 'What ECC is + all the links' },
{ name: 'help', description: 'List ECC bot commands' },
{
name: 'skill',
description: 'Look up an ECC skill by name',
options: [{ type: 3, name: 'name', description: 'skill name or keyword', required: true }],
},
{
name: 'docs',
description: 'Search the ECC docs',
options: [{ type: 3, name: 'query', description: 'search terms', required: true }],
},
{ name: 'release', description: 'Latest ECC release' },
];
const res = await fetch(`https://discord.com/api/v10/applications/${APP_ID}/guilds/${GUILD}/commands`, {
method: 'PUT',
headers: { Authorization: `Bot ${TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify(COMMANDS),
});
if (!res.ok) {
console.error('registration failed:', res.status, (await res.text()).slice(0, 300));
process.exit(1);
}
const registered = await res.json();
console.log('registered:', registered.map(c => `/${c.name}`).join(' '));

View File

@@ -46,21 +46,16 @@ console.log('\n=== Testing GitHub Copilot support surface ===\n');
test('VS Code settings enable Copilot prompt files', () => {
const settings = JSON.parse(read('.vscode/settings.json'));
assert.strictEqual(settings['chat.promptFiles'], true);
assert.ok(!Object.prototype.hasOwnProperty.call(settings, 'github.copilot.chat.reviewSelection.instructions'));
});
test('Copilot prompt files use current VS Code frontmatter', () => {
const promptFiles = fs.readdirSync(promptDir)
const promptFiles = fs
.readdirSync(promptDir)
.filter(file => file.endsWith('.prompt.md'))
.sort();
assert.deepStrictEqual(promptFiles, [
'build-fix.prompt.md',
'code-review.prompt.md',
'plan.prompt.md',
'refactor.prompt.md',
'security-review.prompt.md',
'tdd.prompt.md',
]);
assert.deepStrictEqual(promptFiles, ['build-fix.prompt.md', 'plan.prompt.md', 'refactor.prompt.md', 'security-review.prompt.md', 'tdd.prompt.md']);
for (const file of promptFiles) {
const relativePath = `.github/prompts/${file}`;
@@ -74,18 +69,15 @@ test('Copilot prompt files use current VS Code frontmatter', () => {
});
test('Copilot docs advertise slash prompt invocation instead of hash commands', () => {
const sources = [
'.github/copilot-instructions.md',
'README.md',
].map(read).join('\n');
const sources = ['.github/copilot-instructions.md', 'README.md'].map(read).join('\n');
for (const command of ['plan', 'tdd', 'code-review', 'security-review', 'build-fix', 'refactor']) {
for (const command of ['plan', 'tdd', 'security-review', 'build-fix', 'refactor']) {
assert.ok(!sources.includes(`#${command}`), `Expected no stale #${command} command syntax`);
}
assert.ok(sources.includes('/plan'));
assert.ok(sources.includes('/tdd'));
assert.ok(sources.includes('/code-review'));
assert.ok(sources.includes('/security-review'));
});
test('Copilot instructions include a prompt defense baseline', () => {

View File

@@ -55,7 +55,7 @@ const expectedReleaseFiles = [
'video-suite-production.md',
'partner-sponsor-talks-pack.md',
'owner-approval-packet-2026-05-19.md',
'release-name-plugin-publication-checklist-2026-05-18.md',
'release-name-plugin-publication-checklist-2026-05-18.md'
];
test('release candidate directory includes the public launch pack', () => {
@@ -64,10 +64,10 @@ test('release candidate directory includes the public launch pack', () => {
}
});
test('README links to Hermes setup and rc.1 release notes', () => {
test('README links to Hermes setup and current release notes', () => {
const readme = read('README.md');
assert.ok(readme.includes('docs/HERMES-SETUP.md'), 'README must link to Hermes setup');
assert.ok(readme.includes('docs/releases/2.0.0-rc.1/release-notes.md'), 'README must link to rc.1 release notes');
assert.ok(readme.includes('docs/releases/2.0.0/release-notes.md'), 'README must link to the 2.0.0 release notes');
});
test('cross-harness architecture doc exists and names core harnesses', () => {
@@ -109,37 +109,19 @@ test('release docs do not contain unresolved public-link placeholders', () => {
test('business launch copy stays aligned with the rc.1 public surface', () => {
const source = read('docs/business/social-launch-copy.md');
assert.ok(source.includes('ECC v2.0.0-rc.1'), 'business launch copy should use the rc.1 release');
assert.ok(
source.includes('preview pack is ready for final release review'),
'business launch copy should stay pre-publication until release URLs exist'
);
assert.ok(
source.includes('https://github.com/affaan-m/ECC'),
'business launch copy should include the public repo URL'
);
assert.ok(
source.includes(
'https://github.com/affaan-m/ECC/blob/main/docs/releases/2.0.0-rc.1/release-notes.md'
),
'business launch copy should link to the rc.1 release notes'
);
assert.ok(source.includes('preview pack is ready for final release review'), 'business launch copy should stay pre-publication until release URLs exist');
assert.ok(source.includes('https://github.com/affaan-m/ECC'), 'business launch copy should include the public repo URL');
assert.ok(source.includes('https://github.com/affaan-m/ECC/blob/main/docs/releases/2.0.0-rc.1/release-notes.md'), 'business launch copy should link to the rc.1 release notes');
assert.ok(!source.includes('<repo-link>'), 'business launch copy should not contain repo placeholders');
assert.ok(!source.includes('v1.8.0'), 'business launch copy should not stay pinned to v1.8.0');
});
test('announcement drafts avoid live-release claims before publication', () => {
const announcementFiles = [
'docs/releases/2.0.0-rc.1/linkedin-post.md',
'docs/releases/2.0.0-rc.1/partner-sponsor-talks-pack.md',
'docs/business/social-launch-copy.md',
];
const announcementFiles = ['docs/releases/2.0.0-rc.1/linkedin-post.md', 'docs/releases/2.0.0-rc.1/partner-sponsor-talks-pack.md', 'docs/business/social-launch-copy.md'];
for (const relativePath of announcementFiles) {
const source = read(relativePath);
assert.ok(
!/ECC v2\.0\.0-rc\.1 is live\./.test(source),
`${relativePath} must not claim rc.1 is live before the release gate completes`
);
assert.ok(!/ECC v2\.0\.0-rc\.1 is live\./.test(source), `${relativePath} must not claim rc.1 is live before the release gate completes`);
}
});
@@ -184,7 +166,7 @@ test('preview pack manifest assembles release, Hermes, and publication gates', (
'docs/releases/2.0.0-rc.1/owner-approval-packet-2026-05-19.md',
'docs/releases/2.0.0-rc.1/video-suite-production.md',
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md',
'docs/releases/2.0.0-rc.1/release-name-plugin-publication-checklist-2026-05-18.md',
'docs/releases/2.0.0-rc.1/release-name-plugin-publication-checklist-2026-05-18.md'
]) {
assert.ok(manifest.includes(artifact), `preview pack manifest missing ${artifact}`);
}
@@ -194,7 +176,7 @@ test('preview pack manifest assembles release, Hermes, and publication gates', (
'npm `ecc-universal@2.0.0-rc.1`',
'Claude plugin tag',
'Codex repo-marketplace distribution evidence',
'ECC Tools billing/product readiness',
'ECC Tools billing/product readiness'
]) {
assert.ok(manifest.includes(blocker), `preview pack manifest missing blocker ${blocker}`);
}
@@ -223,7 +205,7 @@ test('owner approval packet consolidates the final gated decisions', () => {
'Video upload',
'Final URL Fill-In',
'Do Not Approve If',
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement is authorized by this packet alone.',
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement is authorized by this packet alone.'
]) {
assert.ok(packet.includes(marker), `owner approval packet missing ${marker}`);
}
@@ -233,18 +215,12 @@ test('owner approval packet consolidates the final gated decisions', () => {
'npm run preview-pack:smoke -- --format json',
'npm run release:approval-gate -- --format json',
'npm run release:video-suite -- --format json',
'node tests/run-all.js',
'node tests/run-all.js'
]) {
assert.ok(packet.includes(command), `owner approval packet missing command ${command}`);
}
for (const urlSurface of [
'GitHub prerelease URL',
'npm rc package URL',
'Claude plugin tag URL',
'Primary launch video URL',
'ECC Tools billing/readiness URL',
]) {
for (const urlSurface of ['GitHub prerelease URL', 'npm rc package URL', 'Claude plugin tag URL', 'Primary launch video URL', 'ECC Tools billing/readiness URL']) {
assert.ok(packet.includes(urlSurface), `owner approval packet missing ${urlSurface}`);
}
@@ -272,7 +248,7 @@ test('GA roadmap mirrors the current May 19 release evidence', () => {
'467d148a-712a-4777-aad9-95593e9f1739',
'7642ee9c-3107-400c-a229-53e2895a8914',
'ecc-may-19-post-pr-2002-sync-64cef8f668e0',
'owner approval packet',
'owner approval packet'
]) {
assert.ok(roadmap.includes(marker), `GA roadmap missing current evidence marker ${marker}`);
}
@@ -331,19 +307,12 @@ test('release video suite manifest gates the content launch lane', () => {
'Do Not Publish If',
'renders/ecc-2-primary-launch-rough-v1.mp4',
'timelines/primary-launch-v1.timeline.json',
'Primary launch video',
'Primary launch video'
]) {
assert.ok(videoManifest.includes(marker), `video suite manifest missing ${marker}`);
}
for (const asset of [
'longform-full-wide.mp4',
'sf-thread-2-whatisecc.mp4',
'thread-2-ghapp-money.mp4',
'coverage-montage-wide.mp4',
'star_history.png',
'x_analytics.png',
]) {
for (const asset of ['longform-full-wide.mp4', 'sf-thread-2-whatisecc.mp4', 'thread-2-ghapp-money.mp4', 'coverage-montage-wide.mp4', 'star_history.png', 'x_analytics.png']) {
assert.ok(videoManifest.includes(asset), `video suite manifest missing asset ${asset}`);
}
@@ -365,7 +334,7 @@ test('release approval gate blocks publication until owner decisions and URLs ar
'owner-decisions-approved',
'release-url-ledger-finalized',
'announcement-copy-finalized',
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement',
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement'
]) {
assert.ok(script.includes(marker), `release approval gate missing ${marker}`);
}
@@ -402,7 +371,7 @@ test('partner sponsor talks pack gates the hypergrowth outbound lane', () => {
'GitHub Discussion Announcement',
'Video CTA Hooks',
'Do Not Send Or Publish If',
'The user has not approved outbound sponsor, partner, consulting, or media',
'The user has not approved outbound sponsor, partner, consulting, or media'
]) {
assert.ok(partnerPack.includes(marker), `partner pack missing ${marker}`);
}
@@ -416,10 +385,7 @@ test('partner sponsor talks pack gates the hypergrowth outbound lane', () => {
});
test('release video suite public docs do not expose private media paths', () => {
const releaseVideoDocs = [
'docs/releases/2.0.0-rc.1/video-suite-production.md',
'docs/releases/2.0.0/ecc-2-hypergrowth-release-command-center.md',
];
const releaseVideoDocs = ['docs/releases/2.0.0-rc.1/video-suite-production.md', 'docs/releases/2.0.0/ecc-2-hypergrowth-release-command-center.md'];
const offenders = [];
for (const relativePath of releaseVideoDocs) {
@@ -437,37 +403,15 @@ test('publication readiness checklist gates public release actions on evidence',
const may15Evidence = read('docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md');
const discussionPlaybook = read('docs/architecture/discussion-response-playbook.md');
for (const section of [
'## Release Identity Matrix',
'## Publication Gates',
'## Required Command Evidence',
'## Do Not Publish If',
'## Announcement Order',
]) {
for (const section of ['## Release Identity Matrix', '## Publication Gates', '## Required Command Evidence', '## Do Not Publish If', '## Announcement Order']) {
assert.ok(source.includes(section), `publication readiness missing ${section}`);
}
for (const field of [
'Fresh check',
'Evidence artifact',
'Owner',
'Status',
'Blocker field',
'Recorded output',
]) {
for (const field of ['Fresh check', 'Evidence artifact', 'Owner', 'Status', 'Blocker field', 'Recorded output']) {
assert.ok(source.includes(field), `publication readiness missing ${field}`);
}
for (const surface of [
'GitHub release',
'npm package',
'Claude plugin',
'Codex plugin',
'Codex repo marketplace',
'OpenCode package',
'ECC Tools billing reference',
'Announcement copy',
]) {
for (const surface of ['GitHub release', 'npm package', 'Claude plugin', 'Codex plugin', 'Codex repo marketplace', 'OpenCode package', 'ECC Tools billing reference', 'Announcement copy']) {
assert.ok(source.includes(surface), `publication readiness missing ${surface}`);
}
@@ -502,14 +446,7 @@ test('publication readiness checklist gates public release actions on evidence',
assert.ok(source.includes('platform audit sampled 59 trunk discussions'));
assert.ok(source.includes('0 needing maintainer touch'));
assert.ok(source.includes('discussion-response-playbook.md'));
for (const expected of [
'Public Support',
'Maintainer Coordination',
'Stale Or Concluded',
'Release Announcement',
'Security Escalation',
'classified as informational',
]) {
for (const expected of ['Public Support', 'Maintainer Coordination', 'Stale Or Concluded', 'Release Announcement', 'Security Escalation', 'classified as informational']) {
assert.ok(discussionPlaybook.includes(expected), `discussion playbook missing ${expected}`);
}
assert.ok(may15Evidence.includes('env -u GITHUB_TOKEN'));
@@ -518,9 +455,7 @@ test('publication readiness checklist gates public release actions on evidence',
});
test('release name and plugin publication checklist freezes rc.1 surfaces', () => {
const checklist = read(
'docs/releases/2.0.0-rc.1/release-name-plugin-publication-checklist-2026-05-18.md'
);
const checklist = read('docs/releases/2.0.0-rc.1/release-name-plugin-publication-checklist-2026-05-18.md');
const launchChecklist = read('docs/releases/2.0.0-rc.1/launch-checklist.md');
const referenceArchitecture = read('docs/ECC-2.0-REFERENCE-ARCHITECTURE.md');
@@ -534,7 +469,7 @@ test('release name and plugin publication checklist freezes rc.1 surfaces', () =
'Codex plugin',
'do not claim official directory listing until OpenAI publishing path is available',
'Do not rename the npm package until rc.1 is published',
'Do not announce billing, Marketplace, or native payments',
'Do not announce billing, Marketplace, or native payments'
]) {
assert.ok(checklist.includes(value), `release name/plugin checklist missing ${value}`);
}
@@ -545,7 +480,7 @@ test('release name and plugin publication checklist freezes rc.1 surfaces', () =
'codex plugin marketplace add --help',
'npm publish --tag next --dry-run',
'npm run preview-pack:smoke',
'npm run release:approval-gate -- --format json',
'npm run release:approval-gate -- --format json'
]) {
assert.ok(checklist.includes(command), `release name/plugin checklist missing command ${command}`);
}
@@ -569,7 +504,7 @@ test('active release identity surfaces use canonical ECC repo URLs', () => {
'docs/releases/2.0.0-rc.1/release-url-ledger-2026-05-19.md',
'ecc2/Cargo.toml',
'scripts/platform-audit.js',
'scripts/discussion-audit.js',
'scripts/discussion-audit.js'
];
const offenders = [];