Commit Graph

2013 Commits

  • fix(project-detect): match packageKeys on boundaries, not substrings (#2181)
    Framework detection matched a dependency against a framework's packageKeys
    with unbounded substring containment (dep.includes(key)), so any dependency
    whose name merely contained a key was misclassified: `preact` and even
    `reactive` were both detected as `react`.
    
    Match only when the dependency equals the key, or the key is a prefix
    immediately followed by a delimiter (/ . _ -). This still matches every real
    case (react-dom, @remix-run/node, spring-boot-starter, org.springframework.boot,
    github.com/labstack/echo/v4, phoenix_live_view) while excluding preact/reactive
    (and incidentally nextra). Adds regression tests.
    
    Co-authored-by: bymle <229636660+bymle@users.noreply.github.com>
  • chore(deps): bump actions/checkout from 6.0.2 to 6.0.3 (#2183)
    Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-version: 6.0.3
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • fix(observer): auto-scale max_turns by analysis batch size (#2062)
    * fix(observer): auto-scale max_turns by analysis batch size (#2035)
    
    The hardcoded default of MAX_TURNS=20 is insufficient when
    MAX_ANALYSIS_LINES=500 (also the default). Claude exhausts its turn
    budget before it can write all discovered instinct files, producing:
    
      Error: Reached max turns (20)
    
    Fix: when ECC_OBSERVER_MAX_TURNS is not explicitly set, compute
    max_turns proportionally to the actual analysis batch size:
      max_turns = clamp(analysis_count / 10, 20, 100)
    
    This gives:
      - 20–199 lines → 20 turns  (existing floor, unchanged)
      - 500 lines    → 50 turns  (resolves the reported failure)
      - 1000 lines   → 100 turns (cap)
    
    Explicitly setting ECC_OBSERVER_MAX_TURNS still overrides the
    auto-scaled value, preserving the existing escape hatch.
    
    * test(observer): update max_turns test for auto-scaling; document validation
    
    The max-turns budget test in tests/hooks/hooks.test.js still asserted the removed literal max_turns="${ECC_OBSERVER_MAX_TURNS:-20}", which would fail against the new auto-scaling logic. Assert the auto-scale formula and the 20/100 clamp bounds instead.
    
    Also add the explanatory comment CodeRabbit requested above the max_turns sanitization block, clarifying it guards the explicit ECC_OBSERVER_MAX_TURNS override path.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
  • fix(.cursor/hooks): route block-no-verify through local hook to fix message-body false positives (#2107) (#2177)
    Cursor hooks still called `npx block-no-verify@1.1.2`, the broken external
    package whose matcher over-matches: it blocks legitimate `git commit`
    whenever `--no-verify` (or `no-verify`) appears anywhere in the command
    string, including inside the commit message body. The Claude Code surface
    already routes through the in-repo `scripts/hooks/block-no-verify.js`,
    which performs flag-position-aware tokenisation and passes 25 regression
    tests covering every false-positive case from #2107.
    
    Add a thin Cursor wrapper (`before-shell-execution-block-no-verify.js`)
    that reads Cursor stdin, transforms to the Claude Code `tool_input.command`
    shape, delegates to the local hook's exported `run()`, and forwards exit
    code and stderr. Update `.cursor/hooks.json` to call the wrapper instead
    of the npx package. New 14-case test file pins the false-positive cases
    from the issue plus the still-blocked real bypass attempts.
    
    Fixes #2107
  • fix(session-start): support ECC_SESSION_RETENTION_DAYS opt-out + document env var (#2151) (#2163)
    * fix(session-start): support ECC_SESSION_RETENTION_DAYS opt-out + document env var
    
    The retention pass for *-session.tmp files (issue #2151) landed previously,
    but the env var that controls it was undocumented in the README and rejected
    falsy values (0, off, disabled), silently falling back to the 30-day default.
    Users who want to keep all sessions for forensic or research workflows had no
    way to opt out.
    
    This patch:
    
    - Extends getSessionRetentionDays() so 0|off|false|disabled|never|none disables
      pruning entirely (returns null sentinel; default behavior unchanged).
    - Updates the call site in main() to skip pruneExpiredSessions when retention
      is null and emits a clear "[SessionStart] Pruning disabled via
      ECC_SESSION_RETENTION_DAYS" log line so the operator can tell pruning is off.
    - Documents ECC_SESSION_RETENTION_DAYS in the README "Hook Runtime Controls"
      section alongside the other ECC_SESSION_* knobs.
    - Adds three regression tests in tests/hooks/hooks.test.js covering opt-out
      via 0, opt-out via off, and garbage-value fallback to default 30.
    
    Verification:
    - node tests/hooks/hooks.test.js  — 240/240 green (incl. 3 new retention tests)
    - node tests/run-all.js           — 2622/2622 green
    - npx eslint scripts/hooks/session-start.js tests/hooks/hooks.test.js — clean
    - node scripts/ci/validate-no-personal-paths.js — clean
    - node scripts/ci/check-unicode-safety.js       — clean
    - node scripts/ci/validate-hooks.js — 28 matchers validated
    - node scripts/ci/validate-rules.js — 115 files validated
    
    Fixes #2151
    
    * docs(readme): list all ECC_SESSION_RETENTION_DAYS opt-out values + add Windows example
    
    Address reviewer feedback on PR #2163:
    - CodeRabbit and cubic both flagged that the README docs only listed 3 of 6
      opt-out values accepted by getSessionRetentionDays() (0, off, disabled),
      while the implementation also accepts false, never, none.
    - cubic also flagged the missing Windows PowerShell example for the new
      variable, breaking the parallel structure of the existing
      ECC_CONTEXT_MONITOR_COST_WARNINGS example block.
    
    Updated the README to:
    - Spell out all six opt-out values (0, off, false, disabled, never, none)
      and clarify they "keep all sessions (disable pruning)".
    - Add an ECC_SESSION_RETENTION_DAYS line to the Windows PowerShell example.
    
    No behavior change. README only.
    
    Verification:
    - npx markdownlint README.md — clean
    - npx eslint scripts/hooks/session-start.js tests/hooks/hooks.test.js — clean
  • feat(gateguard): add env knobs for routine bash gate + extra destructive patterns (#2161)
    * feat(gateguard): add env knobs for routine bash gate + extra destructive patterns
    
    The JS port of gateguard-fact-force has two bash gates: a destructive
    gate (rm -rf, drop table, git push --force, etc.) that operators want
    to keep, and a once-per-session routine gate that fires on the very
    first bash invocation regardless of intent. Operators on hosts where
    the routine gate is friction without signal (Cursor, OpenCode, etc.)
    have been maintaining local patches that get clobbered on every plugin
    update; the Python upstream gateguard-ai already exposes equivalent
    config via .gateguard.yml.
    
    Adds two env vars, both off-by-default so existing behavior is
    preserved:
    
    - GATEGUARD_BASH_ROUTINE_DISABLED — truthy values (1, true, on, yes,
      enabled) skip the routine bash gate. Destructive gate is unaffected.
    - GATEGUARD_BASH_EXTRA_DESTRUCTIVE — regex source string for additional
      destructive patterns. Matches against the same quote-stripped,
      subshell-flattened command the built-in DESTRUCTIVE_SQL_DD regex sees,
      so a custom phrase inside $(...) or backticks is also caught. A
      malformed regex is logged once to stderr and treated as not configured
      rather than crashing the hook (hooks must never block tool execution
      unexpectedly).
    
    Twelve new tests pin both env vars (truthy aliases, falsy values, unset
    baseline, destructive-gate-still-fires, alternation members, malformed
    regex degrades safely, custom phrase inside command substitution).
    Existing 2619/2619 tests still pass; eslint clean.
    
    Fixes #2078
    
    * fix(gateguard): reset extra-destructive warn-once gate when env value changes
    
    Both reviewers (CodeRabbit + cubic) flagged that
    extraDestructiveWarnLogged was never reset when GATEGUARD_BASH_EXTRA_DESTRUCTIVE
    flipped from one invalid regex to a different invalid regex. The
    sticky boolean meant a long-running process saw bad-pattern-a's
    warning then silently swallowed bad-pattern-b's parse failure.
    
    Fix: clear extraDestructiveWarnLogged whenever the cache key changes
    (i.e. before the regex compile attempt). The warn-once-per-distinct-
    pattern invariant now matches the per-key cache invariant.
    
    Adds a same-process regression test via loadDirectHook() that spies on
    process.stderr.write and asserts: same bad pattern warns once across
    multiple invocations; switching to a different bad pattern emits a
    second warning; switching to a valid regex emits zero warnings.
  • fix(suggest-compact): clean up old counter temp files (#2159)
    * fix(suggest-compact): clean up old counter temp files
    
    claude-tool-count-<sessionId> files were written into the OS temp dir
    on every hook run and never removed, accumulating one orphan per
    session indefinitely.
    
    Sweep stale counter files at the top of main() before opening the
    active counter. Retention is env-tunable via COMPACT_STATE_TTL_DAYS
    (default 14 days); invalid values fall back to the default. The
    active session's counter file is preserved unconditionally even if
    its mtime is past the cutoff. Failures during the sweep are swallowed
    to preserve the always-exit-0 hook contract.
    
    Adds 7 regression tests covering the sweep, env-var validation, and
    the always-exit-0 invariant under a populated temp dir.
    
    Fixes #2156
    
    * fix(suggest-compact): preserve counter files at the TTL cutoff boundary
    
    The cleanup sweep used `mtimeMs > cutoffMs` to short-circuit, which
    matched files whose mtime sits exactly on the cutoff boundary and
    deleted them. The cleanupOldCounters docstring promises only files
    *older than* retentionDays are removed; a file at age == retentionDays
    is not older than retentionDays, so it must survive.
    
    Switch the comparison to `>=` so only strictly older files fall
    through to deletion. Add a regression test that pins boundary-aged
    files (mtimeMs sitting just past the projected cutoff) are preserved.
    
    Refs #2156
  • fix(continuous-learning-v2): accept claude-vscode as valid entrypoint (#2134)
    The observe.sh Layer 1 entrypoint guard short-circuits with exit 0 when
    CLAUDE_CODE_ENTRYPOINT is not in {cli, sdk-ts, claude-desktop}. Claude
    Code's VS Code extension sets CLAUDE_CODE_ENTRYPOINT=claude-vscode, so
    VS Code users see no observations recorded — observations.jsonl never
    gets created and the instinct pipeline stays empty.
    
    Add claude-vscode to the allowlist, mirroring the precedent in #1522
    which added claude-desktop the same way.
    
    Add a regression test that spawns observe.sh under bash -x for each
    allowed entrypoint (cli, sdk-ts, claude-desktop, claude-vscode) and
    each denied entrypoint (unknown-host, claude-cody, mcp), asserting
    that allowed entrypoints reach Layer 2's ECC_HOOK_PROFILE check while
    denied entrypoints stop at Layer 1.
    
    Fixes #2102
  • fix: guard two script edge cases (tolerant package.json parse, set -u empty array) (#2088)
    * fix: guard two script edge cases
    
    - scripts/harness-audit.js: getRepoChecks() parsed package.json with raw
      JSON.parse(readText(...)), while the rest of the file (lines 218, 822)
      uses the tolerant safeParseJson(safeRead(...)). In repo target mode a
      project lacking package.json — or with malformed JSON — threw an uncaught
      exception and crashed the audit instead of degrading. Match the existing
      convention so the audit tolerates a missing/invalid package.json.
    
    - skills/frontend-slides/scripts/export-pdf.sh: `set -- "${POSITIONAL[@]}"`
      expands an empty array under `set -u` on bash 3.2 (the macOS system bash),
      aborting with "POSITIONAL[@]: unbound variable" instead of printing the
      usage message when invoked with no positional args. Guard the expansion
      with ${POSITIONAL[@]+"${POSITIONAL[@]}"} (no-op safe under bash 3.2 set -u).
    
    Generated with [Claude Code](https://claude.ai/code)
    via [Happy](https://happy.engineering)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
    Co-Authored-By: Happy <yesreply@happy.engineering>
    
    * fix: null-safe package.json access in getRepoChecks
    
    Review follow-up (CodeRabbit + cubic): switching to safeParseJson at line
    389 means packageJson can be null on a missing/malformed package.json, but
    the quality-ci-validations check dereferenced packageJson.scripts before the
    optional chaining could help — throwing TypeError instead of degrading.
    Guard the base object with packageJson?.scripts?.test at the access site,
    matching the file's existing convention (e.g. line 220 uses packageJson?.name).
    
    Generated with [Claude Code](https://claude.ai/code)
    via [Happy](https://happy.engineering)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
    Co-Authored-By: Happy <yesreply@happy.engineering>
    
    ---------
    
    Co-authored-by: Claude <noreply@anthropic.com>
    Co-authored-by: Happy <yesreply@happy.engineering>
  • feat: worktree-lifecycle service (deterministic conflict prediction + safe GC) (#2164)
    * feat: add worktree-lifecycle service (ecc.worktree-lifecycle.v1)
    
    The "unowned moat" from the orchestrator landscape research: no existing
    tool ships deterministic merge-conflict prediction or a safe worktree GC.
    
    - scripts/lib/worktree-lifecycle/git.js: injectable, hermetic git layer.
      Predicts merge conflicts WITHOUT touching the working tree via
      `git merge-tree`. Strips inherited GIT_* env so it is safe inside hooks.
    - scripts/lib/worktree-lifecycle/lifecycle.js: deterministic state machine
      (main/dirty/conflict/merge-ready/merged/stale/idle) + planCleanup that
      buckets worktrees into remove / salvage / keep. Only fully-merged trees
      are auto-removable; stale (unmerged+inactive) => salvage, never deleted.
    - scripts/worktree-lifecycle.js: CLI (--json/--conflicts/--stale/
      --cleanup-plan/--base/--stale-days/--repo).
    - tests/lib/worktree-lifecycle.test.js: 11 tests (fake-git + real-git).
    
    Safety model mirrors the reference-arch salvage rule, validated by the
    2026-06-05 MacBook->Mac Mini consolidation. Tests: 11/0.
    
    * fix: hermetic git env in session adapters + mcp-inventory lint
    
    - session adapters (codex-worktree, opencode): resolveGitBranch stripped
      no git env, so the "outside a repo" path returned the host branch when
      run inside a git hook (GIT_DIR set). Strip GIT_* before rev-parse.
    - mcp-inventory: fix eslint no-unused-vars (signatures) and a stale
      eslint-disable directive in the merged code.
    
    * test: run each test with inherited git env stripped (hermetic runner)
    
    When the suite runs inside a git hook (pre-push), git sets GIT_DIR/
    GIT_WORK_TREE, which hijack 'git -C <dir>' calls in tests that exercise
    real git, making them operate on the host repo. Strip GIT_* before
    spawning each test so the suite is isolated from ambient git state.
    
    ---------
    
    Co-authored-by: ECC Test <ecc@example.test>
  • feat: MCP inventory (ecc.mcp.v1) — unified cross-harness MCP config view (#2146)
    * feat: add MCP inventory (ecc.mcp.v1) across harnesses
    
    Read-only MCP-gateway groundwork: discover MCP server configs across
    every installed harness, normalize to a canonical ecc.mcp.v1 inventory,
    redact secrets, and report which servers are configured in 2+ harnesses
    (the configure-N-times pain). The read+dedup side of a unified gateway,
    mirroring how the session-adapter layer started read-only.
    
    Readers (per-harness config formats):
    - claude-code: ~/.claude.json mcpServers + project .mcp.json
    - codex: ~/.codex/config.toml [mcp_servers.*] TOML via @iarna/toml
    - opencode: ~/.config/opencode/opencode.json mcp block (command ARRAY)
    
    canonical-mcp.js:
    - normalize transport labels (local=>stdio, remote=>http) to stdio/http/sse
    - merge servers by name across harnesses; flag DRIFT when signatures differ
    - fragmentation report + aggregates
    - SECRET REDACTION: env values stripped to key names; secrets in args
      (--modelApiKey sk-ant-...), inline --flag=secret, and URL userinfo/token
      query params all redacted before storage AND before the dedup signature.
    
    scripts/mcp-inventory.js: CLI (--json, --fragmented, --help).
    tests/lib/mcp-inventory.test.js: 12 tests incl. a regression for the
    real arg-carried-secret leak found while smoke-testing on live configs.
    
    Tests: 12/0. Real-data smoke: 33 servers across 3 harnesses, 21
    configured in 2+ harnesses (7 drift); secret-leak audit clean.
    
    * test: cover reader error paths, collect skip-logic, and CLI main() for mcp-inventory
    
    Lift global branch coverage past the 80% gate (was 79.86%). Adds 6
    tests exercising: missing-file/malformed-JSON/missing-block reader
    fallbacks, codex no-parser path, collect skipping non-function readers
    and swallowing reader errors, CLI usage()/main() help+json+human paths,
    and formatHumanReport no-fragmentation + fragmented-only branches.
    
    Also scrub a real API-key fragment that had leaked into a test fixture;
    all secret-like fixtures are now obviously-fake FAKE... tokens.
    
    mcp-inventory.js branch 30%->93%, collect.js ->100%. Global branch 80.33%.
  • feat: extend session-adapter layer with codex-worktree + opencode adapters (#2145)
    * feat: add codex-worktree session adapter
    
    Adds the third session adapter (after dmux-tmux and claude-history),
    normalizing Codex rollout sessions into the harness-neutral
    ecc.session.v1 snapshot. Reads ~/.codex/sessions rollout JSONL,
    derives objective (skipping the AGENTS.md preamble + leading message
    UUID), model, originator, worktree cwd, and best-effort git branch.
    
    This is step 1 of ECC-2.0-SESSION-ADAPTER-DISCOVERY (move the
    abstraction beyond tmux + Claude-history) and supports the
    wrap/adapt control-pane strategy: ECC reads sessions from any
    harness rather than owning one UX.
    
    - scripts/lib/session-adapters/codex-worktree.js: adapter + rollout parser
    - canonical-session.js: normalizeCodexWorktreeSession
    - registry.js: register adapter, codex/codex-worktree target types
    - tests/lib/session-adapters-codex.test.js: 4 tests (unit + registry routing)
    
    * feat: add opencode session adapter + allow empty intent objective
    
    Adds the fourth session adapter (after dmux-tmux, claude-history,
    codex-worktree), normalizing OpenCode sessions into ecc.session.v1.
    
    Reads ~/.local/share/opencode/storage: session/<project>/ses_*.json
    for metadata (id, directory, title, version, projectID, time) and
    message/<session>/msg_*.json to extract the model (modelID/providerID
    from the first assistant message). Derives objective from the session
    title, treating the auto-generated "New session - <date>" title as no
    objective. Recency-based active/recorded state.
    
    Schema: relax intent.objective from non-empty to allow empty string
    (ensureStringAllowEmpty). Sessions legitimately have no objective yet
    (fresh/auto-titled), and claude-history already emitted "" via
    metadata.title fallback. This fixes a latent over-strict validation.
    
    - scripts/lib/session-adapters/opencode.js: adapter + storage parser
    - canonical-session.js: normalizeOpencodeSession + ensureStringAllowEmpty
    - registry.js: register adapter + opencode target type
    - tests/lib/session-adapters-opencode.test.js: 5 tests
    
    Tests: opencode 5/0, codex 4/0, session-adapters 14/0,
    control-pane-state 10/0, session-inspect 8/0, control-pane 12/0.
    Smoke-tested on a real OpenCode session (140 messages, gpt-5.3-codex).
    
    * test: cover error/fallback branches for codex-worktree + opencode adapters
    
    Lift global branch coverage past the 80% gate (was 79.53%). Adds error
    and fallback path tests: missing-session/unknown-id throws, findRolloutById/
    findSessionInfoById, direct file targets, objective truncation, model
    fallbacks, corrupt-line skip, mtime activity fallback, and the real
    resolveGitBranch path outside a repo.
    
    codex-worktree.js branch 52.8%->78.3%; global branch 80.04%.
  • feat: add dynamic workflow team orchestration surface
    Adds dynamic workflow/team orchestration skills, the content pack, and control-pane work-item/Kanban state DB support. Includes reviewer hardening for state-db CLI validation, optional state DB failure handling, and mergeStateStatus projection.
  • feat: add ECC2 local control pane (#2131)
    * feat: add ECC2 local control pane
    
    * fix: refresh control pane package locks
    
    * test: harden control pane coverage
    
    * test: allow portable control pane shutdown
    
    * test: retry local control pane fetches
    
    * fix: harden control pane error handling
    
    * fix: wrap control pane metadata
  • Add React language track with agents, skills, rules, and commands (#2024)
    * feat(rules): add rules/react/ track
    
    Five rule files mirroring per-language convention (coding-style,
    hooks, patterns, security, testing). Each has `paths:` glob
    frontmatter for auto-activation when editing matching files.
    
    - coding-style.md: file extensions, naming, JSX, RSC boundary
    - hooks.md: React hooks (NOT Claude Code hooks) — rules-of-hooks,
      dep arrays, cleanup, memoization, React 19 additions
    - patterns.md: container/presentational split, state location
      decision tree, Suspense + error boundaries, forms, data fetching
    - security.md: dangerouslySetInnerHTML, unsafe URL schemes,
      server-action validation, env-var leaks, CSP
    - testing.md: RTL queries, userEvent, async, MSW, axe, anti-patterns
    
    Each file extends typescript/* and common/* rules.
    
    * feat(skills): add react-patterns, react-testing, react-performance
    
    Three new skills under skills/ following the SKILL.md convention.
    
    - react-patterns: React 18/19 idioms — hooks discipline, state
      location decision tree, server/client component boundary,
      Suspense + error boundaries, form actions (React 19), data
      fetching matrix, composition recipes, accessibility-first.
    - react-testing: React Testing Library + Vitest/Jest, query
      priority order, userEvent, MSW network mocking, axe a11y
      assertions, RTL vs Playwright CT boundary, TDD workflow.
    - react-performance: 70-rule performance ruleset adapted from
      Vercel Labs react-best-practices (MIT) across 8 priority
      categories — waterfalls, bundle size, server-side, client
      fetch, re-render, rendering, JS micro, advanced patterns.
      Includes Lighthouse / Web Vitals mapping and attribution to
      upstream.
    
    Cross-links between the three skills and out to frontend-patterns,
    accessibility, e2e-testing, tdd-workflow.
    
    * feat(agents): add react-reviewer and react-build-resolver
    
    Two new agents covering React-specific code review and build error
    resolution, plus matching .kiro/ mirrors and a routing pointer
    edit on typescript-reviewer.
    
    - react-reviewer: slim React-only lanes (hooks rules,
      dangerouslySetInnerHTML, unsafe URL schemes, key prop, state
      mutation, derived-state-in-effect, server/client component
      boundary, accessibility, render performance, Server Action
      validation, env-var leaks). Explicitly delegates generic
      TypeScript/async/Node concerns to typescript-reviewer. Both
      agents should be invoked together on .tsx/.jsx PRs.
    - react-build-resolver: React build/bundler/runtime hydration
      failures across Vite, webpack, Next.js, CRA, Parcel, esbuild,
      Bun, Rsbuild. Handles JSX/TSX compile errors, tsconfig fixes,
      Next.js App Router server/client boundary errors, hydration
      mismatches, duplicated React copies, Tailwind/PostCSS pipeline.
    - .kiro/agents/react-reviewer.json + react-build-resolver.json:
      Kiro IDE format mirrors following the per-language precedent.
    - typescript-reviewer: routing pointer added to its MEDIUM React
      block — defers to /react-review for React-specific concerns
      while keeping its block as fallback for repos that only invoke
      typescript-reviewer.
    
    All agents carry the standard Prompt Defense Baseline stanza.
    
    * feat(commands): add /react-review /react-build /react-test
    
    Three new slash commands invoking the React agents.
    
    - /react-review: invokes react-reviewer. Documents the routing
      rule with typescript-reviewer — both should run together on
      TSX/JSX PRs. Lists CRITICAL/HIGH/MEDIUM rule categories and
      the automated checks (eslint with react-hooks + jsx-a11y,
      tsc --noEmit, npm audit).
    - /react-build: invokes react-build-resolver. Documents bundler
      detection, common failure patterns, fix strategy, and stop
      conditions.
    - /react-test: enforces TDD with React Testing Library + Vitest
      or Jest, behavior-focused queries, userEvent + MSW patterns,
      axe accessibility assertions, coverage targets.
    
    Each command file has the required description: frontmatter and
    follows the per-language command convention (cpp-test, go-test,
    kotlin-test, etc.).
    
    * chore: wire react track into manifests and stack mappings
    
    - agent.yaml: add react-patterns, react-performance, react-testing
      to the skills array; add react-build, react-review, react-test to
      the commands array (alphabetically inserted to satisfy the
      ci/agent-yaml-surface sync test).
    - config/project-stack-mappings.json: extend the `react` stack
      entry — add "react" to rules array (was ["common","typescript",
      "web"]); add react-patterns, react-performance, react-testing,
      accessibility to the skills array.
    - docs/COMMAND-REGISTRY.json: bump totalCommands 75 -> 78; add
      three new entries (react-build, react-review, react-test) with
      primaryAgents / allAgents / skills wiring. react-review's
      allAgents includes typescript-reviewer to reflect the dual-agent
      routing convention.
    - CLAUDE.md: add Skills-table row mapping *.tsx / *.jsx /
      components/** to react-patterns + react-testing skills and
      the /react-review, /react-build, /react-test commands.
    
    * chore(catalog): sync counts to 62 agents / 78 commands / 235 skills
    
    Auto-generated via `node scripts/ci/catalog.js --write --text`
    after the react track additions:
    
    - 2 new agents: react-reviewer, react-build-resolver (60 -> 62)
    - 3 new commands: react-build, react-review, react-test (75 -> 78)
    - 3 new skills: react-patterns, react-performance, react-testing
      (232 -> 235)
    
    Files updated by the catalog sync:
    - .claude-plugin/plugin.json description string
    - .claude-plugin/marketplace.json plugin description
    - README.md quick-start summary, project tree, feature parity tables
    - README.zh-CN.md quick-start summary
    - AGENTS.md project structure summary
    - docs/zh-CN/README.md parity table
    - docs/zh-CN/AGENTS.md project structure summary
    
    All counts now match the filesystem catalog (verified by
    ci/catalog.test.js).
    
    * feat(kiro): add react agent markdown companions to JSON entries
    
    * feat(kiro): add react skills into manifests
    
    * fix(ci): sync catalog counts, registry, and package files for react track
    
    - .claude-plugin/{plugin,marketplace}.json: bump description counts to 62/235/78
    - docs/COMMAND-REGISTRY.json: regenerate to include quality-gate and react commands
    - package.json: add skills/react-{patterns,performance,testing}/ to files allowlist so npm-publish-surface aligns with install-modules manifest
    
    * fix(react): address PR #2024 review feedback
    
    Critical:
    - Remove undefined/.claude/session-aliases.json containing __proto__ prototype-pollution
      fixture committed by accident in a7333c14
    
    High:
    - agents/react-build-resolver.md: replace brittle `test -o $(grep -l ...)` and
      `test -a -n $(grep ...)` detection with explicit `{ ... || grep -q ...; }` so
      bundler detection no longer breaks when grep returns empty
    - agents/react-build-resolver.md: drop hardcoded `npm i react@^19 react-dom@^19`
      remediation; replace with version-agnostic pair-upgrade note that honors the
      project's installed major (17/18/19) — surgical fix principle
    - commands/react-review.md: guard `tsc --noEmit -p tsconfig.json` with
      `[ -f tsconfig.json ] &&` so the review skips cleanly on JS-only projects
    
    Medium:
    - rules/react/security.md: correct the React-18-blocks-javascript-URL claim
      (React only warns in dev; production navigation is not blocked)
    - rules/react/security.md: correct CRA env-var exposure row (CRA exposes
      REACT_APP_*, NODE_ENV, PUBLIC_URL — not 'all' variables)
    - skills/react-testing/SKILL.md: instantiate QueryClient once outside the
      wrapper closure so React Query cache survives re-renders (flaky-test fix)
    - skills/react-testing/SKILL.md: restore console.error spy with mockRestore()
      in a try/finally so the mock does not leak across tests
    - commands/react-test.md: switch outer example-session fence to 4 backticks
      so the inner ```tsx/```bash blocks don't prematurely terminate it
    
    * fix(kiro): mirror react-build-resolver react 19 conditional remediation
    
    Discussion r3272907106 flagged the kiro json variant still carrying the hardcoded
    'npm i react@^19 react-dom@^19' line that the .md companion already dropped.
    Replace with the same conditional, version-agnostic guidance so both variants
    stay in sync.
    
    * fix(react): bump react-build example session fence to 4 backticks
    
    Discussion r3272907144 flagged the same nested-fence issue in
    commands/react-build.md that we fixed earlier in commands/react-test.md.
    The outer triple-backtick text block was being prematurely terminated by
    the inner bash/tsx fences inside the Example Session.
    
    * fix(react): bump react-review example usage fence to 4 backticks
    
    Discussion r3272907201 flagged the same nested-fence issue in
    commands/react-review.md. The outer triple-backtick text block was
    being prematurely terminated by the inner tsx/ts fences inside the
    Example Usage transcript.
    
    * fix(docs): clarify commands row as legacy shims in feature parity table
    
    Discussion r3272912003: README comparison table said 'PASS: 78 commands'
    while the install-section and quick-start prose use 'legacy command shims'.
    Aligned the comparison-table cell to 'PASS: 78 commands (legacy shims)' so
    the count word survives the catalog-validator regex while making the legacy
    nature explicit.
    
    Widened the catalog comparison-table commands regex to tolerate an optional
    parenthetical after the count word, so both the existing 'X commands' and
    the new 'X commands (legacy shims)' phrasings validate without breaking
    older READMEs/translations.
    
    * Update rules/react/security.md
    
    Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
    
    * fix(react): guard tsc in react-build-resolver diagnostic commands
    
    Discussion r3288910205: the agent prompt instructed an unconditional
    'tsc --noEmit -p tsconfig.json', which adds noise (or hard-fails) on
    JavaScript-only projects with no tsconfig.json or no installed TypeScript.
    
    Replaced with 'test -f tsconfig.json && npx --yes tsc --noEmit -p tsconfig.json'
    in both variants:
    - agents/react-build-resolver.md
    - .kiro/agents/react-build-resolver.json (prompt string mirrored)
    
    Mirrors the same guard already applied to commands/react-review.md in de135f61.
    
    * fix(react): pin tsc resolution to local install in build resolver
    
    Discussion r3289054157: previous fix used 'npx --yes tsc' which auto-installs
    the latest TypeScript from npm when none is local, producing version drift
    and non-reproducible typecheck results across machines.
    
    Switched to 'npx --no-install tsc' in both variants so the diagnostic uses
    only the project's pinned TypeScript and fails fast if it isn't installed:
    - agents/react-build-resolver.md
    - .kiro/agents/react-build-resolver.json (prompt string mirrored)
    
    * feat(counts): resolve counts for agents, skills...
    
    * fix(ci): regen command registry for golang-testing entry
    
    Removes stale kotlin-patterns entry to satisfy command-registry:check.
    
    * fix: keep local Claude settings out of React track PR
    
    ---------
    
    Co-authored-by: AlexisLeDain <a.ledain@docoon.com>
    Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
    Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
  • docs(i18n): add German localization scout (#2029)
    Adds de-DE docs, installer wiring, and locale tests. Pre-validated on current main with install manifest checks, markdownlint, locale-install tests, and ECC 2.0 release-surface tests.
  • feat(integrations): add opt-in AURA trust adapter (#2026)
    Adds a read-only opt-in AURA trust-check adapter. Synthetic merge passed validators and 22 AURA offline tests via uvx pytest.
  • feat(mcp): add Squish Memory server catalog entry (#2039)
    Adds Squish Memory to the MCP server catalog. Pre-validated in a synthetic current-main merge.
  • feat: add marketing campaign agent skill and command (#2031)
    Adds marketing-agent, marketing-campaign skill, and marketing-campaign command. Pre-validated in a synthetic current-main merge.
  • feat(skills): add social publisher skill (#2052)
    Adds a SocialClaw-backed social-publisher skill. Pre-validated in a synthetic current-main merge.
  • feat(skills): add frontend accessibility skill (#2048)
    Adds the frontend-a11y skill for React and Next.js accessibility patterns. Pre-validated against current main with validate-skills and markdownlint.
  • fix(hooks): prefer fresh harness cost cache (#2054)
    Uses a fresh harness cost cache when available and keeps transcript pricing as the fallback. Focused cost-tracker tests passed locally before merge.
  • fix(install-targets): validate compiled OpenCode plugin before install (#2041)
    Fail fast when the OpenCode home install is attempted from a source checkout without the compiled .opencode/dist payload. PR had the full CI matrix green.
  • fix(nextjs-turbopack): document proxy.ts middleware filename (#2033)
    Documents proxy.ts as the current Next.js/Turbopack middleware filename.
  • docs(security-guide): add LLM Safe Haven reference (#2034)
    Adds LLM Safe Haven to the security guide references.
  • fix(docs): update Chinese marketplace URL (#2050)
    Updates the zh-CN README marketplace URL to match the ECC repo rename.
  • chore(deps): bump actions/stale to 10.3.0 (#2045)
    Bumps actions/stale from 10.2.0 to 10.3.0. Verified by the full CI matrix on the PR.
  • Sync Marketplace Pro readback release gate (#2019)
    * docs: sync marketplace pro readback gate
    
    * docs: refresh operator dashboard after readback sync
    
    * docs: sanitize marketplace readback summary
    
    * docs: refresh operator dashboard after marketplace readback