mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-10 18:23:12 +08:00
Compare commits
6 Commits
dependabot
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c888d2b73f | ||
|
|
ff768db363 | ||
|
|
8ad4151095 | ||
|
|
29edd57708 | ||
|
|
3e30f1a56a | ||
|
|
10c303e609 |
@@ -6,7 +6,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "ecc",
|
||||
"version": "2.0.0-rc.1",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
36
.coderabbit.yaml
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -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 |
|
||||
|
||||
56
.github/prompts/code-review.prompt.md
vendored
56
.github/prompts/code-review.prompt.md
vendored
@@ -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)
|
||||
```
|
||||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/release-announce.yml
vendored
2
.github/workflows/release-announce.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/reusable-test.yml
vendored
2
.github/workflows/reusable-test.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/reusable-validate.yml
vendored
2
.github/workflows/reusable-validate.yml
vendored
@@ -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
|
||||
|
||||
24
.mcp.json
24
.mcp.json
@@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
.opencode/package-lock.json
generated
4
.opencode/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)",
|
||||
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -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." }
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -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
|
||||
|
||||
29
README.md
29
README.md
@@ -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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -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 Alpha(2026年4月)
|
||||
|
||||
- **公共表面已与真实仓库同步** —— 元数据、目录数量、插件清单以及安装文档现在都与实际开源表面保持一致。
|
||||
|
||||
@@ -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.*
|
||||
|
||||
|
||||
@@ -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 |
43
docs/MCP-CONNECTOR-POLICY.md
Normal file
43
docs/MCP-CONNECTOR-POLICY.md
Normal 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.
|
||||
@@ -703,7 +703,7 @@ Suggested payload:
|
||||
"skippedModules": []
|
||||
},
|
||||
"source": {
|
||||
"repoVersion": "2.0.0-rc.1",
|
||||
"repoVersion": "2.0.0",
|
||||
"repoCommit": "git-sha",
|
||||
"manifestVersion": 1
|
||||
},
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
43
docs/releases/2.0.0/release-notes.md
Normal file
43
docs/releases/2.0.0/release-notes.md
Normal 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>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
这是一个**生产就绪的 AI 编码插件**,提供 64 个专业代理、261 项技能、84 条命令以及自动化钩子工作流,用于软件开发。
|
||||
|
||||
**版本:** 2.0.0-rc.1
|
||||
**版本:** 2.0.0
|
||||
|
||||
## 核心原则
|
||||
|
||||
|
||||
@@ -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 Alpha(2026年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 |
|
||||
|
||||
**关键架构决策:**
|
||||
|
||||
|
||||
84
ecc2/Cargo.lock
generated
84
ecc2/Cargo.lock
generated
@@ -157,15 +157,6 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
@@ -291,12 +282,6 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
@@ -321,15 +306,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
@@ -403,15 +379,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csscolorparser"
|
||||
version = "0.6.2"
|
||||
@@ -499,19 +466,8 @@ version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common 0.1.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"const-oid",
|
||||
"crypto-common 0.2.2",
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -572,7 +528,7 @@ dependencies = [
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.11.0",
|
||||
"sha2",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"toml",
|
||||
@@ -812,15 +768,6 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
@@ -1445,7 +1392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"sha2 0.10.9",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1904,19 +1851,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"digest 0.11.3",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2117,7 +2053,7 @@ dependencies = [
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"phf",
|
||||
"sha2 0.10.9",
|
||||
"sha2",
|
||||
"signal-hook",
|
||||
"siphasher",
|
||||
"terminfo",
|
||||
@@ -2346,9 +2282,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.1"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
@@ -2617,7 +2553,7 @@ checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"mac_address",
|
||||
"sha2 0.10.9",
|
||||
"sha2",
|
||||
"thiserror 1.0.69",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -30,7 +30,7 @@ serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
toml = "0.8"
|
||||
regex = "1"
|
||||
sha2 = "0.11"
|
||||
sha2 = "0.10"
|
||||
ureq = { version = "2", features = ["json"] }
|
||||
|
||||
# CLI
|
||||
|
||||
67
greptile.json
Normal file
67
greptile.json
Normal 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
4
package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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
222
scripts/discord/ecc-bot.mjs
Normal 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); });
|
||||
38
scripts/discord/register-commands.mjs
Normal file
38
scripts/discord/register-commands.mjs
Normal 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(' '));
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user