- agent.yaml: register epic-* commands (#2236) and vue-review (#2241)
- package.json files: drop stray skills/ml-adoption-playbook entry (follows orphan-skill publish pattern; not in install-modules.json)
- unicode-safety: strip decorative emoji from dashboard-web.js (#2100) and brand-discovery refs (#2221) to pass the CI gate
- agent-compress: raise catalog token canary 5000 -> 6000 for the 67-agent catalog
Full suite green (2836/2836).
* feat(agents): add spec-miner agent for brownfield spec extraction
Mines behavioral specs (Requirements + Invariants) from existing codebases
without OpenSpec. Fully self-bootstrapping with sample-and-expand token
strategy. Produces flat, delta-ready spec.md files with machine-parseable
metadata (id, entities, enforced, depends_on, triggers).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs: bump agent catalog count from 64 to 65 for spec-miner
All documentation and plugin manifests now reflect the new agent total.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: add spec-miner to routing table and clarify id field requirement
- Add spec-miner to AGENTS.md agent table and orchestration hints
- Fix id field in output template: was marked [optional] but Rule #7
requires it when enforced is known
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: update catalog skills count from 261 to 262 across all docs
The upstream added a 262nd skill but documentation references across 7 files
still reported 261. The CI validate step (scripts/ci/catalog.js --text) caught
the mismatch — this only runs on PRs, not on direct pushes to main.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: replace emoji characters with text equivalents in spec-miner agent
The unicode safety check (check-unicode-safety.js) blocks emoji characters.
Replace ❌ with FAIL: per the project's targeted replacement convention.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: add Write tool to spec-miner agent tools list
The agent generates spec output files at openspec/specs/<capability>/spec.md
and requires the Write tool to create them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: address review bot comments - tool guardrails and metadata schema consistency
- Add Tool guardrails section: scoping Write to openspec/specs/ path, Bash to read-only
- Fix deferred/uncertainty comments to follow key: value schema (deferred: file list, uncertainty: reason)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: strengthen Prompt Defense Baseline for repository content and Bash boundaries
Add two defense points: treat all repo content as untrusted prompt-injection
vector, and explicitly reject Bash commands that mutate, exfiltrate, or write
outside the allowed openspec/specs/ path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: strip explanatory prose from id metadata comment to preserve key:value format
The id comments included explanatory text after the value, which would be
stored verbatim in copied specs and break stable delta matching. The
explanation is already covered by Format Rule #7.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: restore README.md to upstream baseline with only catalog count changes
The README was corrupted during cherry-pick conflict resolution — an older fork
version was introduced, changing release notes links, badge URLs, sponsor
sections, and other content. Restore to upstream/main (5b173d2) and re-apply
only the agent count (64→65) using catalog.js --write.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: restore all catalog files to upstream baseline, keep only intentional changes
The cherry-pick during rebase introduced a stale fork version of multiple files
via git checkout --theirs conflict resolution. Restore from upstream/main and
re-apply only:
- Agent counts: 64→65 (all 7 catalog-tracked files)
- Skills counts: 261→262 (where needed)
- AGENTS.md: spec-miner routing table + orchestration hint (our additions)
This reverts unintended regressions:
- Version downgrades (2.0.0 → 2.0.0-rc.1) in marketplace.json, plugin.json,
AGENTS.md, docs/zh-CN/AGENTS.md, docs/zh-CN/README.md
- Badge URL changes (api.ecc.tools dynamic → hardcoded) in Chinese READMEs
- Deleted v2.0.0 stable release sections in Chinese READMEs
- Wrong release notes path (2.0.0-rc.1 → 2.0.0) in README.md
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: lege962 <1515808962@qq.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: add Rails 8 application CLAUDE.md example
Adds examples/rails-app-CLAUDE.md as a reference template for Rails 8 applications.
- Add examples/rails-app-CLAUDE.md: full-stack Rails 8 template covering Hotwire (Turbo + Stimulus), ViewComponent, the Solid stack (SolidQueue, SolidCache, SolidCable), service objects, query objects, and Pundit authorization
- Aligns with existing rules/ruby/ conventions (Rails Way first, SolidQueue for greenfield, Hotwire-preferred, Rails 8 generated authentication)
- Includes five Key Patterns code blocks: service object, skinny controller, query object, background job, RSpec test
new file: examples/rails-app-CLAUDE.md
* fix(examples): correct Rails 8 CLAUDE.md examples for auth, transactions, and terminology
- Remove Django `select_related` terminology in favor of direct Rails methods
- Replace `authenticate_user!` (Devise-only) with `require_authentication` (Rails 8 generator default), with inline comment noting Devise as the alternative
- Move `send_notifications` outside the transaction block in the service object example so it only runs after a confirmed commit; safe with both SolidQueue and Sidekiq
- Remove `puts` from the N+1 BAD/GOOD example to align with the Ruby Conventions rule that bans `puts` in committed code
* fix(examples): improve idempotency, notification handling, and job argument guidance
- Wrap send_notifications in its own rescue block so notification failures are logged but do not raise out of the service object, preserving the Result-based error handling pattern
- Update the background job example to show an idempotency_key passed to the external API call, so the example is retry-safe by default rather than relying on a comment to flag the limitation
- Add a Background Jobs rule about pairing local idempotency checks with API-level idempotency tokens and considering with_lock for high-concurrency scenarios
- Soften the absolute "never records" claim for job arguments to explain the real reason (ActiveJob::DeserializationError when records are deleted between enqueue and execute)
* fix(examples): use exported_at.present? to match the column the example writes
The previous `exported?` check assumed a predicate method on the model that this example does not define. Using `exported_at.present?` keeps the guard consistent with the column the next line writes to in `update!(exported_at: Time.current)`.
* feat(rules): add vue and nuxt rule sets
Add rules/vue/ and rules/nuxt/, each with the standard 5-file layout (coding-style, hooks, patterns, security, testing) that extends common/, following the Adding a New Language convention in rules/README.md.
Vue rules reference the frontend-patterns and vite-patterns skills. Nuxt rules reference the nuxt4-patterns and vite-patterns skills. Content is concise (1.5 to 4 KB per file) since rules load as always-on context.
* fix(rules): address PR review on vue and nuxt rule sets
- nuxt/coding-style: generalize the srcDir-override note (drop project-specific 'this repo' phrasing so it is correct for any Nuxt project).
- vue/hooks: add **/*.ts and **/*.tsx to paths so the lint/typecheck guidance loads when editing composables and stores.
- nuxt/hooks: add **/*.vue to paths (covers pages/layouts/components) and wrap nuxi typecheck in a timeout, mirroring web/hooks.md.
- nuxt/security: tighten the /security-review auto-trigger scope to external fetch, credential handling, and sensitive mutations, with examples.
- nuxt/testing: correct 'Vitest-only' to note built-in Playwright E2E, and drop the @nuxt/test-utils version pin.
- README: register vue and nuxt in the structure tree and install examples.
Skipped: 'X specific' -> 'X-specific' hyphenation (all existing rule sets use the unhyphenated form, changing only vue/nuxt would be inconsistent); repeating the 80%/TDD mandate in nuxt/testing (already inherited from common/testing.md).
* docs(skills): document tdd plan handoff evidence
Address issue #2138 by clarifying how tdd-workflow should continue from a plan file, preserve human-readable test guarantees, and retain RED/GREEN evidence across squash merges.
* docs(skills): harden tdd plan handoff guidance
Address review feedback on #2235: use angle-bracket argument hint, treat plan files as untrusted input, and prefer project-local documentation paths for TDD evidence reports.
* docs(skills): clarify plan handoff injection guard
Address review feedback by explicitly stating that plan file content is data, not AI instructions, and that validation commands from untrusted plans require sanitization and approval before execution.
* Update skills/tdd-workflow/SKILL.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* docs(skills): address tdd workflow review nits
Clarify plan handoff safety decisions, remove redundant untrusted-input wording, and show consistent TDD evidence path examples.
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: V-001 security vulnerability
Automated security fix generated by OrbisAI Security
* fix: sanitize subprocess call in runner.py
The runner
* fix: address PR review comments on V-001 allowlist and test coverage
Remove dangerous interpreters (python, python3, node, curl, wget) from
ALLOWED_SETUP_EXECUTABLES — they can execute arbitrary code via argument
flags and are not needed for sandbox setup. Rewrite test_invariant_runner
to call _setup_sandbox directly instead of spawning runner.py as a
subprocess (which had no __main__ entrypoint and never exercised the fix).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Three defense-in-depth fixes around untrusted input flowing to subprocess execution:
1. **Control-pane HTTP server (scripts/lib/control-pane/server.js)**
The local control-pane API binds to 127.0.0.1 but had no Host or Origin
validation, so a DNS-rebinding attack from a malicious website could pivot
into the loopback endpoints — including POST /api/actions/:id, which spawns
'cargo run -- graph ...' with caller-supplied query strings. Add a hostname
allowlist (loopback variants plus the explicitly configured --host) and
reject mismatched Host (421) or non-loopback Origin (403) before any route
handler runs.
2. **OpenCode git-summary tool (.opencode/tools/git-summary.ts)**
The tool was building 'git diff ${baseBranch}...HEAD --stat' with execSync
and a raw model-supplied baseBranch string. Switch run() to execFileSync
with an args array (no shell), validate baseBranch against a conservative
git-ref allowlist (rejects shell metacharacters, leading -, embedded ..),
and clamp the depth arg to a small positive integer before interpolating
into 'git log --oneline -<N>'.
3. **Reusable test workflow (.github/workflows/reusable-test.yml)**
The 'Install dependencies' step interpolated ${{ inputs.package-manager }}
directly into a bash 'case' and into an echo, so a downstream caller that
forwarded attacker-controllable input could inject into the runner. Move
the input into a PACKAGE_MANAGER env var and reference $PACKAGE_MANAGER
inside the script per the GitHub script-injection guidance.
Detected by Aeon + semgrep p/security-audit (host check via threat-model
manual-review axis; git-summary via detect-child-process; workflow via
run-shell-injection).
Verification: node tests/run-all.js — 2686/2687 pre-existing tests pass; the
one failure (observe.sh legacy output fallback) reproduces on main without
this branch applied. Added 2 new control-pane tests covering the allowlist
classifier and the DNS-rebinding-gate behavior end-to-end.
---
Filed by [Aeon](https://github.com/aaronjmars/aeon-aaron).
Co-authored-by: aeonframework <aeon@aaronjmars.com>
* fix: prevent IOC scanner false positives on hook filenames and scan .cursor configs
The supply-chain IOC scanner matched CRITICAL_TEXT_INDICATORS with plain
substring search, so legitimate hook filenames that merely end with a known
payload name (e.g. the stock Cursor hook before-shell-execution.js vs the
payload execution.js) were flagged as CRITICAL. Indicator matching now
requires a non-filename character before the match.
Also add .cursor/ to the special config paths so Cursor hooks.json files
(a known persistence vector already listed in PERSISTENCE_FILENAMES) are
actually inspected in normal checkouts - previously they were only scanned
by accident when the repo path happened to contain /.claude/.
* test: cover underscore-prefixed filenames in IOC boundary suppression
Make explicit that '_' is treated as a filename word character, so
snake_case hook names like post_execution.js are intentionally not
flagged by the execution.js indicator (real payload references appear
after '/', quotes, or whitespace).
runHooks() returned the unmodified raw stdin (the PreToolUse/PostToolUse
input event) on stdout whenever no sub-hook produced additionalContext.
Claude Code parses a hook's stdout as JSON and validates it against the
hook-output schema, so echoing the input object
({session_id, hook_event_name, tool_name, tool_input, ...}) fails with
"Hook JSON output validation failed — (root): Invalid input" on nearly
every Bash command.
Track whether a sub-hook deliberately set stdout (string / {stdout}, e.g.
GateGuard) via a rawModified flag and emit '' in the pass-through case
instead of the echoed input. Preserves GateGuard pass-through and
block-no-verify's exit-2 blocking.
Update the three dispatcher tests that codified the buggy echo behavior to
expect empty stdout, and add a regression test for a plain pass-through
command.
Fixes#2239
Co-authored-by: WOZCODE <contact@withwoz.com>
On Windows both cfg(windows) and cfg(not(unix)) evaluate true, so the sync taskkill kill_process and the async taskkill kill_process both compiled in and collided (E0428). Call sites are synchronous and never await it (passed as a fn pointer to enforce_session_heartbeats_with, and called as kill_process(pid)? in stop_session_recorded), so remove the stray async cfg(not(unix)) definition. The sync cfg(windows) version already handles termination via taskkill /T /F.
* 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>