Compare commits

...

6 Commits

Author SHA1 Message Date
ECC Test
c888d2b73f docs: update Greptile sponsor placement 2026-06-09 23:53:13 -04:00
Affaan Mustafa
ff768db363 feat(mcp): single-connector default set + connector policy (#2219)
Reduce the default .mcp.json to one connector (chrome-devtools) per the
new policy in docs/MCP-CONNECTOR-POLICY.md: a default earns its slot only
if it is universal AND MCP beats a CLI/API wrapped in a skill. June 2026
audit verdicts: github -> gh via github-ops skill; context7 -> REST via
documentation-lookup; exa -> harness-native search (+ exa-search skill);
memory -> native harness memory + instincts; playwright -> playwright CLI
skills (vendor moved agent flows off MCP); sequential-thinking -> native
extended thinking. All six remain opt-in in mcp-configs/mcp-servers.json.
Tests updated: plugin-manifest policy assertions + install-apply Cursor
expectations.

Co-authored-by: ECC Test <ecc@example.test>
2026-06-09 23:28:35 -04:00
ECC Test
8ad4151095 ci: remove copilot code-review surface 2026-06-09 21:42:38 -04:00
ECC Test
29edd57708 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.
2026-06-09 21:40:40 -04:00
ECC Test
3e30f1a56a ci: harden workflows and sponsor code review config 2026-06-09 21:20:17 -04:00
ECC Test
10c303e609 ci: harden release announce checkout 2026-06-09 21:03:22 -04:00
44 changed files with 637 additions and 391 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",

36
.coderabbit.yaml Normal file
View File

@@ -0,0 +1,36 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "en-US"
early_access: false
tone_instructions: "Be direct, concise, and evidence-led. Prioritize actionable findings over praise."
reviews:
profile: "assertive"
request_changes_workflow: false
high_level_summary: true
high_level_summary_in_walkthrough: true
review_status: true
review_details: true
commit_status: true
fail_commit_status: true
auto_review:
enabled: true
drafts: false
path_instructions:
- path: ".github/workflows/**"
instructions: |
Treat workflow changes as security-sensitive. Flag unpinned third-party actions, broad write permissions, persisted checkout credentials in write-token jobs, pull_request_target misuse, and untrusted GitHub context interpolated into shell commands.
- path: "{scripts,bin}/**"
instructions: |
Focus on command injection, unsafe subprocess usage, path traversal, SSRF, secret exposure, and missing tests for new CLI behavior.
- path: "skills/**/scripts/**"
instructions: |
Review generated or imported scripts as untrusted-input tooling. Flag RCE, path traversal, network fetches without validation, and writes outside the expected workspace.
- path: "{skills,commands,agents,rules}/**"
instructions: |
Focus on prompt-injection resilience, tool-permission scope, destructive action guards, and secret exfiltration risks.
- path: "{SECURITY.md,docs/security/**}"
instructions: |
Check that official distribution surfaces, disclosure guidance, and supply-chain rules stay accurate and do not endorse unofficial packages.
chat:
auto_reply: true

View File

@@ -49,12 +49,9 @@ stay below provider length limits.
| Server | Purpose |
|---|---|
| `github` | GitHub API access |
| `context7` | Live documentation lookup |
| `exa` | Neural web search |
| `memory` | Persistent memory across sessions |
| `playwright` | Browser automation & E2E testing |
| `sequential-thinking` | Step-by-step reasoning |
| `chrome-devtools` | Interactive browser debugging via Chrome DevTools (CDP sessions, performance traces, console/network inspection) |
The former defaults (`github`, `context7`, `exa`, `memory`, `playwright`, `sequential-thinking`) were retired in the June 2026 connector audit — their jobs are covered by skills wrapping CLIs/REST APIs or by harness-native features. They remain available as opt-in entries in `mcp-configs/mcp-servers.json`. See `docs/MCP-CONNECTOR-POLICY.md` for the policy and the per-connector rationale.
## Notes

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

@@ -83,10 +83,11 @@ Use AAA structure (Arrange / Act / Assert) and descriptive test names that expla
Types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`
PR checklist before requesting review:
PR checklist before requesting sponsored review:
- CI passing, merge conflicts resolved, branch up to date with target
- Full diff reviewed (`git diff [base-branch]...HEAD`)
- Test plan included in PR description
- Code review is handled by CodeRabbit and Greptile. Do not add or route PR code review through Copilot, Claude, Codex, or other reviewer bots.
## Code Quality Checklist
@@ -107,7 +108,6 @@ Use these prompts in Copilot Chat for deeper workflows:
|--------|-------------|---------|
| `/plan` | Complex feature | Phased implementation plan |
| `/tdd` | New feature or bug fix | Test-driven development cycle |
| `/code-review` | After writing code | Quality and security review |
| `/security-review` | Before a release | Deep security analysis |
| `/build-fix` | Build/CI failure | Systematic error resolution |
| `/refactor` | Code maintenance | Dead code cleanup and simplification |

View File

@@ -1,56 +0,0 @@
---
agent: agent
description: Comprehensive code quality and security review of the selected code or recent changes
---
# Code Review
Review the selected code (or the current diff if nothing is selected) across four dimensions. Only report issues you are **confident about** — flag uncertainty explicitly rather than guessing.
## Dimensions
### 1. Security (CRITICAL — block ship if found)
- Hardcoded secrets, tokens, API keys, passwords
- Missing input validation or sanitization at system boundaries
- SQL/NoSQL injection risk (string interpolation in queries)
- XSS risk (unsanitized HTML output)
- Auth/authz checks missing or client-side only
- Sensitive data in logs or error messages exposed to clients
- Missing rate limiting on public endpoints
### 2. Code Quality (HIGH)
- Mutation of existing state instead of creating new objects
- Functions over 50 lines or files over 800 lines
- Nesting deeper than 4 levels
- Duplicated logic that should be extracted
- Misleading or non-descriptive names
### 3. Error Handling (HIGH)
- Silently swallowed errors (`catch {}`, empty catch blocks)
- Missing error handling at async boundaries
- Errors returned but not checked by callers
- User-facing error messages leaking internal details
### 4. Test Coverage (MEDIUM)
- Missing tests for new logic
- Tests that only test happy paths (missing error/edge cases)
- Assertions that always pass
## Output Format
For each issue found:
```
**[CRITICAL|HIGH|MEDIUM|LOW]** — [File:Line if known]
Issue: [What is wrong]
Fix: [Concrete suggestion]
```
End with a summary:
```
## Summary
- Critical: N
- High: N
- Medium: N
- Approved to ship: yes / no (fix CRITICAL and HIGH first)
```

View File

@@ -36,6 +36,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -114,6 +116,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -175,6 +179,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -200,6 +206,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -227,6 +235,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0

View File

@@ -14,6 +14,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Announce release to Discord + Discussions
run: node scripts/discord/release-announce.mjs
env:

View File

@@ -28,6 +28,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0

View File

@@ -18,6 +18,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0

View File

@@ -1,28 +1,8 @@
{
"mcpServers": {
"github": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github@2025.4.8"]
},
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp@2.1.4"]
},
"exa": {
"type": "http",
"url": "https://mcp.exa.ai/mcp"
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory@2026.1.26"]
},
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@0.0.69", "--extension"]
},
"sequential-thinking": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking@2025.12.18"]
"args": ["-y", "chrome-devtools-mcp@latest"]
}
}
}

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

@@ -7,10 +7,6 @@
{ "file": ".github/copilot-instructions.md" },
{ "text": "Always write tests before implementation (TDD). Use Arrange-Act-Assert structure. Target 80%+ coverage. Write descriptive test names that explain the behavior under test, not just the function name." }
],
"github.copilot.chat.reviewSelection.instructions": [
{ "file": ".github/copilot-instructions.md" },
{ "text": "Review for: (1) security issues — hardcoded secrets, missing input validation, injection risks, (2) code quality — mutation, deep nesting, large functions, (3) error handling — swallowed errors, missing boundary validation, (4) test coverage gaps." }
],
"github.copilot.chat.commitMessageGeneration.instructions": [
{ "text": "Use conventional commit format: <type>: <description>. Types: feat, fix, refactor, docs, test, chore, perf, ci. Keep the subject line under 72 characters. Focus on WHY the change was made, not WHAT changed." }
]

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,30 @@
# Changelog
## Unreleased
### Changed
- Default MCP connector set reduced to a single connector (`chrome-devtools`) per the new connector policy (`docs/MCP-CONNECTOR-POLICY.md`). The six previous defaults (`github`, `context7`, `exa`, `memory`, `playwright`, `sequential-thinking`) were retired after the June 2026 audit: their jobs are covered by skills wrapping CLIs/REST APIs (`github-ops`, `documentation-lookup`, `exa-search`, e2e skills) or by harness-native features (memory, extended thinking, web search). All six remain opt-in via `mcp-configs/mcp-servers.json`.
## 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).
---
@@ -80,7 +80,7 @@ ECC v2.0.0-rc.1 adds the public Hermes operator story on top of that reusable la
<sub><strong>Business sponsors</strong></sub><br />
<a href="https://www.coderabbit.ai"><img src="assets/images/sponsors/coderabbit.png" width="72" alt="CodeRabbit logo" /></a>
&nbsp;&nbsp;
<a href="https://greptile.com"><img src="assets/images/sponsors/greptile.png" width="72" alt="Greptile logo" /></a>
<a href="https://www.greptile.com/go/ecc"><img src="assets/images/sponsors/greptile.png" width="72" alt="Greptile logo" /></a>
</div>
---
@@ -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.
@@ -978,10 +982,12 @@ Use Claude Code's `/mcp` command or CLI-managed MCP setup for live Claude Code s
For repo-local MCP access, copy desired MCP server definitions from `mcp-configs/mcp-servers.json` into a project-scoped `.mcp.json`.
ECC ships exactly one default connector (`chrome-devtools`); everything else is a skill wrapping a CLI/REST API or an opt-in catalog entry. The rule and the June 2026 audit that retired the previous six defaults live in [docs/MCP-CONNECTOR-POLICY.md](docs/MCP-CONNECTOR-POLICY.md).
If you already run your own copies of ECC-bundled MCPs, set:
```bash
export ECC_DISABLED_MCPS="github,context7,exa,playwright,sequential-thinking,memory"
export ECC_DISABLED_MCPS="chrome-devtools"
```
ECC-managed install and Codex sync flows will skip or remove those bundled servers instead of re-adding duplicates. `ECC_DISABLED_MCPS` is an ECC install/sync filter, not a live Claude Code toggle.
@@ -1578,10 +1584,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 +1599,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 +1618,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 +1638,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 +1649,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)
@@ -1771,7 +1776,7 @@ ECC stays free because paid sponsors fund the work. Featured README placement is
<div align="center">
<a href="https://www.coderabbit.ai"><img src="assets/images/sponsors/coderabbit.png" width="80" alt="CodeRabbit logo" /></a>
&nbsp;&nbsp;&nbsp;
<a href="https://greptile.com"><img src="assets/images/sponsors/greptile.png" width="80" alt="Greptile logo" /></a>
<a href="https://www.greptile.com/go/ecc"><img src="assets/images/sponsors/greptile.png" width="80" alt="Greptile logo" /></a>
<br />
<sub><strong>CodeRabbit</strong> · <strong>Greptile</strong></sub>
</div>

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

@@ -11,7 +11,7 @@ Thank you to everyone funding ECC's open-source work. Your sponsorship is what l
| Sponsor | Logo | Since |
|---------|------|-------|
| [**CodeRabbit**](https://www.coderabbit.ai) | <img src="assets/images/sponsors/coderabbit.png" width="60" alt="CodeRabbit logo" /> | 2026 |
| [**Greptile**](https://greptile.com) | <img src="assets/images/sponsors/greptile.png" width="60" alt="Greptile logo" /> | 2026 |
| [**Greptile**](https://www.greptile.com/go/ecc) | <img src="assets/images/sponsors/greptile.png" width="60" alt="Greptile logo" /> | 2026 |
*[Become a Business sponsor](https://github.com/sponsors/affaan-m) to get README sponsor placement + SPONSORS.md listing. No seats, SLA, custom development, or preferential technical placement is bundled unless separately agreed.*

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,43 @@
# MCP Connector Policy
ECC ships exactly one default MCP connector. Everything else is a skill wrapping a CLI or REST API, or an opt-in entry in `mcp-configs/mcp-servers.json`.
## The rule
A default connector earns its slot only if both hold:
1. **Universal** — it applies to essentially every user of a coding agent, on every harness ECC targets.
2. **MCP beats a CLI/API wrapped in a skill** — the job genuinely needs what MCP provides: interactive session state, streaming, an auth handshake, or structured browsing. Stateless request/response work is a skill, not a server. Tool schemas load into every session; each default connector taxes every user's context window whether they use it or not.
The default set stays well under ten. In practice the 2026 field default across serious harnesses is zero to two connectors plus native built-ins.
## Current default set
| Server | Why it passes |
|---|---|
| `chrome-devtools` | Google's official DevTools MCP. Interactive CDP sessions — live debugging, performance traces, console and network inspection on a stateful browser. This is the textbook case where MCP beats a CLI: the value is the held-open session, not a one-shot command. Keyless. |
## The six it replaced (June 2026 audit)
| Former default | Verdict | Replacement |
|---|---|---|
| `github` | drop for skill | `gh` CLI via the `github-ops` skill. `gh` is in every model's training data, composes one-shot commands with minimal token overhead, and auths once via `gh auth login`. The MCP server's ~30 tool schemas taxed every session. |
| `context7` | drop for skill | The `documentation-lookup` skill targeting Context7's public REST API (`/api/v2/libs/search`, `/api/v2/context`). Two stateless calls with a bearer key — no session state to justify a server. |
| `exa` | drop for skill | Harness-native search (Claude Code WebSearch, Codex web_search, Cursor @Web) by default; the `exa-search` skill remains for API-key holders. Also required an API key, which fails the universality test for a default. |
| `memory` | drop entirely | Native harness memory (Claude Code auto-memory directories, Cursor memories, AGENTS.md conventions) plus ECC's instinct/continuous-learning system. The knowledge-graph server solved a 2024 problem harnesses have since absorbed. |
| `playwright` | drop for skill | Microsoft's own `@playwright/cli` agent surface — the vendor itself moved agent workflows off MCP because returning full accessibility trees per step burns context. ECC's e2e skills already drive the CLI. Browser *debugging* (the interactive case) is covered by `chrome-devtools`. |
| `sequential-thinking` | drop entirely | Native extended thinking in every modern harness. The server wrapped no external system — a prompting pattern dressed as a connector. |
All six remain available as opt-in entries in `mcp-configs/mcp-servers.json` for users who want them.
## Opt-out
`ECC_DISABLED_MCPS` filters ECC-generated MCP configs at install/sync time:
```bash
export ECC_DISABLED_MCPS="chrome-devtools"
```
## Adding a connector
Open a PR that argues both prongs of the rule explicitly. "Popular" is not an argument; "the job is stateful and universal" is.

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

@@ -1533,10 +1533,9 @@ ECC bietet **GitHub-Copilot-Unterstützung** für VS Code über das native Instr
| Komponente | Datei | Zweck |
|-----------|------|---------|
| Kern-Instructions | `.github/copilot-instructions.md` | Stets geladene Rules: Coding-Style, Sicherheit, Testing, Git-Workflow |
| VS-Code-Einstellungen | `.vscode/settings.json` | Aufgabenspezifische Instruction-Dateien für Codegenerierung, Testgenerierung, Review und Commit-Nachrichten |
| VS-Code-Einstellungen | `.vscode/settings.json` | Aufgabenspezifische Instruction-Dateien für Codegenerierung, Testgenerierung und Commit-Nachrichten |
| Plan-Prompt | `.github/prompts/plan.prompt.md` | Phasenweise Implementierungsplanung |
| TDD-Prompt | `.github/prompts/tdd.prompt.md` | Red-Green-Improve-Zyklus |
| Code-Review-Prompt | `.github/prompts/code-review.prompt.md` | Qualitäts- und Sicherheitsreview |
| Security-Review-Prompt | `.github/prompts/security-review.prompt.md` | Tiefe, OWASP-orientierte Sicherheitsanalyse |
| Build-Fix-Prompt | `.github/prompts/build-fix.prompt.md` | Systematische Behebung von Build- und CI-Fehlern |
| Refactor-Prompt | `.github/prompts/refactor.prompt.md` | Beseitigung von totem Code und Vereinfachung |
@@ -1549,16 +1548,16 @@ Die eingecheckte `.vscode/settings.json` aktiviert `chat.promptFiles`, sodass VS
So verwendest du die Workflow-Prompts in Copilot Chat:
1. Öffne das Copilot-Chat-Panel in VS Code.
2. Klicke auf das **Büroklammer-/Anhängen-Symbol** und wähle **Prompt...**, oder tippe `/` und wähle einen Prompt.
3. Wähle den Prompt aus (z. B. `plan`, `tdd`, `code-review`).
3. Wähle den Prompt aus (z. B. `plan`, `tdd`, `security-review`).
### Wie es funktioniert
GitHub Copilot in VS Code liest zwei Dateitypen automatisch:
- **`.github/copilot-instructions.md`** — Instructions auf Repository-Ebene, die in jede Copilot-Chat-Anfrage injiziert werden. Enthält ECCs Kern-Coding-Standards, Sicherheits-Checkliste, Testanforderungen und Git-Workflow.
- **`.github/prompts/*.prompt.md`** — wiederverwendbare Prompt-Dateien, die Nutzer bei Bedarf aufrufen. Jeder Prompt führt Copilot durch einen bestimmten ECC-Workflow (plan → TDD → review → ship).
- **`.github/prompts/*.prompt.md`** — wiederverwendbare Prompt-Dateien, die Nutzer bei Bedarf aufrufen. Jeder Prompt führt Copilot durch einen bestimmten ECC-Workflow wie Planung, TDD, Security-Review, Build-Fix oder Refactor.
Die **`.vscode/settings.json`** fügt aufgabenspezifische Instruction-Overlays hinzu, sodass Copilot je nachdem, ob du Code generierst, Tests schreibst, eine Auswahl reviewst oder eine Commit-Nachricht entwirfst, den richtigen Kontext erhält.
Die **`.vscode/settings.json`** fügt aufgabenspezifische Instruction-Overlays hinzu, sodass Copilot für Codegenerierung, Testgenerierung und Commit-Nachrichten den richtigen Kontext erhält.
### Feature-Abdeckung
@@ -1568,7 +1567,7 @@ Die **`.vscode/settings.json`** fügt aufgabenspezifische Instruction-Overlays h
| Sicherheits-Checkliste | Stets aktiv + `security-review`-Prompt |
| Testing / TDD | Stets aktiv + `tdd`-Prompt |
| Implementierungsplanung | `plan`-Prompt |
| Code-Review | `code-review`-Prompt |
| Code-Review | Externes PR-Review über CodeRabbit + Greptile |
| Behebung von Build-Fehlern | `build-fix`-Prompt |
| Refactoring | `refactor`-Prompt |
| Commit-Nachrichten-Format | Aufgabenspezifische Instruction in `settings.json` |
@@ -1588,7 +1587,7 @@ ECC ist das **erste Plugin, das jedes große KI-Coding-Tool ausreizt**. So vergl
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode | GitHub Copilot |
|---------|------------|------------|-----------|----------|----------------|
| **Agents** | 60 | Gemeinsam (AGENTS.md) | Gemeinsam (AGENTS.md) | 12 | Nicht verfügbar |
| **Commands** | 75 | Gemeinsam | Instruction-basiert | 35 | 6 Prompts |
| **Commands** | 75 | Gemeinsam | Instruction-basiert | 35 | 5 Prompts |
| **Skills** | 232 | Gemeinsam | 10 (natives Format) | 37 | Über Instructions |
| **Hook-Events** | 8 Typen | 15 Typen | Noch keine | 11 Typen | Keine |
| **Hook-Skripte** | 20+ Skripte | 16 Skripte (DRY-Adapter) | Nicht verfügbar | Plugin-Hooks | Nicht verfügbar |

View File

@@ -1215,10 +1215,9 @@ ECC proporciona **soporte para GitHub Copilot** para VS Code mediante el sistema
| Componente | Archivo | Propósito |
|------------|---------|-----------|
| Instrucciones principales | `.github/copilot-instructions.md` | Reglas siempre cargadas: estilo de código, seguridad, pruebas, flujo de git |
| Configuración de VS Code | `.vscode/settings.json` | Archivos de instrucciones por tarea para generación de código, pruebas, revisión y mensajes de commit |
| Configuración de VS Code | `.vscode/settings.json` | Archivos de instrucciones por tarea para generación de código, pruebas y mensajes de commit |
| Prompt de plan | `.github/prompts/plan.prompt.md` | Planificación de implementación por fases |
| Prompt de TDD | `.github/prompts/tdd.prompt.md` | Ciclo Rojo-Verde-Mejorar |
| Prompt de revisión de código | `.github/prompts/code-review.prompt.md` | Revisión de calidad y seguridad |
| Prompt de revisión de seguridad | `.github/prompts/security-review.prompt.md` | Análisis de seguridad profundo alineado con OWASP |
| Prompt de corrección de build | `.github/prompts/build-fix.prompt.md` | Resolución sistemática de errores de build y CI |
| Prompt de refactorización | `.github/prompts/refactor.prompt.md` | Limpieza de código muerto y simplificación |
@@ -1231,7 +1230,7 @@ El `.vscode/settings.json` confirmado habilita `chat.promptFiles` para que VS Co
Para usar los prompts de flujo de trabajo en Copilot Chat:
1. Abre el panel de Copilot Chat en VS Code.
2. Haz clic en el icono de **clip / adjuntar** y selecciona **Prompt...**, o escribe `/` y elige un prompt.
3. Selecciona el prompt (por ejemplo, `plan`, `tdd`, `code-review`).
3. Selecciona el prompt (por ejemplo, `plan`, `tdd`, `security-review`).
---
@@ -1242,7 +1241,7 @@ ECC es el **primer plugin que maximiza todas las principales herramientas de cod
| Característica | Claude Code | Cursor IDE | Codex CLI | OpenCode | GitHub Copilot |
|----------------|-----------------------|------------|-----------|----------|----------------|
| **Agentes** | 63 | Compartidos (AGENTS.md) | Compartidos (AGENTS.md) | 12 | N/A |
| **Comandos** | 79 | Compartidos | Basados en instrucciones | 35 | 6 prompts |
| **Comandos** | 79 | Compartidos | Basados en instrucciones | 35 | 5 prompts |
| **Skills** | 249 | Compartidas | 10 (formato nativo) | 37 | Mediante instrucciones |
| **Eventos de Hook** | 8 tipos | 15 tipos | Ninguno aún | 11 tipos | Ninguno |
| **Scripts de Hook** | 20+ scripts | 16 scripts (adaptador DRY) | N/A | Hooks de plugin | N/A |

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 |
**关键架构决策:**

67
greptile.json Normal file
View File

@@ -0,0 +1,67 @@
{
"strictness": 2,
"commentTypes": ["logic", "syntax", "style"],
"triggerOnUpdates": true,
"triggerOnDrafts": false,
"shouldUpdateDescription": false,
"updateExistingSummaryComment": true,
"statusCheck": true,
"statusCommentsEnabled": true,
"disabledLabels": ["no-review", "skip-review", "wip"],
"excludeBranches": ["dependabot/**"],
"fileChangeLimit": 80,
"ignoreKeywords": "no-review\nskip-review\nmechanical-format-only",
"ignorePatterns": "node_modules/**\ndist/**\nbuild/**\ncoverage/**\n.vite/**\n.next/**\n.cache/**\n*.lock\npackage-lock.json\nyarn.lock\npnpm-lock.yaml\nassets/**/*.png\nassets/**/*.jpg\nassets/**/*.jpeg\nassets/**/*.gif\nassets/**/*.webp\n**/*.generated.*",
"summarySection": {
"included": true,
"collapsible": true,
"defaultOpen": true
},
"issuesTableSection": {
"included": true,
"collapsible": false,
"defaultOpen": true
},
"confidenceScoreSection": {
"included": true,
"collapsible": true,
"defaultOpen": false
},
"sequenceDiagramSection": {
"included": true,
"collapsible": true,
"defaultOpen": false
},
"customContext": {
"rules": [
{
"scope": [".github/workflows/**"],
"rule": "Flag unpinned third-party GitHub Actions, broad write permissions, persisted checkout credentials in write-token jobs, pull_request_target misuse, and untrusted GitHub context inside shell commands."
},
{
"scope": ["scripts/**", "bin/**", "skills/**/scripts/**"],
"rule": "Treat CLI inputs, URLs, file paths, and subprocess arguments as untrusted. Flag RCE, SSRF, path traversal, unsafe shell usage, and missing regression tests."
},
{
"scope": ["skills/**", "commands/**", "agents/**", "rules/**"],
"rule": "Review for prompt injection, tool-permission creep, destructive-action ambiguity, hidden persistence, and secret exfiltration risks."
},
{
"scope": ["SECURITY.md", "docs/security/**", "README.md"],
"rule": "Ensure package and distribution claims only list official ECC surfaces. Flag unofficial npm packages, stale historical repo paths, and misleading install instructions."
}
],
"files": [
{
"scope": ["**/*"],
"path": "SECURITY.md",
"description": "ECC disclosure policy, official package surfaces, out-of-scope rules, and supply-chain guardrails."
},
{
"scope": ["**/*"],
"path": "AGENTS.md",
"description": "Workspace-level agent routing and operating constraints."
}
]
}
}

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 = [];

