From 0a87323eda77ee412fa3a3bf028a577536966505 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Tue, 28 Apr 2026 22:10:04 -0400 Subject: [PATCH] feat(ecc2): finalize rc1 release surface --- .agents/plugins/marketplace.json | 2 +- .claude-plugin/marketplace.json | 4 +- .claude-plugin/plugin.json | 4 +- .codex-plugin/plugin.json | 2 +- .github/workflows/ci.yml | 3 +- .github/workflows/release.yml | 20 ++-- .github/workflows/reusable-release.yml | 23 ++-- .opencode/.npmignore | 2 + .opencode/package-lock.json | 4 +- .opencode/package.json | 2 +- .opencode/plugins/ecc-hooks.ts | 2 +- AGENTS.md | 6 +- CHANGELOG.md | 20 ++++ README.md | 12 +- README.zh-CN.md | 4 +- VERSION | 2 +- agent.yaml | 2 +- docs/HERMES-SETUP.md | 4 +- docs/SELECTIVE-INSTALL-ARCHITECTURE.md | 2 +- docs/architecture/cross-harness.md | 111 +++++++++++++++++ docs/pt-BR/README.md | 2 +- docs/releases/2.0.0-rc.1/article-outline.md | 61 ++++++++++ docs/releases/2.0.0-rc.1/demo-prompts.md | 42 +++++++ docs/releases/2.0.0-rc.1/launch-checklist.md | 41 +++++++ docs/releases/2.0.0-rc.1/linkedin-post.md | 28 +++++ docs/releases/2.0.0-rc.1/release-notes.md | 54 +++++++++ docs/releases/2.0.0-rc.1/telegram-handoff.md | 26 ++++ docs/releases/2.0.0-rc.1/x-thread.md | 65 ++++++++++ docs/tr/AGENTS.md | 2 +- docs/tr/README.md | 2 +- docs/zh-CN/AGENTS.md | 6 +- docs/zh-CN/README.md | 8 +- package-lock.json | 4 +- package.json | 4 +- scripts/release.sh | 48 ++++++-- skills/hermes-imports/SKILL.md | 88 ++++++++++++++ tests/docs/ecc2-release-surface.test.js | 120 +++++++++++++++++++ tests/plugin-manifest.test.js | 17 +-- tests/scripts/release-publish.test.js | 12 ++ tests/scripts/release.test.js | 78 ++++++++++++ 40 files changed, 863 insertions(+), 76 deletions(-) create mode 100644 .opencode/.npmignore create mode 100644 docs/architecture/cross-harness.md create mode 100644 docs/releases/2.0.0-rc.1/article-outline.md create mode 100644 docs/releases/2.0.0-rc.1/demo-prompts.md create mode 100644 docs/releases/2.0.0-rc.1/launch-checklist.md create mode 100644 docs/releases/2.0.0-rc.1/linkedin-post.md create mode 100644 docs/releases/2.0.0-rc.1/release-notes.md create mode 100644 docs/releases/2.0.0-rc.1/telegram-handoff.md create mode 100644 docs/releases/2.0.0-rc.1/x-thread.md create mode 100644 skills/hermes-imports/SKILL.md create mode 100644 tests/docs/ecc2-release-surface.test.js diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index b088fe52..150176f1 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -6,7 +6,7 @@ "plugins": [ { "name": "ecc", - "version": "1.10.0", + "version": "2.0.0-rc.1", "source": { "source": "local", "path": "../.." diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 2e4cbac2..1d9a03b4 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,8 +11,8 @@ { "name": "everything-claude-code", "source": "./", - "description": "The most comprehensive Claude Code plugin — 38 agents, 156 skills, 72 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning", - "version": "1.10.0", + "description": "The most comprehensive Claude Code plugin — 48 agents, 184 skills, 79 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning", + "version": "2.0.0-rc.1", "author": { "name": "Affaan Mustafa", "email": "me@affaanmustafa.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index a9a7c023..32a0c1d0 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "everything-claude-code", - "version": "1.10.0", - "description": "Battle-tested Claude Code plugin for engineering teams — 38 agents, 156 skills, 72 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use", + "version": "2.0.0-rc.1", + "description": "Battle-tested Claude Code plugin for engineering teams — 48 agents, 184 skills, 79 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index f5c7aa2b..9243140d 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "ecc", - "version": "1.10.0", + "version": "2.0.0-rc.1", "description": "Battle-tested Codex workflows — 156 shared ECC skills, production-ready MCP configs, and selective-install-aligned conventions for TDD, security scanning, code review, and autonomous development.", "author": { "name": "Affaan Mustafa", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f78199d5..b25af984 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,8 @@ name: CI on: push: - branches: [main] + branches: [main, 'release/**'] + tags: ['v*'] pull_request: branches: [main] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8252d63..a52fb848 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,8 +33,8 @@ jobs: - name: Validate version tag run: | - if ! [[ "${REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Invalid version tag format. Expected vX.Y.Z" + if ! [[ "${REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version tag format. Expected vX.Y.Z or vX.Y.Z-prerelease" exit 1 fi @@ -60,17 +60,13 @@ jobs: run: | PACKAGE_NAME=$(node -p "require('./package.json').name") PACKAGE_VERSION=$(node -p "require('./package.json').version") + NPM_DIST_TAG=$(node -p "require('./package.json').version.includes('-') ? 'next' : 'latest'") if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then echo "already_published=true" >> "$GITHUB_OUTPUT" else echo "already_published=false" >> "$GITHUB_OUTPUT" fi - - - name: Publish npm package - if: steps.npm_publish_state.outputs.already_published != 'true' - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm publish --access public --provenance + echo "dist_tag=${NPM_DIST_TAG}" >> "$GITHUB_OUTPUT" - name: Generate release highlights id: highlights @@ -102,3 +98,11 @@ jobs: with: body_path: release_body.md generate_release_notes: true + prerelease: ${{ contains(github.ref_name, '-') }} + make_latest: ${{ contains(github.ref_name, '-') && 'false' || 'true' }} + + - name: Publish npm package + if: steps.npm_publish_state.outputs.already_published != 'true' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish --access public --provenance --tag "${{ steps.npm_publish_state.outputs.dist_tag }}" diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 8a48ba46..a6fe2cec 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -18,7 +18,7 @@ on: workflow_dispatch: inputs: tag: - description: 'Version tag to release or republish (e.g., v1.10.0)' + description: 'Version tag to release or republish (e.g., v2.0.0-rc.1)' required: true type: string generate-notes: @@ -41,6 +41,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + ref: ${{ inputs.tag }} - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 @@ -58,8 +59,8 @@ jobs: env: INPUT_TAG: ${{ inputs.tag }} run: | - if ! [[ "$INPUT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Invalid version tag format. Expected vX.Y.Z" + if ! [[ "$INPUT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version tag format. Expected vX.Y.Z or vX.Y.Z-prerelease" exit 1 fi @@ -83,17 +84,13 @@ jobs: run: | PACKAGE_NAME=$(node -p "require('./package.json').name") PACKAGE_VERSION=$(node -p "require('./package.json').version") + NPM_DIST_TAG=$(node -p "require('./package.json').version.includes('-') ? 'next' : 'latest'") if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then echo "already_published=true" >> "$GITHUB_OUTPUT" else echo "already_published=false" >> "$GITHUB_OUTPUT" fi - - - name: Publish npm package - if: steps.npm_publish_state.outputs.already_published != 'true' - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm publish --access public --provenance + echo "dist_tag=${NPM_DIST_TAG}" >> "$GITHUB_OUTPUT" - name: Generate release highlights env: @@ -119,3 +116,11 @@ jobs: tag_name: ${{ inputs.tag }} body_path: release_body.md generate_release_notes: ${{ inputs.generate-notes }} + prerelease: ${{ contains(inputs.tag, '-') }} + make_latest: ${{ contains(inputs.tag, '-') && 'false' || 'true' }} + + - name: Publish npm package + if: steps.npm_publish_state.outputs.already_published != 'true' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish --access public --provenance --tag "${{ steps.npm_publish_state.outputs.dist_tag }}" diff --git a/.opencode/.npmignore b/.opencode/.npmignore new file mode 100644 index 00000000..d286b7c1 --- /dev/null +++ b/.opencode/.npmignore @@ -0,0 +1,2 @@ +node_modules +bun.lock diff --git a/.opencode/package-lock.json b/.opencode/package-lock.json index 2998e1bc..ef323f1d 100644 --- a/.opencode/package-lock.json +++ b/.opencode/package-lock.json @@ -1,12 +1,12 @@ { "name": "ecc-universal", - "version": "1.10.0", + "version": "2.0.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ecc-universal", - "version": "1.10.0", + "version": "2.0.0-rc.1", "license": "MIT", "devDependencies": { "@opencode-ai/plugin": "^1.4.3", diff --git a/.opencode/package.json b/.opencode/package.json index 729524c9..ee99154b 100644 --- a/.opencode/package.json +++ b/.opencode/package.json @@ -1,6 +1,6 @@ { "name": "ecc-universal", - "version": "1.10.0", + "version": "2.0.0-rc.1", "description": "Everything Claude Code (ECC) plugin for OpenCode - agents, commands, hooks, and skills", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/.opencode/plugins/ecc-hooks.ts b/.opencode/plugins/ecc-hooks.ts index 51bde010..fa96b805 100644 --- a/.opencode/plugins/ecc-hooks.ts +++ b/.opencode/plugins/ecc-hooks.ts @@ -456,7 +456,7 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({ const contextBlock = [ "# ECC Context (preserve across compaction)", "", - "## Active Plugin: Everything Claude Code v1.10.0", + "## Active Plugin: Everything Claude Code v2.0.0-rc.1", "- 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)", diff --git a/AGENTS.md b/AGENTS.md index e25ea6e9..e4edf5fa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,8 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 48 specialized agents, 183 skills, 79 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 48 specialized agents, 184 skills, 79 commands, and automated hook workflows for software development. -**Version:** 1.10.0 +**Version:** 2.0.0-rc.1 ## Core Principles @@ -146,7 +146,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 48 specialized subagents -skills/ — 183 workflow skills and domain knowledge +skills/ — 184 workflow skills and domain knowledge commands/ — 79 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9826e9b0..5d796711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 2.0.0-rc.1 - 2026-04-28 + +### Highlights + +- Adds the public ECC 2.0 release-candidate surface for the Hermes operator story. +- Documents ECC as the reusable cross-harness substrate across Claude Code, Codex, Cursor, OpenCode, and Gemini. +- Adds a sanitized Hermes import skill surface instead of publishing private operator state. + +### Release Surface + +- Updated package, plugin, marketplace, OpenCode, agent, and README metadata to `2.0.0-rc.1`. +- Added `docs/releases/2.0.0-rc.1/` with release notes, social drafts, launch checklist, handoff notes, and demo prompts. +- Added `docs/architecture/cross-harness.md` and regression coverage for the ECC/Hermes boundary. +- Kept `ecc2/` versioning independent for now; it remains an alpha control-plane scaffold unless release engineering decides otherwise. + +### Notes + +- This is a release candidate, not a GA claim for the full ECC 2.0 control-plane roadmap. +- Prerelease npm publishing should use the `next` dist-tag unless release engineering explicitly chooses otherwise. + ## 1.10.0 - 2026-04-05 ### Highlights diff --git a/README.md b/README.md index d5c65da6..df991c7b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Not just configs. A complete system: skills, instincts, memory optimization, con Works across **Claude Code**, **Codex**, **Cursor**, **OpenCode**, **Gemini**, 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). + --- ## The Guides @@ -84,7 +86,7 @@ This repo is the raw code only. The guides explain everything. ## What's New -### v1.10.0 — Surface Refresh, Operator Workflows, and ECC 2.0 Alpha (Apr 2026) +### 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. - **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 38 agents, 156 skills, and 72 legacy command shims. @@ -247,7 +249,7 @@ For manual install instructions see the README in the `rules/` folder. When copy /plugin list everything-claude-code@everything-claude-code ``` -**That's it!** You now have access to 48 agents, 183 skills, and 79 legacy command shims. +**That's it!** You now have access to 48 agents, 184 skills, and 79 legacy command shims. ### Dashboard GUI @@ -1215,7 +1217,7 @@ The configuration is automatically detected from `.opencode/opencode.json`. |---------|-------------|----------|--------| | Agents | PASS: 48 agents | PASS: 12 agents | **Claude Code leads** | | Commands | PASS: 79 commands | PASS: 31 commands | **Claude Code leads** | -| Skills | PASS: 183 skills | PASS: 37 skills | **Claude Code leads** | +| Skills | PASS: 184 skills | PASS: 37 skills | **Claude Code leads** | | Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** | | Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** | | MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** | @@ -1324,7 +1326,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e |---------|------------|------------|-----------|----------| | **Agents** | 48 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | | **Commands** | 79 | Shared | Instruction-based | 31 | -| **Skills** | 183 | Shared | 10 (native format) | 37 | +| **Skills** | 184 | Shared | 10 (native format) | 37 | | **Hook Events** | 8 types | 15 types | None yet | 11 types | | **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | | **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions | @@ -1334,7 +1336,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 | | **Secret Detection** | Hook-based | beforeSubmitPrompt hook | Sandbox-based | Hook-based | | **Auto-Format** | PostToolUse hook | afterFileEdit hook | N/A | file.edited hook | -| **Version** | Plugin | Plugin | Reference config | 1.10.0 | +| **Version** | Plugin | Plugin | Reference config | 2.0.0-rc.1 | **Key architectural decisions:** - **AGENTS.md** at root is the universal cross-tool file (read by all 4 tools) diff --git a/README.zh-CN.md b/README.zh-CN.md index 2be90204..8f1db967 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -80,7 +80,7 @@ ## 最新动态 -### v1.10.0 — 表面同步、运营工作流与 ECC 2.0 Alpha(2026年4月) +### v2.0.0-rc.1 — 表面同步、运营工作流与 ECC 2.0 Alpha(2026年4月) - **公共表面已与真实仓库同步** —— 元数据、目录数量、插件清单以及安装文档现在都与实际开源表面保持一致。 - **运营与外向型工作流扩展** —— `brand-voice`、`social-graph-ranker`、`customer-billing-ops`、`google-workspace-ops` 等运营型 skill 已纳入同一系统。 @@ -160,7 +160,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list everything-claude-code@everything-claude-code ``` -**完成!** 你现在可以使用 48 个代理、183 个技能和 79 个命令。 +**完成!** 你现在可以使用 48 个代理、184 个技能和 79 个命令。 ### multi-* 命令需要额外配置 diff --git a/VERSION b/VERSION index 81c871de..97041a78 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.10.0 +2.0.0-rc.1 diff --git a/agent.yaml b/agent.yaml index 8241c085..ba6b3d9e 100644 --- a/agent.yaml +++ b/agent.yaml @@ -1,6 +1,6 @@ spec_version: "0.1.0" name: everything-claude-code -version: 1.10.0 +version: 2.0.0-rc.1 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 diff --git a/docs/HERMES-SETUP.md b/docs/HERMES-SETUP.md index 9cb414e5..14e7605d 100644 --- a/docs/HERMES-SETUP.md +++ b/docs/HERMES-SETUP.md @@ -100,9 +100,9 @@ This stack is useful when you want: - automation that can nudge, audit, and escalate - a public repo that shows the system shape without exposing your private operator state -## Public Preview Scope +## Public Release Candidate Scope -ECC 2.0 preview documents the Hermes surface and ships launch collateral now. +ECC v2.0.0-rc.1 documents the Hermes surface and ships launch collateral now. The remaining private pieces can be layered later: diff --git a/docs/SELECTIVE-INSTALL-ARCHITECTURE.md b/docs/SELECTIVE-INSTALL-ARCHITECTURE.md index 6c5f6501..1e9f2bc2 100644 --- a/docs/SELECTIVE-INSTALL-ARCHITECTURE.md +++ b/docs/SELECTIVE-INSTALL-ARCHITECTURE.md @@ -703,7 +703,7 @@ Suggested payload: "skippedModules": [] }, "source": { - "repoVersion": "1.10.0", + "repoVersion": "2.0.0-rc.1", "repoCommit": "git-sha", "manifestVersion": 1 }, diff --git a/docs/architecture/cross-harness.md b/docs/architecture/cross-harness.md new file mode 100644 index 00000000..74df8aea --- /dev/null +++ b/docs/architecture/cross-harness.md @@ -0,0 +1,111 @@ +# Cross-Harness Architecture + +ECC is the reusable workflow layer. Harnesses are execution surfaces. + +The goal is to keep the durable parts of agentic work in one repo: + +- skills +- rules and instructions +- hooks where the harness supports them +- MCP configuration +- install manifests +- session and orchestration patterns + +Claude Code, Codex, OpenCode, Cursor, Gemini, and future harnesses should adapt those assets at the edge instead of requiring a new workflow model for every tool. + +## Portability Model + +| Surface | Shared Source | Harness Adapter | Current Status | +|---------|---------------|-----------------|----------------| +| Skills | `skills/*/SKILL.md` | Claude plugin, Codex plugin, `.agents/skills`, Cursor skill copies, OpenCode plugin/config | Supported with harness-specific packaging | +| Rules and instructions | `rules/`, `AGENTS.md`, translated docs | Claude rules install, Codex `AGENTS.md`, Cursor rules, OpenCode instructions | Supported, but not identical across harnesses | +| Hooks | `hooks/hooks.json`, `scripts/hooks/` | Claude native hooks, OpenCode plugin events, Cursor hook adapter | Hook-backed in Claude/OpenCode/Cursor; instruction-backed in Codex | +| MCPs | `.mcp.json`, `mcp-configs/` | Native MCP config import per harness | Supported where the harness exposes MCP | +| Commands | `commands/`, CLI scripts | Claude slash commands, compatibility shims, CLI entrypoints | Supported, but command semantics vary | +| Sessions | `ecc2/`, session adapters, orchestration scripts | TUI/daemon, tmux/worktree orchestration, harness-specific runners | Alpha | + +## What Travels Unchanged + +`SKILL.md` is the most portable unit. + +A good ECC skill should: + +- use YAML frontmatter with `name`, `description`, and `origin` +- describe when to use the skill +- state required tools or connectors without embedding secrets +- keep examples repo-relative or generic +- avoid harness-only command assumptions unless the section is clearly labeled + +The same source skill can be installed into multiple harnesses because it is mostly instructions, constraints, and workflow shape. + +## What Gets Adapted + +Each harness has different loading and enforcement behavior: + +- Claude Code loads plugin assets and has native hook execution. +- Codex reads `AGENTS.md`, plugin metadata, skills, and MCP config, but hook parity is instruction-driven. +- OpenCode has a plugin/event system that can reuse ECC hook logic through an adapter layer. +- Cursor uses its own rule and hook layout, so ECC maintains translated surfaces under `.cursor/`. +- Gemini support is install/instruction oriented and should be treated as a compatibility surface, not as full hook parity. + +Adapters should stay thin. The shared behavior belongs in `skills/`, `rules/`, `hooks/`, `scripts/`, and `mcp-configs/`. + +## Hermes Boundary + +Hermes is not the public ECC runtime. + +Hermes is an operator shell that can consume ECC assets: + +- import selected ECC skills into a Hermes skills directory +- use ECC MCP conventions for tool access +- route chat, CLI, cron, and handoff workflows through reusable ECC patterns +- distill repeated local operator work back into sanitized ECC skills + +The public repo should ship reusable patterns, not local Hermes state. + +Do ship: + +- sanitized setup docs +- repo-relative demo prompts +- general operator skills +- examples that do not depend on private credentials + +Do not ship: + +- OAuth tokens or API keys +- raw `~/.hermes` exports +- personal workspace memory +- private datasets +- local-only automation packs that have not been reviewed + +## Today vs Later + +Supported today: + +- shared skill source in `skills/` +- Claude Code plugin packaging +- Codex plugin metadata and MCP reference config +- OpenCode package/plugin surface +- Cursor-adapted rules, hooks, and skills +- `ecc2/` as an alpha Rust control plane + +Still maturing: + +- exact hook parity across all harnesses +- automated skill sync into Hermes +- release packaging for `ecc2/` +- cross-harness session resume semantics +- deeper memory and operator planning layers + +## Rule For New Work + +When adding a workflow, put the durable behavior in ECC first. + +Use harness-specific files only for: + +- loading the shared asset +- adapting event shapes +- mapping command names +- handling platform limits + +If a workflow only works in one harness, document that boundary directly. diff --git a/docs/pt-BR/README.md b/docs/pt-BR/README.md index 3c490ccb..f6b75484 100644 --- a/docs/pt-BR/README.md +++ b/docs/pt-BR/README.md @@ -80,7 +80,7 @@ Este repositório contém apenas o código. Os guias explicam tudo. ## O Que Há de Novo -### v1.10.0 — Sincronização de Superfície, Fluxos Operacionais e ECC 2.0 Alpha (Abr 2026) +### 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. - **Expansão dos fluxos operacionais e externos** — `brand-voice`, `social-graph-ranker`, `customer-billing-ops`, `google-workspace-ops` e skills relacionadas fortalecem a trilha operacional dentro do mesmo sistema. diff --git a/docs/releases/2.0.0-rc.1/article-outline.md b/docs/releases/2.0.0-rc.1/article-outline.md new file mode 100644 index 00000000..e85e729a --- /dev/null +++ b/docs/releases/2.0.0-rc.1/article-outline.md @@ -0,0 +1,61 @@ +# Article Outline - ECC v2.0.0-rc.1 + +## Working Title + +Turning ECC Into a Cross-Harness Operator System + +## Core Argument + +Most agentic work breaks down because the tools stay isolated. + +The leverage comes from treating the harness, reusable workflow layer, and operator shell as one system: + +- skills for repeatable work +- hooks and tests for enforcement +- MCPs for tool access +- memory and handoffs for continuity +- one operator shell that can route daily execution + +## Structure + +### 1. The Problem + +- too many chat windows +- too many tool-specific workflows +- too much context living in personal habit instead of reusable system shape + +### 2. What ECC Already Solved + +- reusable skill format +- cross-harness install surfaces +- hooks and verification discipline +- security and review patterns +- operator workflow skills around content, research, and business ops + +### 3. Why Hermes Is the Operator Layer + +- chat, CLI, TUI, cron, and handoffs can sit above the reusable ECC layer +- business and content work can run next to engineering work +- the daily loop becomes easier to inspect and improve + +### 4. What Ships in rc.1 + +- sanitized Hermes setup guide +- release and distribution collateral +- cross-harness architecture doc +- Hermes import guidance +- clearer 2.0 positioning in the repo + +### 5. What Stays Local + +- secrets and auth +- raw workspace exports +- personal datasets +- operator-specific automations that have not been sanitized +- deeper CRM, finance, and Google Workspace playbooks + +### 6. Closing Point + +The goal is not to copy one exact stack. + +The goal is to build an operator system that turns repeated work into reusable, measurable surfaces. diff --git a/docs/releases/2.0.0-rc.1/demo-prompts.md b/docs/releases/2.0.0-rc.1/demo-prompts.md new file mode 100644 index 00000000..dbce2310 --- /dev/null +++ b/docs/releases/2.0.0-rc.1/demo-prompts.md @@ -0,0 +1,42 @@ +# Hermes x ECC Demo Prompts + +## Prompt 1: ECC Builds ECC + +Use the current ECC repo and the public release pack at `docs/releases/2.0.0-rc.1/`. + +Do 4 things in order: + +1. Inspect git status and the current repo diff, then give me a concise ECC v2.0.0-rc.1 PR or release summary that proves ECC is being used to build ECC itself. +2. Finalize one strong X thread. +3. Finalize one strong LinkedIn post. +4. Tell me the exact 3 recordings I should do next plus what Hermes can generate automatically after I record. + +Keep it decisive and practical. + +## Prompt 2: Turn Recording Into Assets + +Assume I just recorded: + +- one face-camera hook +- one screen capture of Hermes using ECC to ship ECC v2.0.0-rc.1 +- one setup walkthrough of the Hermes x ECC workspace + +Give me: + +1. a short-form edit plan for X, LinkedIn, TikTok, and YouTube Shorts +2. a voiceover script if I want to re-record clean audio +3. the exact repo-relative filenames and folders I should use for raw footage +4. the assets Hermes can generate automatically after I drop the files in place + +Keep it operational. + +## Prompt 3: Public Launch Push + +Using the ECC v2.0.0-rc.1 release pack, give me: + +1. one release tweet +2. one follow-up tweet +3. one LinkedIn comment I can paste under the post +4. one short Telegram handoff I can send to Hermes later to keep distributing this launch across channels + +Make it sound like an operator shipping real work, not a launch thread cliche. diff --git a/docs/releases/2.0.0-rc.1/launch-checklist.md b/docs/releases/2.0.0-rc.1/launch-checklist.md new file mode 100644 index 00000000..51257077 --- /dev/null +++ b/docs/releases/2.0.0-rc.1/launch-checklist.md @@ -0,0 +1,41 @@ +# ECC v2.0.0-rc.1 Launch Checklist + +## Repo + +- verify local `main` is synced to `origin/main` +- verify `docs/HERMES-SETUP.md` is present +- verify `docs/architecture/cross-harness.md` is present +- verify this release directory is committed +- keep private tokens, personal docs, and raw workspace exports out of the repo + +## Release Surface + +- confirm package and plugin version policy for `2.0.0-rc.1` (drafted in manifest bump prep) +- confirm whether `ecc2/Cargo.toml` moves from `0.1.0` to `2.0.0-rc.1` +- update release metadata in one dedicated release-version PR +- run the root test suite +- run `cd ecc2 && cargo test` + +## Content + +- publish the X thread from `x-thread.md` +- publish the LinkedIn draft from `linkedin-post.md` +- use `article-outline.md` for the longer writeup +- record one 30-60 second proof-of-work clip + +## Demo Asset Suggestions + +- Hermes plus ECC side by side +- release docs being generated or reviewed from the repo +- a workflow moving from brief to post to checklist +- `ecc2/` dashboard or session surface with alpha framing + +## Messaging + +Use language like: + +- "release candidate" +- "sanitized operator stack" +- "cross-harness operating system for agentic work" +- "ECC is the reusable substrate; Hermes is the operator shell" +- "private/local integrations land after sanitization" diff --git a/docs/releases/2.0.0-rc.1/linkedin-post.md b/docs/releases/2.0.0-rc.1/linkedin-post.md new file mode 100644 index 00000000..e2d3d86f --- /dev/null +++ b/docs/releases/2.0.0-rc.1/linkedin-post.md @@ -0,0 +1,28 @@ +# LinkedIn Draft - ECC v2.0.0-rc.1 + +ECC v2.0.0-rc.1 is live as the first release-candidate pass at the 2.0 direction. + +The practical shift is simple: ECC is no longer framed as only a Claude Code plugin or config bundle. + +It is becoming a cross-harness operating system for agentic work: + +- reusable skills instead of one-off prompts +- hooks and tests instead of manual discipline +- MCP-backed access to docs, code, browser automation, and research +- Codex, OpenCode, Cursor, Gemini, and Claude Code surfaces that share the same core workflow layer +- Hermes as the operator shell for chat, cron, handoffs, and daily work routing + +For this release-candidate surface, I kept the repo honest. + +I did not publish private workspace state. I shipped the reusable layer: + +- sanitized Hermes setup documentation +- release notes and launch collateral +- cross-harness architecture notes +- Hermes import guidance for turning local operator patterns into public ECC skills + +The leverage is not just better prompting. + +It is reducing the number of isolated surfaces, turning repeated workflows into reusable skills, and making the operating system around the agent measurable. + +There is still more to harden before GA, especially around packaging, installers, and the `ecc2/` control plane. But rc.1 is enough to show the shape clearly. diff --git a/docs/releases/2.0.0-rc.1/release-notes.md b/docs/releases/2.0.0-rc.1/release-notes.md new file mode 100644 index 00000000..541a02c2 --- /dev/null +++ b/docs/releases/2.0.0-rc.1/release-notes.md @@ -0,0 +1,54 @@ +# ECC v2.0.0-rc.1 Release Notes + +## Positioning + +ECC v2.0.0-rc.1 is the first release-candidate surface for ECC as a cross-harness operating system for agentic work. + +Claude Code remains a core target. Codex, OpenCode, Cursor, Gemini, and other harnesses are treated as execution surfaces that can share the same skills, rules, MCP conventions, and operator workflows. ECC is the reusable substrate; Hermes is documented as the operator shell that can sit on top of that layer. + +## What Changed + +- Added the sanitized Hermes setup guide to the public release story. +- Added launch collateral in-repo so the release can ship from one reviewed surface. +- Clarified the split between ECC as the reusable substrate and Hermes as the operator shell. +- Documented the cross-harness portability model for skills, hooks, MCPs, rules, and instructions. +- Added a Hermes import playbook for turning local operator patterns into publishable ECC skills. + +## Why This Matters + +ECC is no longer only a Claude Code plugin or config bundle. + +The system now has a clearer shape: + +- reusable skills instead of one-off prompts +- hooks and tests for workflow discipline +- MCP-backed access to docs, code, browser automation, and research +- cross-harness install surfaces for Claude Code, Codex, OpenCode, Cursor, and related tools +- Hermes as an optional operator shell for chat, cron, handoffs, and daily work routing + +## Preview Boundaries + +This is a release candidate, not the final GA claim. + +What ships in this surface: + +- public Hermes setup documentation +- release notes and launch collateral +- cross-harness architecture documentation +- Hermes import guidance for sanitized operator workflows + +What stays local: + +- secrets, OAuth tokens, and API keys +- private workspace exports +- personal datasets +- operator-specific automations that have not been sanitized +- deeper CRM, finance, and Google Workspace playbooks + +## Upgrade Motion + +1. Read the [Hermes setup guide](../../HERMES-SETUP.md). +2. Review the [cross-harness architecture](../../architecture/cross-harness.md). +3. Start with one workflow lane: engineering, research, content, or outreach. +4. Import only sanitized operator patterns into ECC skills. +5. Treat `ecc2/` as an alpha control plane until release packaging and installer behavior are finalized. diff --git a/docs/releases/2.0.0-rc.1/telegram-handoff.md b/docs/releases/2.0.0-rc.1/telegram-handoff.md new file mode 100644 index 00000000..22020295 --- /dev/null +++ b/docs/releases/2.0.0-rc.1/telegram-handoff.md @@ -0,0 +1,26 @@ +# Telegram Handoff For Hermes + +Send this to Hermes when you want it to help package the launch workflow. + +```text +Use the public ECC release pack in the repo: + +- docs/releases/2.0.0-rc.1/release-notes.md +- docs/releases/2.0.0-rc.1/x-thread.md +- docs/releases/2.0.0-rc.1/linkedin-post.md +- docs/releases/2.0.0-rc.1/article-outline.md +- docs/releases/2.0.0-rc.1/launch-checklist.md +- docs/HERMES-SETUP.md +- docs/architecture/cross-harness.md + +Task: + +1. Finalize one strong X thread for ECC v2.0.0-rc.1. +2. Finalize one strong LinkedIn post for ECC v2.0.0-rc.1. +3. Give me one 30-60 second Hermes x ECC video script and one 15-30 second variant. +4. Tell me exactly what to record now with screen capture, face camera, and voice lines. +5. Tell me what Hermes can generate automatically after I record. +6. End with a minimal checklist of the assets or logins still needed. + +Be decisive. Return final drafts plus a practical recording checklist. +``` diff --git a/docs/releases/2.0.0-rc.1/x-thread.md b/docs/releases/2.0.0-rc.1/x-thread.md new file mode 100644 index 00000000..7ae8c9b7 --- /dev/null +++ b/docs/releases/2.0.0-rc.1/x-thread.md @@ -0,0 +1,65 @@ +# X Thread Draft - ECC v2.0.0-rc.1 + +1/ ECC v2.0.0-rc.1 is the first release-candidate pass at the 2.0 direction. + +The repo is moving from a Claude Code config pack into a cross-harness operating system for agentic work. + +2/ The important split: + +ECC is the reusable substrate. +Hermes is the operator shell that can run on top. + +Skills, hooks, MCP configs, rules, and workflow packs live in ECC. + +3/ Claude Code is still a core target. + +Codex, OpenCode, Cursor, Gemini, and other harnesses are part of the same story now. + +The goal is fewer one-off harness tricks and more reusable workflow surface. + +4/ The rc.1 surface ships the public pieces: + +- Hermes setup guide +- release notes +- launch checklist +- X and LinkedIn drafts +- cross-harness architecture doc +- Hermes import guidance + +5/ It does not ship private workspace state. + +No secrets. +No OAuth tokens. +No raw local exports. +No personal datasets. + +The point is to publish the reusable system shape. + +6/ Why Hermes matters: + +Most agent systems fail in the daily operating loop. + +They can code, but they do not keep research, content, handoffs, reminders, and execution in one measurable surface. + +7/ ECC gives the reusable layer. + +Hermes gives the operator shell. + +Together they make the work feel less like scattered chat windows and more like a system you can run. + +8/ This is still a release candidate. + +The public docs and reusable surfaces are ready for review. + +The deeper local integrations stay local until they are sanitized. + +9/ Start here: + +Repo: + + +Hermes x ECC setup: + + +Release notes: + diff --git a/docs/tr/AGENTS.md b/docs/tr/AGENTS.md index 3b3dcccc..c133c113 100644 --- a/docs/tr/AGENTS.md +++ b/docs/tr/AGENTS.md @@ -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:** 1.10.0 +**Sürüm:** 2.0.0-rc.1 ## Temel İlkeler diff --git a/docs/tr/README.md b/docs/tr/README.md index adfc6ab7..c2dea630 100644 --- a/docs/tr/README.md +++ b/docs/tr/README.md @@ -79,7 +79,7 @@ Bu repository yalnızca ham kodu içerir. Rehberler her şeyi açıklıyor. ## Yenilikler -### v1.10.0 — Surface Sync, Operatör İş Akışları ve ECC 2.0 Alpha (Nis 2026) +### 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. - **Operatör ve dışa dönük iş akışları büyüdü** — `brand-voice`, `social-graph-ranker`, `customer-billing-ops`, `google-workspace-ops` ve ilgili operatör skill'leri aynı sistem içinde tamamlandı. diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index e94456c2..7fe7978c 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,8 +1,8 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 48 个专业代理、183 项技能、79 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 48 个专业代理、184 项技能、79 条命令以及自动化钩子工作流,用于软件开发。 -**版本:** 1.10.0 +**版本:** 2.0.0-rc.1 ## 核心原则 @@ -147,7 +147,7 @@ ``` agents/ — 48 个专业子代理 -skills/ — 183 个工作流技能和领域知识 +skills/ — 184 个工作流技能和领域知识 commands/ — 79 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 021eb184..9dbbe9e3 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -215,7 +215,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list everything-claude-code@everything-claude-code ``` -**搞定!** 你现在可以使用 48 个智能体、183 项技能和 79 个命令了。 +**搞定!** 你现在可以使用 48 个智能体、184 项技能和 79 个命令了。 *** @@ -1102,7 +1102,7 @@ opencode |---------|-------------|----------|--------| | 智能体 | PASS: 48 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 79 个 | PASS: 31 个 | **Claude Code 领先** | -| 技能 | PASS: 183 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 184 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1214,7 +1214,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|------------|------------|-----------|----------| | **智能体** | 48 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 79 | 共享 | 基于指令 | 31 | -| **技能** | 183 | 共享 | 10 (原生格式) | 37 | +| **技能** | 184 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | @@ -1224,7 +1224,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 | **上下文文件** | CLAUDE.md + AGENTS.md | AGENTS.md | AGENTS.md | AGENTS.md | | **秘密检测** | 基于钩子 | beforeSubmitPrompt 钩子 | 基于沙箱 | 基于钩子 | | **自动格式化** | PostToolUse 钩子 | afterFileEdit 钩子 | N/A | file.edited 钩子 | -| **版本** | 插件 | 插件 | 参考配置 | 1.10.0 | +| **版本** | 插件 | 插件 | 参考配置 | 2.0.0-rc.1 | **关键架构决策:** diff --git a/package-lock.json b/package-lock.json index d334390a..1f4432fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ecc-universal", - "version": "1.10.0", + "version": "2.0.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ecc-universal", - "version": "1.10.0", + "version": "2.0.0-rc.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index baa8c775..e0039c8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecc-universal", - "version": "1.10.0", + "version": "2.0.0-rc.1", "description": "Complete collection of battle-tested Claude Code configs — agents, skills, hooks, rules, and legacy command shims evolved over 10+ months of intensive daily use by an Anthropic hackathon winner", "publishConfig": { "access": "public" @@ -274,4 +274,4 @@ "node": ">=18" }, "packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c" -} \ No newline at end of file +} diff --git a/scripts/release.sh b/scripts/release.sh index 7fdbf082..57d9c96b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -20,6 +20,9 @@ OPENCODE_PACKAGE_JSON=".opencode/package.json" OPENCODE_PACKAGE_LOCK_JSON=".opencode/package-lock.json" OPENCODE_ECC_HOOKS_PLUGIN=".opencode/plugins/ecc-hooks.ts" README_FILE="README.md" +ROOT_ZH_CN_README_FILE="README.zh-CN.md" +TR_README_FILE="docs/tr/README.md" +PT_BR_README_FILE="docs/pt-BR/README.md" ZH_CN_README_FILE="docs/zh-CN/README.md" SELECTIVE_INSTALL_ARCHITECTURE_DOC="docs/SELECTIVE-INSTALL-ARCHITECTURE.md" @@ -36,9 +39,9 @@ if [[ -z "$VERSION" ]]; then usage fi -# Validate VERSION is semver format (X.Y.Z) -if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: VERSION must be in semver format (e.g., 1.5.0)" +# Validate VERSION is semver format (X.Y.Z or X.Y.Z-prerelease) +if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Error: VERSION must be in semver format (e.g., 1.5.0 or 2.0.0-rc.1)" exit 1 fi @@ -56,7 +59,7 @@ if [[ -n "$(git status --porcelain --untracked-files=all)" ]]; then fi # Verify versioned manifests exist -for FILE in "$ROOT_PACKAGE_JSON" "$PACKAGE_LOCK_JSON" "$ROOT_AGENTS_MD" "$TR_AGENTS_MD" "$ZH_CN_AGENTS_MD" "$AGENT_YAML" "$VERSION_FILE" "$PLUGIN_JSON" "$MARKETPLACE_JSON" "$CODEX_MARKETPLACE_JSON" "$CODEX_PLUGIN_JSON" "$OPENCODE_PACKAGE_JSON" "$OPENCODE_PACKAGE_LOCK_JSON" "$OPENCODE_ECC_HOOKS_PLUGIN" "$README_FILE" "$ZH_CN_README_FILE" "$SELECTIVE_INSTALL_ARCHITECTURE_DOC"; do +for FILE in "$ROOT_PACKAGE_JSON" "$PACKAGE_LOCK_JSON" "$ROOT_AGENTS_MD" "$TR_AGENTS_MD" "$ZH_CN_AGENTS_MD" "$AGENT_YAML" "$VERSION_FILE" "$PLUGIN_JSON" "$MARKETPLACE_JSON" "$CODEX_MARKETPLACE_JSON" "$CODEX_PLUGIN_JSON" "$OPENCODE_PACKAGE_JSON" "$OPENCODE_PACKAGE_LOCK_JSON" "$OPENCODE_ECC_HOOKS_PLUGIN" "$README_FILE" "$ROOT_ZH_CN_README_FILE" "$TR_README_FILE" "$PT_BR_README_FILE" "$ZH_CN_README_FILE" "$SELECTIVE_INSTALL_ARCHITECTURE_DOC"; do if [[ ! -f "$FILE" ]]; then echo "Error: $FILE not found" exit 1 @@ -64,7 +67,7 @@ for FILE in "$ROOT_PACKAGE_JSON" "$PACKAGE_LOCK_JSON" "$ROOT_AGENTS_MD" "$TR_AGE done # Read current version from plugin.json -OLD_VERSION=$(grep -oE '"version": *"[^"]*"' "$PLUGIN_JSON" | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') +OLD_VERSION=$(grep -oE '"version": *"[^"]*"' "$PLUGIN_JSON" | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?') if [[ -z "$OLD_VERSION" ]]; then echo "Error: Could not extract current version from $PLUGIN_JSON" exit 1 @@ -123,7 +126,7 @@ update_readme_version_row() { const current = fs.readFileSync(file, "utf8"); const updated = current.replace( new RegExp( - `^\\| \\*\\*${escape(label)}\\*\\* \\| ${escape(firstCol)} \\| ${escape(secondCol)} \\| ${escape(thirdCol)} \\| [0-9]+\\.[0-9]+\\.[0-9]+ \\|$`, + `^\\| \\*\\*${escape(label)}\\*\\* \\| ${escape(firstCol)} \\| ${escape(secondCol)} \\| ${escape(thirdCol)} \\| [0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)? \\|$`, "m" ), `| **${label}** | ${firstCol} | ${secondCol} | ${thirdCol} | ${version} |` @@ -136,6 +139,25 @@ update_readme_version_row() { ' "$file" "$VERSION" "$label" "$first_col" "$second_col" "$third_col" } +update_latest_release_heading() { + local file="$1" + node -e ' + const fs = require("fs"); + const file = process.argv[1]; + const version = process.argv[2]; + const current = fs.readFileSync(file, "utf8"); + const updated = current.replace( + /^### v[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?( .*)$/m, + `### v${version}$1` + ); + if (updated === current) { + console.error(`Error: could not update latest release heading in ${file}`); + process.exit(1); + } + fs.writeFileSync(file, updated); + ' "$file" "$VERSION" +} + update_selective_install_repo_version() { local file="$1" node -e ' @@ -144,7 +166,7 @@ update_selective_install_repo_version() { const version = process.argv[2]; const current = fs.readFileSync(file, "utf8"); const updated = current.replace( - /("repoVersion":\s*")[0-9][0-9.]*(")/, + /("repoVersion":\s*")[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(")/, `$1${version}$2` ); if (updated === current) { @@ -165,7 +187,7 @@ update_agents_version() { const label = process.argv[3]; const current = fs.readFileSync(file, "utf8"); const updated = current.replace( - new RegExp(`^\\*\\*${label}:\\*\\* [0-9][0-9.]*$`, "m"), + new RegExp(`^\\*\\*${label}:\\*\\* [0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?$`, "m"), `**${label}:** ${version}` ); if (updated === current) { @@ -183,7 +205,7 @@ update_agent_yaml_version() { const version = process.argv[2]; const current = fs.readFileSync(file, "utf8"); const updated = current.replace( - /^version:\s*[0-9][0-9.]*$/m, + /^version:\s*[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/m, `version: ${version}` ); if (updated === current) { @@ -225,7 +247,7 @@ update_opencode_hook_banner_version() { const version = process.argv[2]; const current = fs.readFileSync(file, "utf8"); const updated = current.replace( - /(## Active Plugin: Everything Claude Code v)[0-9]+\.[0-9]+\.[0-9]+/, + /(## Active Plugin: Everything Claude Code v)[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?/, `$1${version}` ); if (updated === current) { @@ -253,6 +275,10 @@ update_package_lock_version "$OPENCODE_PACKAGE_LOCK_JSON" update_opencode_hook_banner_version update_readme_version_row "$README_FILE" "Version" "Plugin" "Plugin" "Reference config" update_readme_version_row "$ZH_CN_README_FILE" "版本" "插件" "插件" "参考配置" +update_latest_release_heading "$README_FILE" +update_latest_release_heading "$ROOT_ZH_CN_README_FILE" +update_latest_release_heading "$TR_README_FILE" +update_latest_release_heading "$PT_BR_README_FILE" update_selective_install_repo_version "$SELECTIVE_INSTALL_ARCHITECTURE_DOC" # Verify the bumped release surface is still internally consistent before @@ -263,7 +289,7 @@ node tests/scripts/build-opencode.test.js node tests/plugin-manifest.test.js # Stage, commit, tag, and push -git add "$ROOT_PACKAGE_JSON" "$PACKAGE_LOCK_JSON" "$ROOT_AGENTS_MD" "$TR_AGENTS_MD" "$ZH_CN_AGENTS_MD" "$AGENT_YAML" "$VERSION_FILE" "$PLUGIN_JSON" "$MARKETPLACE_JSON" "$CODEX_MARKETPLACE_JSON" "$CODEX_PLUGIN_JSON" "$OPENCODE_PACKAGE_JSON" "$OPENCODE_PACKAGE_LOCK_JSON" "$OPENCODE_ECC_HOOKS_PLUGIN" "$README_FILE" "$ZH_CN_README_FILE" "$SELECTIVE_INSTALL_ARCHITECTURE_DOC" +git add "$ROOT_PACKAGE_JSON" "$PACKAGE_LOCK_JSON" "$ROOT_AGENTS_MD" "$TR_AGENTS_MD" "$ZH_CN_AGENTS_MD" "$AGENT_YAML" "$VERSION_FILE" "$PLUGIN_JSON" "$MARKETPLACE_JSON" "$CODEX_MARKETPLACE_JSON" "$CODEX_PLUGIN_JSON" "$OPENCODE_PACKAGE_JSON" "$OPENCODE_PACKAGE_LOCK_JSON" "$OPENCODE_ECC_HOOKS_PLUGIN" "$README_FILE" "$ROOT_ZH_CN_README_FILE" "$TR_README_FILE" "$PT_BR_README_FILE" "$ZH_CN_README_FILE" "$SELECTIVE_INSTALL_ARCHITECTURE_DOC" git commit -m "chore: bump plugin version to $VERSION" git tag "v$VERSION" git push origin main "v$VERSION" diff --git a/skills/hermes-imports/SKILL.md b/skills/hermes-imports/SKILL.md new file mode 100644 index 00000000..d1537eb8 --- /dev/null +++ b/skills/hermes-imports/SKILL.md @@ -0,0 +1,88 @@ +--- +name: hermes-imports +description: Convert local Hermes operator workflows into sanitized ECC skills and release-pack artifacts. Use when preparing a Hermes workflow for public ECC reuse without leaking private workspace state, credentials, or local-only paths. +origin: ECC +--- + +# Hermes Imports + +Use this skill when turning a repeated Hermes workflow into something safe to ship in ECC. + +Hermes is the operator shell. ECC is the reusable workflow layer. Imports should move stable patterns from Hermes into ECC without moving private state. + +## When To Use + +- A Hermes workflow has repeated enough times to become reusable. +- A local operator prompt should become a public ECC skill. +- A launch, content, research, or engineering workflow needs sanitized handoff docs. +- A workflow mentions local paths, credentials, personal datasets, or private account names that must be removed before publication. + +## Import Rules + +- Convert local paths to repo-relative paths or placeholders. +- Replace live account names with role labels such as `operator`, `default profile`, or `workspace owner`. +- Describe credential requirements by provider name only. +- Keep examples narrow and operational. +- Do not ship raw workspace exports, tokens, OAuth files, health data, CRM data, or finance data. +- If the workflow requires private state to make sense, keep it local. + +## Sanitization Checklist + +Before committing an imported workflow, scan for: + +- absolute paths such as `/Users/...` +- `~/.hermes` paths unless the doc is explicitly explaining local setup +- API keys, tokens, cookies, OAuth files, or bearer strings +- phone numbers, private email addresses, and personal contact graphs +- client names, family names, or account names that are not already public +- revenue, health, or CRM details +- raw logs that include tool output from private systems + +## Conversion Pattern + +1. Identify the repeatable operator loop. +2. Strip private inputs and outputs. +3. Rewrite local paths as repo-relative examples. +4. Turn one-off instructions into a `When To Use` section and a short process. +5. Add concrete output requirements. +6. Run a secret and local-path scan before opening a PR. + +## Example: Launch Handoff + +Local Hermes prompt: + +```text +Read my local workspace files and finalize launch copy. +``` + +ECC-safe version: + +```text +Use the public release pack under docs/releases//. +Return one X thread, one LinkedIn post, one recording checklist, and the missing assets list. +``` + +## Example: Quiet-Hours Operator Job + +Local Hermes job: + +```text +Run my private inbox, finance, and content checks overnight. +``` + +ECC-safe version: + +```text +Describe the scheduler policy, the quiet-hours window, the escalation rules, and the categories of checks. Do not include private data sources or credentials. +``` + +## Output Contract + +Return: + +- candidate ECC skill name +- sanitized workflow summary +- required public inputs +- private inputs removed +- remaining risks +- files that should be created or updated diff --git a/tests/docs/ecc2-release-surface.test.js b/tests/docs/ecc2-release-surface.test.js new file mode 100644 index 00000000..aff19b7f --- /dev/null +++ b/tests/docs/ecc2-release-surface.test.js @@ -0,0 +1,120 @@ +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const repoRoot = path.resolve(__dirname, '..', '..'); +const releaseDir = path.join(repoRoot, 'docs', 'releases', '2.0.0-rc.1'); + +let passed = 0; +let failed = 0; + +function test(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + passed++; + } catch (error) { + console.log(` ✗ ${name}`); + console.log(` Error: ${error.message}`); + failed++; + } +} + +function read(relativePath) { + return fs.readFileSync(path.join(repoRoot, relativePath), 'utf8'); +} + +function walkMarkdown(rootPath) { + const files = []; + for (const entry of fs.readdirSync(rootPath, { withFileTypes: true })) { + const nextPath = path.join(rootPath, entry.name); + if (entry.isDirectory()) { + files.push(...walkMarkdown(nextPath)); + } else if (entry.isFile() && entry.name.endsWith('.md')) { + files.push(nextPath); + } + } + return files; +} + +console.log('\n=== Testing ECC 2.0 release surface ===\n'); + +const expectedReleaseFiles = [ + 'release-notes.md', + 'x-thread.md', + 'linkedin-post.md', + 'article-outline.md', + 'launch-checklist.md', + 'telegram-handoff.md', + 'demo-prompts.md', +]; + +test('release candidate directory includes the public launch pack', () => { + for (const fileName of expectedReleaseFiles) { + assert.ok(fs.existsSync(path.join(releaseDir, fileName)), `Missing ${fileName}`); + } +}); + +test('README links to Hermes setup and rc.1 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'); +}); + +test('cross-harness architecture doc exists and names core harnesses', () => { + const source = read('docs/architecture/cross-harness.md'); + for (const harness of ['Claude Code', 'Codex', 'OpenCode', 'Cursor', 'Gemini', 'Hermes']) { + assert.ok(source.includes(harness), `Expected cross-harness doc to mention ${harness}`); + } +}); + +test('Hermes import skill exists and declares sanitization rules', () => { + const source = read('skills/hermes-imports/SKILL.md'); + assert.ok(source.includes('name: hermes-imports')); + assert.ok(source.includes('Sanitization Checklist')); + assert.ok(source.includes('Do not ship raw workspace exports')); +}); + +test('release docs do not contain private local workspace paths', () => { + const offenders = []; + for (const filePath of walkMarkdown(releaseDir)) { + const source = fs.readFileSync(filePath, 'utf8'); + if (source.includes('/Users/') || source.includes('/.hermes/')) { + offenders.push(path.relative(repoRoot, filePath)); + } + } + assert.deepStrictEqual(offenders, []); +}); + +test('release docs do not contain unresolved public-link placeholders', () => { + const offenders = []; + for (const filePath of walkMarkdown(releaseDir)) { + const source = fs.readFileSync(filePath, 'utf8'); + if (source.includes('')) { + offenders.push(path.relative(repoRoot, filePath)); + } + } + assert.deepStrictEqual(offenders, []); +}); + +test('Hermes setup uses release-candidate wording for the rc.1 surface', () => { + const source = read('docs/HERMES-SETUP.md'); + assert.ok(source.includes('Public Release Candidate Scope')); + assert.ok(source.includes('ECC v2.0.0-rc.1 documents the Hermes surface')); + assert.ok(!source.includes('Public Preview Scope')); +}); + +test('release docs preserve the ECC/Hermes boundary', () => { + const releaseNotes = read('docs/releases/2.0.0-rc.1/release-notes.md'); + assert.ok(releaseNotes.includes('ECC is the reusable substrate')); + assert.ok(releaseNotes.includes('Hermes as the operator shell')); +}); + +if (failed > 0) { + console.log(`\nFailed: ${failed}`); + process.exit(1); +} + +console.log(`\nPassed: ${passed}`); diff --git a/tests/plugin-manifest.test.js b/tests/plugin-manifest.test.js index dc56d118..68e7d154 100644 --- a/tests/plugin-manifest.test.js +++ b/tests/plugin-manifest.test.js @@ -35,6 +35,7 @@ const selectiveInstallArchitecturePath = path.join(repoRoot, 'docs', 'SELECTIVE- const opencodePackageJsonPath = path.join(repoRoot, '.opencode', 'package.json'); const opencodePackageLockPath = path.join(repoRoot, '.opencode', 'package-lock.json'); const opencodeHooksPluginPath = path.join(repoRoot, '.opencode', 'plugins', 'ecc-hooks.ts'); +const semverPattern = '[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?'; let passed = 0; let failed = 0; @@ -118,28 +119,28 @@ test('package-lock.json root version matches package.json', () => { test('AGENTS.md version line matches package.json', () => { const agentsSource = fs.readFileSync(rootAgentsPath, 'utf8'); - const match = agentsSource.match(/^\*\*Version:\*\* ([0-9]+\.[0-9]+\.[0-9]+)$/m); + const match = agentsSource.match(new RegExp(`^\\*\\*Version:\\*\\* (${semverPattern})$`, 'm')); assert.ok(match, 'Expected AGENTS.md to declare a top-level version line'); assert.strictEqual(match[1], expectedVersion); }); test('docs/tr/AGENTS.md version line matches package.json', () => { const agentsSource = fs.readFileSync(trAgentsPath, 'utf8'); - const match = agentsSource.match(/^\*\*Sürüm:\*\* ([0-9]+\.[0-9]+\.[0-9]+)$/m); + const match = agentsSource.match(new RegExp(`^\\*\\*Sürüm:\\*\\* (${semverPattern})$`, 'm')); assert.ok(match, 'Expected docs/tr/AGENTS.md to declare a top-level version line'); assert.strictEqual(match[1], expectedVersion); }); test('docs/zh-CN/AGENTS.md version line matches package.json', () => { const agentsSource = fs.readFileSync(zhCnAgentsPath, 'utf8'); - const match = agentsSource.match(/^\*\*版本:\*\* ([0-9]+\.[0-9]+\.[0-9]+)$/m); + const match = agentsSource.match(new RegExp(`^\\*\\*版本:\\*\\* (${semverPattern})$`, 'm')); assert.ok(match, 'Expected docs/zh-CN/AGENTS.md to declare a top-level version line'); assert.strictEqual(match[1], expectedVersion); }); test('agent.yaml version matches package.json', () => { const agentYamlSource = fs.readFileSync(agentYamlPath, 'utf8'); - const match = agentYamlSource.match(/^version:\s*([0-9]+\.[0-9]+\.[0-9]+)$/m); + const match = agentYamlSource.match(new RegExp(`^version:\\s*(${semverPattern})$`, 'm')); assert.ok(match, 'Expected agent.yaml to declare a top-level version field'); assert.strictEqual(match[1], expectedVersion); }); @@ -152,14 +153,14 @@ test('VERSION file matches package.json', () => { test('docs/SELECTIVE-INSTALL-ARCHITECTURE.md repoVersion example matches package.json', () => { const source = fs.readFileSync(selectiveInstallArchitecturePath, 'utf8'); - const match = source.match(/"repoVersion":\s*"([0-9]+\.[0-9]+\.[0-9]+)"/); + const match = source.match(new RegExp(`"repoVersion":\\s*"(${semverPattern})"`)); assert.ok(match, 'Expected docs/SELECTIVE-INSTALL-ARCHITECTURE.md to declare a repoVersion example'); assert.strictEqual(match[1], expectedVersion); }); test('.opencode/plugins/ecc-hooks.ts active plugin banner matches package.json', () => { const source = fs.readFileSync(opencodeHooksPluginPath, 'utf8'); - const match = source.match(/## Active Plugin: Everything Claude Code v([0-9]+\.[0-9]+\.[0-9]+)/); + const match = source.match(new RegExp(`## Active Plugin: Everything Claude Code v(${semverPattern})`)); assert.ok(match, 'Expected .opencode/plugins/ecc-hooks.ts to declare an active plugin banner'); assert.strictEqual(match[1], expectedVersion); }); @@ -445,7 +446,7 @@ test('.opencode/package-lock.json root version matches package.json', () => { test('README version row matches package.json', () => { const readme = fs.readFileSync(path.join(repoRoot, 'README.md'), 'utf8'); - const match = readme.match(/^\| \*\*Version\*\* \| Plugin \| Plugin \| Reference config \| ([0-9][0-9.]*) \|$/m); + const match = readme.match(new RegExp(`^\\| \\*\\*Version\\*\\* \\| Plugin \\| Plugin \\| Reference config \\| (${semverPattern}) \\|$`, 'm')); assert.ok(match, 'Expected README version summary row'); assert.strictEqual(match[1], expectedVersion); }); @@ -497,7 +498,7 @@ test('user-facing docs do not use the legacy non-URL marketplace add form', () = test('docs/zh-CN/README.md version row matches package.json', () => { const readme = fs.readFileSync(zhCnReadmePath, 'utf8'); - const match = readme.match(/^\| \*\*版本\*\* \| 插件 \| 插件 \| 参考配置 \| ([0-9][0-9.]*) \|$/m); + const match = readme.match(new RegExp(`^\\| \\*\\*版本\\*\\* \\| 插件 \\| 插件 \\| 参考配置 \\| (${semverPattern}) \\|$`, 'm')); assert.ok(match, 'Expected docs/zh-CN/README.md version summary row'); assert.strictEqual(match[1], expectedVersion); }); diff --git a/tests/scripts/release-publish.test.js b/tests/scripts/release-publish.test.js index b09c6979..6a061b50 100644 --- a/tests/scripts/release-publish.test.js +++ b/tests/scripts/release-publish.test.js @@ -50,6 +50,18 @@ for (const workflow of [ assert.match(content, /npm publish --access public --provenance/); assert.match(content, /NODE_AUTH_TOKEN:\s*\$\{\{\s*secrets\.NPM_TOKEN\s*\}\}/); }); + + test(`${workflow} creates the GitHub Release before publishing to npm`, () => { + const releaseIndex = content.indexOf('name: Create GitHub Release'); + const publishIndex = content.indexOf('name: Publish npm package'); + + assert.ok(releaseIndex >= 0, `${workflow} should create a GitHub Release`); + assert.ok(publishIndex >= 0, `${workflow} should publish the npm package`); + assert.ok( + releaseIndex < publishIndex, + `${workflow} should not publish to npm until GitHub Release creation has succeeded` + ); + }); } if (failed > 0) { diff --git a/tests/scripts/release.test.js b/tests/scripts/release.test.js index 299f5352..b255969c 100644 --- a/tests/scripts/release.test.js +++ b/tests/scripts/release.test.js @@ -8,6 +8,19 @@ const path = require('path'); const scriptPath = path.join(__dirname, '..', '..', 'scripts', 'release.sh'); const source = fs.readFileSync(scriptPath, 'utf8'); +const releaseWorkflowPath = path.join(__dirname, '..', '..', '.github', 'workflows', 'release.yml'); +const reusableReleaseWorkflowPath = path.join( + __dirname, + '..', + '..', + '.github', + 'workflows', + 'reusable-release.yml' +); +const ciWorkflowPath = path.join(__dirname, '..', '..', '.github', 'workflows', 'ci.yml'); +const releaseWorkflowSource = fs.readFileSync(releaseWorkflowPath, 'utf8'); +const reusableReleaseWorkflowSource = fs.readFileSync(reusableReleaseWorkflowPath, 'utf8'); +const ciWorkflowSource = fs.readFileSync(ciWorkflowPath, 'utf8'); function test(name, fn) { try { @@ -64,6 +77,71 @@ function runTests() { ); })) passed++; else failed++; + if (test('release script supports prerelease semver and release heading sync', () => { + assert.ok( + source.includes('2.0.0-rc.1'), + 'release.sh should document an accepted prerelease semver example' + ); + assert.ok( + source.includes('(-[0-9A-Za-z.-]+)?'), + 'release.sh should allow prerelease semver suffixes' + ); + assert.ok( + source.includes('update_latest_release_heading "$ROOT_ZH_CN_README_FILE"'), + 'release.sh should update localized latest-release headings that plugin-manifest.test.js verifies' + ); + })) passed++; else failed++; + + if (test('release workflows mark prerelease tags as GitHub prereleases', () => { + assert.ok( + releaseWorkflowSource.includes('prerelease: ${{ contains(github.ref_name, \'-\') }}'), + 'release.yml should mark hyphenated tag pushes as GitHub prereleases' + ); + assert.ok( + releaseWorkflowSource.includes('make_latest: ${{ contains(github.ref_name, \'-\') && \'false\' || \'true\' }}'), + 'release.yml should avoid making hyphenated prereleases the latest GitHub release' + ); + assert.ok( + reusableReleaseWorkflowSource.includes('prerelease: ${{ contains(inputs.tag, \'-\') }}'), + 'reusable-release.yml should mark hyphenated manual tags as GitHub prereleases' + ); + assert.ok( + reusableReleaseWorkflowSource.includes('make_latest: ${{ contains(inputs.tag, \'-\') && \'false\' || \'true\' }}'), + 'reusable-release.yml should avoid making hyphenated prereleases the latest GitHub release' + ); + })) passed++; else failed++; + + if (test('reusable release checks out the requested tag before validating and publishing', () => { + const checkoutIndex = reusableReleaseWorkflowSource.indexOf('uses: actions/checkout@'); + const refIndex = reusableReleaseWorkflowSource.indexOf('ref: ${{ inputs.tag }}'); + const validateIndex = reusableReleaseWorkflowSource.indexOf('name: Validate version tag'); + + assert.ok(checkoutIndex >= 0, 'reusable-release.yml should check out repository content'); + assert.ok(refIndex >= 0, 'reusable-release.yml checkout should use inputs.tag as ref'); + assert.ok(validateIndex >= 0, 'reusable-release.yml should validate requested tag'); + assert.ok( + checkoutIndex < refIndex && refIndex < validateIndex, + 'reusable release should check out inputs.tag before tag validation and publish steps' + ); + })) passed++; else failed++; + + if (test('CI runs for release branches and version tags before release workflows execute', () => { + const pushBlockMatch = ciWorkflowSource.match(/on:\n\s+push:\n([\s\S]*?)\n\s+pull_request:/); + const pushBlock = pushBlockMatch ? pushBlockMatch[1] : ''; + + assert.ok(pushBlock, 'ci.yml should define a push trigger block'); + assert.match( + pushBlock, + /branches:\s*\[[^\]]*main[^\]]*['"]release\/\*\*['"][^\]]*\]/, + 'ci.yml push branches should include release/**' + ); + assert.match( + pushBlock, + /tags:\s*\[[^\]]*['"]v\*['"][^\]]*\]/, + 'ci.yml push tags should include v*' + ); + })) passed++; else failed++; + console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); }