* docs: add official-sources security warning to README
Add a GFM [!WARNING] alert near the top of README.md identifying
github.com/affaan-m/ECC and the ecc-universal / ecc-agentshield npm
packages as the only verified distribution channels, and warning users
that third-party re-uploads may contain malware.
Closes#2242
* Update README.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Affaan Mustafa <affaan.mustafa09@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
- suggest-compact hook now reads the latest usage record from the session
transcript and suggests /compact at a window-scaled token threshold
(160k/200k window, 250k/1M window; COMPACT_CONTEXT_THRESHOLD and
COMPACT_CONTEXT_INTERVAL overridable), re-firing per 60k-token growth
bucket; tool-call count stays as the secondary signal (#2155)
- Codex repo marketplace now points at ./plugins/ecc instead of ./ — Codex
never discovers plugins whose local marketplace source.path is the
marketplace root (verified on Codex CLI 0.137.0); plugins/ecc is a thin
folder referencing root skills/.mcp.json per maintainer direction on
#2097; docs flag plugin mode as experimental with the upstream blocker
openai/codex#26037 linked (#2128)
- README badges for installs/stars/forks now use shields endpoint badges
backed by api.ecc.tools (live install count 3,712 vs the stale static
150), which also eliminates shields' 'Unable to select next GitHub token
from pool' render in the stars badge
Closes#2155Closes#2128
- competitive-platform-analysis: add ## Examples section per ECC
guidelines (8-axis taxonomy walkthrough + pre-filter scoring matrix)
- competitive-report-structure: clarify dimension 9 poles are client-
specific (e.g., Memorability/Hireability) not hard-coded names
- brand-discovery: fix terminal state — set inProgressModule to null
after 90_SYNTHESIS.md is complete to prevent misleading resumption
All fixes mirrored to .agents/ copies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v2.0.0-rc.1 shipped in April 2026 with 261 skills; the four new skills added
in this PR bring the count to 265 only in v2.0.0+. Retroactively updating the
rc.1 entry rewrote past release facts — restore the accurate historical count.
Addresses cubic-dev-ai review finding (README.md:137).
Adds four community skills covering brand identity discovery and a
three-skill competitive benchmarking pipeline.
**brand-discovery** — Adaptive multi-session brand identity interview
spanning 8 modules (purpose, positioning, audience, personality, voice,
narrative, founder-brand tension, synthesis). Uses laddering, 5 Whys,
and projective techniques. State persisted to disk via state.json so
sessions resume across conversations without losing elicited knowledge.
Frameworks: Sinek, Dunford, Baker, Enns, Kapferer, Aaker, Neumeier,
Mark & Pearson, Lencioni. Includes 8 module output templates in
references/.
**competitive-platform-analysis** — Scopes and tiers a competitor set
before benchmarking begins. Categorizes candidates along 8 generic
creative-industry axes (positioning stance, specialization, size/model,
engagement format, distinctiveness posture, evidence model, brand
strength, market/reach) into Direct / Adjacent / Aspirational tiers.
Includes a pre-filter scoring matrix. First step in the pipeline.
**benchmark-methodology** — Scores each competitor across 9 weighted
dimensions (positioning 18%, brand voice 15%, visual craft 15%, offer
packaging 12%, evidence 12%, enterprise-readiness 10%, thought
leadership 8%, pricing 5%, client's strategic tension 5%) with explicit
1–5 rubrics and bias controls. Produces one profile card per competitor.
**competitive-report-structure** — Assembles scored cards into a
decision-grade report: executive summary, landscape map, competitor
tiers, heatmap matrix, deep dives, white-space and threats, strategic
recommendations, sources appendix.
brand-discovery complements brand-voice (ECC): brand-voice extracts a
style profile from existing source material; brand-discovery elicits
identity from scratch through structured interviews when no prior
material exists.
A competitive set scoped without the client's positioning brief is
noise, not intelligence — each skill enforces this by requiring the
brief before proceeding. The 9-dimension scoring framework deliberately
reports the client's strategic tension as two separate poles (never
averaged) because the gap between them is the strategic finding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The store write was unconditional, persisting work items even during dry
runs. Move it inside the !dryRun block alongside editIssue and initialize
snapshot to null beforehand so results.push still receives snapshot: null
for dry runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
applyPublish was forcing review='approved' for any state that wasn't
'changes-requested', bypassing policy.review.required entirely. Add a
guard that throws before buildIssueStateFromAction when review approval
is required but not yet granted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove circular validation-status check in applyValidate that prevented
fresh claims (validation='pending') from ever reaching 'passed'
- Add staleCoordinationLabels helper to compute coordination:* labels to
remove on state transitions; replaces hardcoded removeLabels:[] across
all six editIssue call sites
- Fix duplicate label writes in applySync: syncIssueLabels already calls
editIssue for labels, so the follow-up editIssue now only updates body
- Skip acquireLock finding: store.acquireLock does not exist; comment
updated to explain why the fix was not applied
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
actions.js:
- Add assertValidRepo/assertValidIssueNumber guards at the top of all
action handlers (applyClaim, applySync, applyValidate, applyPublish,
applyReview, applyDecompose, applyUnblock) for fast-fail validation
- applyValidate: fix status transition — set 'validated' unconditionally
when ok=true instead of preserving 'blocked' (was inconsistent with
projectState becoming 'ready')
gh-api.js:
- runGh: preserve GITHUB_TOKEN by default; only delete when caller
explicitly sets options.stripGithubToken=true (was deleting by
default, breaking CI)
parsing.js:
- extractCoordinationState: throw SyntaxError on malformed JSON instead
of silently returning null — lets callers distinguish bad JSON from
absent marker
- normalizeBodyForComparison: fix regex to match JSON-quoted form
"lastSyncAt": ... instead of bare lastSyncAt: ...
policy.js:
- loadPolicy: validate that parsed JSON is a plain object before
spreading; coerce nested fields (labels, review, validation,
branchModel, project, fieldNames) to objects before merging
state.js:
- assertIssueClaimable: block re-claim on status alone (not status AND
owner) to prevent {status:'claimed', owner:null} bypass; use
state.owner || 'unknown' in error message
- getCoordinationState: catch SyntaxError from extractCoordinationState,
log warning to stderr, fall back to default state
tests/lib:
- Update malformed-JSON test to expect SyntaxError throw instead of null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(deps): bump crossterm from 0.28.1 to 0.29.0 in /ecc2
Bumps [crossterm](https://github.com/crossterm-rs/crossterm) from 0.28.1 to 0.29.0.
- [Release notes](https://github.com/crossterm-rs/crossterm/releases)
- [Changelog](https://github.com/crossterm-rs/crossterm/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossterm-rs/crossterm/commits/0.29)
---
updated-dependencies:
- dependency-name: crossterm
dependency-version: 0.29.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix(ecc2): switch ratatui feature to crossterm_0_29
Keep a single crossterm version in the tree after the 0.29 bump;
with crossterm_0_28 the lockfile carried both 0.28.1 and 0.29.0.
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Affaan Mustafa <me@affaanmustafa.com>
* fix(assets): replace hero brand mark with website coral circuit mark
The top-left mark in the hero banner was the assets/ecc-icon.svg double-E
lettermark, not the actual brand logo. Swap in the coral vector circuit
mark from the ECC-website header (src/styles/brandMarks.ts), keeping the
~70px footprint, the soft coral glow, and every other element identical.
PNG re-rendered at 2400x1350 via sharp with palette compression.
* docs: sync skill count to 262 across catalog surfaces
catalog:check was failing on main after config-gc (#2216) landed without
a count bump. Ran npm run catalog:sync.
#2209 bumped ureq to 3.x but the AgentBuilder-based webhook sender
was not ported (branch update raced the merge). ureq 3 replaces
AgentBuilder with Agent::config_builder(); timeouts are Option-wrapped
and status() returns http::StatusCode.
Recreates the v1.10 hero banner design (sourced from commit 602894ef)
that PR #2225 replaced with a plain HTML header:
- Wordmark and breadcrumb now read ECC / affaan-m/ECC
- Version badge reads v2.0.0 · Jun 2026, eyebrow updated to V2.0
- Top-left mark is the actual assets/ecc-icon.svg lettermark (amber E,
coral CC) instead of a generic coral square
- Catalog columns refreshed with live counts (261 skills, 64 agents,
84 commands, 409 catalog) and real item names from the repo
- Harness pills updated to the current README list (Claude Code, Codex,
Cursor, OpenCode, Gemini, Zed, Copilot)
- SVG source committed as assets/hero.svg so future edits never need
image archaeology; rendered to PNG at 2400x1350 via sharp
README hero line restored to the markdown image; badges, sponsor table,
and guide cards from #2225 kept intact.
* docs(zh-CN): translate ecc-guide and parallel-execution-optimizer skills
Adds Simplified Chinese translations for two untranslated skills,
following the existing docs/zh-CN/skills/ conventions (frontmatter
name/origin preserved, code blocks and output templates kept in
English, prose fully translated).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(zh-CN): polish two phrasings per review
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* docs(zh-CN): add Chinese translation of SKILL-DEVELOPMENT-GUIDE
Translate the comprehensive Skill Development Guide to Chinese,
enabling Chinese-speaking contributors to learn how to create
effective ECC skills.
* fix(docs): correct all relative links in zh-CN translation
Fix CONTRIBUTING.md link to zh-CN local copy, and skills links
to point to repo-root skills/ directory instead of non-existent
docs/skills/.
---------
Co-authored-by: lege962 <1515808962@qq.com>
Co-authored-by: legeZZZ <277193585+legeZZZ@users.noreply.github.com>
- add ecc2/rust-toolchain.toml pinning stable 1.96 (deps now require
edition2024, which needs rustc 1.85+; local 1.84 could no longer build)
- make git test fixtures hermetic: disable core.hooksPath inside temp
repos so global identity-checking pre-push hooks cannot fail tests
* fix(hooks): fail open on oversized stdin instead of echoing truncated JSON (#2222)
run-with-flags.js capped stdin at 1MB but every fallthrough path still
echoed the truncated string to stdout. The harness parses hook stdout as
JSON, got a document cut mid-stream, and blocked the tool call — so any
Edit/Write with a >1MB hook payload was permanently blocked by every
registered pre-write hook, before ECC_HOOK_PROFILE / ECC_DISABLED_HOOKS
gating could run.
- Exit 0 with empty stdout (no opinion) when the stdin cap trips, before
any echo or gating logic.
- Flush stdout via write callback before process.exit: exiting right
after stdout.write() dropped everything past the ~64KB pipe buffer,
cutting even sub-cap pass-through payloads mid-JSON.
Regression tests cover the enabled, disabled, and missing-arg paths for
oversized payloads plus full echo of sub-cap >64KB payloads.
* fix(codex): stop emitting invalid exa url entry, align merge with connector policy (#2224)
The Codex MCP merge declared exa with a url key, but Codex's
[mcp_servers.*] TOML schema is stdio-only — the url key makes the
entire config.toml fail to load, bricking both the codex CLI and the
desktop app. Every install/update re-injected the line because the
urlEntry branch treated the broken entry as present.
- ECC_SERVERS now emits only the current default set per
docs/MCP-CONNECTOR-POLICY.md: chrome-devtools (stdio, command/args).
Retired servers (supabase, playwright, context7, exa, github, memory,
sequential-thinking) are never re-emitted; existing user-managed
entries are untouched.
- The merge now repairs the exact ECC-emitted broken form (url-only
exa entry) on every run so re-running the installer fixes broken
configs instead of preserving them. User stdio exa entries
(command + mcp-remote) are left alone.
- check-codex-global-state.sh requires chrome-devtools instead of the
retired set, and flags url-only exa entries with a repair hint.
Tests cover repair, re-run idempotence, stdio-entry preservation, and
no-retired-server emission in add, update, dry-run, and disabled modes.
* fix(hooks): never echo truncated stdin from Stop hooks (#2090)
Stop hooks follow the ECC pass-through convention (echo stdin on
stdout), but every echoing Stop hook capped stdin and echoed the capped
string. The Stop payload carries last_assistant_message, so a long
final assistant message produced a JSON document cut mid-stream on
stdout, which the harness reports as 'Stop hook error: JSON validation
failed' across the whole Stop chain.
Reproduced: a Stop payload with a >64KB last_assistant_message run
through run-with-flags + cost-tracker emitted exactly 65536 bytes of
invalid JSON (cost-tracker capped stdin at 64KB — far below realistic
Stop payloads).
- cost-tracker: raise the cap to 1MB (matching all other hooks) and
suppress the pass-through echo when stdin was truncated.
- check-console-log, stop-format-typecheck, desktop-notify: suppress
the echo when stdin was truncated; flush stdout before process.exit
so sub-cap payloads are not cut at the ~64KB pipe buffer.
- All hooks keep exiting 0 (fail-open); diagnostics go to stderr.
New stop-hooks-stdout test asserts the contract for every registered
Stop hook: stdout is empty or valid JSON, exit code 0 — for realistic
100KB payloads and oversized >1MB payloads, via the production runner
and via direct invocation. Updated the old hooks.test.js case that
codified the truncated-echo behavior.
* fix(hooks): dampen GateGuard fact-force repetition in long sessions (#2142)
In long autonomous sessions the fact-force gate produced 10+
near-identical 'state facts -> blocked -> restate -> retry' blocks in
one context window, which measurably raises the odds of the model
collapsing into a degenerate single-token repetition loop.
- Track a per-session fact_force_denials counter in GateGuard state
(merged max across concurrent writers, reset with the session, robust
to malformed on-disk values).
- The first GATEGUARD_FACT_FORCE_FULL_DENIALS denials (default 3) keep
the full four-fact block; later denials emit a condensed single-line
message that carries the denial ordinal, so consecutive denials are
structurally different and never textually identical.
- True retries of the same target remain allowed without re-prompting
(unchanged). Destructive-Bash and routine-Bash gates are unchanged,
as are the ECC_GATEGUARD=off / ECC_DISABLED_HOOKS escape hatches.
Eight new tests cover budget counting, condensed format, ordinal
advancement, retry pass-through, env tuning, malformed state, MultiEdit
dampening, and destructive-gate exemption.
* fix(hooks): keep security hooks able to block on oversized stdin (#2222)
Refine the truncation fail-open: instead of skipping the hook entirely,
the runner now suppresses only its own raw-echo when stdin was
truncated. The hook still executes and receives the truncated flag
(run() context / ECC_HOOK_INPUT_TRUNCATED), so config-protection keeps
blocking truncated protected-config payloads (its test requires exit 2)
while pass-through hooks fail open with empty stdout as before.
* style: apply repo formatter to touched hook files
- Replace off-brand hero PNG (wrong product name + baked version) with a
centered HTML header using assets/ecc-icon.svg, h1, and tagline
- Consolidate duplicated sponsor sections: polished centered sponsor table
at top (CodeRabbit, Greptile, community sponsors, sponsor links); bottom
section reduced to a one-liner pointing to SPONSORS.md
- Convert guide links to visual cards using the guides' own header images,
linked to the local guide files
- Fix broken tmux video URL in the shortform guide to the in-repo asset
Git commands (log, diff, show) can execute arbitrary code via:
- core.pager set in repo-local .git/config
- diff.external pointing to an attacker-controlled binary
- filter drivers in .gitattributes
Mitigation: require --no-pager flag, recommend -c core.pager=cat
to disable pager-driven execution. Moved git commands from the
unqualified allowlist to a hardened allowlist with explicit flags.
- Add top-level hooks wrapper to second JSON example (consistent with hooks.json format)
- Extract hardcoded thresholds as module-level constants (WALL_OF_TEXT_WORDS,
SUMMARY_CHECK_WORDS, SUMMARY_CHECK_FIRST_N, TASK_OUTPUT_RATIO_HIGH/MEDIUM)
Skipped (not applicable):
- 'Scoring defaults to 5/5' — by design for heuristic fallback; SKILL.md already
documents pairing with LLM judge for production use
- '--output silently ignored' — already fixed by _read_input refactor (checks
args.output directly, not elif args.task and args.output)
Validator (scripts/ci/validate-hooks.js line 182-184) only errors when
matcher is missing for non-EVENTS_WITHOUT_MATCHER events. For Stop (in
EVENTS_WITHOUT_MATCHER), matcher is optional — presence is allowed and
validated for type correctness, absence is also accepted.