diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b19c87b8d..d5385a381 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "ecc", "source": "./", - "description": "Harness-native ECC operator layer - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", + "description": "Harness-native ECC operator layer - 68 agents, 293 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", "version": "2.2.2", "author": { "name": "Affaan Mustafa", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 072edddfe..8871e37d2 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ecc", "version": "2.2.2", - "description": "Harness-native ECC plugin for engineering teams - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", + "description": "Harness-native ECC plugin for engineering teams - 68 agents, 293 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/.gemini/GEMINI.md b/.gemini/GEMINI.md index 7b9b2d670..d4984a2b1 100644 --- a/.gemini/GEMINI.md +++ b/.gemini/GEMINI.md @@ -4,7 +4,7 @@ This file provides Gemini CLI with the baseline ECC workflow, review standards, ## Overview -Everything Claude Code (ECC) is a cross-harness coding system with 36 specialized agents, 142 skills, and 68 commands. +Everything Claude Code (ECC) is a cross-harness coding system with 68 specialized agents, 293 skills, and 94 commands. Gemini support is currently focused on a strong project-local instruction layer via `.gemini/GEMINI.md`, plus the shared MCP catalog and package-manager setup assets shipped by the installer. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bdad0d483..b23a2862e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,8 @@ on: tags: ['v*'] permissions: + actions: read + checks: read contents: read jobs: @@ -12,6 +14,8 @@ jobs: name: Verify Release runs-on: ubuntu-latest outputs: + release_sha: ${{ steps.release_gate.outputs.release_sha }} + tag_object_sha: ${{ steps.release_gate.outputs.tag_object_sha }} already_published: ${{ steps.npm_publish_state.outputs.already_published }} dist_tag: ${{ steps.npm_publish_state.outputs.dist_tag }} publish_tag: ${{ steps.npm_publish_state.outputs.publish_tag }} @@ -43,6 +47,13 @@ jobs: node-version: '20.x' registry-url: 'https://registry.npmjs.org' + - name: Verify signed tag and exact-SHA CI gates + id: release_gate + env: + GITHUB_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.ref_name }} + run: RELEASE_SHA="$(git rev-parse HEAD)" node scripts/ci/verify-release-gates.js + - name: Install dependencies run: npm ci --ignore-scripts @@ -194,6 +205,31 @@ jobs: ECC_RELEASE_SHA256: ${{ needs.verify.outputs.package_sha256 }} run: node -e "const crypto = require('crypto'); const fs = require('fs'); const file = process.env.ECC_RELEASE_PACKAGE; const expected = process.env.ECC_RELEASE_SHA256; if (!/^ecc-universal-[0-9A-Za-z.+-]+\.tgz$/.test(file || '')) throw new Error('Unexpected packed filename'); if (!/^[a-f0-9]{64}$/.test(expected || '')) throw new Error('Invalid packed SHA-256'); const archives = fs.readdirSync('.').filter(name => name.endsWith('.tgz')); if (archives.length !== 1 || archives[0] !== file) throw new Error('Expected exactly one downloaded archive'); const actual = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'); if (actual !== expected) throw new Error('Downloaded publish artifact SHA-256 mismatch')" + - name: Bind gate source to triggering commit + env: + EVENT_SHA: ${{ github.sha }} + VERIFIED_RELEASE_SHA: ${{ needs.verify.outputs.release_sha }} + run: node -e "const actual = process.env.EVENT_SHA; const expected = process.env.VERIFIED_RELEASE_SHA; if (typeof actual !== 'string' || actual.length !== 40 || !/^[a-f0-9]{40}$/.test(actual) || expected !== actual) throw new Error('Verified release differs from triggering commit')" + + - name: Checkout verified gate source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + path: release-gate-source + persist-credentials: false + sparse-checkout: scripts/ci/verify-release-gates.js + sparse-checkout-cone-mode: false + + # This read-only API check uses the existing publish job token. It is a + # snapshot; preventing subsequent tag movement requires protected tags. + - name: Recheck verified tag before publish + env: + GITHUB_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.ref_name }} + RELEASE_SHA: ${{ needs.verify.outputs.release_sha }} + RELEASE_TAG_OBJECT_SHA: ${{ needs.verify.outputs.tag_object_sha }} + run: node release-gate-source/scripts/ci/verify-release-gates.js --tag-only + - name: Publish npm package if: needs.verify.outputs.already_published != 'true' env: diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index b038b1b8c..74ab4c003 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -18,6 +18,8 @@ on: type: string permissions: + actions: read + checks: read contents: read jobs: @@ -25,6 +27,8 @@ jobs: name: Verify Release runs-on: ubuntu-latest outputs: + release_sha: ${{ steps.release_gate.outputs.release_sha }} + tag_object_sha: ${{ steps.release_gate.outputs.tag_object_sha }} already_published: ${{ steps.npm_publish_state.outputs.already_published }} dist_tag: ${{ steps.npm_publish_state.outputs.dist_tag }} publish_tag: ${{ steps.npm_publish_state.outputs.publish_tag }} @@ -57,6 +61,13 @@ jobs: node-version: '20.x' registry-url: 'https://registry.npmjs.org' + - name: Verify signed tag and exact-SHA CI gates + id: release_gate + env: + GITHUB_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ inputs.tag }} + run: RELEASE_SHA="$(git rev-parse HEAD)" node scripts/ci/verify-release-gates.js + - name: Install dependencies run: npm ci --ignore-scripts @@ -208,6 +219,25 @@ jobs: ECC_RELEASE_SHA256: ${{ needs.verify.outputs.package_sha256 }} run: node -e "const crypto = require('crypto'); const fs = require('fs'); const file = process.env.ECC_RELEASE_PACKAGE; const expected = process.env.ECC_RELEASE_SHA256; if (!/^ecc-universal-[0-9A-Za-z.+-]+\.tgz$/.test(file || '')) throw new Error('Unexpected packed filename'); if (!/^[a-f0-9]{64}$/.test(expected || '')) throw new Error('Invalid packed SHA-256'); const archives = fs.readdirSync('.').filter(name => name.endsWith('.tgz')); if (archives.length !== 1 || archives[0] !== file) throw new Error('Expected exactly one downloaded archive'); const actual = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'); if (actual !== expected) throw new Error('Downloaded publish artifact SHA-256 mismatch')" + - name: Checkout verified gate source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.verify.outputs.release_sha }} + path: release-gate-source + persist-credentials: false + sparse-checkout: scripts/ci/verify-release-gates.js + sparse-checkout-cone-mode: false + + # This read-only API check uses the existing publish job token. It is a + # snapshot; preventing subsequent tag movement requires protected tags. + - name: Recheck verified tag before publish + env: + GITHUB_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ inputs.tag }} + RELEASE_SHA: ${{ needs.verify.outputs.release_sha }} + RELEASE_TAG_OBJECT_SHA: ${{ needs.verify.outputs.tag_object_sha }} + run: node release-gate-source/scripts/ci/verify-release-gates.js --tag-only + - name: Publish npm package if: needs.verify.outputs.already_published != 'true' env: diff --git a/.mcp.json b/.mcp.json index 045baea18..9860a4dbe 100644 --- a/.mcp.json +++ b/.mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "chrome-devtools": { "command": "npx", - "args": ["-y", "chrome-devtools-mcp@latest"] + "args": ["-y", "chrome-devtools-mcp@1.10.1"] } } } diff --git a/AGENTS.md b/AGENTS.md index 17330b848..2107af90a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 68 specialized agents, 292 skills, 94 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 68 specialized agents, 293 skills, 94 commands, and automated hook workflows for software development. **Version:** 2.2.2 @@ -48,6 +48,42 @@ This is a **production-ready AI coding plugin** providing 68 specialized agents, | mle-reviewer | Production ML pipeline review | ML pipelines, evals, serving, monitoring, rollback | | rag-pipeline-reviewer | RAG pipeline review | Retrieval quality, chunking, reranking, RAGAS evaluation coverage | | typescript-reviewer | TypeScript/JavaScript code review | TypeScript/JavaScript projects | +| react-reviewer | React/JSX code review | React component and hook changes | +| react-build-resolver | React/Vite/Next.js/webpack build errors | React build failures | +| vue-reviewer | Vue.js Composition API and reactivity review | Vue component, Pinia, and Nuxt changes | +| swift-reviewer | Swift/iOS code review | Swift code changes | +| swift-build-resolver | Swift/Xcode/SPM build errors | Swift build failures | +| flutter-reviewer | Flutter/Dart widget and state review | Flutter app changes | +| dart-build-resolver | Dart/Flutter build and pub dependency errors | Flutter compilation failures | +| csharp-reviewer | C#/.NET async patterns, nullability, security | All C# code changes | +| fastapi-reviewer | FastAPI async correctness, Pydantic, OpenAPI | FastAPI endpoint and schema changes | +| php-reviewer | PHP/PSR-12, Eloquent, security review | PHP code changes | +| harmonyos-app-resolver | HarmonyOS ArkTS/ArkUI code and API review | HarmonyOS/OpenHarmony application changes | +| healthcare-reviewer | Clinical safety, PHI compliance, CDSS accuracy | Healthcare, EMR/EHR application code | +| a11y-architect | WCAG 2.2 accessibility architecture | Designing UI components, accessibility audits | +| code-architect | Feature architecture blueprints from codebase patterns | New features needing implementation design | +| network-architect | Enterprise multi-site network architecture | Complex network design decisions | +| homelab-architect | Home/small-lab network design | Home infrastructure planning | +| network-config-reviewer | Router/switch config security and correctness | Network configuration changes | +| network-troubleshooter | OSI-layer connectivity and routing diagnosis | Network connectivity and routing issues | +| performance-optimizer | Bottleneck detection, bundle size, memory leaks | Slow code or high resource usage | +| silent-failure-hunter | Swallowed errors and missing propagation | Code reliability audits | +| type-design-analyzer | Type encapsulation and invariant design | Type design and invariant reviews | +| pr-test-analyzer | PR test coverage quality and completeness | Before merging pull requests | +| code-explorer | Execution path tracing and architecture mapping | Understanding unfamiliar code paths | +| code-simplifier | Clarity-focused code refinement without behavior change | Post-implementation cleanup | +| comment-analyzer | Comment accuracy, freshness, and rot risk | Code comment audits | +| agent-evaluator | 5-axis quality scoring for agent output | Evaluating task completion quality | +| chief-of-staff | Multi-channel communication triage and drafting | Managing email/Slack communication workflows | +| conversation-analyzer | Extract hook behaviors from session transcripts | Creating hooks from observed patterns | +| marketing-agent | Campaign planning, copy creation, content calendars | Product launches, marketing campaigns | +| seo-specialist | Technical SEO audit, structured data, Core Web Vitals | Site audits, meta tag and schema issues | +| opensource-forker | Fork projects and strip secrets for open-sourcing | Starting an open-source release | +| opensource-sanitizer | Verify sanitized fork is release-ready | Before any public release | +| opensource-packager | Generate OSS packaging boilerplate (README, LICENSE, etc.) | Finalizing an open-source release | +| gan-planner | Expand a prompt into a full product specification | Starting a GAN harness session | +| gan-generator | Implement features per spec, iterate on evaluator feedback | GAN harness implementation phase | +| gan-evaluator | Test running application via Playwright and score it | GAN harness evaluation phase | ## Agent Orchestration @@ -61,6 +97,17 @@ Use agents proactively without user prompt: - Autonomous loops / loop monitoring → **ecc:loop-operator** - Harness config reliability and cost → **ecc:harness-optimizer** - RAG/retrieval pipeline changes → **ecc:rag-pipeline-reviewer** +- Performance bottleneck or slow code → **ecc:performance-optimizer** +- React/JSX changes → **ecc:react-reviewer** +- Vue changes → **ecc:vue-reviewer** +- Swift changes → **ecc:swift-reviewer** +- C# changes → **ecc:csharp-reviewer** +- PHP changes → **ecc:php-reviewer** +- Flutter/Dart changes → **ecc:flutter-reviewer** +- Healthcare/clinical code → **ecc:healthcare-reviewer** +- UI component design → **ecc:a11y-architect** +- Open-source release prep → **ecc:opensource-forker** → **ecc:opensource-sanitizer** → **ecc:opensource-packager** +- Agent output quality check → **ecc:agent-evaluator** Use parallel execution for independent operations — launch multiple agents simultaneously. @@ -154,7 +201,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 68 specialized subagents -skills/ — 292 workflow skills and domain knowledge +skills/ — 293 workflow skills and domain knowledge commands/ — 94 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index 117552c2b..f6a5ed208 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ ไทย | Deutsch | Español | - Українська + Українська | + Polski
@@ -114,9 +115,9 @@ Use the [guided setup](#install-ecc) or [native plugin commands](#claude-code-de
@@ -136,13 +137,13 @@ plan -> test -> implement -> review -> verify -> remember -> improve
ECC は MIT ライセンスのオープンソースです。現時点では Claude Code で最もよく機能し、サポート対象の Codex 同期パスを備え、Cursor、OpenCode、Gemini、Zed、GitHub Copilot、Antigravity、Qwen、その他のハーネス向けには機能が限定されたアダプターを提供しています。機能の同等性を前提にする前に、[サポート状況マトリクス](#プラットフォームサポート)を確認してください。
-68 の agents、292 の skills、95 のレガシー command シムに加えて、hooks、rules、メモリ、継続的学習、AgentShield セキュリティスキャンを利用できます。agents は計画、レビュー、ビルド修復、セキュリティ、アーキテクチャ、ドメイン作業に特化しています。
+68 の agents、293 の skills、94 のレガシー command シムに加えて、hooks、rules、メモリ、継続的学習、AgentShield セキュリティスキャンを利用できます。agents は計画、レビュー、ビルド修復、セキュリティ、アーキテクチャ、ドメイン作業に特化しています。
| 含まれるもの | 数 | 得られるもの |
| ---------------- | ----------: | ------------------------------------------------------------------------------------ |
| Agents | 68 agents | 計画、レビュー、ビルド修復、セキュリティ、アーキテクチャ、ドメイン作業 |
-| Skills | 292 skills | TDD、リサーチ、セキュリティ、ドキュメント、フロントエンド、データ、ML、運用など |
-| Commands | 95 commands | ECC が skills ファーストの構成へ移行する間の便利なエントリーポイント |
+| Skills | 293 skills | TDD、リサーチ、セキュリティ、ドキュメント、フロントエンド、データ、ML、運用など |
+| Commands | 94 commands | ECC が skills ファーストの構成へ移行する間の便利なエントリーポイント |
| Hooks とメモリ | ランタイム | 強制、セッションサマリー、継続的学習、instincts、コンテキスト制御 |
| Rules | 選択式 | 言語やプロジェクトごとに選ぶ、常時ロードされる標準 |
| AgentShield | 同梱 | プロンプト、hooks、MCP 設定、パーミッション、シークレット、agent ファイルのスキャン |
@@ -794,7 +795,7 @@ Kimi Code はインストールされた `.kimi-code/AGENTS.md` の指示と `.k
```text
ECC/
|-- agents/ # 委譲用の 68 の専門サブエージェント
-|-- skills/ # オンデマンドで読み込まれる 292 の再利用可能なワークフロー
+|-- skills/ # オンデマンドで読み込まれる 293 の再利用可能なワークフロー
|-- commands/ # メンテナンスされている 94 のスラッシュコマンドシム
|-- rules/ # オプトインの共通標準と言語別標準
|-- hooks/ # ランタイムの自動化と強制
diff --git a/docs/ja-JP/agents/build-error-resolver.md b/docs/ja-JP/agents/build-error-resolver.md
index 6362ac24a..5eda1ba50 100644
--- a/docs/ja-JP/agents/build-error-resolver.md
+++ b/docs/ja-JP/agents/build-error-resolver.md
@@ -2,7 +2,7 @@
name: build-error-resolver
description: ビルドおよびTypeScriptエラー解決のスペシャリスト。ビルドが失敗した際やタイプエラーが発生した際に積極的に使用してください。最小限の差分でビルド/タイプエラーのみを修正し、アーキテクチャの変更は行いません。ビルドを迅速に成功させることに焦点を当てます。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+model: sonnet
---
# ビルドエラーリゾルバー
diff --git a/docs/ja-JP/agents/chief-of-staff.md b/docs/ja-JP/agents/chief-of-staff.md
index d62cc0be0..13ad1f21c 100644
--- a/docs/ja-JP/agents/chief-of-staff.md
+++ b/docs/ja-JP/agents/chief-of-staff.md
@@ -2,7 +2,7 @@
name: chief-of-staff
description: メール、Slack、LINE、Messengerをトリアージするパーソナルコミュニケーションチーフオブスタッフ。メッセージを4つのティア(skip/info_only/meeting_info/action_required)に分類し、返信ドラフトを生成し、送信後のフォロースルーをフックで強制します。マルチチャネルコミュニケーションワークフローの管理時に使用します。
tools: ["Read", "Grep", "Glob", "Bash", "Edit", "Write"]
-model: opus
+model: sonnet
---
## プロンプト防御ベースライン
diff --git a/docs/ja-JP/agents/code-reviewer.md b/docs/ja-JP/agents/code-reviewer.md
index b5c5c5d72..bf26d6a5a 100644
--- a/docs/ja-JP/agents/code-reviewer.md
+++ b/docs/ja-JP/agents/code-reviewer.md
@@ -2,7 +2,7 @@
name: code-reviewer
description: 専門コードレビュースペシャリスト。品質、セキュリティ、保守性のためにコードを積極的にレビューします。コードの記述または変更直後に使用してください。すべてのコード変更に対して必須です。
tools: ["Read", "Grep", "Glob", "Bash"]
-model: opus
+model: sonnet
---
あなたはコード品質とセキュリティの高い基準を確保するシニアコードレビュアーです。
diff --git a/docs/ja-JP/agents/comment-analyzer.md b/docs/ja-JP/agents/comment-analyzer.md
index 1db18900e..63255e383 100644
--- a/docs/ja-JP/agents/comment-analyzer.md
+++ b/docs/ja-JP/agents/comment-analyzer.md
@@ -1,7 +1,7 @@
---
name: comment-analyzer
description: コードコメントの正確性、完全性、保守性、コメント劣化リスクを分析します。
-model: sonnet
+model: haiku
tools: [Read, Grep, Glob]
---
diff --git a/docs/ja-JP/agents/conversation-analyzer.md b/docs/ja-JP/agents/conversation-analyzer.md
index bc8ddb8e0..26e31f7ca 100644
--- a/docs/ja-JP/agents/conversation-analyzer.md
+++ b/docs/ja-JP/agents/conversation-analyzer.md
@@ -1,7 +1,7 @@
---
name: conversation-analyzer
description: 会話のトランスクリプトを分析し、フックで防止すべき動作を見つけるためにこのエージェントを使用します。引数なしの/hookifyでトリガーされます。
-model: sonnet
+model: haiku
tools: [Read, Grep]
---
diff --git a/docs/ja-JP/agents/database-reviewer.md b/docs/ja-JP/agents/database-reviewer.md
index 30d814b82..f76e50e95 100644
--- a/docs/ja-JP/agents/database-reviewer.md
+++ b/docs/ja-JP/agents/database-reviewer.md
@@ -1,8 +1,8 @@
---
name: database-reviewer
description: クエリ最適化、スキーマ設計、セキュリティ、パフォーマンスのためのPostgreSQLデータベーススペシャリスト。SQL作成、マイグレーション作成、スキーマ設計、データベースパフォーマンスのトラブルシューティング時に積極的に使用してください。Supabaseのベストプラクティスを組み込んでいます。
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+tools: ["Read", "Grep", "Glob", "Bash"]
+model: sonnet
---
# データベースレビューアー
diff --git a/docs/ja-JP/agents/doc-updater.md b/docs/ja-JP/agents/doc-updater.md
index c54876458..adf807954 100644
--- a/docs/ja-JP/agents/doc-updater.md
+++ b/docs/ja-JP/agents/doc-updater.md
@@ -2,7 +2,7 @@
name: doc-updater
description: ドキュメントとコードマップのスペシャリスト。コードマップとドキュメントの更新に積極的に使用してください。/update-codemapsと/update-docsを実行し、docs/CODEMAPS/*を生成し、READMEとガイドを更新します。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+model: haiku
---
# ドキュメント & コードマップスペシャリスト
diff --git a/docs/ja-JP/agents/docs-lookup.md b/docs/ja-JP/agents/docs-lookup.md
index e18c0e55a..721baf95c 100644
--- a/docs/ja-JP/agents/docs-lookup.md
+++ b/docs/ja-JP/agents/docs-lookup.md
@@ -2,7 +2,7 @@
name: docs-lookup
description: ユーザーがライブラリ、フレームワーク、APIの使い方を質問したり、最新のコード例が必要な場合に、Context7 MCPを使用して最新のドキュメントを取得し、例付きの回答を返します。ドキュメント/API/セットアップの質問時に呼び出します。
tools: ["Read", "Grep", "mcp__context7__resolve-library-id", "mcp__context7__query-docs"]
-model: sonnet
+model: haiku
---
## プロンプト防御ベースライン
diff --git a/docs/ja-JP/agents/e2e-runner.md b/docs/ja-JP/agents/e2e-runner.md
index e6eb35f87..03cc2890c 100644
--- a/docs/ja-JP/agents/e2e-runner.md
+++ b/docs/ja-JP/agents/e2e-runner.md
@@ -2,7 +2,7 @@
name: e2e-runner
description: Vercel Agent Browser(推奨)とPlaywrightフォールバックを使用するエンドツーエンドテストスペシャリスト。E2Eテストの生成、メンテナンス、実行に積極的に使用してください。テストジャーニーの管理、不安定なテストの隔離、アーティファクト(スクリーンショット、ビデオ、トレース)のアップロード、重要なユーザーフローの動作確認を行います。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+model: sonnet
---
# E2Eテストランナー
diff --git a/docs/ja-JP/agents/gan-evaluator.md b/docs/ja-JP/agents/gan-evaluator.md
index 8e2e1b8b6..7dd200268 100644
--- a/docs/ja-JP/agents/gan-evaluator.md
+++ b/docs/ja-JP/agents/gan-evaluator.md
@@ -1,8 +1,8 @@
---
name: gan-evaluator
description: "GANハーネス — エバリュエーターエージェント。Playwrightを使用してライブ実行中のアプリケーションをテストし、ルーブリックに対してスコアリングし、ジェネレーターに実行可能なフィードバックを提供します。"
-tools: ["Read", "Write", "Bash", "Grep", "Glob"]
-model: opus
+tools: ["Read", "Write", "Bash", "Grep", "Glob", "mcp__playwright__browser_navigate", "mcp__playwright__browser_click", "mcp__playwright__browser_take_screenshot", "mcp__playwright__browser_snapshot", "mcp__playwright__browser_type", "mcp__playwright__browser_fill_form", "mcp__playwright__browser_resize", "mcp__playwright__browser_press_key"]
+model: sonnet
color: red
---
diff --git a/docs/ja-JP/agents/gan-generator.md b/docs/ja-JP/agents/gan-generator.md
index f31d4c02c..9a3862608 100644
--- a/docs/ja-JP/agents/gan-generator.md
+++ b/docs/ja-JP/agents/gan-generator.md
@@ -2,7 +2,7 @@
name: gan-generator
description: "GANハーネス — ジェネレーターエージェント。仕様に従って機能を実装し、エバリュエーターのフィードバックを読み、品質閾値を満たすまでイテレーションします。"
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+model: sonnet
color: green
---
diff --git a/docs/ja-JP/agents/gan-planner.md b/docs/ja-JP/agents/gan-planner.md
index 084bb60b4..acf2752d2 100644
--- a/docs/ja-JP/agents/gan-planner.md
+++ b/docs/ja-JP/agents/gan-planner.md
@@ -2,7 +2,7 @@
name: gan-planner
description: "GANハーネス — プランナーエージェント。1行のプロンプトを、機能、スプリント、評価基準、デザイン方向を含む完全な製品仕様に展開します。"
tools: ["Read", "Write", "Grep", "Glob"]
-model: opus
+model: sonnet
color: purple
---
diff --git a/docs/ja-JP/agents/go-build-resolver.md b/docs/ja-JP/agents/go-build-resolver.md
index 4f360fce3..64f2f7df5 100644
--- a/docs/ja-JP/agents/go-build-resolver.md
+++ b/docs/ja-JP/agents/go-build-resolver.md
@@ -2,7 +2,7 @@
name: go-build-resolver
description: Goビルド、vet、コンパイルエラー解決スペシャリスト。最小限の変更でビルドエラー、go vet問題、リンターの警告を修正します。Goビルドが失敗したときに使用してください。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+model: sonnet
---
# Goビルドエラーリゾルバー
diff --git a/docs/ja-JP/agents/go-reviewer.md b/docs/ja-JP/agents/go-reviewer.md
index abab6fe57..0dd66d876 100644
--- a/docs/ja-JP/agents/go-reviewer.md
+++ b/docs/ja-JP/agents/go-reviewer.md
@@ -4,7 +4,7 @@ description: 慣用的なGo、並行処理パターン、エラー処理、パ
コード変更に使用してください。Goプロジェクトに必須です。
tools: ["Read", "Grep", "Glob", "Bash"]
-model: opus
+model: sonnet
---
あなたは慣用的なGoとベストプラクティスの高い基準を確保するシニアGoコードレビュアーです。
diff --git a/docs/ja-JP/agents/opensource-forker.md b/docs/ja-JP/agents/opensource-forker.md
index c1c21dd9a..30a81d7ae 100644
--- a/docs/ja-JP/agents/opensource-forker.md
+++ b/docs/ja-JP/agents/opensource-forker.md
@@ -2,7 +2,7 @@
name: opensource-forker
description: あらゆるプロジェクトをオープンソース化のためにフォークします。ファイルのコピー、シークレットと認証情報の除去(20以上のパターン)、内部参照のプレースホルダー置換、.env.exampleの生成、git履歴のクリーンアップを行います。opensource-pipelineスキルの第1ステージです。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: sonnet
+model: haiku
---
## プロンプト防御ベースライン
diff --git a/docs/ja-JP/agents/opensource-packager.md b/docs/ja-JP/agents/opensource-packager.md
index 6916ed6c8..916147b04 100644
--- a/docs/ja-JP/agents/opensource-packager.md
+++ b/docs/ja-JP/agents/opensource-packager.md
@@ -2,7 +2,7 @@
name: opensource-packager
description: サニタイズ済みプロジェクトの完全なオープンソースパッケージングを生成します。CLAUDE.md、setup.sh、README.md、LICENSE、CONTRIBUTING.md、GitHubイシューテンプレートを作成します。あらゆるリポジトリをClaude Codeですぐに使えるようにします。opensource-pipelineスキルの第3ステージです。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: sonnet
+model: haiku
---
## プロンプト防御ベースライン
diff --git a/docs/ja-JP/agents/python-reviewer.md b/docs/ja-JP/agents/python-reviewer.md
index 06059fea3..a8ffe16db 100644
--- a/docs/ja-JP/agents/python-reviewer.md
+++ b/docs/ja-JP/agents/python-reviewer.md
@@ -2,7 +2,7 @@
name: python-reviewer
description: PEP 8準拠、Pythonイディオム、型ヒント、セキュリティ、パフォーマンスを専門とする専門Pythonコードレビュアー。すべてのPythonコード変更に使用してください。Pythonプロジェクトに必須です。
tools: ["Read", "Grep", "Glob", "Bash"]
-model: opus
+model: sonnet
---
あなたはPythonicコードとベストプラクティスの高い基準を確保するシニアPythonコードレビュアーです。
diff --git a/docs/ja-JP/agents/refactor-cleaner.md b/docs/ja-JP/agents/refactor-cleaner.md
index e378ba949..a6757490d 100644
--- a/docs/ja-JP/agents/refactor-cleaner.md
+++ b/docs/ja-JP/agents/refactor-cleaner.md
@@ -2,7 +2,7 @@
name: refactor-cleaner
description: デッドコードクリーンアップと統合スペシャリスト。未使用コード、重複の削除、リファクタリングに積極的に使用してください。分析ツール(knip、depcheck、ts-prune)を実行してデッドコードを特定し、安全に削除します。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+model: sonnet
---
# リファクタ&デッドコードクリーナー
diff --git a/docs/ja-JP/agents/security-reviewer.md b/docs/ja-JP/agents/security-reviewer.md
index a9367460d..d2285a3e3 100644
--- a/docs/ja-JP/agents/security-reviewer.md
+++ b/docs/ja-JP/agents/security-reviewer.md
@@ -1,8 +1,8 @@
---
name: security-reviewer
description: セキュリティ脆弱性検出および修復のスペシャリスト。ユーザー入力、認証、APIエンドポイント、機密データを扱うコードを書いた後に積極的に使用してください。シークレット、SSRF、インジェクション、安全でない暗号、OWASP Top 10の脆弱性を検出します。
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
-model: opus
+tools: ["Read", "Grep", "Glob", "Bash"]
+model: sonnet
---
# セキュリティレビューアー
diff --git a/docs/ja-JP/agents/tdd-guide.md b/docs/ja-JP/agents/tdd-guide.md
index 7726ce367..79bac72df 100644
--- a/docs/ja-JP/agents/tdd-guide.md
+++ b/docs/ja-JP/agents/tdd-guide.md
@@ -2,7 +2,7 @@
name: tdd-guide
description: テスト駆動開発スペシャリストで、テストファースト方法論を強制します。新しい機能の記述、バグの修正、コードのリファクタリング時に積極的に使用してください。80%以上のテストカバレッジを確保します。
tools: ["Read", "Write", "Edit", "Bash", "Grep"]
-model: opus
+model: sonnet
---
あなたはテスト駆動開発(TDD)スペシャリストで、すべてのコードがテストファーストの方法論で包括的なカバレッジをもって開発されることを確保します。
diff --git a/docs/ja-JP/skills/scientific-db-pubmed-database/SKILL.md b/docs/ja-JP/skills/scientific-db-pubmed-database/SKILL.md
index 22ba0f37f..2e0394375 100644
--- a/docs/ja-JP/skills/scientific-db-pubmed-database/SKILL.md
+++ b/docs/ja-JP/skills/scientific-db-pubmed-database/SKILL.md
@@ -1,5 +1,5 @@
---
-name: pubmed-database
+name: scientific-db-pubmed-database
description: 生物医学文献、MeSH クエリ、PMID 検索、引用取得、および API を利用した文献モニタリングのための PubMed および NCBI E-utilities の直接検索ワークフロー。
origin: community
---
diff --git a/docs/ja-JP/skills/scientific-db-uspto-database/SKILL.md b/docs/ja-JP/skills/scientific-db-uspto-database/SKILL.md
index 67783cc01..2826a3332 100644
--- a/docs/ja-JP/skills/scientific-db-uspto-database/SKILL.md
+++ b/docs/ja-JP/skills/scientific-db-uspto-database/SKILL.md
@@ -1,5 +1,5 @@
---
-name: uspto-database
+name: scientific-db-uspto-database
description: 公式記録の検索、PatentSearch クエリ、TSDR チェック、譲渡データ、および再現可能な IP 調査ログのための USPTO 特許・商標データワークフロー。
origin: community
---
diff --git a/docs/ja-JP/skills/scientific-pkg-gget/SKILL.md b/docs/ja-JP/skills/scientific-pkg-gget/SKILL.md
index b8edca572..bae76ad58 100644
--- a/docs/ja-JP/skills/scientific-pkg-gget/SKILL.md
+++ b/docs/ja-JP/skills/scientific-pkg-gget/SKILL.md
@@ -1,5 +1,5 @@
---
-name: gget
+name: scientific-pkg-gget
description: ゲノムデータベースへのクイック検索、配列検索、BLAST スタイルの検索、エンリッチメントチェック、および再現可能なバイオインフォマティクス証拠ログのための gget CLI および Python ワークフロー。
origin: community
---
diff --git a/docs/ja-JP/skills/scientific-thinking-literature-review/SKILL.md b/docs/ja-JP/skills/scientific-thinking-literature-review/SKILL.md
index c5c065f63..d5b997b0b 100644
--- a/docs/ja-JP/skills/scientific-thinking-literature-review/SKILL.md
+++ b/docs/ja-JP/skills/scientific-thinking-literature-review/SKILL.md
@@ -1,5 +1,5 @@
---
-name: literature-review
+name: scientific-thinking-literature-review
description: 学術、生物医学、技術、科学的なトピックに対するシステマティックな文献レビューワークフロー。検索計画、ソースのスクリーニング、統合、引用確認、証拠ログを含む。
origin: community
---
diff --git a/docs/ja-JP/skills/scientific-thinking-scholar-evaluation/SKILL.md b/docs/ja-JP/skills/scientific-thinking-scholar-evaluation/SKILL.md
index 9533fd205..28dd79b6f 100644
--- a/docs/ja-JP/skills/scientific-thinking-scholar-evaluation/SKILL.md
+++ b/docs/ja-JP/skills/scientific-thinking-scholar-evaluation/SKILL.md
@@ -1,5 +1,5 @@
---
-name: scholar-evaluation
+name: scientific-thinking-scholar-evaluation
description: 論文、提案書、文献レビュー、方法論セクション、証拠の質、引用サポート、研究論文フィードバックのための構造化された学術的作業評価。
origin: community
---
diff --git a/docs/ko-KR/README.md b/docs/ko-KR/README.md
index 9adc19ea1..3d20673fc 100644
--- a/docs/ko-KR/README.md
+++ b/docs/ko-KR/README.md
@@ -1,4 +1,4 @@
-**언어:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | 한국어 | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+**언어:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | 한국어 | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
# Everything Claude Code
@@ -24,7 +24,7 @@
**Language / 语言 / 語言 / 언어 / Dil / Язык / Ngôn ngữ**
-[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
diff --git a/docs/ko-KR/agents/database-reviewer.md b/docs/ko-KR/agents/database-reviewer.md
index a5023cbd1..bac391711 100644
--- a/docs/ko-KR/agents/database-reviewer.md
+++ b/docs/ko-KR/agents/database-reviewer.md
@@ -1,7 +1,7 @@
---
name: database-reviewer
description: PostgreSQL 데이터베이스 전문가. 쿼리 최적화, 스키마 설계, 보안, 성능을 다룹니다. SQL 작성, 마이그레이션 생성, 스키마 설계, 데이터베이스 성능 트러블슈팅 시 사용하세요. Supabase 모범 사례를 포함합니다.
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
+tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
diff --git a/docs/ko-KR/agents/security-reviewer.md b/docs/ko-KR/agents/security-reviewer.md
index 49dcff92e..5370ae126 100644
--- a/docs/ko-KR/agents/security-reviewer.md
+++ b/docs/ko-KR/agents/security-reviewer.md
@@ -1,7 +1,7 @@
---
name: security-reviewer
description: 보안 취약점 감지 및 수정 전문가. 사용자 입력 처리, 인증, API 엔드포인트, 민감한 데이터를 다루는 코드 작성 후 사용하세요. 시크릿, SSRF, 인젝션, 안전하지 않은 암호화, OWASP Top 10 취약점을 플래그합니다.
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
+tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
diff --git a/docs/pl/GLOSSARY.md b/docs/pl/GLOSSARY.md
new file mode 100644
index 000000000..8fe10c585
--- /dev/null
+++ b/docs/pl/GLOSSARY.md
@@ -0,0 +1,65 @@
+# Glosariusz / Glossary
+
+Ujednolicona terminologia polskiego tłumaczenia ECC.
+
+Nazwy powierzchni ECC oraz powszechne terminy techniczne pozostają po angielsku, gdy ich
+spolszczenie utrudniałoby powiązanie tekstu z nazwą katalogu, poleceniem lub interfejsem.
+W zwykłym opisie można użyć polskiego odpowiednika wskazanego poniżej.
+
+| English | Polski | Uwagi |
+|---|---|---|
+| Agent | Agent | nazwa powierzchni `agents/`; liczba mnoga: Agenty |
+| Skill | Skill | nazwa powierzchni `skills/`; liczba mnoga: Skille |
+| Hook | Hook | nazwa powierzchni `hooks/`; liczba mnoga: Hooki |
+| Command | Command | nazwa powierzchni `commands/`; ogólnie: „polecenie” |
+| Rule | Rule | nazwa powierzchni `rules/`; ogólnie: „reguła” |
+| Harness | środowisko agenta | „Harness” dopuszczalne w kontekście nazwy technicznej |
+| Instinct | Instinct | termin funkcji Continuous Learning |
+| Plugin | plugin | |
+| Marketplace | marketplace | nazwa powierzchni produktu |
+| Worktree | worktree | termin Git |
+| Subagent | subagent | |
+| Frontmatter | frontmatter | nazwy pól YAML pozostają po angielsku |
+| Continuous Learning | Continuous Learning | nazwa funkcji; opisowo: „ciągłe uczenie” |
+| Memory | pamięć | jako nazwa funkcji może pozostać po angielsku |
+| Context window | okno kontekstu | |
+| Token | token | |
+| Coverage | pokrycie testami | |
+| Test-Driven Development | programowanie sterowane testami | zachowaj skrót TDD |
+| Code review | przegląd kodu | |
+| Refactoring | refaktoryzacja | |
+| Pull request | pull request | zachowaj skrót PR |
+| Commit | commit | |
+| Branch | gałąź | |
+| Merge | scalenie | jako czasownik: „scalić” |
+| Build | build | opisowo: „kompilacja” lub „artefakt” zależnie od kontekstu |
+| Deploy | wdrożenie | |
+| Pipeline | pipeline | |
+| Orchestration | orkiestracja | |
+| Repository | repozytorium | skrót: repo |
+| Dependency | zależność | |
+| Edge case | przypadek brzegowy | |
+| Best practice | dobra praktyka | |
+| Anti-pattern | antywzorzec | |
+| Middleware | middleware | |
+| Endpoint | endpoint | |
+| Schema | schemat | |
+| Payload | payload | opisowo: „dane żądania” |
+| Callback | callback | |
+| Checkpoint | punkt kontrolny | |
+| Linter | linter | |
+| Formatter | formatter | |
+| Staging | środowisko testowe | zależnie od kontekstu także „staging” |
+| Production | produkcja | „środowisko produkcyjne” |
+| Debugging | debugowanie | |
+| Logging | logowanie | nie mylić z logowaniem użytkownika; w razie potrzeby „rejestrowanie zdarzeń” |
+| Monitoring | monitoring | |
+| Rate limit | limit żądań | |
+| Retry | ponowienie | |
+| Fallback | rozwiązanie zapasowe | |
+| Sandboxing | izolacja w sandboxie | |
+| Sanitization | sanityzacja | |
+| Selective install | instalacja selektywna | |
+| Profile | profil | profil instalacji |
+| Component | komponent | komponent instalatora |
+| Module | moduł | moduł instalatora |
diff --git a/docs/pl/README.md b/docs/pl/README.md
new file mode 100644
index 000000000..582549f6b
--- /dev/null
+++ b/docs/pl/README.md
@@ -0,0 +1,193 @@
+**Język:** [English](../../README.md) | **Polski** | [Deutsch](../de-DE/README.md) | [Español](../es/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md)
+
+# ECC
+
+
+
+> Tłumaczenie obejmuje przewodnik startowy i najważniejsze powierzchnie ECC. Źródło angielskie:
+> commit `8321021c54d670126ce3b2969d5deb880b4b0c2a` z gałęzi `main`.
+> Pełny, aktualny katalog pozostaje w [angielskim README](../../README.md); kolejne obszary będą
+> tłumaczone etapami, aby ograniczyć rozmiar i ryzyko nieaktualnych zmian.
+
+---
+
+**Natywny dla środowisk agentów system operacyjny do pracy agentowej.**
+
+ECC to nie tylko zestaw konfiguracji. Łączy gotowe do użycia Agenty, Skille, Hooki, Rules,
+konfiguracje MCP i warstwę zgodności ze starszymi Commands. System powstał na podstawie
+rzeczywistych przepływów pracy i działa w wielu środowiskach: **Claude Code**, **Codex**,
+**Cursor**, **OpenCode**, **Gemini**, **Zed**, **GitHub Copilot** i innych.
+
+## Oficjalne źródła
+
+Instaluj ECC wyłącznie ze zweryfikowanych kanałów:
+
+- repozytorium [github.com/affaan-m/ECC](https://github.com/affaan-m/ECC),
+- pakiety npm [`ecc-universal`](https://www.npmjs.com/package/ecc-universal) i
+ [`ecc-agentshield`](https://www.npmjs.com/package/ecc-agentshield),
+- aplikacja [ECC Tools dla GitHub](https://github.com/apps/ecc-tools),
+- identyfikator pluginu `ecc@ecc`,
+- witryna [ecc.tools](https://ecc.tools).
+
+Nieoficjalne kopie i mirrory nie są utrzymywane ani sprawdzane przez projekt.
+
+## Szybki start
+
+Wybierz **jedną** ścieżkę instalacji. Łączenie instalacji pluginu z pełną instalacją ręczną
+jest najczęstszą przyczyną zduplikowanych Agentów, Skilli i Hooków.
+
+### Uniwersalna konfiguracja prowadzona
+
+```bash
+npx ecc-universal@2.2.1 setup
+```
+
+Możesz także użyć właściwego menedżera pakietów:
+
+```bash
+pnpm dlx ecc-universal@2.2.1 setup
+yarn dlx ecc-universal@2.2.1 setup
+bunx ecc-universal@2.2.1 setup
+```
+
+Przed uruchomieniem kodu pakietu sprawdź źródło wydania i integralność rejestru.
+
+### Claude Code
+
+W Claude Code dodaj marketplace i zainstaluj plugin:
+
+```text
+/plugin marketplace add https://github.com/affaan-m/ECC
+/plugin install ecc@ecc
+```
+
+Następnie zacznij od `rules/common` oraz tylko tych pakietów językowych lub frameworków,
+których rzeczywiście używasz. Po instalacji pluginu nie uruchamiaj dodatkowo pełnego
+`./install.sh --profile full`.
+
+### Codex
+
+```bash
+codex plugin marketplace add affaan-m/ECC
+codex plugin add ecc@ecc
+```
+
+W Codex użyj `$configure-ecc`, aby przejść przez konfigurację dostosowaną do dostawcy.
+
+### Inne środowiska
+
+Polecenia uruchamiaj z głównego katalogu pobranego repozytorium. Wiersz OpenCode wybiera
+pełny profil i jawnie włącza automatyczne Hooki.
+
+| Środowisko | Polecenie instalacji z repozytorium |
+|---|---|
+| Cursor | `./install.sh --profile minimal --target cursor` |
+| Gemini CLI | `./install.sh --profile minimal --target gemini` |
+| Zed | `./install.sh --profile minimal --target zed` |
+| OpenCode | `npm install && npm run build:opencode && ./install.sh --profile full --target opencode --enable-hooks` |
+| Hermes | `./install.sh --profile minimal --target hermes` |
+| OpenClaw | `./install.sh --profile minimal --target openclaw` |
+| Kimi Code CLI | `./install.sh --profile minimal --target kimi` |
+
+Pełna macierz środowisk i wymagania znajdują się w
+[angielskiej sekcji Platform Support](../../README.md#platform-support).
+
+## Instalacja polskiej dokumentacji
+
+Polski używa kodu `pl`; akceptowany jest także alias `pl-PL`. Użyj lokalnej kopii
+repozytorium zawierającej katalog `docs/pl/` i komponent `locale:pl`. Uruchom poniższe
+polecenia z głównego katalogu tej kopii, po zainstalowaniu zależności repozytorium.
+Wymagany jest Node.js 18 lub nowszy. Wydanie `ecc-universal@2.2.1` nie zawiera jeszcze
+polskiej dokumentacji.
+
+Najpierw sprawdź plan bez zapisywania plików instalacji:
+
+```bash
+node scripts/install-apply.js --target claude --locale pl --dry-run
+```
+
+Następnie zainstaluj polską dokumentację w `~/.claude/docs/pl/`:
+
+```bash
+node scripts/install-apply.js --target claude --locale pl
+```
+
+Ten wybór instaluje moduł dokumentacji `docs-pl`; nie instaluje pełnego profilu ECC
+ani automatycznych Hooków.
+
+## Co zawiera ECC
+
+| Powierzchnia | Rola |
+|---|---|
+| `agents/` | wyspecjalizowane Agenty do planowania, implementacji, przeglądu i diagnostyki |
+| `skills/` | modułowe procedury i wiedza aktywowane zależnie od zadania |
+| `hooks/` | automatyzacje uruchamiane przy zdarzeniach środowiska |
+| `rules/` | stałe zasady wspólne oraz reguły języków i frameworków |
+| `commands/` | starsza warstwa zgodności dla poleceń slash |
+| `mcp-configs/` | konfiguracje serwerów Model Context Protocol |
+| `manifests/` | deklaratywne moduły, komponenty i profile instalatora |
+
+Kierunek projektu jest **skills-first**: nowe przepływy pracy powinny trafiać najpierw do
+`skills/`; `commands/` pozostaje powierzchnią zgodności tam, gdzie nadal jest potrzebna.
+
+## Najważniejsze pojęcia
+
+### Agenty
+
+Agent ma określoną rolę, zestaw narzędzi i sposób pracy. Przykłady obejmują planistę,
+recenzentów kodu dla konkretnych języków oraz specjalistów od rozwiązywania błędów kompilacji.
+
+### Skille
+
+Skill przechowuje skoncentrowaną procedurę lub wiedzę dziedzinową. Dzięki temu kontekst jest
+ładowany tylko wtedy, gdy pasuje do zadania, zamiast powiększać każdy prompt systemowy.
+
+### Hooki
+
+Hook reaguje na zdarzenia takie jak rozpoczęcie sesji lub użycie narzędzia. Hooki muszą być
+przenośne i bezpieczne; nie kopiuj ich drugi raz do ustawień po instalacji pluginu, ponieważ
+nowe wersje Claude Code ładują `hooks/hooks.json` automatycznie.
+
+### Rules
+
+Rules opisują zawsze obowiązujące konwencje. Instaluj wspólny rdzeń i tylko pasujące pakiety,
+aby nie obciążać okna kontekstu nieistotnymi regułami.
+
+Sposób tłumaczenia terminów ECC opisuje [polski glosariusz](GLOSSARY.md).
+
+## Bezpieczeństwo
+
+- Nie zapisuj kluczy API, haseł ani tokenów w repozytorium.
+- Przeglądaj skrypty i źródła pakietów przed uruchomieniem.
+- Nie łącz wielu metod instalacji.
+- Nie kopiuj surowego `hooks/hooks.json` do `~/.claude/settings.json` po instalacji pluginu.
+- Używaj minimalnych uprawnień i weryfikuj wejścia na granicach systemu.
+
+Szczegółowe informacje znajdują się w [sekcji Security](../../README.md#security).
+
+## Aktualizowanie tłumaczenia
+
+Tłumaczenia są utrzymywane według zasady „best effort”. Każdy PR powinien podawać:
+
+1. commit angielskiego źródła,
+2. dokładny zakres przetłumaczonej treści,
+3. zmiany w terminologii względem [GLOSSARY.md](GLOSSARY.md),
+4. wykonane sprawdzenia linków, Markdownu i manifestów instalatora.
+
+Kolejne PR-y powinny być małe i podzielone według domen, na przykład `commands/`, `agents/`,
+`rules/` i `skills/`. Pozwala to uniknąć nakładających się tłumaczeń i ułatwia synchronizację
+z szybko zmieniającym się źródłem angielskim.
+
+## Współtworzenie
+
+Przed rozpoczęciem większego tłumaczenia sprawdź istniejące issues i PR-y, aby uniknąć
+równoległej pracy nad tym samym zakresem. Zasady tworzenia zmian i opisów PR znajdują się w
+[CONTRIBUTING.md](../../CONTRIBUTING.md).
+
+## Licencja
+
+MIT — możesz swobodnie używać i dostosowywać projekt oraz dzielić się ulepszeniami.
+
+---
+
+**Jeśli ECC Ci pomaga, zostaw gwiazdkę. Przeczytaj przewodniki. Zbuduj coś świetnego.**
diff --git a/docs/pt-BR/README.md b/docs/pt-BR/README.md
index e33eff641..e0ecf8d41 100644
--- a/docs/pt-BR/README.md
+++ b/docs/pt-BR/README.md
@@ -1,4 +1,4 @@
-**Idioma:** [English](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | Português (Brasil) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+**Idioma:** [English](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | Português (Brasil) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
# Everything Claude Code
@@ -24,7 +24,7 @@
**Idioma / Language / 语言 / Dil / Язык / Ngôn ngữ**
-[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Português (Brasil)](README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Português (Brasil)](README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
---
diff --git a/docs/pt-BR/agents/database-reviewer.md b/docs/pt-BR/agents/database-reviewer.md
index 31b05e0a0..c88abe9eb 100644
--- a/docs/pt-BR/agents/database-reviewer.md
+++ b/docs/pt-BR/agents/database-reviewer.md
@@ -1,7 +1,7 @@
---
name: database-reviewer
description: Especialista em banco de dados PostgreSQL para otimização de queries, design de schema, segurança e performance. Use PROATIVAMENTE ao escrever SQL, criar migrações, projetar schemas ou solucionar problemas de performance. Incorpora boas práticas do Supabase.
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
+tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
diff --git a/docs/pt-BR/agents/security-reviewer.md b/docs/pt-BR/agents/security-reviewer.md
index 54e456753..3355ff84c 100644
--- a/docs/pt-BR/agents/security-reviewer.md
+++ b/docs/pt-BR/agents/security-reviewer.md
@@ -1,7 +1,7 @@
---
name: security-reviewer
description: Especialista em detecção e remediação de vulnerabilidades de segurança. Use PROATIVAMENTE após escrever código que trata input de usuário, autenticação, endpoints de API ou dados sensíveis. Sinaliza segredos, SSRF, injection, criptografia insegura e vulnerabilidades OWASP Top 10.
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
+tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
diff --git a/docs/ru/README.md b/docs/ru/README.md
index 537770e85..64f6f0f6d 100644
--- a/docs/ru/README.md
+++ b/docs/ru/README.md
@@ -1,4 +1,4 @@
-**Язык:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | **Русский** | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+**Язык:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | **Русский** | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
# Everything Claude Code
@@ -27,7 +27,7 @@
**Язык / 语言 / 語言 / Dil / Ngôn ngữ**
-[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | **Русский** | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | **Русский** | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
diff --git a/docs/th/README.md b/docs/th/README.md
index 01e48b871..2110b5e77 100644
--- a/docs/th/README.md
+++ b/docs/th/README.md
@@ -1,4 +1,4 @@
-**ภาษา:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+**ภาษา:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
# Everything Claude Code
@@ -18,7 +18,7 @@
**ภาษา / Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
-[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
diff --git a/docs/tr/AGENTS.md b/docs/tr/AGENTS.md
index a67004d7b..53e74b8f1 100644
--- a/docs/tr/AGENTS.md
+++ b/docs/tr/AGENTS.md
@@ -1,6 +1,6 @@
# Everything Claude Code (ECC) — Agent Talimatları
-Bu, yazılım geliştirme için 68 özel agent, 292 skill, 94 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
+Bu, yazılım geliştirme için 68 özel agent, 293 skill, 94 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
**Sürüm:** 2.2.2
@@ -142,7 +142,7 @@ Başarısızlık sorunlarını giderin: test izolasyonunu kontrol edin → mockl
```
agents/ — 68 özel subagent
-skills/ — 292 iş akışı skillleri ve alan bilgisi
+skills/ — 293 iş akışı skillleri ve alan bilgisi
commands/ — 94 slash command
hooks/ — Tetikleyici tabanlı otomasyonlar
rules/ — Her zaman uyulması gereken kurallar (ortak + dile özel)
diff --git a/docs/tr/README.md b/docs/tr/README.md
index 1fc5e2f5b..00046afc6 100644
--- a/docs/tr/README.md
+++ b/docs/tr/README.md
@@ -23,7 +23,7 @@
**Dil / Language / 语言 / 語言 / Язык / Ngôn ngữ**
-[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [**Türkçe**](README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md)
+[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [**Türkçe**](README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
diff --git a/docs/tr/agents/chief-of-staff.md b/docs/tr/agents/chief-of-staff.md
index f7924608f..d41816d58 100644
--- a/docs/tr/agents/chief-of-staff.md
+++ b/docs/tr/agents/chief-of-staff.md
@@ -2,7 +2,7 @@
name: chief-of-staff
description: Personal communication chief of staff that triages email, Slack, LINE, and Messenger. Classifies messages into 4 tiers (skip/info_only/meeting_info/action_required), generates draft replies, and enforces post-send follow-through via hooks. Use when managing multi-channel communication workflows.
tools: ["Read", "Grep", "Glob", "Bash", "Edit", "Write"]
-model: opus
+model: sonnet
---
Tüm iletişim kanallarını — e-posta, Slack, LINE, Messenger ve takvim — birleşik bir triyaj hattı üzerinden yöneten kişisel bir başkan yardımcısısınız.
diff --git a/docs/tr/agents/database-reviewer.md b/docs/tr/agents/database-reviewer.md
index c1cc651b9..cae06aa28 100644
--- a/docs/tr/agents/database-reviewer.md
+++ b/docs/tr/agents/database-reviewer.md
@@ -1,7 +1,7 @@
---
name: database-reviewer
description: PostgreSQL database specialist for query optimization, schema design, security, and performance. Use PROACTIVELY when writing SQL, creating migrations, designing schemas, or troubleshooting database performance. Incorporates Supabase best practices.
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
+tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
diff --git a/docs/tr/agents/docs-lookup.md b/docs/tr/agents/docs-lookup.md
index 942d97091..9e2afc1a6 100644
--- a/docs/tr/agents/docs-lookup.md
+++ b/docs/tr/agents/docs-lookup.md
@@ -2,7 +2,7 @@
name: docs-lookup
description: Kullanıcı bir kütüphaneyi, framework'ü veya API'yi nasıl kullanacağını sorduğunda veya güncel kod örneklerine ihtiyaç duyduğunda, güncel dokümantasyon getirmek ve örneklerle cevaplar döndürmek için Context7 MCP kullanın. Docs/API/kurulum soruları için çağrılır.
tools: ["Read", "Grep", "mcp__context7__resolve-library-id", "mcp__context7__query-docs"]
-model: sonnet
+model: haiku
---
Bir dokümantasyon specialistisiniz. Kütüphaneler, framework'ler ve API'ler hakkındaki soruları Context7 MCP (resolve-library-id ve query-docs) aracılığıyla getirilen güncel dokümantasyonu kullanarak cevaplarsınız, eğitim verilerini değil.
diff --git a/docs/tr/agents/security-reviewer.md b/docs/tr/agents/security-reviewer.md
index 8beb9e1c4..dd9d415f5 100644
--- a/docs/tr/agents/security-reviewer.md
+++ b/docs/tr/agents/security-reviewer.md
@@ -1,7 +1,7 @@
---
name: security-reviewer
description: Güvenlik açığı tespit ve düzeltme specialisti. Kullanıcı girdisi, kimlik doğrulama, API endpoint'leri veya hassas veri işleyen kod yazdıktan sonra PROAKTİF olarak kullanın. Secret'ları, SSRF, injection, güvensiz kriptografiyi ve OWASP Top 10 güvenlik açıklarını işaretler.
-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
+tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
diff --git a/docs/tr/the-longform-guide.md b/docs/tr/the-longform-guide.md
index b9f23cc56..c1b78a472 100644
--- a/docs/tr/the-longform-guide.md
+++ b/docs/tr/the-longform-guide.md
@@ -1,12 +1,12 @@
# Claude Code'un Her Şeyine Dair Uzun Kılavuz
-
+
---
> **Ön Koşul**: Bu kılavuz [Claude Code'un Her Şeyine Dair Kısa Kılavuz](./the-shortform-guide.md) üzerine kuruludur. Skill'leri, hook'ları, subagent'ları, MCP'leri ve plugin'leri henüz kurmadıysanız önce onu okuyun.
-
+
*Kısa Kılavuz - önce onu okuyun*
Kısa kılavuzda, temel kurulumu ele aldım: etkili bir Claude Code iş akışının omurgasını oluşturan skill'ler ve command'lar, hook'lar, subagent'lar, MCP'ler, plugin'ler ve yapılandırma desenleri. Bu kurulum kılavuzu ve temel altyapıydı.
@@ -39,7 +39,7 @@ Lazy loading ile, context window sorunu çoğunlukla çözülmüştür. Ancak to
Oturumlar arasında memory paylaşımı için, ilerlemeyi özetleyen ve kontrol eden, ardından `.claude` klasörünüzde bir `.tmp` dosyasına kaydeden ve oturumunuz sonuna kadar ona ekleyen bir skill veya command en iyi bahistir. Ertesi gün bunu context olarak kullanabilir ve kaldığı yerden devam edebilir, her oturum için yeni bir dosya oluşturun böylece eski context'i yeni işe kirletmezsiniz.
-
+
*Oturum depolama örneği ->
@@ -103,13 +104,13 @@
-Спонсори спільноти: Mike Morgan · @jasonwu513 · @1anter · @massimotodaro · @meadmccabe +Спонсори спільноти: @jasonwu513 · @1anter · @massimotodaro · @meadmccabe +Минулі спонсори: Atlas Cloud · Mike Morgan (неактивний) Стати спонсором · Рівні спонсорства · Програма спонсорства @@ -736,6 +737,7 @@ ECC також постачає розширені керовані адапте - **Ціль встановлення Kimi Code** (`--target kimi`): ECC встановлюється нативно в Kimi Code CLI від [Moonshot AI](https://www.moonshot.ai) - **Самостійний хостинг на GPU**: перевірений шлях з [Itô](https://compute.itomarkets.com), бажаним обчислювальним спонсором ECC, включно з опційним мостом RFQ `ecc ito find` (деталі та розкриття вище в опціях встановлення) - **Moonshot AI (Kimi), Itô та Atlas Cloud** тепер публічні спонсори +- **Поточний статус спонсорства:** Atlas Cloud є минулим спонсором. Оголошення вище збережено як історичний запис випуску 2.1. - **Цілі встановлення Hermes + OpenClaw**, посібник з навігації Codex, консолідовані хуки PostToolUse та зміцнення ланцюжка поставок ### Поточна розробка: Уніфікованe сховище пам'яті diff --git a/docs/ur/README.md b/docs/ur/README.md index 32185989e..d98a0dc4c 100644 --- a/docs/ur/README.md +++ b/docs/ur/README.md @@ -1,4 +1,4 @@ -**زبان:** [English](../../README.md) | [اردو](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) +**زبان:** [English](../../README.md) | [اردو](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) # ECC @@ -27,7 +27,7 @@ **زبان / Language / 语言** -[English](../../README.md) | [**اردو**](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) +[English](../../README.md) | [**اردو**](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) diff --git a/docs/vi-VN/README.md b/docs/vi-VN/README.md index 4c9b3d8f7..aff90f2cb 100644 --- a/docs/vi-VN/README.md +++ b/docs/vi-VN/README.md @@ -1,4 +1,4 @@ -**Ngôn ngữ:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) +**Ngôn ngữ:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) # Everything Claude Code @@ -18,7 +18,7 @@ **Ngôn ngữ / Language / 语言 / 語言 / Dil / Язык** -[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) +[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index 31e1a3817..555599e2e 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、292 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、293 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 2.2.2 @@ -147,7 +147,7 @@ ``` agents/ — 68 个专业子代理 -skills/ — 292 个工作流技能和领域知识 +skills/ — 293 个工作流技能和领域知识 commands/ — 94 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 3228c6159..2ce4ef8e5 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -1,4 +1,4 @@ -**语言:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) +**语言:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) # Everything Claude Code @@ -25,7 +25,7 @@ **语言 / Language / 語言 / Dil / Язык / Ngôn ngữ** -[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) +[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) @@ -260,7 +260,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 68 个智能体、292 项技能和 94 个命令了。 +**搞定!** 你现在可以使用 68 个智能体、293 项技能和 94 个命令了。 *** @@ -1174,7 +1174,7 @@ opencode |---------|---------------|----------|--------| | 智能体 | PASS: 68 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 94 个 | PASS: 35 个 | **Claude Code 领先** | -| 技能 | PASS: 292 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 293 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1282,7 +1282,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|-----------------------|------------|-----------|----------| | **智能体** | 68 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 94 | 共享 | 基于指令 | 35 | -| **技能** | 292 | 共享 | 10 (原生格式) | 37 | +| **技能** | 293 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | diff --git a/docs/zh-CN/agents/chief-of-staff.md b/docs/zh-CN/agents/chief-of-staff.md index 157c84fb1..733f97545 100644 --- a/docs/zh-CN/agents/chief-of-staff.md +++ b/docs/zh-CN/agents/chief-of-staff.md @@ -2,7 +2,7 @@ name: chief-of-staff description: 个人通讯首席参谋,负责筛选电子邮件、Slack、LINE和Messenger中的消息。将消息分为4个等级(跳过/仅信息/会议信息/需要行动),生成草稿回复,并通过钩子强制执行发送后的跟进。适用于管理多渠道通讯工作流程时。 tools: ["Read", "Grep", "Glob", "Bash", "Edit", "Write"] -model: opus +model: sonnet --- 你是一位个人幕僚长,通过一个统一的分类处理管道管理所有通信渠道——电子邮件、Slack、LINE、Messenger 和日历。 diff --git a/docs/zh-CN/agents/comment-analyzer.md b/docs/zh-CN/agents/comment-analyzer.md index ba1dff182..b2b1e68b5 100644 --- a/docs/zh-CN/agents/comment-analyzer.md +++ b/docs/zh-CN/agents/comment-analyzer.md @@ -1,7 +1,7 @@ --- name: comment-analyzer description: 分析代码注释的准确性、完整性、可维护性和注释腐烂风险。 -model: sonnet +model: haiku tools: [Read, Grep, Glob] --- diff --git a/docs/zh-CN/agents/conversation-analyzer.md b/docs/zh-CN/agents/conversation-analyzer.md index a91ed543f..e54a87d53 100644 --- a/docs/zh-CN/agents/conversation-analyzer.md +++ b/docs/zh-CN/agents/conversation-analyzer.md @@ -1,7 +1,7 @@ --- name: conversation-analyzer description: 使用此代理分析对话记录,以找到值得通过钩子预防的行为。由不带参数的 /hookify 触发。 -model: sonnet +model: haiku tools: [Read, Grep] --- diff --git a/docs/zh-CN/agents/database-reviewer.md b/docs/zh-CN/agents/database-reviewer.md index f7a4dd6a0..d4d8fcd33 100644 --- a/docs/zh-CN/agents/database-reviewer.md +++ b/docs/zh-CN/agents/database-reviewer.md @@ -1,7 +1,7 @@ --- name: database-reviewer description: PostgreSQL 数据库专家,专注于查询优化、模式设计、安全性和性能。在编写 SQL、创建迁移、设计模式或排查数据库性能问题时,请主动使用。融合了 Supabase 最佳实践。 -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: ["Read", "Grep", "Glob", "Bash"] model: sonnet --- diff --git a/docs/zh-CN/agents/docs-lookup.md b/docs/zh-CN/agents/docs-lookup.md index 2a6188dbf..bb98cfa0b 100644 --- a/docs/zh-CN/agents/docs-lookup.md +++ b/docs/zh-CN/agents/docs-lookup.md @@ -2,7 +2,7 @@ name: docs-lookup description: 当用户询问如何使用库、框架或API,或需要最新的代码示例时,使用Context7 MCP获取当前文档,并返回带有示例的答案。针对文档/API/设置问题调用。 tools: ["Read", "Grep", "mcp__context7__resolve-library-id", "mcp__context7__query-docs"] -model: sonnet +model: haiku --- 你是一名文档专家。你使用通过 Context7 MCP(resolve-library-id 和 query-docs)获取的当前文档来回答关于库、框架和 API 的问题,而不是使用训练数据。 diff --git a/docs/zh-CN/agents/gan-evaluator.md b/docs/zh-CN/agents/gan-evaluator.md index b48f4fe57..70eff2ff3 100644 --- a/docs/zh-CN/agents/gan-evaluator.md +++ b/docs/zh-CN/agents/gan-evaluator.md @@ -1,8 +1,8 @@ --- name: gan-evaluator description: "GAN Harness — Evaluator agent. Tests the live running application via Playwright, scores against rubric, and provides actionable feedback to the Generator." -tools: ["Read", "Write", "Bash", "Grep", "Glob"] -model: opus +tools: ["Read", "Write", "Bash", "Grep", "Glob", "mcp__playwright__browser_navigate", "mcp__playwright__browser_click", "mcp__playwright__browser_take_screenshot", "mcp__playwright__browser_snapshot", "mcp__playwright__browser_type", "mcp__playwright__browser_fill_form", "mcp__playwright__browser_resize", "mcp__playwright__browser_press_key"] +model: sonnet color: red --- diff --git a/docs/zh-CN/agents/gan-generator.md b/docs/zh-CN/agents/gan-generator.md index 63d99d7f8..d1e8367b7 100644 --- a/docs/zh-CN/agents/gan-generator.md +++ b/docs/zh-CN/agents/gan-generator.md @@ -2,7 +2,7 @@ name: gan-generator description: "GAN Harness — Generator agent. Implements features according to the spec, reads evaluator feedback, and iterates until quality threshold is met." tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +model: sonnet color: green --- diff --git a/docs/zh-CN/agents/gan-planner.md b/docs/zh-CN/agents/gan-planner.md index f08c015fc..ceb3dbe1b 100644 --- a/docs/zh-CN/agents/gan-planner.md +++ b/docs/zh-CN/agents/gan-planner.md @@ -2,7 +2,7 @@ name: gan-planner description: "GAN Harness — Planner agent. Expands a one-line prompt into a full product specification with features, sprints, evaluation criteria, and design direction." tools: ["Read", "Write", "Grep", "Glob"] -model: opus +model: sonnet color: purple --- diff --git a/docs/zh-CN/agents/opensource-forker.md b/docs/zh-CN/agents/opensource-forker.md index c8f3e1fca..122ed9a76 100644 --- a/docs/zh-CN/agents/opensource-forker.md +++ b/docs/zh-CN/agents/opensource-forker.md @@ -2,7 +2,7 @@ name: opensource-forker description: 分叉任何项目以进行开源。复制文件,剥离机密和凭据(20多种模式),用占位符替换内部引用,生成.env.example,并清理git历史。这是opensource-pipeline技能的第一阶段。 tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: sonnet +model: haiku --- # 开源分叉工具 diff --git a/docs/zh-CN/agents/opensource-packager.md b/docs/zh-CN/agents/opensource-packager.md index 480247b33..c3b091774 100644 --- a/docs/zh-CN/agents/opensource-packager.md +++ b/docs/zh-CN/agents/opensource-packager.md @@ -2,7 +2,7 @@ name: opensource-packager description: 为经过清理的项目生成完整的开源打包文件。生成 CLAUDE.md、setup.sh、README.md、LICENSE、CONTRIBUTING.md 和 GitHub 问题模板。使任何仓库都能立即与 Claude Code 配合使用。这是 opensource-pipeline 技能的第三阶段。 tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: sonnet +model: haiku --- # 开源打包工具 diff --git a/docs/zh-CN/agents/security-reviewer.md b/docs/zh-CN/agents/security-reviewer.md index f2067a56c..75f0bdd6d 100644 --- a/docs/zh-CN/agents/security-reviewer.md +++ b/docs/zh-CN/agents/security-reviewer.md @@ -1,7 +1,7 @@ --- name: security-reviewer description: 安全漏洞检测与修复专家。在编写处理用户输入、身份验证、API端点或敏感数据的代码后主动使用。标记密钥、SSRF、注入、不安全的加密以及OWASP Top 10漏洞。 -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: ["Read", "Grep", "Glob", "Bash"] model: sonnet --- diff --git a/docs/zh-CN/agents/seo-specialist.md b/docs/zh-CN/agents/seo-specialist.md index e1625f769..9b220ebb8 100644 --- a/docs/zh-CN/agents/seo-specialist.md +++ b/docs/zh-CN/agents/seo-specialist.md @@ -1,7 +1,7 @@ --- name: seo-specialist description: SEO专家,负责技术SEO审计、页面优化、结构化数据、核心网页指标以及内容/关键词映射。用于网站审计、元标签审查、架构标记、站点地图和robots问题以及SEO修复计划。 -tools: ["Read", "Grep", "Glob", "Bash", "WebSearch", "WebFetch"] +tools: ["Read", "Grep", "Glob", "WebSearch", "WebFetch"] model: sonnet --- diff --git a/docs/zh-TW/README.md b/docs/zh-TW/README.md index 4d46dfce2..194e98af5 100644 --- a/docs/zh-TW/README.md +++ b/docs/zh-TW/README.md @@ -13,7 +13,7 @@ **Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ** -[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | **繁體中文** | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) +[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | **繁體中文** | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) diff --git a/docs/zh-TW/agents/build-error-resolver.md b/docs/zh-TW/agents/build-error-resolver.md index 412479019..9aca9d21f 100644 --- a/docs/zh-TW/agents/build-error-resolver.md +++ b/docs/zh-TW/agents/build-error-resolver.md @@ -2,7 +2,7 @@ name: build-error-resolver description: Build and TypeScript error resolution specialist. Use PROACTIVELY when build fails or type errors occur. Fixes build/type errors only with minimal diffs, no architectural edits. Focuses on getting the build green quickly. tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +model: sonnet --- # 建置錯誤解決專家 diff --git a/docs/zh-TW/agents/code-reviewer.md b/docs/zh-TW/agents/code-reviewer.md index 2a732d0d8..fa8aaeb10 100644 --- a/docs/zh-TW/agents/code-reviewer.md +++ b/docs/zh-TW/agents/code-reviewer.md @@ -2,7 +2,7 @@ name: code-reviewer description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code. MUST BE USED for all code changes. tools: ["Read", "Grep", "Glob", "Bash"] -model: opus +model: sonnet --- 您是一位資深程式碼審查員,確保程式碼品質和安全性的高標準。 diff --git a/docs/zh-TW/agents/database-reviewer.md b/docs/zh-TW/agents/database-reviewer.md index 1e8c2ad71..8e8e8a734 100644 --- a/docs/zh-TW/agents/database-reviewer.md +++ b/docs/zh-TW/agents/database-reviewer.md @@ -1,8 +1,8 @@ --- name: database-reviewer description: PostgreSQL database specialist for query optimization, schema design, security, and performance. Use PROACTIVELY when writing SQL, creating migrations, designing schemas, or troubleshooting database performance. Incorporates Supabase best practices. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +tools: ["Read", "Grep", "Glob", "Bash"] +model: sonnet --- # 資料庫審查員 diff --git a/docs/zh-TW/agents/doc-updater.md b/docs/zh-TW/agents/doc-updater.md index c2df8b51a..13c4054ed 100644 --- a/docs/zh-TW/agents/doc-updater.md +++ b/docs/zh-TW/agents/doc-updater.md @@ -2,7 +2,7 @@ name: doc-updater description: Documentation and codemap specialist. Use PROACTIVELY for updating codemaps and documentation. Runs /update-codemaps and /update-docs, generates docs/CODEMAPS/*, updates READMEs and guides. tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +model: haiku --- # 文件與程式碼地圖專家 diff --git a/docs/zh-TW/agents/e2e-runner.md b/docs/zh-TW/agents/e2e-runner.md index a88b0c1e3..037889b1f 100644 --- a/docs/zh-TW/agents/e2e-runner.md +++ b/docs/zh-TW/agents/e2e-runner.md @@ -2,7 +2,7 @@ name: e2e-runner description: End-to-end testing specialist using Vercel Agent Browser (preferred) with Playwright fallback. Use PROACTIVELY for generating, maintaining, and running E2E tests. Manages test journeys, quarantines flaky tests, uploads artifacts (screenshots, videos, traces), and ensures critical user flows work. tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +model: sonnet --- # E2E 測試執行器 diff --git a/docs/zh-TW/agents/go-build-resolver.md b/docs/zh-TW/agents/go-build-resolver.md index 217b7bdcc..91361d6c8 100644 --- a/docs/zh-TW/agents/go-build-resolver.md +++ b/docs/zh-TW/agents/go-build-resolver.md @@ -2,7 +2,7 @@ name: go-build-resolver description: Go build, vet, and compilation error resolution specialist. Fixes build errors, go vet issues, and linter warnings with minimal changes. Use when Go builds fail. tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +model: sonnet --- # Go 建置錯誤解決專家 diff --git a/docs/zh-TW/agents/go-reviewer.md b/docs/zh-TW/agents/go-reviewer.md index b6a96b880..2e0af2d4a 100644 --- a/docs/zh-TW/agents/go-reviewer.md +++ b/docs/zh-TW/agents/go-reviewer.md @@ -2,7 +2,7 @@ name: go-reviewer description: Expert Go code reviewer specializing in idiomatic Go, concurrency patterns, error handling, and performance. Use for all Go code changes. MUST BE USED for Go projects. tools: ["Read", "Grep", "Glob", "Bash"] -model: opus +model: sonnet --- 您是一位資深 Go 程式碼審查員,確保慣用 Go 和最佳實務的高標準。 diff --git a/docs/zh-TW/agents/refactor-cleaner.md b/docs/zh-TW/agents/refactor-cleaner.md index b5f3a9154..02f5c0255 100644 --- a/docs/zh-TW/agents/refactor-cleaner.md +++ b/docs/zh-TW/agents/refactor-cleaner.md @@ -2,7 +2,7 @@ name: refactor-cleaner description: Dead code cleanup and consolidation specialist. Use PROACTIVELY for removing unused code, duplicates, and refactoring. Runs analysis tools (knip, depcheck, ts-prune) to identify dead code and safely removes it. tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +model: sonnet --- # 重構與無用程式碼清理專家 diff --git a/docs/zh-TW/agents/security-reviewer.md b/docs/zh-TW/agents/security-reviewer.md index 4acd77f6c..0b6255d44 100644 --- a/docs/zh-TW/agents/security-reviewer.md +++ b/docs/zh-TW/agents/security-reviewer.md @@ -1,8 +1,8 @@ --- name: security-reviewer description: Security vulnerability detection and remediation specialist. Use PROACTIVELY after writing code that handles user input, authentication, API endpoints, or sensitive data. Flags secrets, SSRF, injection, unsafe crypto, and OWASP Top 10 vulnerabilities. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] -model: opus +tools: ["Read", "Grep", "Glob", "Bash"] +model: sonnet --- # 安全性審查員 diff --git a/docs/zh-TW/agents/tdd-guide.md b/docs/zh-TW/agents/tdd-guide.md index 256c2e3fc..3dbac8bfa 100644 --- a/docs/zh-TW/agents/tdd-guide.md +++ b/docs/zh-TW/agents/tdd-guide.md @@ -2,7 +2,7 @@ name: tdd-guide description: Test-Driven Development specialist enforcing write-tests-first methodology. Use PROACTIVELY when writing new features, fixing bugs, or refactoring code. Ensures 80%+ test coverage. tools: ["Read", "Write", "Edit", "Bash", "Grep"] -model: opus +model: sonnet --- 您是一位 TDD(測試驅動開發)專家,確保所有程式碼都以測試先行的方式開發,並具有全面的覆蓋率。 diff --git a/manifests/context-packs/skill-triggers@1.json b/manifests/context-packs/skill-triggers@1.json index d591dee56..c70b8d116 100644 --- a/manifests/context-packs/skill-triggers@1.json +++ b/manifests/context-packs/skill-triggers@1.json @@ -1 +1 @@ -{"coverage":{"skills":292,"withTriggers":32},"generatedAt":"2026-09-24T23:51:22.784Z","id":"skill-triggers@1","model":{"effort":null,"id":"hand-seeded","source":"manual-curation-pending-regeneration"},"registryDigest":"2c24ec8ddbe6837f0187e2c953e17e14d83b45d348850643e9bd806e00efe70c","schemaVersion":1,"triggers":{"skill:api-connector-builder":["add api integration","new provider connector","match existing integration pattern"],"skill:api-design":["rest endpoint design","pagination api","status codes","api versioning","rate limiting api","resource naming","filtering api","api error responses","offset pagination","limit query parameter","pagination defaults"],"skill:backend-patterns":["express api","node backend architecture","nextjs api routes","server side patterns","data access layer","static file server","url path handling","file server"],"skill:browser-qa":["deployed feature test","visual regression screenshots","core web vitals check","axe accessibility audit","ship do not ship","staging verification"],"skill:canary-watch":["post deploy monitoring","smoke test url","production url check","console errors production","sse stream check","after deploy verification"],"skill:code-tour":["onboarding walkthrough","explain subsystem","architecture tour","pr walkthrough","rca tour"],"skill:coding-standards":["code review standards","naming conventions","readability review","immutability conventions","fix naming typo","export naming","consistent exports"],"skill:content-hash-cache-pattern":["cache file processing","content addressed cache","sha256 hash cache"],"skill:database-migrations":["zero downtime migration","schema change production","add column large table","backfill data","expand contract","concurrent index","migration rollback","prisma migration","django migration"],"skill:deployment-patterns":["ci cd setup","dockerize app","health checks","rollback strategy","production readiness","deploy pipeline","containerize application"],"skill:design-system":["design tokens","visual consistency audit","css custom properties","ui audit","design system bootstrap"],"skill:django-patterns":["django orm","drf api","django rest framework","django caching","django signals","django middleware"],"skill:django-security":["django authentication","csrf protection","sql injection prevention","xss prevention","django deployment security","role based access control","authorization middleware","permissions checks"],"skill:docker-patterns":["dockerfile review","docker compose setup","container security","multi service orchestration"],"skill:error-handling":["error types","retry logic","circuit breaker","user facing errors","exception handling patterns","typed errors","error boundaries","go error handling","custom error class","error codes","config validation"],"skill:evm-token-decimals":["token decimals","wei conversion","erc20 balance off","bridge token precision"],"skill:frontend-a11y":["aria attributes","screen reader support","focus management","semantic html","form labeling","keyboard navigation react","a11y lint errors"],"skill:git-workflow":["merge vs rebase","commit conventions","resolve merge conflict","branching strategy","clean up commits","pull request cleanup","git history tidy"],"skill:hexagonal-architecture":["ports and adapters","dependency injection boundaries","decouple domain from io"],"skill:kubernetes-patterns":["kubernetes manifests","kubectl debugging","pod probes","k8s rbac","autoscaling config","configmap secrets"],"skill:orch-fix-defect":["fix a bug","broken behavior","regression fix","reproduce bug","defect repair"],"skill:postgres-patterns":["slow postgres query","query optimization","index design","rls policies","supabase schema","postgres indexing","database performance","schema design postgres","postgres driver","node postgres","query planner"],"skill:python-patterns":["pythonic code","pep 8","type hints python","python code review","idiomatic python"],"skill:python-testing":["pytest fixtures","mocking python","parametrized tests","coverage python","tdd python"],"skill:redis-patterns":["cache aside pattern","distributed lock","redis rate limiting","cache invalidation"],"skill:regex-vs-llm-structured-text":["parse invoice","extract receipt data","text extraction pipeline","parse form fields","cheap document parser","extract table data","parse log lines","parse access logs","common log format","log line parsing"],"skill:rust-patterns":["rust ownership","borrow checker","rust error handling","traits rust","rust concurrency","idiomatic rust"],"skill:search-first":["find existing library","npm package research","before writing custom code","evaluate existing tools","add dependency research"],"skill:security-review":["security audit","authentication review","sanitize user input","secrets handling","payment security checklist","prevent injection attacks","secure api endpoints","authn authz review","vulnerability checklist","input validation security","parameterized queries","sql injection"],"skill:security-scan":["audit claude config","claudemd security","mcp server audit","agentshield scan","hook configuration audit","settings json security"],"skill:tdd-workflow":["write test first","failing test","red green refactor","test driven development","regression test first","write a regression test"],"skill:verification-loop":["pre pr checks","verification report","quality gates","build lint test coverage","before creating a pr"]},"triggersDigest":"25b97a9e06fc336c7cf95ab854ed1a41033a54bcd6e1fb1cf69dc906332462aa"} +{"coverage":{"skills":293,"withTriggers":32},"generatedAt":"2026-09-24T23:51:22.784Z","id":"skill-triggers@1","model":{"effort":null,"id":"hand-seeded","source":"manual-curation-pending-regeneration"},"registryDigest":"9577e4e4d33f6fe2ec0312506fa23e0165685947f8e36c6c6dc2236bfee46813","schemaVersion":1,"triggers":{"skill:api-connector-builder":["add api integration","new provider connector","match existing integration pattern"],"skill:api-design":["rest endpoint design","pagination api","status codes","api versioning","rate limiting api","resource naming","filtering api","api error responses","offset pagination","limit query parameter","pagination defaults"],"skill:backend-patterns":["express api","node backend architecture","nextjs api routes","server side patterns","data access layer","static file server","url path handling","file server"],"skill:browser-qa":["deployed feature test","visual regression screenshots","core web vitals check","axe accessibility audit","ship do not ship","staging verification"],"skill:canary-watch":["post deploy monitoring","smoke test url","production url check","console errors production","sse stream check","after deploy verification"],"skill:code-tour":["onboarding walkthrough","explain subsystem","architecture tour","pr walkthrough","rca tour"],"skill:coding-standards":["code review standards","naming conventions","readability review","immutability conventions","fix naming typo","export naming","consistent exports"],"skill:content-hash-cache-pattern":["cache file processing","content addressed cache","sha256 hash cache"],"skill:database-migrations":["zero downtime migration","schema change production","add column large table","backfill data","expand contract","concurrent index","migration rollback","prisma migration","django migration"],"skill:deployment-patterns":["ci cd setup","dockerize app","health checks","rollback strategy","production readiness","deploy pipeline","containerize application"],"skill:design-system":["design tokens","visual consistency audit","css custom properties","ui audit","design system bootstrap"],"skill:django-patterns":["django orm","drf api","django rest framework","django caching","django signals","django middleware"],"skill:django-security":["django authentication","csrf protection","sql injection prevention","xss prevention","django deployment security","role based access control","authorization middleware","permissions checks"],"skill:docker-patterns":["dockerfile review","docker compose setup","container security","multi service orchestration"],"skill:error-handling":["error types","retry logic","circuit breaker","user facing errors","exception handling patterns","typed errors","error boundaries","go error handling","custom error class","error codes","config validation"],"skill:evm-token-decimals":["token decimals","wei conversion","erc20 balance off","bridge token precision"],"skill:frontend-a11y":["aria attributes","screen reader support","focus management","semantic html","form labeling","keyboard navigation react","a11y lint errors"],"skill:git-workflow":["merge vs rebase","commit conventions","resolve merge conflict","branching strategy","clean up commits","pull request cleanup","git history tidy"],"skill:hexagonal-architecture":["ports and adapters","dependency injection boundaries","decouple domain from io"],"skill:kubernetes-patterns":["kubernetes manifests","kubectl debugging","pod probes","k8s rbac","autoscaling config","configmap secrets"],"skill:orch-fix-defect":["fix a bug","broken behavior","regression fix","reproduce bug","defect repair"],"skill:postgres-patterns":["slow postgres query","query optimization","index design","rls policies","supabase schema","postgres indexing","database performance","schema design postgres","postgres driver","node postgres","query planner"],"skill:python-patterns":["pythonic code","pep 8","type hints python","python code review","idiomatic python"],"skill:python-testing":["pytest fixtures","mocking python","parametrized tests","coverage python","tdd python"],"skill:redis-patterns":["cache aside pattern","distributed lock","redis rate limiting","cache invalidation"],"skill:regex-vs-llm-structured-text":["parse invoice","extract receipt data","text extraction pipeline","parse form fields","cheap document parser","extract table data","parse log lines","parse access logs","common log format","log line parsing"],"skill:rust-patterns":["rust ownership","borrow checker","rust error handling","traits rust","rust concurrency","idiomatic rust"],"skill:search-first":["find existing library","npm package research","before writing custom code","evaluate existing tools","add dependency research"],"skill:security-review":["security audit","authentication review","sanitize user input","secrets handling","payment security checklist","prevent injection attacks","secure api endpoints","authn authz review","vulnerability checklist","input validation security","parameterized queries","sql injection"],"skill:security-scan":["audit claude config","claudemd security","mcp server audit","agentshield scan","hook configuration audit","settings json security"],"skill:tdd-workflow":["write test first","failing test","red green refactor","test driven development","regression test first","write a regression test"],"skill:verification-loop":["pre pr checks","verification report","quality gates","build lint test coverage","before creating a pr"]},"triggersDigest":"25b97a9e06fc336c7cf95ab854ed1a41033a54bcd6e1fb1cf69dc906332462aa"} diff --git a/manifests/install-components.json b/manifests/install-components.json index 8a6205bbd..5f8970c75 100644 --- a/manifests/install-components.json +++ b/manifests/install-components.json @@ -677,6 +677,14 @@ "modules": [ "docs-uk-ua" ] + }, + { + "id": "locale:pl", + "family": "locale", + "description": "Polish (pl) translated reference docs installed to ~/.claude/docs/pl/.", + "modules": [ + "docs-pl" + ] } ] } diff --git a/manifests/install-modules.json b/manifests/install-modules.json index 884c7d39d..b61d4e20f 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -173,6 +173,7 @@ "skills/fastapi-patterns", "skills/frontend-design-direction", "skills/frontend-patterns", + "skills/i18n-sync", "skills/frontend-slides", "skills/make-interfaces-feel-better", "skills/golang-patterns", @@ -1177,6 +1178,22 @@ "defaultInstall": false, "cost": "heavy", "stability": "stable" + }, + { + "id": "docs-pl", + "kind": "docs", + "description": "Polish (pl) getting-started and core-concepts guide with a terminology glossary.", + "paths": [ + "docs/pl" + ], + "targets": [ + "claude", + "claude-project" + ], + "dependencies": [], + "defaultInstall": false, + "cost": "heavy", + "stability": "stable" } ] } diff --git a/package.json b/package.json index 76a3f0290..57357af0e 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "docs/ja-JP/", "docs/ko-KR/", "docs/pt-BR/", + "docs/pl/", "docs/ru/", "docs/tr/", "docs/uk-UA/", @@ -252,6 +253,7 @@ "skills/homelab-network-readiness/", "skills/homelab-network-setup/", "skills/hookify-rules/", + "skills/i18n-sync/", "skills/inventory-demand-planning/", "skills/ito-baskets/", "skills/ito-compute/", diff --git a/scripts/ci/catalog.js b/scripts/ci/catalog.js index d538dad36..fffa37a3d 100644 --- a/scripts/ci/catalog.js +++ b/scripts/ci/catalog.js @@ -18,6 +18,8 @@ const path = require('path'); const ROOT = path.join(__dirname, '../..'); const README_PATH = path.join(ROOT, 'README.md'); const AGENTS_PATH = path.join(ROOT, 'AGENTS.md'); +const SOUL_PATH = path.join(ROOT, 'SOUL.md'); +const GEMINI_PATH = path.join(ROOT, '.gemini', 'GEMINI.md'); const README_ZH_CN_PATH = path.join(ROOT, 'README.zh-CN.md'); const DOCS_ZH_CN_README_PATH = path.join(ROOT, 'docs', 'zh-CN', 'README.md'); const DOCS_ZH_CN_AGENTS_PATH = path.join(ROOT, 'docs', 'zh-CN', 'AGENTS.md'); @@ -273,6 +275,30 @@ function parseAgentsDocExpectations(agentsContent) { return expectations; } +function parseCrossHarnessIdentityExpectations(content, source) { + const match = content.match(/with\s+(\d+)\s+specialized agents,\s+(\d+)\s+skills,\s+(?:and\s+)?(\d+)\s+commands/i); + if (!match) { + throw new Error(`${source} is missing the catalog summary line`); + } + + return [ + { category: 'agents', mode: 'exact', expected: Number(match[1]), source }, + { category: 'skills', mode: 'exact', expected: Number(match[2]), source }, + { category: 'commands', mode: 'exact', expected: Number(match[3]), source }, + ]; +} + +function syncCrossHarnessIdentity(content, catalog, source) { + return replaceOrThrow( + content, + /(with\s+)(\d+)(\s+specialized agents,\s+)(\d+)(\s+skills,\s+(?:and\s+)?)(\d+)(\s+commands)/i, + (_, prefix, __, agentsSuffix, ___, skillsSuffix, ____, commandsSuffix) => ( + `${prefix}${catalog.agents.count}${agentsSuffix}${catalog.skills.count}${skillsSuffix}${catalog.commands.count}${commandsSuffix}` + ), + source + ); +} + function parseZhAgentsDocExpectations(agentsContent) { const summaryMatch = agentsContent.match(/提供\s+(\d+)\s+个专业代理、\s*(\d+)(\+)?\s*项技能、\s*(\d+)\s+条命令/i); if (!summaryMatch) { @@ -563,6 +589,8 @@ function createDocumentSpecs(paths = {}) { const { readmePath = README_PATH, agentsPath = AGENTS_PATH, + soulPath = SOUL_PATH, + geminiPath = GEMINI_PATH, zhRootReadmePath = README_ZH_CN_PATH, zhDocsReadmePath = DOCS_ZH_CN_README_PATH, zhDocsAgentsPath = DOCS_ZH_CN_AGENTS_PATH, @@ -581,6 +609,16 @@ function createDocumentSpecs(paths = {}) { parseExpectations: parseAgentsDocExpectations, syncContent: syncEnglishAgents, }, + { + filePath: soulPath, + parseExpectations: content => parseCrossHarnessIdentityExpectations(content, 'SOUL.md'), + syncContent: (content, catalog) => syncCrossHarnessIdentity(content, catalog, 'SOUL.md'), + }, + { + filePath: geminiPath, + parseExpectations: content => parseCrossHarnessIdentityExpectations(content, '.gemini/GEMINI.md'), + syncContent: (content, catalog) => syncCrossHarnessIdentity(content, catalog, '.gemini/GEMINI.md'), + }, { filePath: zhRootReadmePath, parseExpectations: parseZhRootReadmeExpectations, @@ -633,6 +671,8 @@ function createDocumentSpecsForRoot(root) { return createDocumentSpecs({ readmePath: path.join(root, 'README.md'), agentsPath: path.join(root, 'AGENTS.md'), + soulPath: path.join(root, 'SOUL.md'), + geminiPath: path.join(root, '.gemini', 'GEMINI.md'), zhRootReadmePath: path.join(root, 'README.zh-CN.md'), zhDocsReadmePath: path.join(root, 'docs', 'zh-CN', 'README.md'), zhDocsAgentsPath: path.join(root, 'docs', 'zh-CN', 'AGENTS.md'), @@ -742,6 +782,7 @@ module.exports = { formatExpectation, main, parseAgentsDocExpectations, + parseCrossHarnessIdentityExpectations, parseCatalogDescriptionExpectations, parseReadmeExpectations, parseZhAgentsDocExpectations, @@ -751,6 +792,7 @@ module.exports = { syncCatalogDescription, syncEnglishAgents, syncEnglishReadme, + syncCrossHarnessIdentity, syncZhAgents, syncZhDocsReadme, syncZhRootReadme, diff --git a/scripts/ci/verify-release-gates.js b/scripts/ci/verify-release-gates.js new file mode 100644 index 000000000..bed334456 --- /dev/null +++ b/scripts/ci/verify-release-gates.js @@ -0,0 +1,365 @@ +'use strict'; + +const fs = require('node:fs'); +const { performance } = require('node:perf_hooks'); + +const API_VERSION = '2022-11-28'; +const SHA = /^[0-9a-f]{40}$/; +const MAX_PAGES = 10; +const MAX_ITEMS = 1000; +const DEFAULT_ATTEMPTS = 20; +const DEFAULT_DELAY_MS = 30_000; +const TOTAL_TIMEOUT_MS = 600_000; +const REQUEST_TIMEOUT_MS = 15_000; +const CI_PATH = '.github/workflows/ci.yml'; +const CODEQL_PATH = 'dynamic/github-code-scanning/codeql'; +// Repository policy: default CodeQL must complete all three categories in ONE +// attempt. A new category requires an explicit policy update, not silent approval. +const REQUIRED_CODEQL = ['Analyze (actions)', 'Analyze (javascript-typescript)', 'Analyze (python)']; +const ACTIONS_APP = { id: 15368, slug: 'github-actions' }; + +const record = value => value !== null && typeof value === 'object' && !Array.isArray(value); +const id = value => Number.isSafeInteger(value) && value > 0; +const sha = value => typeof value === 'string' && SHA.test(value); +const text = value => typeof value === 'string' && value.length > 0; +const resultShape = value => record(value) && text(value.status) + && (value.conclusion === null || text(value.conclusion)); +const repoShape = value => record(value) && id(value.id) && text(value.full_name); +const objectShape = value => record(value) && text(value.type) && sha(value.sha); +const referenceShape = value => record(value) && text(value.ref) && objectShape(value.object); +const tagShape = value => record(value) && sha(value.sha) && text(value.tag) + && objectShape(value.object) && record(value.verification) + && typeof value.verification.verified === 'boolean' && text(value.verification.reason); +const workflowShape = value => record(value) && id(value.id) && text(value.path) && text(value.state); +const runShape = value => resultShape(value) && id(value.id) && id(value.workflow_id) + && text(value.path) && sha(value.head_sha) && text(value.head_branch) && text(value.event) + && id(value.run_attempt) && id(value.check_suite_id) + && repoShape(value.repository) && repoShape(value.head_repository); +const checkShape = value => resultShape(value) && id(value.id) && text(value.name) + && sha(value.head_sha) && record(value.check_suite) && id(value.check_suite.id) + && record(value.app) && id(value.app.id) && text(value.app.slug); +const jobShape = value => resultShape(value) && id(value.id) && text(value.name) + && id(value.run_id) && id(value.run_attempt) && sha(value.head_sha) + && text(value.head_branch) && text(value.check_run_url); + +function requiredEnvironment(env = process.env) { + const inputs = { + repository: env.GITHUB_REPOSITORY, + releaseSha: env.RELEASE_SHA, + releaseTag: env.RELEASE_TAG, + token: env.GITHUB_TOKEN, + tagObjectSha: env.RELEASE_TAG_OBJECT_SHA, + }; + for (const name of ['repository', 'releaseSha', 'releaseTag', 'token']) { + if (!text(inputs[name])) throw new Error(`Missing required release gate input: ${name}`); + } + validateInputs(inputs); + return inputs; +} + +function validateInputs(inputs) { + if (!/^[A-Za-z0-9_-][A-Za-z0-9_.-]*\/[A-Za-z0-9_-][A-Za-z0-9_.-]*$/.test(inputs.repository || '')) { + throw new Error('Invalid release repository'); + } + if (!sha(inputs.releaseSha)) throw new Error('RELEASE_SHA must be a full lowercase commit SHA'); + if (!/^v[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/.test(inputs.releaseTag || '')) { + throw new Error('RELEASE_TAG is not a supported version tag'); + } + if (!text(inputs.token)) throw new Error('Missing release gate token'); + if (inputs.tagObjectSha !== undefined && !sha(inputs.tagObjectSha)) { + throw new Error('Invalid expected tag object SHA'); + } +} + +function setting(value, fallback, maximum) { + const parsed = value === undefined ? fallback : Number(value); + if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > maximum) { + throw new Error('Release gate settings must be positive integers within their finite limits'); + } + return parsed; +} + +class ReleaseGateDeadlineError extends Error {} + +function createGithubClient(inputs, fetchImpl = fetch, options = {}) { + validateInputs(inputs); + const now = options.now || (() => performance.now()); + const deadline = now() + setting(options.timeoutMs, TOTAL_TIMEOUT_MS, TOTAL_TIMEOUT_MS); + const requestMs = setting(options.requestTimeoutMs, REQUEST_TIMEOUT_MS, REQUEST_TIMEOUT_MS); + const base = `https://api.github.com/repos/${inputs.repository}`; + + function remaining() { + const left = deadline - now(); + if (left <= 0) throw new ReleaseGateDeadlineError('Release gate global deadline exceeded'); + return left; + } + + async function bounded(operation, limit) { + const controller = new AbortController(); + let timer; + try { + return await Promise.race([ + Promise.resolve().then(() => operation(controller.signal)), + new Promise((_, reject) => { + timer = setTimeout(() => { + controller.abort(); + reject(new ReleaseGateDeadlineError('Release gate request or global deadline exceeded')); + }, Math.min(limit, remaining())); + }), + ]); + } finally { + clearTimeout(timer); + } + } + + function urlFor(pathOrUrl) { + const url = new URL(pathOrUrl === '' || pathOrUrl.startsWith('/') ? base + pathOrUrl : pathOrUrl); + if (url.origin !== 'https://api.github.com' || url.username || url.password || url.hash + || (url.pathname !== `/repos/${inputs.repository}` && !url.pathname.startsWith(`/repos/${inputs.repository}/`))) { + throw new Error('GitHub API URL escaped the release repository'); + } + return url; + } + + async function page(url, validator) { + remaining(); + return bounded(async signal => { + const response = await fetchImpl(url.toString(), { + redirect: 'error', signal, + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${inputs.token}`, + 'X-GitHub-Api-Version': API_VERSION, + }, + }); + if (!response.ok) throw new Error(`GitHub API failed with status ${response.status}`); + let payload; + try { payload = await response.json(); } catch { throw new Error('Invalid GitHub API JSON response'); } + if (!validator(payload)) throw new Error('GitHub API response validation failed'); + remaining(); + return { payload, link: response.headers?.get?.('link') }; + }, requestMs); + } + + async function get(path, validator) { + return (await page(urlFor(path), validator)).payload; + } + + async function pages(path, key, itemValidator) { + const first = urlFor(path); + const seen = new Set(); + const identities = new Set(); + let next = first; + let total; + const items = []; + while (next) { + const identity = paginationIdentity(next, first); + if (seen.has(identity)) throw new Error('GitHub API pagination cycle'); + if (seen.size >= MAX_PAGES) throw new Error('GitHub API page limit exceeded'); + seen.add(identity); + const { payload, link } = await page(next, value => record(value) + && Number.isSafeInteger(value.total_count) && value.total_count >= 0 + && Array.isArray(value[key])); + if (payload.total_count > MAX_ITEMS || payload[key].length > 100) { + throw new Error('GitHub API item limit exceeded'); + } + if (total !== undefined && total !== payload.total_count) throw new Error('GitHub API collection total changed'); + total = payload.total_count; + for (const item of payload[key]) { + if (!itemValidator(item)) throw new Error('GitHub API response validation failed'); + if (identities.has(item.id)) throw new Error('Ambiguous duplicate GitHub API item'); + identities.add(item.id); + items.push(item); + } + if (items.length > MAX_ITEMS || items.length > total) throw new Error('GitHub API item limit or total exceeded'); + const linkUrl = nextPageUrl(link); + next = linkUrl ? urlFor(linkUrl) : null; + } + if (items.length !== total) throw new Error('Incomplete GitHub API collection total'); + return items; + } + + return { get, pages, pause: sleep => bounded(signal => sleep(signal), remaining()), remaining }; +} + +function paginationIdentity(url, first) { + const query = candidate => { + const keys = [...candidate.searchParams.keys()]; + if (new Set(keys).size !== keys.length) throw new Error('Ambiguous pagination query'); + return [...candidate.searchParams].filter(([key]) => key !== 'page').sort().map(pair => JSON.stringify(pair)).join(','); + }; + const page = url.searchParams.get('page'); + if (url.pathname !== first.pathname || query(url) !== query(first) + || (page !== null && !/^[1-9][0-9]*$/.test(page))) { + throw new Error('GitHub API pagination escaped the endpoint collection'); + } + return `${url.pathname}?${query(url)}&page=${page || '1'}`; +} + +function nextPageUrl(header) { + if (!header) return null; + let next = null; + for (const entry of header.split(',')) { + const match = entry.trim().match(/^<([^>]+)>;\s*rel="(next|prev|first|last)"$/); + if (!match) throw new Error('Malformed GitHub API pagination Link'); + if (match[2] === 'next') { + if (next) throw new Error('Ambiguous GitHub API next page'); + next = match[1]; + } + } + return next; +} + +async function verifySignedAnnotatedTag(inputs, fetchImpl = fetch, options = {}) { + validateInputs(inputs); + const client = options.client || createGithubClient(inputs, fetchImpl, options); + const reference = await client.get(`/git/ref/tags/${encodeURIComponent(inputs.releaseTag)}`, referenceShape); + if (reference.ref !== `refs/tags/${inputs.releaseTag}` || reference.object.type !== 'tag') { + throw new Error('Release ref must match the requested annotated tag; lightweight tags are rejected'); + } + if (inputs.tagObjectSha && reference.object.sha !== inputs.tagObjectSha) { + throw new Error('Release tag object changed after initial verification'); + } + const tag = await client.get(`/git/tags/${reference.object.sha}`, tagShape); + if (tag.sha !== reference.object.sha || tag.tag !== inputs.releaseTag) { + throw new Error('Signed tag object identity or name does not match the release ref'); + } + // GitHub signature validity is not a project-specific authorized-signer list. + if (tag.verification.verified !== true || tag.verification.reason !== 'valid') { + throw new Error('Release tag signature is not verified'); + } + if (tag.object.type !== 'commit' || tag.object.sha !== inputs.releaseSha) { + throw new Error('Verified release tag does not point at the checked-out commit'); + } + return tag.sha; +} + +async function trustedProducers(client, inputs) { + const repository = await client.get('', value => repoShape(value) && value.default_branch === 'main'); + if (repository.full_name !== inputs.repository) throw new Error('Repository identity mismatch'); + const workflows = await client.pages('/actions/workflows?per_page=100', 'workflows', workflowShape); + const select = path => { + const matches = workflows.filter(workflow => workflow.path === path); + if (matches.length !== 1 || matches[0].state !== 'active') throw new Error('Missing or ambiguous active trusted workflow'); + return matches[0]; + }; + return { repository, ci: select(CI_PATH), codeql: select(CODEQL_PATH) }; +} + +function selectRuns(runs, inputs, trusted) { + const sameRepo = repo => repo.id === trusted.repository.id && repo.full_name === inputs.repository; + const select = (workflow, event) => runs.filter(run => run.workflow_id === workflow.id + && run.path === workflow.path && run.head_sha === inputs.releaseSha && run.head_branch === 'main' + && run.event === event && sameRepo(run.repository) && sameRepo(run.head_repository)) + .sort((a, b) => b.id - a.id || b.run_attempt - a.run_attempt)[0]; + return { ci: select(trusted.ci, 'push'), codeql: select(trusted.codeql, 'dynamic') }; +} + +function statusOf(result, label, pendingLabel = label) { + if (!result) return { state: 'pending', reason: `${pendingLabel} run not found for release SHA` }; + if (result.status !== 'completed') return { state: 'pending', reason: `${pendingLabel} is ${result.status}` }; + return result.conclusion === 'success' ? { state: 'passed' } + : { state: 'failed', reason: `${label} concluded ${result.conclusion}` }; +} + +function assessExactShaGates(selected, checks, jobs, inputs) { + for (const [name, run] of Object.entries(selected)) { + const assessment = statusOf(run, name); + if (assessment.state !== 'passed') return assessment; + } + const run = selected.codeql; + if (jobs.some(job => !REQUIRED_CODEQL.includes(job.name))) { + throw new Error('Unexpected CodeQL category; review the explicit required-category policy'); + } + for (const name of REQUIRED_CODEQL) { + const matches = jobs.filter(job => job.name === name); + if (matches.length > 1) throw new Error('Ambiguous required CodeQL job'); + const job = matches[0]; + if (!job) return { state: 'pending', reason: `CodeQL job "${name}" missing from selected attempt` }; + if (job.run_id !== run.id || job.run_attempt !== run.run_attempt + || job.head_sha !== inputs.releaseSha || job.head_branch !== 'main') { + throw new Error('CodeQL job does not belong to the selected run attempt'); + } + const check = checks.find(candidate => job.check_run_url + === `https://api.github.com/repos/${inputs.repository}/check-runs/${candidate.id}`); + if (!check || check.name !== name || check.head_sha !== inputs.releaseSha + || check.check_suite.id !== run.check_suite_id || check.app.id !== ACTIONS_APP.id + || check.app.slug !== ACTIONS_APP.slug) { + return { state: 'pending', reason: `CodeQL check "${name}" missing or not bound to trusted job` }; + } + for (const [kind, result] of [['job', job], ['check', check]]) { + const assessment = statusOf(result, name, `CodeQL ${kind} "${name}"`); + if (assessment.state !== 'passed') return assessment; + } + } + return { state: 'passed' }; +} + +function defaultSleep(delay, signal) { + return new Promise(resolve => { + const timer = setTimeout(resolve, delay); + signal.addEventListener('abort', () => { clearTimeout(timer); resolve(); }, { once: true }); + }); +} + +async function waitForExactShaGates(inputs, fetchImpl = fetch, sleep = defaultSleep, options = {}) { + const client = options.client || createGithubClient(inputs, fetchImpl, options); + const attempts = setting(options.attempts ?? process.env.RELEASE_GATE_ATTEMPTS, DEFAULT_ATTEMPTS, DEFAULT_ATTEMPTS); + const delay = setting(options.delayMs ?? process.env.RELEASE_GATE_DELAY_MS, DEFAULT_DELAY_MS, DEFAULT_DELAY_MS); + let lastReason = 'no gate assessment completed'; + try { + const trusted = await trustedProducers(client, inputs); + const readRuns = async () => selectRuns(await client.pages( + `/actions/runs?head_sha=${inputs.releaseSha}&branch=main&per_page=100`, 'workflow_runs', runShape + ), inputs, trusted); + for (let attempt = 1; attempt <= attempts; attempt += 1) { + const selected = await readRuns(); + let assessment = statusOf(selected.ci, 'CI'); + if (assessment.state === 'passed') assessment = statusOf(selected.codeql, 'CodeQL'); + if (assessment.state === 'passed') { + const run = selected.codeql; + const jobs = await client.pages(`/actions/runs/${run.id}/attempts/${run.run_attempt}/jobs?per_page=100`, 'jobs', jobShape); + const checks = await client.pages(`/check-suites/${run.check_suite_id}/check-runs?filter=all&per_page=100`, 'check_runs', checkShape); + assessment = assessExactShaGates(selected, checks, jobs, inputs); + if (assessment.state === 'passed') { + // Do not approve an attempt superseded while its jobs/checks were read. + const finalRuns = await readRuns(); + if (JSON.stringify(finalRuns) === JSON.stringify(selected)) return; + assessment = { state: 'pending', reason: 'Trusted CI or CodeQL run changed during verification' }; + } + } + if (assessment.state === 'failed') throw new Error(assessment.reason); + lastReason = assessment.reason; + if (attempt < attempts) await client.pause(signal => sleep(delay, signal)); + } + } catch (error) { + if (error instanceof ReleaseGateDeadlineError) { + throw new Error(`${error.message}; last pending gate: ${lastReason}`, { cause: error }); + } + throw error; + } + throw new Error(`Timed out waiting for successful exact-SHA CI and CodeQL checks; last pending gate: ${lastReason}`); +} + +async function main() { + const inputs = requiredEnvironment(); + const tagOnly = process.argv.includes('--tag-only'); + if (tagOnly && !inputs.tagObjectSha) throw new Error('Tag-only recheck requires the original tag object SHA'); + const client = createGithubClient(inputs); + const tagObjectSha = await verifySignedAnnotatedTag(inputs, fetch, { client }); + if (!tagOnly) await waitForExactShaGates(inputs, fetch, defaultSleep, { client }); + if (process.env.GITHUB_OUTPUT) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `release_sha=${inputs.releaseSha}\ntag_object_sha=${tagObjectSha}\n`); + } + console.log(tagOnly ? 'Verified unchanged release tag snapshot.' + : 'Verified signed annotated tag and successful exact-SHA CI/CodeQL gates.'); +} + +if (require.main === module) { + main().catch(error => { + console.error(`Release gate verification failed: ${error.message}`); + process.exitCode = 1; + }); +} + +module.exports = { assessExactShaGates, createGithubClient, requiredEnvironment, verifySignedAnnotatedTag, waitForExactShaGates }; diff --git a/scripts/codex/merge-mcp-config.js b/scripts/codex/merge-mcp-config.js index 721e3c29f..64f42d333 100644 --- a/scripts/codex/merge-mcp-config.js +++ b/scripts/codex/merge-mcp-config.js @@ -94,7 +94,7 @@ const DEFAULT_MCP_STARTUP_TIMEOUT_TOML = `startup_timeout_sec = ${DEFAULT_MCP_ST // mcp-configs/mcp-servers.json. Existing user-managed entries are never // touched by the merge (add-only), except the known-invalid repair below. const ECC_SERVERS = { - 'chrome-devtools': dlxServer('chrome-devtools', 'chrome-devtools-mcp@latest', { startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC }, DEFAULT_MCP_STARTUP_TIMEOUT_TOML) + 'chrome-devtools': dlxServer('chrome-devtools', 'chrome-devtools-mcp@1.10.1', { startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC }, DEFAULT_MCP_STARTUP_TIMEOUT_TOML) }; // ECC <= 2.0.0 emitted [mcp_servers.exa] with a `url` key. Codex rejects diff --git a/scripts/gan-harness.sh b/scripts/gan-harness.sh index 79dd5038f..3b4164bec 100755 --- a/scripts/gan-harness.sh +++ b/scripts/gan-harness.sh @@ -19,6 +19,7 @@ # GAN_PROJECT_DIR — Working directory (default: current dir) # GAN_SKIP_PLANNER — Set to "true" to skip planner phase # GAN_EVAL_MODE — playwright, screenshot, or code-only (default: playwright) +# playwright requires a connected MCP server named "playwright" set -euo pipefail @@ -96,6 +97,38 @@ score_passes() { awk -v s="$score" -v t="$threshold" 'BEGIN { exit !(s >= t) }' } +playwright_mcp_is_connected() { + local status + local status_value + local check_mark + local heavy_check_mark + status=$(NO_COLOR=1 claude mcp get playwright 2>/dev/null) || return 1 + status_value=$(printf '%s\n' "$status" | awk ' + /^[[:space:]]*Status:[[:space:]]*/ { + sub(/^[[:space:]]*Status:[[:space:]]*/, "") + sub(/[[:space:]]*$/, "") + print + exit + } + ') + check_mark=$(printf '\342\234\223') + heavy_check_mark=$(printf '\342\234\224') + + [ "$status_value" = "$check_mark Connected" ] || \ + [ "$status_value" = "$heavy_check_mark Connected" ] +} + +evaluator_tools_for_mode() { + local base_tools="Read,Write,Bash,Grep,Glob" + local playwright_tools="mcp__playwright__browser_navigate,mcp__playwright__browser_click,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_snapshot,mcp__playwright__browser_type,mcp__playwright__browser_fill_form,mcp__playwright__browser_resize,mcp__playwright__browser_press_key" + + if [ "$1" = "playwright" ]; then + printf '%s,%s\n' "$base_tools" "$playwright_tools" + else + printf '%s\n' "$base_tools" + fi +} + elapsed() { local now=$(date +%s) local diff=$((now - START_TIME)) @@ -104,6 +137,30 @@ elapsed() { # ─── Setup ─────────────────────────────────────────────────────────────────── +case "$EVAL_MODE" in + playwright) + if ! playwright_mcp_is_connected; then + fail "GAN_EVAL_MODE=playwright requires a connected MCP server named 'playwright'." + fail "Run 'claude mcp get playwright' to inspect its status, or choose GAN_EVAL_MODE=screenshot or code-only." + exit 1 + fi + ;; + screenshot|code-only) + ;; + *) + fail "Unsupported GAN_EVAL_MODE. Expected playwright, screenshot, or code-only." + exit 1 + ;; +esac + +EVALUATOR_TOOLS=$(evaluator_tools_for_mode "$EVAL_MODE") +EVALUATOR_OPTIONS=(--allowedTools "$EVALUATOR_TOOLS") +if [ "$EVAL_MODE" != "playwright" ]; then + # Allow rules only pre-approve calls. Deny this server's tools explicitly + # in modes that inspect existing screenshots or source instead. + EVALUATOR_OPTIONS+=(--disallowedTools 'mcp__playwright__*') +fi + phase "GAN-STYLE HARNESS — Setup" log "Brief: ${CYAN}${BRIEF}${NC}" @@ -205,8 +262,14 @@ Update gan-harness/generator-state.md." \ # ── EVALUATE ── echo -e "${RED}>> EVALUATOR (iteration $i)${NC}" + if [ "$EVAL_MODE" = "playwright" ] && ! playwright_mcp_is_connected; then + fail "The Playwright MCP server disconnected before evaluator iteration $i." + fail "Run 'claude mcp get playwright' to inspect its status, then retry the harness." + exit 1 + fi + claude -p --model "$EVALUATOR_MODEL" \ - --allowedTools "Read,Write,Bash,Grep,Glob" \ + "${EVALUATOR_OPTIONS[@]}" \ "You are the Evaluator in a GAN-style harness. Read agents/gan-evaluator.md for full instructions. Iteration: $i diff --git a/scripts/hooks/block-no-verify.js b/scripts/hooks/block-no-verify.js index 16e0044d7..80ea60396 100644 --- a/scripts/hooks/block-no-verify.js +++ b/scripts/hooks/block-no-verify.js @@ -15,26 +15,11 @@ 'use strict'; +const { createBudget, scanShell } = require('./lib/shell-scan'); + const MAX_STDIN = 1024 * 1024; let raw = ''; -/** - * Git commands that support the --no-verify flag. - */ -const GIT_COMMANDS_WITH_NO_VERIFY = [ - 'commit', - 'push', - 'merge', - 'cherry-pick', - 'rebase', - 'am', -]; - -/** - * Characters that can appear immediately before 'git' in a command string. - */ -const VALID_BEFORE_GIT = ' \t\n\r;&|$`(<{!"\']/.~\\'; - // Git config section and variable names are case-insensitive // (subsection names are case-sensitive but core.hooksPath has none), // so we normalize the candidate token to lowercase before matching. @@ -56,21 +41,10 @@ const COMMIT_OPTIONS_WITH_VALUE = new Set([ '--template', '--fixup', '--squash', - '--pathspec-from-file', + '--pathspec-from-file' ]); -const COMMIT_OPTIONS_WITH_INLINE_VALUE = [ - '--message=', - '--file=', - '--reuse-message=', - '--reedit-message=', - '--author=', - '--date=', - '--template=', - '--fixup=', - '--squash=', - '--pathspec-from-file=', -]; +const COMMIT_OPTIONS_WITH_INLINE_VALUE = ['--message=', '--file=', '--reuse-message=', '--reedit-message=', '--author=', '--date=', '--template=', '--fixup=', '--squash=', '--pathspec-from-file=']; // Short options that take a value. When seen as part of a combined // short-option token (e.g. -tn), git's parser treats the rest of the @@ -83,130 +57,12 @@ const COMMIT_SHORT_OPTIONS_WITH_VALUE = new Set(['m', 'F', 'C', 'c', 't']); // after them is not the -n flag: `git commit -uno` means --untracked-files=no. const COMMIT_SHORT_OPTIONS_WITH_OPTIONAL_VALUE = new Set(['u', 'S']); -function tokenizeShellWords(input, start = 0, end = input.length) { - const tokens = []; - let value = ''; - let tokenStart = null; - let quote = null; - let escaped = false; - - function beginToken(index) { - if (tokenStart === null) { - tokenStart = index; - } - } - - function pushToken(index) { - if (tokenStart === null) { - return; - } - - tokens.push({ - value, - start: tokenStart, - end: index, - }); - value = ''; - tokenStart = null; - } - - for (let i = start; i < end; i++) { - const char = input.charAt(i); - - if (escaped) { - beginToken(i - 1); - value += char; - escaped = false; - continue; - } - - if (quote) { - if (char === quote) { - quote = null; - continue; - } - - if (quote === '"' && char === '\\') { - beginToken(i); - escaped = true; - continue; - } - - beginToken(i); - value += char; - continue; - } - - if (char === '"' || char === "'") { - beginToken(i); - quote = char; - continue; - } - - if (char === '\\') { - beginToken(i); - escaped = true; - continue; - } - - if (/\s/.test(char)) { - pushToken(i); - continue; - } - - beginToken(i); - value += char; - } - - if (escaped) { - value += '\\'; - } - pushToken(end); - - return tokens; -} - -function findCommandSegmentEnd(input, start) { - let quote = null; - let escaped = false; - - for (let i = start; i < input.length; i++) { - const char = input.charAt(i); - - if (escaped) { - escaped = false; - continue; - } - - if (quote) { - if (quote === '"' && char === '\\') { - escaped = true; - continue; - } - if (char === quote) { - quote = null; - } - continue; - } - - if (char === '"' || char === "'") { - quote = char; - continue; - } - - if (char === '\\') { - escaped = true; - continue; - } - - if (char === ';' || char === '|' || char === '&' || char === '\n') { - return i; - } - } - - return input.length; -} - +/** + * Return true when a commit option consumes the following token as its value. + * + * @param {string} value + * @returns {boolean} + */ function commitOptionConsumesNextValue(value) { if (isCommitNoVerifyShortFlag(value)) { return false; @@ -220,6 +76,12 @@ function commitOptionConsumesNextValue(value) { return Boolean(shortValueOption && shortValueOption.consumesNextValue); } +/** + * Return true when a commit option already carries its value in the same token. + * + * @param {string} value + * @returns {boolean} + */ function commitOptionContainsInlineValue(value) { if (isCommitNoVerifyShortFlag(value)) { return false; @@ -233,6 +95,12 @@ function commitOptionContainsInlineValue(value) { return Boolean(shortValueOption && shortValueOption.containsInlineValue); } +/** + * Classify a combined short-option token that includes a value-taking option. + * + * @param {string} value + * @returns {{consumesNextValue: boolean, containsInlineValue: boolean}|null} + */ function getCommitShortValueOption(value) { if (!value.startsWith('-') || value.startsWith('--') || value === '-') { return null; @@ -243,7 +111,7 @@ function getCommitShortValueOption(value) { if (COMMIT_SHORT_OPTIONS_WITH_VALUE.has(options.charAt(i))) { return { consumesNextValue: i === options.length - 1, - containsInlineValue: i < options.length - 1, + containsInlineValue: i < options.length - 1 }; } } @@ -251,6 +119,12 @@ function getCommitShortValueOption(value) { return null; } +/** + * Return true when a token is commit's `-n` / `--no-verify` short form. + * + * @param {string} value + * @returns {boolean} + */ function isCommitNoVerifyShortFlag(value) { if (!value.startsWith('-') || value.startsWith('--') || value === '-') { return false; @@ -274,125 +148,6 @@ function isCommitNoVerifyShortFlag(value) { return false; } -/** - * Check if a position in the input is inside a shell comment. - */ -function isInComment(input, idx) { - const lineStart = input.lastIndexOf('\n', idx - 1) + 1; - const before = input.slice(lineStart, idx); - for (let i = 0; i < before.length; i++) { - if (before.charAt(i) === '#') { - const prev = i > 0 ? before.charAt(i - 1) : ''; - if (prev !== '$' && prev !== '\\') return true; - } - } - return false; -} - -/** - * Find the next 'git' token in the input starting from a position. - */ -function findGit(input, start) { - let pos = start; - while (pos < input.length) { - const idx = input.indexOf('git', pos); - if (idx === -1) return null; - - const isExe = input.slice(idx + 3, idx + 7).toLowerCase() === '.exe'; - const len = isExe ? 7 : 3; - const after = input[idx + len] || ' '; - if (!/[\s"']/.test(after)) { - pos = idx + 1; - continue; - } - - const before = idx > 0 ? input[idx - 1] : ' '; - if (VALID_BEFORE_GIT.includes(before)) return { idx, len }; - pos = idx + 1; - } - return null; -} - -/** - * Detect which git subcommand (commit, push, etc.) is being invoked. - * Returns { command, offset } where offset is the position right after the - * subcommand keyword, so callers can scope flag checks to only that portion. - */ -function detectGitCommand(input, start = 0) { - while (start < input.length) { - const git = findGit(input, start); - if (!git) return null; - - if (isInComment(input, git.idx)) { - start = git.idx + git.len; - continue; - } - - // Find the first matching subcommand token after "git". - // We pick the one closest to "git" so that argument values like - // "git push origin commit" don't misclassify "commit" as the subcommand. - let bestCmd = null; - let bestIdx = Infinity; - - for (const cmd of GIT_COMMANDS_WITH_NO_VERIFY) { - let searchPos = git.idx + git.len; - while (searchPos < input.length) { - const cmdIdx = input.indexOf(cmd, searchPos); - if (cmdIdx === -1) break; - - const before = cmdIdx > 0 ? input[cmdIdx - 1] : ' '; - const after = input[cmdIdx + cmd.length] || ' '; - if (!/\s/.test(before)) { searchPos = cmdIdx + 1; continue; } - if (!/[\s;|>)\]}"']/.test(after) && after !== '') { searchPos = cmdIdx + 1; continue; } - if (/[;|]/.test(input.slice(git.idx + git.len, cmdIdx))) break; - if (isInComment(input, cmdIdx)) { searchPos = cmdIdx + 1; continue; } - - // Verify this token is the first non-flag word after "git" — i.e. the - // actual subcommand, not an argument value to a different subcommand. - const gap = input.slice(git.idx + git.len, cmdIdx); - const tokens = gap.trim().split(/\s+/).filter(Boolean); - // Every token before the candidate must be a flag or a flag argument. - // Git global flags like -c take a value argument (e.g. -c key=value). - let onlyFlagsAndArgs = true; - let expectFlagArg = false; - for (const t of tokens) { - if (expectFlagArg) { expectFlagArg = false; continue; } - if (t.startsWith('-')) { - // -c is a git global flag that takes the next token as its argument - if (t === '-c' || t === '-C' || t === '--work-tree' || t === '--git-dir' || - t === '--namespace' || t === '--super-prefix') { - expectFlagArg = true; - } - continue; - } - onlyFlagsAndArgs = false; - break; - } - if (!onlyFlagsAndArgs) { searchPos = cmdIdx + 1; continue; } - - if (cmdIdx < bestIdx) { - bestIdx = cmdIdx; - bestCmd = cmd; - } - break; - } - } - - if (bestCmd) { - return { - command: bestCmd, - offset: bestIdx + bestCmd.length, - gitStart: git.idx, - gitEnd: git.idx + git.len, - commandStart: bestIdx, - }; - } - - start = git.idx + git.len; - } - return null; -} - /** * git's option parser accepts any unambiguous prefix of a long option, so * `--no-veri` and `--no-verif` run as --no-verify. Shorter prefixes such as @@ -403,131 +158,584 @@ function isNoVerifyLongFlag(value) { return value.length >= '--no-v'.length && '--no-verify'.startsWith(value); } -/** - * Check if the input contains a --no-verify flag for a specific git command. - * Only inspects the portion of the input starting at `offset` (the position - * right after the detected subcommand keyword) so that flags belonging to - * earlier commands in a chain are not falsely matched. - */ -function hasNoVerifyFlag(input, command, offset) { - const segmentEnd = findCommandSegmentEnd(input, offset); - const tokens = tokenizeShellWords(input, offset, segmentEnd); +const PROTECTED_GIT_COMMANDS = new Set(['commit', 'push', 'merge', 'cherry-pick', 'rebase', 'am']); +const GIT_GLOBAL_VALUES = new Set(['-c', '-C', '--config-env', '--work-tree', '--git-dir', '--namespace', '--super-prefix']); +const SHELLS = new Set(['sh', 'bash', 'dash', 'zsh', 'ksh']); +const DATA_COMMANDS = new Set(['echo', 'printf', 'cat', 'tee', 'grep', 'head', 'tail', 'wc', 'sort', 'uniq', ':', 'true', 'false']); +const CONTROL_WORDS = new Set(['!', 'if', 'then', 'elif', 'while', 'until', 'do', 'else']); + +function basename(value) { + return value.replace(/\\/g, '/').split('/').pop(); +} + +function isGitExecutable(value) { + const name = basename(value).toLowerCase(); + return name === 'git' || name === 'git.exe'; +} + +// Only literal values from this supplied shell task are tracked. No host +// environment, arbitrary expansion or external configuration is read. +function gitEnvironmentOverride(environment, budget) { + const count = environment.get('GIT_CONFIG_COUNT') || ''; + budget.spend(count.length + environment.size + 1); + // Git uses strtoul: leading ASCII whitespace/+ are accepted, trailing bytes + // and counts above INT_MAX are rejected. Bound work by assignments we own. + const configured = /^[ \t\r\n\v\f]*\+?[0-9]+(?![\s\S])/.test(count) ? Number(count) : 0; + if (configured > 0 && configured <= 0x7fffffff && configured <= environment.size / 2) { + let override = false; + let complete = true; + for (let i = 0; i < configured; i++) { + budget.spend(); + const key = environment.get(`GIT_CONFIG_KEY_${i}`); + if (key === undefined || !environment.has(`GIT_CONFIG_VALUE_${i}`)) { complete = false; break; } + budget.spend(key.length + 1); + override ||= key.toLowerCase() === 'core.hookspath'; + } + if (complete && override) return true; + } + const parameters = environment.get('GIT_CONFIG_PARAMETERS'); + if (parameters) { + budget.spend(parameters.length + 1); + // Git's old 'key=value' and new 'key'='value' forms both use quote removal. + // This inspects literal keys only; nested regions are never executed. + for (const command of scanShell(parameters, budget).commands) { + for (const word of command.words) { + budget.spend(word.value.length + 1); + if (word.value.toLowerCase().startsWith(GIT_CONFIG_KEY_PREFIX)) return true; + } + } + } + return false; +} + +function checkGitWords(words, budget, start = 0, environmentOverride = false) { + let index = start + 1; + let override = environmentOverride; + for (; index < words.length; index++) { + const value = words[index].value; + budget.spend(value.length + 1); + if (!value.startsWith('-')) break; + if (value === '--') { index++; break; } + if (value === '-c' || value === '--config-env') { + const setting = words[index + 1]?.value || ''; + budget.spend(setting.length + 1); + override ||= setting.toLowerCase().startsWith(GIT_CONFIG_KEY_PREFIX); + } else if (value.toLowerCase().startsWith(`-c${GIT_CONFIG_KEY_PREFIX}`) || value.toLowerCase().startsWith(`--config-env=${GIT_CONFIG_KEY_PREFIX}`)) override = true; + if (GIT_GLOBAL_VALUES.has(value)) index++; + } + const command = words[index]?.value; + budget.spend((command?.length || 0) + 1); + if (!PROTECTED_GIT_COMMANDS.has(command)) return null; + if (override) return `BLOCKED: Overriding core.hooksPath is not allowed with git ${command}. Git hooks must not be bypassed.`; let skipNext = false; - - for (const token of tokens) { - const value = token.value; - - if (skipNext) { - skipNext = false; - continue; - } - - if (value === '--') { - break; - } - + for (index++; index < words.length; index++) { + const value = words[index].value; + budget.spend(value.length + 1); + if (skipNext) { skipNext = false; continue; } + if (value === '--') break; if (command === 'commit') { - if (commitOptionConsumesNextValue(value)) { - skipNext = true; - continue; - } - - if (commitOptionContainsInlineValue(value)) { - continue; - } + if (commitOptionConsumesNextValue(value)) { skipNext = true; continue; } + if (commitOptionContainsInlineValue(value)) continue; } - - if (isNoVerifyLongFlag(value)) return true; - - // For commit, -n is shorthand for --no-verify. - if (command === 'commit' && isCommitNoVerifyShortFlag(value)) { - return true; + if (isNoVerifyLongFlag(value) || (command === 'commit' && isCommitNoVerifyShortFlag(value))) { + return `BLOCKED: --no-verify flag is not allowed with git ${command}. Git hooks must not be bypassed.`; } } - - return false; + return null; } -/** - * Check if the input contains a -c core.hooksPath= override. - */ -function hasHooksPathOverride(input, detected) { - const tokens = tokenizeShellWords(input, detected.gitEnd, detected.commandStart); +// Keep literal outcomes and the empty result of an unresolved expansion. The +// latter is a base for later visible += operands, not arbitrary evaluation. +function assignmentValues(prior, operand, append, dynamic, budget) { + const base = prior === undefined ? '' : prior; + budget.spend((append ? base.length : 0) + operand.length + 1); + const values = new Set([append ? base + operand : operand]); + if (dynamic) { + if (prior !== undefined) values.add(prior); + values.add(append ? base : ''); + } + return [...values]; +} - for (let i = 0; i < tokens.length; i++) { - const value = tokens[i].value; - // Git config section + variable names are case-insensitive, so a - // bypass attempt like `core.HOOKSPATH=...` or `core.hookspath=...` - // must compare against the lowercased token. - const lowered = value.toLowerCase(); - - if (value === '-c') { - const next = tokens[i + 1] && tokens[i + 1].value; - if (typeof next === 'string' && next.toLowerCase().startsWith(GIT_CONFIG_KEY_PREFIX)) { - return true; +// Only explicit option grammars remove wrapper operands. Unknown launchers are +// opaque/conservative, never guessed from a name found among data arguments. +function executableWords(words, budget, inherited = new Map(), callerValues = inherited) { + budget.spend(inherited.size + 1); + const environments = [new Map(inherited)]; + const prefixAssignments = new Map(); + let local = true; + let assignmentOnly = true; + const dynamicAssignments = new Set(); + function result(values) { return { words: values, environments, prefixAssignments, local, assignmentOnly, dynamicAssignments }; } + function suffix(start) { + budget.spend(words.length - start); + assignmentOnly = false; + return result(words.slice(start)); + } + function assignment(token) { + const { value, dynamic } = token; + const equals = value.indexOf('='); + const append = !environmentAssignments && value[equals - 1] === '+'; + const key = value.slice(0, append ? equals - 1 : equals); + if (/^GIT_CONFIG_(?:COUNT|PARAMETERS|(?:KEY|VALUE)_[0-9]+)$/.test(key)) { + const operand = value.slice(equals + 1); + const count = environments.length; + budget.spend(count + 1); + for (let n = 0; n < count; n++) { + const environment = environments[n]; + // Shell prefix appends can see local values, even when not exported. + // Repeated operands use the prior outcome in this same prefix. + const prior = prefixAssignments.has(key) && environment.has(key) + ? environment.get(key) : callerValues.get(key); + const values = assignmentValues(prior, operand, append, dynamic, budget); + for (const alternative of values.slice(1)) { + budget.spend(environment.size + 1); + const variant = new Map(environment); + variant.set(key, alternative); + environments.push(variant); + } + environment.set(key, values[0]); } + // Retain ordered operations so same-shell states apply each append once. + if (!prefixAssignments.has(key)) prefixAssignments.set(key, []); + prefixAssignments.get(key).push({ value: operand, append, dynamic }); + if (dynamic) dynamicAssignments.add(key); + } + } + function resetEnvironment(name) { + budget.spend(environments.length + 1); + for (const environment of environments) { + if (name === undefined) environment.clear(); + else environment.delete(name); + } + } + let i = 0; + let assignments = true; + let environmentAssignments = false; + while (i < words.length) { + const token = words[i]; + budget.spend(token.value.length + token.raw.length + 1); + if (assignments && /^[A-Za-z_][A-Za-z0-9_]*\+?=/.test(environmentAssignments ? token.value : token.raw)) { assignment(token); i++; continue; } + if (!token.quoted && CONTROL_WORDS.has(token.value)) { i++; continue; } + const name = basename(token.value); + if (name === 'command') { + assignmentOnly = false; + local &&= token.value === 'command'; i++; - continue; + while (words[i]?.value.startsWith('-')) { + const flag = words[i++].value; + budget.spend(flag.length + 1); + if (flag === '--') break; + if (/^-[pvV]+$/.test(flag) && /[vV]/.test(flag)) return result([]); + if (!/^-p+$/.test(flag)) return suffix(i - 1); + } + assignments = false; continue; } - - if (lowered.startsWith(`-c${GIT_CONFIG_KEY_PREFIX}`)) { - return true; + if (name === 'exec') { + assignmentOnly = false; + local = false; + i++; + while (words[i]?.value.startsWith('-')) { + const flag = words[i++].value; + budget.spend(flag.length + 1); + if (flag === '--') break; + if (/^-[cl]*a$/.test(flag)) i++; + else if (!/^-([cl]*a.+|[cl]+)$/.test(flag)) return suffix(i - 1); + if (flag.slice(1).split('a', 1)[0].includes('c')) resetEnvironment(); + } + assignments = false; continue; } + if (name === 'env' || name === 'sudo' || name === 'doas') { + assignmentOnly = false; + local = false; + const env = name === 'env'; + const values = env + ? new Set(['-u', '--unset', '-C', '--chdir']) + : new Set(['-u', '--user', '-g', '--group', '-h', '--host', '-p', '--prompt', '-C', '-T', '-R', '-D']); + const flags = env ? new Set(['-i', '--ignore-environment', '-0', '--null']) : new Set(['-n', '-E', '-H', '-S', '-k', '-K', '-b']); + i++; + while (words[i]?.value.startsWith('-')) { + const flag = words[i].value; + budget.spend(flag.length + 1); + if (flag === '--') { i++; break; } + if (env && (flag === '-i' || flag === '--ignore-environment')) resetEnvironment(); + if (env && (flag === '-u' || flag === '--unset')) resetEnvironment(words[i + 1]?.value || ''); + else if (env && flag.startsWith('--unset=')) resetEnvironment(flag.slice('--unset='.length)); + else if (env && flag.startsWith('-u')) resetEnvironment(flag.slice(2)); + if (values.has(flag)) i += 2; + else if (flags.has(flag) || [...values].some(value => value.startsWith('--') ? flag.startsWith(`${value}=`) : flag.startsWith(value) && flag.length > value.length)) i++; + else return suffix(i - 1); // Includes opaque env -S / sudo shell modes. + } + assignments = true; environmentAssignments = true; continue; + } + return suffix(i); } - - return false; + return result([]); } -/** - * Check a command string for git hook bypass attempts. - */ -function checkCommand(input) { - let start = 0; - - while (start < input.length) { - const detected = detectGitCommand(input, start); - if (!detected) return { blocked: false }; - - const { command: gitCommand, offset } = detected; - - if (hasHooksPathOverride(input, detected)) { - return { - blocked: true, - reason: `BLOCKED: Overriding core.hooksPath is not allowed with git ${gitCommand}. Git hooks must not be bypassed.`, - }; +function shellRole(words, budget, shell) { + let i = 1; + let stdin = false; + let code = false; + while (i < words.length) { + const option = words[i].value; + budget.spend(option.length + 1); + if (option === '--' || option === '-') { i++; break; } + if (!/^[+-]/.test(option)) break; + if (option === '--rcfile' || option === '--init-file') { i += 2; continue; } + if (option.startsWith('--')) { + if (!['--noprofile', '--norc', '--posix', '--restricted', '--verbose', '--login'].includes(option)) return { kind: 'opaque', stdin: true }; + i++; continue; } - - if (hasNoVerifyFlag(input, gitCommand, offset)) { - return { - blocked: true, - reason: `BLOCKED: --no-verify flag is not allowed with git ${gitCommand}. Git hooks must not be bypassed.`, - }; + // Bash accepts either sign and consumes a separate operand for each o/O + // even inside a cluster. The command string follows ALL option processing, + // not necessarily the argv word immediately after the first c flag. + let next = i + 1; + for (let j = 1; j < option.length; j++) { + budget.spend(); + const flag = option[j]; + // Named-option arity is unproved for sh/dash/ksh: keep the invocation + // opaque instead of consuming a code flag as a guessed option operand. + if ((flag === 'o' || flag === 'O') && shell !== 'bash' && shell !== 'zsh') return { kind: 'opaque', stdin: true }; + if (flag === 'c') code = true; + else if (flag === 's') stdin = true; + else if (shell === 'zsh' && flag === 'o') { + // zsh consumes the rest of this argv word as the option name, or one + // separate word if no suffix exists, then ends this option cluster. + if (j + 1 === option.length && next < words.length) next++; + break; + } else if (shell === 'zsh' && (flag === 'O' || flag === 'b')) { + // These are not Bash's operand grammar; unmodeled zsh modes stay opaque. + return { kind: 'opaque', stdin: true }; + } else if (flag === 'o' || flag === 'O') { if (next < words.length) next++; } + else if (!'abefhiklmnprtuvxBCEHPTD'.includes(flag)) return { kind: 'opaque', stdin: true }; } - - start = findCommandSegmentEnd(input, offset) + 1; + i = next; } + if (code) return { kind: 'shell', code: words[i]?.value, stdin: false }; + // A script filename and its positional arguments are not shell source text. + return { kind: 'shell', stdin: stdin || i === words.length }; +} +function commandRole(words, budget) { + if (!words.length) return { kind: 'data' }; + budget.spend(words[0].value.length + 1); + const name = basename(words[0].value); + if (isGitExecutable(words[0].value)) return { kind: 'git' }; + if (SHELLS.has(name)) return shellRole(words, budget, name); + if (name === 'eval') { + for (const word of words) budget.spend(word.value.length + 3); + return { kind: 'shell', code: words.slice(words[1]?.value === '--' ? 2 : 1).map(word => word.value).join(' '), stdin: false }; + } + if (DATA_COMMANDS.has(name)) return { kind: 'data' }; + return { kind: 'opaque', stdin: true }; +} + +// Literal producers only. Unmodeled transformations remain conservative rather +// than executing a formatter, interpreter, shell or user-supplied command. +function pipelineSources(command, budget) { + const sources = []; + for (let current = command; current; current = current.pipeFrom) { + budget.spend(current.words.length + 1); + const { words } = executableWords(current.words, budget); + for (const word of words) budget.spend(word.value.length + 3); + const name = basename(words[0]?.value || ''); + if (name === 'echo') sources.push({ text: words.slice(1).filter(word => !/^-[neE]+$/.test(word.value)).map(word => word.value).join(' ') }); + if (name === 'printf') { + const format = words[1]?.value || ''; + if (format !== '-v') sources.push({ text: (format === '%s' || format === '%s\\n') ? words.slice(2).map(word => word.value).join('\n') : words.slice(1).map(word => word.value).join(' ') }); + } + if (!DATA_COMMANDS.has(name)) { + // Foreign transformations can introduce literal bypasses into executable + // stdin. Treat their punctuation as delimiters, not as proved shell syntax. + // This deliberately may refuse a transformation that removes a bypass; it + // does not evaluate sed/interpreters or detect arbitrary generated source. + const text = words.map(word => word.value).join(' '); + budget.spend(2 * text.length + 1); + sources.push({ text: text.replace(/[^\w$=.+-]/g, ' '), opaque: true }); + } + for (const redirect of current.redirects) { + if (redirect.operator === '<<<') sources.push({ text: redirect.word.value }); + else if (redirect.operator === '<<' || redirect.operator === '<<-') sources.push({ text: redirect.body }); + } + } + return sources; +} + +const GIT_ENV_NAME = /^GIT_CONFIG_(?:COUNT|PARAMETERS|(?:KEY|VALUE)_[0-9]+)$/; +const DECLARATIONS = new Set(['export', 'declare', 'typeset', 'readonly', 'unset']); + +function shellState(environment, budget) { + budget.spend(2 * environment.size + 1); + return { variables: new Map(environment), exported: new Set(environment.keys()), readonly: new Set() }; +} + +function copyShellState(state, budget) { + budget.spend(state.variables.size + state.exported.size + state.readonly.size + 1); + return { variables: new Map(state.variables), exported: new Set(state.exported), readonly: new Set(state.readonly) }; +} + +function copyShellContext(context, budget) { + budget.spend(context.states.length + 1); + return { states: context.states.map(state => copyShellState(state, budget)) }; +} + +function exportedEnvironment(state, budget) { + const environment = new Map(); + budget.spend(state.exported.size + 1); + for (const name of state.exported) { + if (state.variables.has(name)) environment.set(name, state.variables.get(name)); + } + return environment; +} + +// Literal declaration operands are data, not executable source. A value and +// its export attribute are separate: an assignment-only command does not start +// exporting a previously local variable. No host shell state is consulted. +function updateShellState(state, normalized, budget) { + const { words, prefixAssignments, local, assignmentOnly, dynamicAssignments } = normalized; + const states = [state]; + const result = (handled, changed, uncertain = false) => ({ handled, changed, uncertain, states }); + if (!local) return result(false, false); + function assign(name, value, dynamic = false, append = false) { + const count = states.length; + budget.spend(count + 1); + for (let n = 0; n < count; n++) { + const current = states[n]; + if (current.readonly.has(name)) continue; + const values = assignmentValues(current.variables.get(name), value, append, dynamic, budget); + for (const alternative of values.slice(1)) { + const variant = copyShellState(current, budget); + variant.variables.set(name, alternative); + states.push(variant); + } + current.variables.set(name, values[0]); + } + } + function assignPrefixes() { + budget.spend(prefixAssignments.size + 1); + for (const [key, operations] of prefixAssignments) { + budget.spend(operations.length + 1); + for (const operation of operations) assign(key, operation.value, operation.dynamic, operation.append); + } + } + if (assignmentOnly) { + assignPrefixes(); + return result(true, prefixAssignments.size > 0, dynamicAssignments.size > 0); + } + // Exact builtin names only: /some/path/export is an external executable. + const name = words[0]?.value; + if (!DECLARATIONS.has(name)) return result(false, false); + let exported = name === 'export' ? true : null; + let readonly = name === 'readonly'; + let passive = false; + let uncertain = dynamicAssignments.size > 0; + let i = 1; + for (; i < words.length; i++) { + const flag = words[i].value; + budget.spend(flag.length + 1); + if (flag === '--') { i++; break; } + if (!/^[+-]/.test(flag)) break; + if (name === 'export' && /^-[npf]+$/.test(flag)) { + if (flag.includes('n')) exported = false; + passive ||= flag.includes('f'); + } else if ((name === 'declare' || name === 'typeset') && /^[+-][xrgpf]+$/.test(flag)) { + if (flag.includes('x')) exported = flag[0] === '-'; + if (flag[0] === '-' && flag.includes('r')) readonly = true; + passive ||= /[pf]/.test(flag); + } else if (name === 'readonly' && /^-[pf]+$/.test(flag)) passive ||= flag.includes('f'); + else if (name === 'unset' && /^-[vf]+$/.test(flag)) passive ||= flag.includes('f'); + else uncertain = true; + } + if (passive && !uncertain) return result(true, false); + let changed = prefixAssignments.size > 0; + assignPrefixes(); + for (; i < words.length; i++) { + const value = words[i].value; + budget.spend(2 * value.length + 1); + const equals = value.indexOf('='); + const append = equals > 0 && value[equals - 1] === '+'; + const key = equals < 0 ? value : value.slice(0, append ? equals - 1 : equals); + if (!GIT_ENV_NAME.test(key)) continue; + changed = true; + if (name !== 'unset' && equals >= 0) assign(key, value.slice(equals + 1), words[i].dynamic, append); + budget.spend(states.length + 1); + for (const current of states) { + if (name === 'unset') { + if (equals < 0 && !current.readonly.has(key)) { + current.variables.delete(key); current.exported.delete(key); + } + } else { + if (exported === true || uncertain) current.exported.add(key); + else if (exported === false) current.exported.delete(key); + if (readonly || uncertain) current.readonly.add(key); + } + } + } + // Unsupported attributes may transform values or reject the declaration. + // Retain old and conservative literal states; never use them to prove reset. + return result(true, changed, uncertain); +} + +function checkCommand(input) { + const budget = createBudget(input.length); + const pending = [{ text: input, opaque: false, context: { states: [shellState(new Map(), budget)] } }]; + function enqueue(text, opaque = false, context = { states: [shellState(new Map(), budget)] }) { + if (!text) return; + budget.spend(text.length + 1); + pending.push({ text, opaque, context }); + } + function inspectOpaque(words, text, environment) { + for (let index = 0; index < words.length; index++) { + const word = words[index]; + budget.spend(word.value.length + 1); + if (isGitExecutable(word.value)) { + const reason = checkGitWords(words, budget, index, gitEnvironmentOverride(environment, budget)); + if (reason) return reason; + } + if (word.value !== text && /git/i.test(word.value) && /[\s'"()]/.test(word.value)) enqueue(word.value, true, { states: [shellState(environment, budget)] }); + } + return null; + } + try { + while (pending.length) { + const task = pending.pop(); + if (task.mergeInto) { + budget.spend(task.context.states.length + 1); + task.mergeInto.states.push(...task.context.states); + continue; + } + if (!task.command) { + const scan = scanShell(task.text, budget); + const contexts = new Map([[scan.rootScope, task.context]]); + budget.spend(scan.commands.length + 1); + for (let i = scan.commands.length - 1; i >= 0; i--) pending.push({ ...task, command: scan.commands[i], contexts }); + continue; + } + const { command, contexts } = task; + if (command.scopeExit) { + const closing = command.scopeExit; + const exited = contexts.get(closing); + const enclosing = contexts.get(closing.parent); + if (closing.pipelineLast && exited && enclosing) { + budget.spend(exited.states.length + 1); + enclosing.states.push(...exited.states); + } + continue; + } + const missing = []; + for (let scope = command.scope; !contexts.has(scope); scope = scope.parent) { budget.spend(); missing.push(scope); } + while (missing.length) { + const scope = missing.pop(); + const parent = contexts.get(scope.parent); + contexts.set(scope, scope.isolated ? copyShellContext(parent, budget) : parent); + } + const parent = contexts.get(command.scope); + const isolated = command.pipeFrom || command.pipeTo || command.background; + const context = task.commandContext || (isolated ? copyShellContext(parent, budget) : parent); + if (!task.nestedDone && command.nested.length) { + pending.push({ ...task, nestedDone: true, commandContext: context }); + budget.spend(command.nested.length + 1); + for (let i = command.nested.length - 1; i >= 0; i--) enqueue(command.nested[i], false, copyShellContext(context, budget)); + continue; + } + let conditional = false; + for (let scope = command.scope; scope; scope = scope.parent) { budget.spend(); conditional ||= scope.conditional; } + const alternatives = []; + const childEnvironments = []; + let sameShellCode = null; + let changed = false; + budget.spend(context.states.length + 1); + for (const state of context.states) { + const normalized = executableWords(command.words, budget, exportedEnvironment(state, budget), state.variables); + const { words, environments } = normalized; + const next = copyShellState(state, budget); + const evalPrefix = normalized.local && words[0]?.value === 'eval' && normalized.prefixAssignments.size > 0; + const mutation = updateShellState(next, evalPrefix ? { ...normalized, assignmentOnly: true } : normalized, budget); + if (evalPrefix) { + alternatives.push(state); + budget.spend(mutation.states.length * (normalized.prefixAssignments.size + 1)); + for (const variant of mutation.states) for (const name of normalized.prefixAssignments.keys()) variant.exported.add(name); + } + budget.spend(mutation.states.length + 1); + alternatives.push(...mutation.states); + if (mutation.changed && (conditional || mutation.uncertain)) alternatives.push(state); + changed ||= mutation.changed; + if (mutation.handled && !evalPrefix) continue; + const role = commandRole(words, budget); + budget.spend(environments.length + 1); + for (const environment of environments) { + const reason = task.opaque || role.kind === 'opaque' + ? inspectOpaque(command.words, task.text, environment) + : role.kind === 'git' ? checkGitWords(words, budget, 0, gitEnvironmentOverride(environment, budget)) : null; + if (reason) return { blocked: true, reason }; + if (role.code) { + if (normalized.local && words[0]?.value === 'eval') { + sameShellCode = role.code; + } + else childEnvironments.push({ code: role.code, opaque: false, environment }); + } + if (role.stdin) { + for (const redirect of command.redirects) { + if (redirect.operator === '<<<') childEnvironments.push({ code: redirect.word.value, opaque: role.kind === 'opaque', environment }); + else if (redirect.operator === '<<' || redirect.operator === '<<-') childEnvironments.push({ code: redirect.body, opaque: role.kind === 'opaque', environment }); + } + if (command.pipeFrom) { + for (const source of pipelineSources(command.pipeFrom, budget)) childEnvironments.push({ code: source.text, opaque: source.opaque || role.kind === 'opaque', environment }); + } + } + } + } + context.states = alternatives; + // Bash lastpipe and zsh can execute a final pipeline builtin in the + // parent shell. Preserve that possible state as well as isolation; this + // is deliberately conservative when the host shell/options are unknown. + if (command.pipeFrom && !command.pipeTo && !command.background && (changed || sameShellCode)) pending.push({ mergeInto: parent, context }); + for (const child of childEnvironments) enqueue(child.code, child.opaque, { states: [shellState(child.environment, budget)] }); + if (sameShellCode) { + // A conditional eval may not run. Its nested scans have fresh lexical + // roots, so preserve the skipped branch across all delayed updates. + const evaluated = conditional ? copyShellContext(context, budget) : context; + if (conditional) pending.push({ mergeInto: context, context: evaluated }); + enqueue(sameShellCode, false, evaluated); + } + } + } catch (error) { + if (!(error instanceof RangeError)) throw error; + return { blocked: true, reason: 'BLOCKED: Shell analysis work budget exceeded; hook-bypass safety could not be established.' }; + } return { blocked: false }; } /** * Extract the command string from hook input (JSON or plain text). + * + * @param {string} rawInput + * @returns {string} */ function extractCommand(rawInput) { const trimmed = rawInput.trim(); - if (!trimmed.startsWith('{')) return trimmed; + if (!trimmed.startsWith('{')) { + return trimmed; + } try { const parsed = JSON.parse(trimmed); - if (typeof parsed !== 'object' || parsed === null) return trimmed; + if (typeof parsed !== 'object' || parsed === null) { + return trimmed; + } // Claude Code format: { tool_input: { command: "..." } } const cmd = parsed.tool_input?.command; - if (typeof cmd === 'string') return cmd; + if (typeof cmd === 'string') { + return cmd; + } // Generic JSON formats for (const key of ['command', 'cmd', 'input', 'shell', 'script']) { - if (typeof parsed[key] === 'string') return parsed[key]; + if (typeof parsed[key] === 'string') { + return parsed[key]; + } } return trimmed; @@ -538,6 +746,9 @@ function extractCommand(rawInput) { /** * Exportable run() for in-process execution via run-with-flags.js. + * + * @param {string} rawInput + * @returns {{exitCode: number, stderr?: string}} */ function run(rawInput) { const command = extractCommand(rawInput); @@ -546,7 +757,7 @@ function run(rawInput) { if (result.blocked) { return { exitCode: 2, - stderr: result.reason, + stderr: result.reason }; } diff --git a/scripts/hooks/config-protection.js b/scripts/hooks/config-protection.js index 2da5358c2..75a7a0781 100644 --- a/scripts/hooks/config-protection.js +++ b/scripts/hooks/config-protection.js @@ -43,9 +43,12 @@ const PROTECTED_FILES = new Set([ 'prettier.config.js', 'prettier.config.cjs', 'prettier.config.mjs', - // Biome + // Biome's discovered filenames. Custom --config-path/extends targets need + // reference context; an arbitrary biome.* basename is not sufficient. 'biome.json', 'biome.jsonc', + '.biome.json', + '.biome.jsonc', // Ruff (Python) '.ruff.toml', 'ruff.toml', @@ -57,11 +60,74 @@ const PROTECTED_FILES = new Set([ '.stylelintrc', '.stylelintrc.json', '.stylelintrc.yml', + '.stylelintrc.yaml', + '.stylelintrc.js', + '.stylelintrc.cjs', + '.stylelintrc.mjs', + // Stylelint's current spelling; only the legacy `.stylelintrc*` forms were + // listed, so a project using the documented `stylelint.config.js` had no + // protection at all. + 'stylelint.config.js', + 'stylelint.config.cjs', + 'stylelint.config.mjs', + 'stylelint.config.ts', + 'stylelint.config.mts', + 'stylelint.config.cts', '.markdownlint.json', + '.markdownlint.jsonc', '.markdownlint.yaml', - '.markdownlintrc' + '.markdownlint.yml', + '.markdownlint.cjs', + '.markdownlint.mjs', + '.markdownlintrc', + // markdownlint-cli2 reads its own config names, not `.markdownlint.*`. + '.markdownlint-cli2.jsonc', + '.markdownlint-cli2.yaml', + '.markdownlint-cli2.cjs', + '.markdownlint-cli2.mjs', + // Ignore files are the cheapest way to make a check pass without touching + // the code OR the config: adding one path to .eslintignore silences the + // failing file outright. Blocking the config while leaving its ignore list + // open left the hook's whole purpose one line away from being defeated. + // First-time creation stays allowed by the same existence check below. + '.eslintignore', + '.prettierignore', + '.stylelintignore', + '.markdownlintignore' ]); +/** + * Exact basenames only catch a tool's canonical entry point. Real repos split + * flat config across files: a shared `eslint.config.base.mjs` holding the + * ignore list and rule severities, imported by per-workspace + * `eslint.config.mjs` files. That is the common monorepo shape, and matching + * basenames alone protected the leaves while leaving the trunk -- the file that + * actually carries the rules -- freely editable. + * + * These patterns cover `${session.file} no longer exists.
`, { csp: false }); + return sendHtml(res, 404, `${escapeHtml(session.file)} no longer exists.
`); } const ext = path.extname(session.file).toLowerCase(); if (ext === '.md' || ext === '.markdown') { @@ -501,30 +619,43 @@ function createPlanCanvasServer({ title: path.basename(session.file), sdkSrc: '/sdk.js' }); - return sendHtml(res, 200, html, { csp: false }); + return sendHtml(res, 200, html, { csp: 'artifact' }); } const sdkTag = ''; const injected = content.includes('') ? content.replace('