View File

@@ -61,10 +61,7 @@ function loadJsonObject(filePath, label) {
assert.fail(`Expected ${label} to contain valid JSON: ${error.message}`);
}
assert.ok(
parsed && typeof parsed === 'object' && !Array.isArray(parsed),
`Expected ${label} to contain a JSON object`,
);
assert.ok(parsed && typeof parsed === 'object' && !Array.isArray(parsed), `Expected ${label} to contain a JSON object`);
return parsed;
}
@@ -161,34 +158,22 @@ test('.opencode/plugins/ecc-hooks.ts active plugin banner matches package.json',
test('docs/pt-BR/README.md latest release heading matches package.json', () => {
const source = fs.readFileSync(ptBrReadmePath, 'utf8');
assert.ok(
source.includes(`### v${expectedVersion} `),
'Expected docs/pt-BR/README.md to advertise the current release heading',
);
assert.ok(source.includes(`### v${expectedVersion} `), 'Expected docs/pt-BR/README.md to advertise the current release heading');
});
test('docs/tr/README.md latest release heading matches package.json', () => {
const source = fs.readFileSync(trReadmePath, 'utf8');
assert.ok(
source.includes(`### v${expectedVersion} `),
'Expected docs/tr/README.md to advertise the current release heading',
);
assert.ok(source.includes(`### v${expectedVersion} `), 'Expected docs/tr/README.md to advertise the current release heading');
});
test('README.zh-CN.md latest release heading matches package.json', () => {
const source = fs.readFileSync(rootZhCnReadmePath, 'utf8');
assert.ok(
source.includes(`### v${expectedVersion} `),
'Expected README.zh-CN.md to advertise the current release heading',
);
assert.ok(source.includes(`### v${expectedVersion} `), 'Expected README.zh-CN.md to advertise the current release heading');
});
test('docs/zh-CN/README.md latest release heading matches package.json', () => {
const source = fs.readFileSync(zhCnReadmePath, 'utf8');
assert.ok(
source.includes(`### v${expectedVersion} `),
'Expected docs/zh-CN/README.md to advertise the current release heading',
);
assert.ok(source.includes(`### v${expectedVersion} `), 'Expected docs/zh-CN/README.md to advertise the current release heading');
});
// ── Claude plugin manifest ────────────────────────────────────────────────────
@@ -216,10 +201,7 @@ test('claude plugin.json uses short plugin slug', () => {
});
test('claude plugin.json does NOT have agents field (unsupported by Claude Code validator)', () => {
assert.ok(
!('agents' in claudePlugin),
'agents field must NOT be declared — Claude Code plugin validator rejects it',
);
assert.ok(!('agents' in claudePlugin), 'agents field must NOT be declared — Claude Code plugin validator rejects it');
});
test('claude plugin.json skills is an array', () => {
@@ -234,26 +216,13 @@ test('claude plugin.json disables bundled MCP servers for provider tool-name com
const legacyPluginName = 'everything-claude-code';
const reportedOverlongToolName = `mcp__plugin_${legacyPluginName}_github__create_pull_request_review`;
assert.ok(
reportedOverlongToolName.length > 64,
'Expected the reported GitHub MCP tool name to exceed strict provider limits without the MCP opt-out',
);
assert.ok(
Object.prototype.hasOwnProperty.call(claudePlugin, 'mcpServers'),
'Expected mcpServers to be explicitly declared so Claude Code does not auto-load root .mcp.json',
);
assert.deepStrictEqual(
claudePlugin.mcpServers,
{},
'Claude plugin installs must not auto-bundle root MCP servers; document/manual MCP install remains supported',
);
assert.ok(reportedOverlongToolName.length > 64, 'Expected the reported GitHub MCP tool name to exceed strict provider limits without the MCP opt-out');
assert.ok(Object.prototype.hasOwnProperty.call(claudePlugin, 'mcpServers'), 'Expected mcpServers to be explicitly declared so Claude Code does not auto-load root .mcp.json');
assert.deepStrictEqual(claudePlugin.mcpServers, {}, 'Claude plugin installs must not auto-bundle root MCP servers; document/manual MCP install remains supported');
});
test('claude plugin.json does NOT have explicit hooks declaration', () => {
assert.ok(
!('hooks' in claudePlugin),
'hooks field must NOT be declared — Claude Code v2.1+ auto-loads hooks/hooks.json by convention',
);
assert.ok(!('hooks' in claudePlugin), 'hooks field must NOT be declared — Claude Code v2.1+ auto-loads hooks/hooks.json by convention');
});
console.log('\n=== .claude-plugin/marketplace.json ===\n');
@@ -267,10 +236,7 @@ const claudeMarketplace = loadJsonObject(claudeMarketplacePath, '.claude-plugin/
test('claude marketplace.json keeps only Claude-supported top-level keys', () => {
const unsupportedTopLevelKeys = ['$schema', 'description'];
for (const key of unsupportedTopLevelKeys) {
assert.ok(
!(key in claudeMarketplace),
`.claude-plugin/marketplace.json must not declare unsupported top-level key "${key}"`,
);
assert.ok(!(key in claudeMarketplace), `.claude-plugin/marketplace.json must not declare unsupported top-level key "${key}"`);
}
});
@@ -316,39 +282,21 @@ test('codex plugin.json version matches package.json', () => {
});
test('codex plugin.json skills is a string (not array) per official spec', () => {
assert.strictEqual(
typeof codexPlugin.skills,
'string',
'skills must be a string path per Codex official docs, not an array',
);
assert.strictEqual(typeof codexPlugin.skills, 'string', 'skills must be a string path per Codex official docs, not an array');
});
test('codex plugin.json mcpServers is a string path (not array) per official spec', () => {
assert.strictEqual(
typeof codexPlugin.mcpServers,
'string',
'mcpServers must be a string path per Codex official docs',
);
assert.strictEqual(typeof codexPlugin.mcpServers, 'string', 'mcpServers must be a string path per Codex official docs');
});
test('codex plugin.json mcpServers exactly matches "./.mcp.json"', () => {
assert.strictEqual(
codexPlugin.mcpServers,
'./.mcp.json',
'mcpServers must point exactly to "./.mcp.json" per official docs',
);
assert.strictEqual(codexPlugin.mcpServers, './.mcp.json', 'mcpServers must point exactly to "./.mcp.json" per official docs');
const mcpPath = path.join(repoRoot, codexPlugin.mcpServers.replace(/^\.\//, ''));
assert.ok(
fs.existsSync(mcpPath),
`mcpServers file missing at plugin root: ${codexPlugin.mcpServers}`,
);
assert.ok(fs.existsSync(mcpPath), `mcpServers file missing at plugin root: ${codexPlugin.mcpServers}`);
});
test('codex plugin.json has interface.displayName', () => {
assert.ok(
codexPlugin.interface && codexPlugin.interface.displayName,
'Expected interface.displayName for plugin directory presentation',
);
assert.ok(codexPlugin.interface && codexPlugin.interface.displayName, 'Expected interface.displayName for plugin directory presentation');
});
test('codex plugin.json uses canonical ECC repo and display name', () => {
@@ -363,20 +311,11 @@ test('codex plugin presentation assets exist and ship in npm package', () => {
for (const field of ['composerIcon', 'logo']) {
const assetPath = codexPlugin.interface[field];
assert.ok(assetPath, `Expected interface.${field}`);
assert.ok(
assetPath.startsWith('./assets/'),
`Expected interface.${field} to point at a root assets path, got ${assetPath}`,
);
assert.ok(assetPath.startsWith('./assets/'), `Expected interface.${field} to point at a root assets path, got ${assetPath}`);
const packagePath = assetPath.replace(/^\.\//, '');
assert.ok(
fs.existsSync(path.join(repoRoot, packagePath)),
`Expected interface.${field} asset to exist: ${packagePath}`,
);
assert.ok(
packageFiles.has(packagePath),
`Expected package.json files to include interface.${field} asset: ${packagePath}`,
);
assert.ok(fs.existsSync(path.join(repoRoot, packagePath)), `Expected interface.${field} asset to exist: ${packagePath}`);
assert.ok(packageFiles.has(packagePath), `Expected package.json files to include interface.${field} asset: ${packagePath}`);
}
});
@@ -388,31 +327,27 @@ const mcpJsonPath = path.join(repoRoot, '.mcp.json');
test('.mcp.json exists at plugin root (not inside .codex-plugin/)', () => {
assert.ok(fs.existsSync(mcpJsonPath), 'Expected .mcp.json at repo root (plugin root)');
assert.ok(
!fs.existsSync(path.join(repoRoot, '.codex-plugin', '.mcp.json')),
'.mcp.json must NOT be inside .codex-plugin/ — only plugin.json belongs there',
);
assert.ok(!fs.existsSync(path.join(repoRoot, '.codex-plugin', '.mcp.json')), '.mcp.json must NOT be inside .codex-plugin/ — only plugin.json belongs there');
});
const mcpConfig = loadJsonObject(mcpJsonPath, '.mcp.json');
test('.mcp.json has mcpServers object', () => {
assert.ok(
mcpConfig.mcpServers && typeof mcpConfig.mcpServers === 'object',
'Expected mcpServers object',
);
assert.ok(mcpConfig.mcpServers && typeof mcpConfig.mcpServers === 'object', 'Expected mcpServers object');
});
test('.mcp.json includes at least github, context7, and exa servers', () => {
test('.mcp.json default set follows the connector policy', () => {
const servers = Object.keys(mcpConfig.mcpServers);
assert.ok(servers.includes('github'), 'Expected github MCP server');
assert.ok(servers.includes('context7'), 'Expected context7 MCP server');
assert.ok(servers.includes('exa'), 'Expected exa MCP server');
assert.ok(servers.includes('chrome-devtools'), 'Expected chrome-devtools as the default browser connector');
assert.ok(servers.length <= 2, `Default connector set must stay minimal per docs/MCP-CONNECTOR-POLICY.md (found ${servers.length})`);
});
test('.mcp.json declares exa as an http MCP server', () => {
assert.strictEqual(mcpConfig.mcpServers.exa.type, 'http', 'Expected exa MCP server to declare type=http');
assert.strictEqual(mcpConfig.mcpServers.exa.url, 'https://mcp.exa.ai/mcp', 'Expected exa MCP server URL to remain unchanged');
test('.mcp.json does not reintroduce retired default connectors', () => {
const retired = ['github', 'context7', 'exa', 'memory', 'playwright', 'sequential-thinking'];
const servers = Object.keys(mcpConfig.mcpServers);
for (const name of retired) {
assert.ok(!servers.includes(name), `${name} was retired from the default set (June 2026 audit) — it lives in mcp-configs/mcp-servers.json as opt-in; see docs/MCP-CONNECTOR-POLICY.md`);
}
});
// ── Codex marketplace file ────────────────────────────────────────────────────
@@ -422,10 +357,7 @@ console.log('\n=== .agents/plugins/marketplace.json ===\n');
const marketplacePath = path.join(repoRoot, '.agents', 'plugins', 'marketplace.json');
test('marketplace.json exists at .agents/plugins/', () => {
assert.ok(
fs.existsSync(marketplacePath),
'Expected .agents/plugins/marketplace.json for Codex repo marketplace discovery',
);
assert.ok(fs.existsSync(marketplacePath), 'Expected .agents/plugins/marketplace.json for Codex repo marketplace discovery');
});
const marketplace = loadJsonObject(marketplacePath, '.agents/plugins/marketplace.json');
@@ -467,24 +399,11 @@ test('marketplace local plugin path resolves to the repo-root Codex bundle', ()
continue;
}
assert.ok(
plugin.source.path.startsWith('./'),
`Codex marketplace source.path must be ./-prefixed: ${plugin.source.path}`,
);
assert.ok(plugin.source.path.startsWith('./'), `Codex marketplace source.path must be ./-prefixed: ${plugin.source.path}`);
const resolvedRoot = path.resolve(repoRoot, plugin.source.path);
assert.strictEqual(
resolvedRoot,
repoRoot,
`Expected local marketplace path to resolve to repo root from marketplace root, got: ${plugin.source.path}`,
);
assert.ok(
fs.existsSync(path.join(resolvedRoot, '.codex-plugin', 'plugin.json')),
`Codex plugin manifest missing under resolved marketplace root: ${plugin.source.path}`,
);
assert.ok(
fs.existsSync(path.join(resolvedRoot, '.mcp.json')),
`Root MCP config missing under resolved marketplace root: ${plugin.source.path}`,
);
assert.strictEqual(resolvedRoot, repoRoot, `Expected local marketplace path to resolve to repo root from marketplace root, got: ${plugin.source.path}`);
assert.ok(fs.existsSync(path.join(resolvedRoot, '.codex-plugin', 'plugin.json')), `Codex plugin manifest missing under resolved marketplace root: ${plugin.source.path}`);
assert.ok(fs.existsSync(path.join(resolvedRoot, '.mcp.json')), `Root MCP config missing under resolved marketplace root: ${plugin.source.path}`);
}
});
@@ -510,7 +429,7 @@ test('user-facing docs do not use overlong legacy marketplace install commands',
path.join(repoRoot, 'README.md'),
path.join(repoRoot, 'README.zh-CN.md'),
path.join(repoRoot, 'skills', 'configure-ecc', 'SKILL.md'),
...collectMarkdownFiles(path.join(repoRoot, 'docs')),
...collectMarkdownFiles(path.join(repoRoot, 'docs'))
].filter(filePath => !path.relative(repoRoot, filePath).startsWith(`docs${path.sep}drafts${path.sep}`));
const offenders = [];
@@ -521,19 +440,11 @@ test('user-facing docs do not use overlong legacy marketplace install commands',
}
}
assert.deepStrictEqual(
offenders,
[],
`Overlong legacy install commands must not appear in user-facing docs: ${offenders.join(', ')}`,
);
assert.deepStrictEqual(offenders, [], `Overlong legacy install commands must not appear in user-facing docs: ${offenders.join(', ')}`);
});
test('user-facing docs do not use the legacy non-URL marketplace add form', () => {
const markdownFiles = [
path.join(repoRoot, 'README.md'),
path.join(repoRoot, 'README.zh-CN.md'),
...collectMarkdownFiles(path.join(repoRoot, 'docs')),
];
const markdownFiles = [path.join(repoRoot, 'README.md'), path.join(repoRoot, 'README.zh-CN.md'), ...collectMarkdownFiles(path.join(repoRoot, 'docs'))];
const offenders = [];
for (const filePath of markdownFiles) {
@@ -543,31 +454,15 @@ test('user-facing docs do not use the legacy non-URL marketplace add form', () =
}
}
assert.deepStrictEqual(
offenders,
[],
`Legacy non-URL marketplace add form must not appear in user-facing docs: ${offenders.join(', ')}`,
);
assert.deepStrictEqual(offenders, [], `Legacy non-URL marketplace add form must not appear in user-facing docs: ${offenders.join(', ')}`);
});
test('.codex-plugin README uses current marketplace add flow', () => {
const readme = fs.readFileSync(path.join(repoRoot, '.codex-plugin', 'README.md'), 'utf8');
assert.ok(
readme.includes('codex plugin marketplace add'),
'Expected .codex-plugin README to document codex plugin marketplace add',
);
assert.ok(
readme.includes('codex plugin marketplace add affaan-m/ECC'),
'Expected .codex-plugin README to document the canonical ECC repo marketplace source',
);
assert.ok(
readme.includes('Official Plugin Directory publishing is coming soon'),
'Expected .codex-plugin README to document current official directory status',
);
assert.ok(
!/\bcodex plugin install\b/.test(readme),
'codex plugin install is not a current Codex CLI command',
);
assert.ok(readme.includes('codex plugin marketplace add'), 'Expected .codex-plugin README to document codex plugin marketplace add');
assert.ok(readme.includes('codex plugin marketplace add affaan-m/ECC'), 'Expected .codex-plugin README to document the canonical ECC repo marketplace source');
assert.ok(readme.includes('Official Plugin Directory publishing is coming soon'), 'Expected .codex-plugin README to document current official directory status');
assert.ok(!/\bcodex plugin install\b/.test(readme), 'codex plugin install is not a current Codex CLI command');
});
test('docs/zh-CN/README.md version row matches package.json', () => {

View File

@@ -150,8 +150,7 @@ function runTests() {
const mcpConfig = readJson(path.join(projectDir, '.cursor', 'mcp.json'));
assert.strictEqual(hooksConfig.version, 1);
assert.ok(hooksConfig.hooks.sessionStart, 'Should keep Cursor sessionStart hooks');
assert.ok(mcpConfig.mcpServers.github, 'Should install shared MCP servers into Cursor');
assert.ok(mcpConfig.mcpServers.context7, 'Should include bundled documentation MCPs');
assert.ok(mcpConfig.mcpServers['chrome-devtools'], 'Should install shared MCP servers into Cursor');
const statePath = path.join(projectDir, '.cursor', 'ecc-install-state.json');
const state = readJson(statePath);
@@ -194,8 +193,7 @@ function runTests() {
const mcpConfig = readJson(path.join(projectDir, '.cursor', 'mcp.json'));
assert.ok(mcpConfig.mcpServers.custom, 'Should preserve existing custom Cursor MCP servers');
assert.ok(mcpConfig.mcpServers.github, 'Should merge bundled GitHub MCP server');
assert.ok(mcpConfig.mcpServers.playwright, 'Should merge bundled Playwright MCP server');
assert.ok(mcpConfig.mcpServers['chrome-devtools'], 'Should merge the bundled chrome-devtools MCP server');
} finally {
cleanup(homeDir);
cleanup(projectDir);