mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-13 03:33:15 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b0eeacd66 |
@@ -37,8 +37,6 @@ Use only the lanes that fit the system in front of you. This skill is useful for
|
||||
|
||||
Do not treat MLE as separate from software engineering. Most ECC SWE workflows apply directly to ML systems, often with stricter failure modes:
|
||||
|
||||
The recommended `minimal --with capability:machine-learning` install keeps the core agent surface available alongside this skill. For skill-only or agent-limited harnesses, pair `skill:mle-workflow` with `agent:mle-reviewer` where the target supports agents.
|
||||
|
||||
| SWE surface | MLE use |
|
||||
|-------------|---------|
|
||||
| `product-capability` / `architecture-decision-records` | Turn model work into explicit product contracts and record irreversible data, model, and rollout choices |
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{
|
||||
"name": "ecc",
|
||||
"source": "./",
|
||||
"description": "The most comprehensive Claude Code plugin — 58 agents, 220 skills, 74 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
|
||||
"description": "The most comprehensive Claude Code plugin — 54 agents, 204 skills, 69 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
|
||||
"version": "2.0.0-rc.1",
|
||||
"author": {
|
||||
"name": "Affaan Mustafa",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ecc",
|
||||
"version": "2.0.0-rc.1",
|
||||
"description": "Battle-tested Claude Code plugin for engineering teams — 58 agents, 220 skills, 74 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
|
||||
"description": "Battle-tested Claude Code plugin for engineering teams — 54 agents, 204 skills, 69 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
|
||||
"author": {
|
||||
"name": "Affaan Mustafa",
|
||||
"url": "https://x.com/affaanmustafa"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ecc",
|
||||
"version": "2.0.0-rc.1",
|
||||
"description": "Battle-tested Codex workflows — 207 shared ECC skills, production-ready MCP configs, and selective-install-aligned conventions for TDD, security scanning, code review, and autonomous development.",
|
||||
"description": "Battle-tested Codex workflows — 200 shared ECC skills, production-ready MCP configs, and selective-install-aligned conventions for TDD, security scanning, code review, and autonomous development.",
|
||||
"author": {
|
||||
"name": "Affaan Mustafa",
|
||||
"email": "me@affaanmustafa.com",
|
||||
@@ -15,7 +15,7 @@
|
||||
"mcpServers": "./.mcp.json",
|
||||
"interface": {
|
||||
"displayName": "Everything Claude Code",
|
||||
"shortDescription": "207 battle-tested ECC skills plus MCP configs for TDD, security, code review, and autonomous development.",
|
||||
"shortDescription": "200 battle-tested ECC skills plus MCP configs for TDD, security, code review, and autonomous development.",
|
||||
"longDescription": "Everything Claude Code (ECC) is a community-maintained collection of Codex-ready skills and MCP configs evolved over 10+ months of intensive daily use. It covers TDD workflows, security scanning, code review, architecture decisions, operator workflows, and more — all in one installable plugin.",
|
||||
"developerName": "Affaan Mustafa",
|
||||
"category": "Productivity",
|
||||
|
||||
@@ -20,16 +20,6 @@ GITHUB_TOKEN=
|
||||
# ─── Optional: Package manager override ──────────────────────────────────────
|
||||
# CLAUDE_CODE_PACKAGE_MANAGER=npm # npm | pnpm | yarn | bun
|
||||
|
||||
# --- Optional: Astraflow / UModelVerse (OpenAI-compatible) -------------------
|
||||
# Global endpoint: https://api.umodelverse.ai/v1
|
||||
ASTRAFLOW_API_KEY=
|
||||
# ASTRAFLOW_MODEL=gpt-4o-mini
|
||||
# ASTRAFLOW_BASE_URL=https://api.umodelverse.ai/v1
|
||||
# China endpoint: https://api.modelverse.cn/v1
|
||||
ASTRAFLOW_CN_API_KEY=
|
||||
# ASTRAFLOW_CN_MODEL=gpt-4o-mini
|
||||
# ASTRAFLOW_CN_BASE_URL=https://api.modelverse.cn/v1
|
||||
|
||||
# ─── Session & Security ─────────────────────────────────────────────────────
|
||||
# GitHub username (used by CI scripts for credential context)
|
||||
GITHUB_USER="your-github-username"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
description: Run a deterministic repository harness audit and return a prioritized scorecard.
|
||||
---
|
||||
|
||||
# Harness Audit Command
|
||||
|
||||
Run a deterministic repository harness audit and return a prioritized scorecard.
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
description: Run AgentShield against agent, hook, MCP, permission, and secret surfaces.
|
||||
agent: everything-claude-code:security-reviewer
|
||||
subtask: true
|
||||
---
|
||||
|
||||
# Security Scan Command
|
||||
|
||||
Run AgentShield against the current project or a target path, then turn the findings into a prioritized remediation plan.
|
||||
|
||||
## Usage
|
||||
|
||||
`/security-scan [path] [--format text|json|markdown|html] [--min-severity low|medium|high|critical] [--fix]`
|
||||
|
||||
- `path` (optional): defaults to the current project. Use a `.claude/` path, a repo root, or a checked-in template directory.
|
||||
- `--format`: output format. Use `json` for CI, `markdown` for handoffs, and `html` for standalone review reports.
|
||||
- `--min-severity`: filters lower-priority findings.
|
||||
- `--fix`: applies only AgentShield fixes explicitly marked as safe and auto-fixable.
|
||||
|
||||
## Deterministic Engine
|
||||
|
||||
Prefer the packaged scanner:
|
||||
|
||||
```bash
|
||||
npx ecc-agentshield scan --path "${TARGET_PATH:-.}" --format text
|
||||
```
|
||||
|
||||
For local AgentShield development, run from the AgentShield checkout:
|
||||
|
||||
```bash
|
||||
npm run scan -- --path "${TARGET_PATH:-.}" --format text
|
||||
```
|
||||
|
||||
Do not invent findings. Use AgentShield output as the source of truth and separate scanner facts from follow-up judgment.
|
||||
|
||||
## Review Checklist
|
||||
|
||||
1. Identify active runtime findings first:
|
||||
- hardcoded secrets
|
||||
- broad permissions
|
||||
- executable hooks
|
||||
- MCP servers with shell, filesystem, remote transport, or unpinned `npx`
|
||||
- agent prompts that handle untrusted content without defenses
|
||||
2. Separate lower-confidence inventory:
|
||||
- docs examples
|
||||
- template examples
|
||||
- plugin manifests
|
||||
- project-local optional settings
|
||||
3. For each critical or high finding, return:
|
||||
- file path
|
||||
- severity
|
||||
- runtime confidence
|
||||
- why it matters
|
||||
- exact remediation
|
||||
- whether it is safe to auto-fix
|
||||
4. If `--fix` is requested, state the planned edits before applying fixes.
|
||||
5. Re-run the scan after fixes and report the before/after score.
|
||||
|
||||
## Output Contract
|
||||
|
||||
Return:
|
||||
|
||||
1. Security grade and score.
|
||||
2. Counts by severity and runtime confidence.
|
||||
3. Critical/high findings with exact paths.
|
||||
4. Lower-confidence findings grouped separately.
|
||||
5. A remediation order.
|
||||
6. Commands run and whether the scan was local, CI, or npx-backed.
|
||||
|
||||
## CI Pattern
|
||||
|
||||
Use AgentShield in GitHub Actions for enforced gates:
|
||||
|
||||
```yaml
|
||||
- uses: affaan-m/agentshield@v1
|
||||
with:
|
||||
path: "."
|
||||
min-severity: "medium"
|
||||
fail-on-findings: true
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- Skill: `skills/security-scan/SKILL.md`
|
||||
- Agent: `agents/security-reviewer.md`
|
||||
- Scanner: <https://github.com/affaan-m/agentshield>
|
||||
|
||||
## Arguments
|
||||
|
||||
$ARGUMENTS:
|
||||
- optional target path
|
||||
- optional AgentShield flags
|
||||
@@ -45,7 +45,7 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
|
||||
function hasProjectFile(relativePath: string): boolean {
|
||||
try {
|
||||
return fs.statSync(resolvePath(relativePath)).isFile()
|
||||
return fs.existsSync(resolvePath(relativePath))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Everything Claude Code (ECC) — Agent Instructions
|
||||
|
||||
This is a **production-ready AI coding plugin** providing 58 specialized agents, 220 skills, 74 commands, and automated hook workflows for software development.
|
||||
This is a **production-ready AI coding plugin** providing 54 specialized agents, 204 skills, 69 commands, and automated hook workflows for software development.
|
||||
|
||||
**Version:** 2.0.0-rc.1
|
||||
|
||||
@@ -27,7 +27,6 @@ This is a **production-ready AI coding plugin** providing 58 specialized agents,
|
||||
| doc-updater | Documentation and codemaps | Updating docs |
|
||||
| cpp-reviewer | C/C++ code review | C and C++ projects |
|
||||
| cpp-build-resolver | C/C++ build errors | C and C++ build failures |
|
||||
| fsharp-reviewer | F# functional code review | F# projects |
|
||||
| docs-lookup | Documentation lookup via Context7 | API/docs questions |
|
||||
| go-reviewer | Go code review | Go projects |
|
||||
| go-build-resolver | Go build errors | Go build failures |
|
||||
@@ -147,9 +146,9 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
agents/ — 58 specialized subagents
|
||||
skills/ — 220 workflow skills and domain knowledge
|
||||
commands/ — 74 slash commands
|
||||
agents/ — 54 specialized subagents
|
||||
skills/ — 204 workflow skills and domain knowledge
|
||||
commands/ — 69 slash commands
|
||||
hooks/ — Trigger-based automations
|
||||
rules/ — Always-follow guidelines (common + per-language)
|
||||
scripts/ — Cross-platform Node.js utilities
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md)
|
||||
**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md)
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
**Language / 语言 / 語言 / Dil / Язык**
|
||||
|
||||
[**English**](README.md) | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
|
||||
| [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md)
|
||||
| [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,7 @@ This repo is the raw code only. The guides explain everything.
|
||||
### v2.0.0-rc.1 — Surface Refresh, Operator Workflows, and ECC 2.0 Alpha (Apr 2026)
|
||||
|
||||
- **Dashboard GUI** — New Tkinter-based desktop application (`ecc_dashboard.py` or `npm run dashboard`) with dark/light theme toggle, font customization, and project logo in header and taskbar.
|
||||
- **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 55 agents, 208 skills, and 72 legacy command shims.
|
||||
- **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 54 agents, 204 skills, and 69 legacy command shims.
|
||||
- **Operator and outbound workflow expansion** — `brand-voice`, `social-graph-ranker`, `connections-optimizer`, `customer-billing-ops`, `ecc-tools-cost-audit`, `google-workspace-ops`, `project-flow-ops`, and `workspace-surface-audit` round out the operator lane.
|
||||
- **Media and launch tooling** — `manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system.
|
||||
- **Framework and product surface growth** — `nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone.
|
||||
@@ -358,7 +358,7 @@ If you stacked methods, clean up in this order:
|
||||
/plugin list ecc@ecc
|
||||
```
|
||||
|
||||
**That's it!** You now have access to 58 agents, 220 skills, and 74 legacy command shims.
|
||||
**That's it!** You now have access to 54 agents, 204 skills, and 69 legacy command shims.
|
||||
|
||||
### Dashboard GUI
|
||||
|
||||
@@ -456,7 +456,7 @@ everything-claude-code/
|
||||
| |-- plugin.json # Plugin metadata and component paths
|
||||
| |-- marketplace.json # Marketplace catalog for /plugin marketplace add
|
||||
|
|
||||
|-- agents/ # 58 specialized subagents for delegation
|
||||
|-- agents/ # 54 specialized subagents for delegation
|
||||
| |-- planner.md # Feature implementation planning
|
||||
| |-- architect.md # System design decisions
|
||||
| |-- tdd-guide.md # Test-driven development
|
||||
@@ -472,7 +472,6 @@ everything-claude-code/
|
||||
| |-- harness-optimizer.md # Harness config tuning
|
||||
| |-- cpp-reviewer.md # C++ code review
|
||||
| |-- cpp-build-resolver.md # C++ build error resolution
|
||||
| |-- fsharp-reviewer.md # F# functional code review
|
||||
| |-- go-reviewer.md # Go code review
|
||||
| |-- go-build-resolver.md # Go build error resolution
|
||||
| |-- python-reviewer.md # Python code review
|
||||
@@ -482,7 +481,6 @@ everything-claude-code/
|
||||
| |-- java-build-resolver.md # Java/Maven/Gradle build errors
|
||||
| |-- kotlin-reviewer.md # Kotlin/Android/KMP code review
|
||||
| |-- kotlin-build-resolver.md # Kotlin/Gradle build errors
|
||||
| |-- harmonyos-app-resolver.md # HarmonyOS/ArkTS app development
|
||||
| |-- rust-reviewer.md # Rust code review
|
||||
| |-- rust-build-resolver.md # Rust build error resolution
|
||||
| |-- pytorch-build-resolver.md # PyTorch/CUDA training errors
|
||||
@@ -526,10 +524,6 @@ everything-claude-code/
|
||||
| |-- springboot-security/ # Spring Boot security (NEW)
|
||||
| |-- springboot-tdd/ # Spring Boot TDD (NEW)
|
||||
| |-- springboot-verification/ # Spring Boot verification (NEW)
|
||||
| |-- quarkus-patterns/ # Quarkus REST, Panache, and messaging patterns (NEW)
|
||||
| |-- quarkus-security/ # Quarkus JWT/OIDC and RBAC security (NEW)
|
||||
| |-- quarkus-tdd/ # Quarkus testing with JUnit, REST Assured, and Dev Services (NEW)
|
||||
| |-- quarkus-verification/ # Quarkus build, test, security, and native verification (NEW)
|
||||
| |-- configure-ecc/ # Interactive installation wizard (NEW)
|
||||
| |-- security-scan/ # AgentShield security auditor integration (NEW)
|
||||
| |-- java-coding-standards/ # Java coding standards (NEW)
|
||||
@@ -612,7 +606,6 @@ everything-claude-code/
|
||||
| |-- golang/ # Go specific
|
||||
| |-- swift/ # Swift specific
|
||||
| |-- php/ # PHP specific (NEW)
|
||||
| |-- arkts/ # HarmonyOS / ArkTS specific
|
||||
|
|
||||
|-- hooks/ # Trigger-based automations
|
||||
| |-- README.md # Hook documentation, recipes, and customization guide
|
||||
@@ -847,7 +840,6 @@ cp -r everything-claude-code/rules/typescript ~/.claude/rules/ecc/ # pick your
|
||||
cp -r everything-claude-code/rules/python ~/.claude/rules/ecc/
|
||||
cp -r everything-claude-code/rules/golang ~/.claude/rules/ecc/
|
||||
cp -r everything-claude-code/rules/php ~/.claude/rules/ecc/
|
||||
cp -r everything-claude-code/rules/arkts ~/.claude/rules/ecc/
|
||||
|
||||
# Copy skills first (primary workflow surface)
|
||||
# Recommended (new users): core/general skills only
|
||||
@@ -967,7 +959,6 @@ rules/
|
||||
golang/ # Go specific patterns and tools
|
||||
swift/ # Swift specific patterns and tools
|
||||
php/ # PHP specific patterns and tools
|
||||
arkts/ # HarmonyOS / ArkTS patterns and constraints
|
||||
```
|
||||
|
||||
See [`rules/README.md`](rules/README.md) for installation and structure details.
|
||||
@@ -991,9 +982,7 @@ Not sure where to start? Use this quick reference. Skills are the canonical work
|
||||
| Update documentation | `/update-docs` | doc-updater |
|
||||
| Review Go code | `/go-review` | go-reviewer |
|
||||
| Review Python code | `/python-review` | python-reviewer |
|
||||
| Review F# code | *(invoke `fsharp-reviewer` directly)* | fsharp-reviewer |
|
||||
| Review TypeScript/JavaScript code | *(invoke `typescript-reviewer` directly)* | typescript-reviewer |
|
||||
| Develop HarmonyOS apps | *(invoke `harmonyos-app-resolver` directly)* | harmonyos-app-resolver |
|
||||
| Audit database queries | *(auto-delegated)* | database-reviewer |
|
||||
| Review production ML changes | `mle-workflow` skill + `mle-reviewer` agent | mle-reviewer |
|
||||
|
||||
@@ -1152,7 +1141,7 @@ Please contribute! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
### Ideas for Contributions
|
||||
|
||||
- Language-specific skills (Rust, C#, Kotlin, Java) — Go, Python, Perl, Swift, TypeScript, and HarmonyOS/ArkTS already included
|
||||
- Language-specific skills (Rust, C#, Kotlin, Java) — Go, Python, Perl, Swift, and TypeScript already included
|
||||
- Framework-specific configs (Rails, FastAPI) — Django, NestJS, Spring Boot, and Laravel already included
|
||||
- DevOps agents (Kubernetes, Terraform, AWS, Docker)
|
||||
- Testing strategies (different frameworks, visual regression)
|
||||
@@ -1360,9 +1349,9 @@ The configuration is automatically detected from `.opencode/opencode.json`.
|
||||
|
||||
| Feature | Claude Code | OpenCode | Status |
|
||||
|---------|-------------|----------|--------|
|
||||
| Agents | PASS: 58 agents | PASS: 12 agents | **Claude Code leads** |
|
||||
| Commands | PASS: 74 commands | PASS: 35 commands | **Claude Code leads** |
|
||||
| Skills | PASS: 220 skills | PASS: 37 skills | **Claude Code leads** |
|
||||
| Agents | PASS: 54 agents | PASS: 12 agents | **Claude Code leads** |
|
||||
| Commands | PASS: 69 commands | PASS: 31 commands | **Claude Code leads** |
|
||||
| Skills | PASS: 204 skills | PASS: 37 skills | **Claude Code leads** |
|
||||
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
||||
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
||||
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
||||
@@ -1465,9 +1454,9 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
|
||||
|
||||
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||
|---------|------------|------------|-----------|----------|
|
||||
| **Agents** | 58 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
||||
| **Commands** | 74 | Shared | Instruction-based | 35 |
|
||||
| **Skills** | 220 | Shared | 10 (native format) | 37 |
|
||||
| **Agents** | 54 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
||||
| **Commands** | 69 | Shared | Instruction-based | 31 |
|
||||
| **Skills** | 204 | Shared | 10 (native format) | 37 |
|
||||
| **Hook Events** | 8 types | 15 types | None yet | 11 types |
|
||||
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks |
|
||||
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions |
|
||||
@@ -1601,7 +1590,6 @@ Projects built on or inspired by Everything Claude Code:
|
||||
| Project | Description |
|
||||
|---------|-------------|
|
||||
| [EVC](https://github.com/SaigonXIII/evc) | Marketing agent workspace — 42 commands for content operators, brand governance, and multi-channel publishing. [Visual overview](https://saigonxiii.github.io/evc). |
|
||||
| [trading-skills](https://github.com/VictorVVedtion/trading-skills) | 68 trading-themed Claude Code skills with pre-trade review prompts and risk gates inspired by market operators. |
|
||||
|
||||
Built something with ECC? Open a PR to add it here.
|
||||
|
||||
|
||||
+3
-3
@@ -21,9 +21,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
**Language / 语言 / 語言 / Dil**
|
||||
|
||||
[**English**](README.md) | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md)
|
||||
[**English**](README.md) | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -160,7 +160,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
||||
/plugin list ecc@ecc
|
||||
```
|
||||
|
||||
**完成!** 你现在可以使用 58 个代理、220 个技能和 74 个命令。
|
||||
**完成!** 你现在可以使用 54 个代理、204 个技能和 69 个命令。
|
||||
|
||||
### multi-* 命令需要额外配置
|
||||
|
||||
|
||||
-15
@@ -9,12 +9,10 @@ model:
|
||||
fallback:
|
||||
- claude-sonnet-4-6
|
||||
skills:
|
||||
- agent-architecture-audit
|
||||
- agent-eval
|
||||
- agent-harness-construction
|
||||
- agent-payment-x402
|
||||
- agentic-engineering
|
||||
- agentic-os
|
||||
- ai-first-engineering
|
||||
- ai-regression-testing
|
||||
- android-clean-architecture
|
||||
@@ -63,7 +61,6 @@ skills:
|
||||
- e2e-testing
|
||||
- energy-procurement
|
||||
- enterprise-agent-ops
|
||||
- error-handling
|
||||
- eval-harness
|
||||
- exa-search
|
||||
- fal-ai-media
|
||||
@@ -71,7 +68,6 @@ skills:
|
||||
- foundation-models-on-device
|
||||
- frontend-patterns
|
||||
- frontend-slides
|
||||
- fsharp-testing
|
||||
- git-workflow
|
||||
- golang-patterns
|
||||
- golang-testing
|
||||
@@ -99,7 +95,6 @@ skills:
|
||||
- logistics-exception-management
|
||||
- market-research
|
||||
- mcp-server-patterns
|
||||
- motion-ui
|
||||
- nanoclaw-repl
|
||||
- nextjs-turbopack
|
||||
- nutrient-document-processing
|
||||
@@ -108,7 +103,6 @@ skills:
|
||||
- perl-security
|
||||
- perl-testing
|
||||
- plankton-code-quality
|
||||
- plan-orchestrate
|
||||
- postgres-patterns
|
||||
- product-lens
|
||||
- production-scheduling
|
||||
@@ -117,10 +111,6 @@ skills:
|
||||
- python-testing
|
||||
- pytorch-patterns
|
||||
- quality-nonconformance
|
||||
- quarkus-patterns
|
||||
- quarkus-security
|
||||
- quarkus-tdd
|
||||
- quarkus-verification
|
||||
- ralphinho-rfc-pipeline
|
||||
- regex-vs-llm-structured-text
|
||||
- repo-scan
|
||||
@@ -161,7 +151,6 @@ commands:
|
||||
- cpp-build
|
||||
- cpp-review
|
||||
- cpp-test
|
||||
- ecc-guide
|
||||
- evolve
|
||||
- fastapi-review
|
||||
- feature-dev
|
||||
@@ -197,12 +186,9 @@ commands:
|
||||
- multi-plan
|
||||
- multi-workflow
|
||||
- plan
|
||||
- plan-prd
|
||||
- pm2
|
||||
- projects
|
||||
- promote
|
||||
- project-init
|
||||
- pr
|
||||
- prp-commit
|
||||
- prp-implement
|
||||
- prp-plan
|
||||
@@ -219,7 +205,6 @@ commands:
|
||||
- rust-test
|
||||
- santa-loop
|
||||
- save-session
|
||||
- security-scan
|
||||
- sessions
|
||||
- setup-pm
|
||||
- skill-create
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
---
|
||||
name: fsharp-reviewer
|
||||
description: Expert F# code reviewer specializing in functional idioms, type safety, pattern matching, computation expressions, and performance. Use for all F# code changes. MUST BE USED for F# projects.
|
||||
tools: ["Read", "Grep", "Glob", "Bash"]
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are a senior F# code reviewer ensuring high standards of idiomatic functional F# code and best practices.
|
||||
|
||||
When invoked:
|
||||
1. Run `git diff -- '*.fs' '*.fsx'` to see recent F# file changes
|
||||
2. Run `dotnet build` and `fantomas --check .` if available
|
||||
3. Focus on modified `.fs` and `.fsx` files
|
||||
4. Begin review immediately
|
||||
|
||||
## Review Priorities
|
||||
|
||||
### CRITICAL - Security
|
||||
- **SQL Injection**: String concatenation/interpolation in queries - use parameterized queries
|
||||
- **Command Injection**: Unvalidated input in `Process.Start` - validate and sanitize
|
||||
- **Path Traversal**: User-controlled file paths - use `Path.GetFullPath` + prefix check
|
||||
- **Insecure Deserialization**: `BinaryFormatter`, unsafe JSON settings
|
||||
- **Hardcoded secrets**: API keys, connection strings in source - use configuration/secret manager
|
||||
- **CSRF/XSS**: Missing anti-forgery tokens, unencoded output in views
|
||||
|
||||
### CRITICAL - Error Handling
|
||||
- **Swallowed exceptions**: `with _ -> ()` or `with _ -> None` - handle or reraise
|
||||
- **Missing disposal**: Manual disposal of `IDisposable` - use `use` or `use!` bindings
|
||||
- **Blocking async**: `.Result`, `.Wait()`, `.GetAwaiter().GetResult()` - use `let!` or `do!`
|
||||
- **Bare `failwith` in library code**: Prefer `Result` or `Option` for expected failures
|
||||
|
||||
### HIGH - Functional Idioms
|
||||
- **Mutable state in domain logic**: `mutable`, `ref` cells where immutable alternatives exist
|
||||
- **Incomplete pattern matches**: Missing cases or catch-all `_` that hides new union cases
|
||||
- **Imperative loops**: `for`/`while` where `List.map`, `Seq.filter`, `Array.fold` are clearer
|
||||
- **Null usage**: Using `null` instead of `Option<'T>` for missing values
|
||||
- **Class-heavy design**: OOP-style classes where modules + functions + records suffice
|
||||
|
||||
### HIGH - Type Safety
|
||||
- **Primitive obsession**: Raw strings/ints for domain concepts - use single-case DUs
|
||||
- **Unvalidated input**: Missing validation at system boundaries - use smart constructors
|
||||
- **Downcasting**: `:?>` without type test - use pattern matching with `:? T as t`
|
||||
- **`obj` usage**: Avoid `obj` boxing; prefer generics or explicit union types
|
||||
|
||||
### HIGH - Code Quality
|
||||
- **Large functions**: Over 40 lines - extract helper functions
|
||||
- **Deep nesting**: More than 3 levels - use early returns, `Result.bind`, or computation expressions
|
||||
- **Missing `[<RequireQualifiedAccess>]`**: On modules/unions that could cause name collisions
|
||||
- **Unused `open` declarations**: Remove unused module imports
|
||||
|
||||
### MEDIUM - Performance
|
||||
- **Seq in hot paths**: Lazy sequences recomputed repeatedly - materialize with `Seq.toList` or `Seq.toArray`
|
||||
- **String concatenation in loops**: Use `StringBuilder` or `String.concat`
|
||||
- **Excessive boxing**: Value types passed through `obj` - use generic functions
|
||||
- **N+1 queries**: Lazy loading in loops when using EF Core - use eager loading
|
||||
|
||||
### MEDIUM - Best Practices
|
||||
- **Naming conventions**: camelCase for functions/values, PascalCase for types/modules/DU cases
|
||||
- **Pipe operator readability**: Overly long chains - break into named intermediate bindings
|
||||
- **Computation expression misuse**: Nested `task { task { } }` - flatten with `let!`
|
||||
- **Module organization**: Related functions scattered across files - group cohesively
|
||||
|
||||
## Diagnostic Commands
|
||||
|
||||
```bash
|
||||
dotnet build # Compilation check
|
||||
fantomas --check . # Format check
|
||||
dotnet test --no-build # Run tests
|
||||
dotnet test --collect:"XPlat Code Coverage" # Coverage
|
||||
```
|
||||
|
||||
## Review Output Format
|
||||
|
||||
```text
|
||||
[SEVERITY] Issue title
|
||||
File: path/to/File.fs:42
|
||||
Issue: Description
|
||||
Fix: What to change
|
||||
```
|
||||
|
||||
## Approval Criteria
|
||||
|
||||
- **Approve**: No CRITICAL or HIGH issues
|
||||
- **Warning**: MEDIUM issues only (can merge with caution)
|
||||
- **Block**: CRITICAL or HIGH issues found
|
||||
|
||||
## Framework Checks
|
||||
|
||||
- **ASP.NET Core**: Giraffe or Saturn handlers, model validation, auth policies, middleware order
|
||||
- **EF Core**: Migration safety, eager loading, `AsNoTracking` for reads
|
||||
- **Fable**: Elmish architecture, message handling completeness, view function purity
|
||||
|
||||
## Reference
|
||||
|
||||
For detailed .NET patterns, see skill: `dotnet-patterns`.
|
||||
For testing guidelines, see skill: `fsharp-testing`.
|
||||
|
||||
---
|
||||
|
||||
Review with the mindset: "Is this idiomatic F# that leverages the type system and functional patterns effectively?"
|
||||
@@ -1,173 +0,0 @@
|
||||
---
|
||||
name: harmonyos-app-resolver
|
||||
description: HarmonyOS application development expert specializing in ArkTS and ArkUI. Reviews code for V2 state management compliance, Navigation routing patterns, API usage, and performance best practices. Use for HarmonyOS/OpenHarmony projects.
|
||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# HarmonyOS Application Development Expert
|
||||
|
||||
You are a senior HarmonyOS application development expert specializing in ArkTS and ArkUI for building high-quality HarmonyOS native applications. You have deep understanding of HarmonyOS system components, APIs, and underlying mechanisms, and always apply industry best practices.
|
||||
|
||||
## Core Tech Stack Constraints (Strictly Enforced)
|
||||
|
||||
In all code generation, Q&A, and technical recommendations, you MUST strictly follow these technology choices - **no compromise**:
|
||||
|
||||
### 1. State Management: V2 Only (ArkUI State Management V2)
|
||||
|
||||
- **MUST use**: ArkUI State Management V2 decorators/patterns (use applicable decorators by context), including `@ComponentV2`, `@Local`, `@Param`, `@Event`, `@Provider`, `@Consumer`, `@Monitor`, `@Computed`; use `@ObservedV2` + `@Trace` for observable model classes/properties when needed.
|
||||
- **MUST NOT use**: V1 decorators (`@Component`, `@State`, `@Prop`, `@Link`, `@ObjectLink`, `@Observed`, `@Provide`, `@Consume`, `@Watch`)
|
||||
|
||||
### 2. Routing: Navigation Only
|
||||
|
||||
- **MUST use**: `Navigation` component with `NavPathStack` for route management; use `NavDestination` as root container for sub-pages
|
||||
- **MUST NOT use**: Legacy `router` module (`@ohos.router`) for page navigation
|
||||
|
||||
## Your Role
|
||||
|
||||
- **ArkTS & ArkUI mastery** - Write elegant, efficient, type-safe declarative UI code with deep understanding of V2 state management observation mechanisms and UI update logic
|
||||
- **Full-stack component & API expertise** - Proficient with UI components (List, Grid, Swiper, Tabs, etc.) and system APIs (network, media, file, preferences, etc.) to rapidly implement complex business requirements
|
||||
- **Best practice enforcement**:
|
||||
- **Architecture**: Modular, layered architecture ensuring high cohesion and low coupling
|
||||
- **Performance**: Use `LazyForEach`, component reuse, async processing for expensive tasks
|
||||
- **Code standards**: Consistent style, rigorous logic, clear comments, compliant with HarmonyOS official guidelines
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Understand Project Context
|
||||
|
||||
- Read `CLAUDE.md`, `module.json5`, `oh-package.json5` for project conventions
|
||||
- Identify existing state management version (V1 vs V2) and routing approach
|
||||
- Check `build-profile.json5` for API level and device targets
|
||||
|
||||
### Step 2: Review or Implement
|
||||
|
||||
When reviewing code:
|
||||
- Flag any V1 state management usage - recommend V2 migration
|
||||
- Flag any `@ohos.router` usage - recommend Navigation migration
|
||||
- Check API level compatibility and permission declarations
|
||||
- Verify resource references use `$r()` instead of hardcoded literals
|
||||
- Check i18n completeness across all language directories
|
||||
|
||||
When implementing features:
|
||||
- Use V2 state management exclusively
|
||||
- Use Navigation + NavPathStack for routing
|
||||
- Define UI constants in resources, reference via `$r()`
|
||||
- Add i18n strings to all language directories
|
||||
- Consider dark theme support for new color resources
|
||||
|
||||
### Step 3: Validate
|
||||
|
||||
```bash
|
||||
# Build HAP package (global hvigor environment)
|
||||
hvigorw assembleHap -p product=default
|
||||
```
|
||||
|
||||
- Run build after every implementation to verify compilation
|
||||
- Check for ArkTS syntax constraint violations
|
||||
- Verify permission declarations in `module.json5`
|
||||
|
||||
## ArkTS Syntax Constraints (Compilation Blockers)
|
||||
|
||||
ArkTS is a strict subset of TypeScript. The following are NOT supported and will cause compilation failures:
|
||||
|
||||
**Type System:**
|
||||
- No `any` or `unknown` types - use explicit types
|
||||
- No index access types - use type names
|
||||
- No conditional type aliases or `infer` keyword
|
||||
- No intersection types - use inheritance
|
||||
- No mapped types - use classes
|
||||
- No `typeof` for type annotations - use explicit type declarations
|
||||
- No `as const` assertions - use explicit type annotations
|
||||
- No structural typing - use inheritance, interfaces, or type aliases
|
||||
- No TypeScript utility types except `Partial`, `Required`, `Readonly`, `Record`
|
||||
|
||||
**Functions & Classes:**
|
||||
- No function expressions - use arrow functions
|
||||
- No nested functions - use lambdas
|
||||
- No generator functions - use async/await
|
||||
- No `Function.apply`, `Function.call`, `Function.bind`
|
||||
- No constructor type expressions - use lambdas
|
||||
- No constructor signatures in interfaces or object types
|
||||
- No declaring class fields in constructors - declare in class body
|
||||
- No `this` in standalone functions or static methods
|
||||
- No `new.target`
|
||||
|
||||
**Object & Property Access:**
|
||||
- No dynamic field declaration or `obj["field"]` access - use `obj.field`
|
||||
- No `delete` operator - use nullable type with `null`
|
||||
- No prototype assignment
|
||||
- No `in` operator - use `instanceof`
|
||||
- No `Symbol()` API (except `Symbol.iterator`)
|
||||
- No `globalThis` or global scope - use explicit module exports/imports
|
||||
|
||||
**Destructuring & Spread:**
|
||||
- No destructuring assignments or variable declarations
|
||||
- No destructuring parameter declarations
|
||||
- Spread operator only for arrays into rest parameters or array literals
|
||||
|
||||
**Modules & Imports:**
|
||||
- No `require()` imports - use regular `import`
|
||||
- No `export = ...` syntax - use normal export/import
|
||||
- No import assertions
|
||||
- No UMD modules
|
||||
- No wildcards in module names
|
||||
- All `import` statements must precede other statements
|
||||
|
||||
**Other:**
|
||||
- No `var` keyword - use `let`
|
||||
- No `for...in` loops - use regular `for` loops for arrays
|
||||
- No `with` statements
|
||||
- No JSX expressions
|
||||
- No `#` private identifiers - use `private` keyword
|
||||
- No declaration merging
|
||||
- No index signatures - use arrays
|
||||
- No class literals - use named class types
|
||||
- Comma operator only in `for` loops
|
||||
- Unary operators `+`, `-`, `~` only for numeric types
|
||||
- Omit type annotations in `catch` clauses
|
||||
|
||||
**Object Literals:**
|
||||
- Supported only when compiler can infer the corresponding class/interface
|
||||
- Not supported for: `any`/`Object`/`object` types, classes with methods, classes with parameterized constructors, classes with `readonly` fields
|
||||
|
||||
## HarmonyOS API Usage Guidelines
|
||||
|
||||
- Prefer official HarmonyOS APIs, UI components, animations, and code templates
|
||||
- Verify API parameters, return values, API level, and device support before use
|
||||
- When uncertain about syntax or API usage, search official Huawei developer documentation - never guess
|
||||
- Confirm `import` statements are added at file header before using APIs
|
||||
- Verify required permissions in `module.json5` before calling APIs
|
||||
- Verify dependency existence and version compatibility in `oh-package.json5`
|
||||
- Enforce `@ComponentV2` for all new or modified ArkUI components; when encountering legacy `@Component`, recommend migration to V2
|
||||
- Define UI display constants as resources, reference via `$r()` - avoid hardcoded literals
|
||||
- Add i18n resource strings to all language directories when creating new entries
|
||||
- Check if new color resources need dark theme support (recommended for new projects)
|
||||
|
||||
## ArkUI Animation Guidelines
|
||||
|
||||
- Prefer native HarmonyOS animation APIs and advanced templates
|
||||
- Use declarative UI with state-driven animations (change state variables to trigger animations)
|
||||
- Set `renderGroup(true)` for complex sub-component animations to reduce render batches
|
||||
- NEVER frequently change `width`, `height`, `padding`, `margin` during animations - severe performance impact
|
||||
|
||||
## Behavior Guidelines
|
||||
|
||||
- **Proactive refactoring**: If user code contains V1 state management or `router` routing, proactively flag it and refactor to V2 + Navigation
|
||||
- **Explain best practices**: Briefly explain why a solution is "best practice" (e.g., performance advantages of `@ComponentV2` over V1)
|
||||
- **Rigor**: Ensure code snippets are complete, runnable, and handle common edge cases (empty data, loading states, error handling)
|
||||
|
||||
## Output Format
|
||||
|
||||
```text
|
||||
[REVIEW] src/main/ets/pages/HomePage.ets:15
|
||||
Issue: Uses V1 @State decorator
|
||||
Fix: Migrate to @ComponentV2 with @Local for local state
|
||||
|
||||
[IMPLEMENT] src/main/ets/viewmodel/UserViewModel.ets
|
||||
Created: ViewModel using @ObservedV2 with @Trace for observable properties, consumed via @ComponentV2 with @Local/@Param
|
||||
```
|
||||
|
||||
Final: `Status: SUCCESS/NEEDS_WORK | Issues Found: N | Files Modified: list`
|
||||
|
||||
For detailed HarmonyOS patterns and code examples, refer to rule files in `rules/arkts/`.
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
name: homelab-architect
|
||||
description: Designs home and small-lab network plans from hardware inventory, goals, and operator experience level, with safe staged changes and rollback guidance.
|
||||
tools: ["Read", "Grep"]
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are a practical homelab network architect. Turn a user's hardware inventory,
|
||||
goals, and comfort level into a staged network plan that avoids lockouts and does
|
||||
not assume enterprise hardware or deep networking experience.
|
||||
|
||||
## Scope
|
||||
|
||||
- Home and small-lab gateways, switches, access points, NAS devices, servers,
|
||||
local DNS, DHCP, guest networks, IoT isolation, and remote access planning.
|
||||
- Planning and review only. Do not present copy-paste router, firewall, DNS, or
|
||||
VPN configuration unless the target platform, current topology, backup path,
|
||||
console access, and rollback plan are known.
|
||||
|
||||
Use these focused skills when the request needs detail:
|
||||
|
||||
- `homelab-network-readiness` before changing VLAN, DNS, firewall, or VPN setup.
|
||||
- `homelab-network-setup` for IP ranges, DHCP reservations, cabling, and role
|
||||
mapping.
|
||||
- `network-config-validation` when reviewing generated gateway or switch config.
|
||||
- `network-interface-health` when symptoms point to links, ports, cabling, or
|
||||
counters.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Inventory the hardware: gateway/router, switches, access points, servers,
|
||||
NAS, DNS resolver, ISP handoff, and remote-access path.
|
||||
2. Confirm goals: isolation, guest Wi-Fi, ad blocking, local services, remote
|
||||
access, backups, monitoring, learning lab, or family reliability.
|
||||
3. Match goals to hardware capability. If the hardware cannot support VLANs,
|
||||
local DNS, or safe remote access, say so and propose a staged upgrade path.
|
||||
4. Design the smallest useful topology first, then optional later phases.
|
||||
5. Define rollback and access safety before any disruptive change.
|
||||
6. Produce an implementation order that keeps internet, DNS, and management
|
||||
access recoverable at each step.
|
||||
|
||||
## Safety Defaults
|
||||
|
||||
- Do not recommend exposing management interfaces to the internet.
|
||||
- Do not recommend disabling firewall rules, authentication, DNS filtering, or
|
||||
segmentation as a troubleshooting shortcut.
|
||||
- Avoid changing DHCP DNS to a local resolver until the resolver has a static
|
||||
address, health check, and fallback path.
|
||||
- Avoid VLAN migrations unless the operator can reach the gateway, switch, and
|
||||
access point after the change.
|
||||
- Prefer plain-English explanations and small reversible phases.
|
||||
|
||||
## Output Format
|
||||
|
||||
```text
|
||||
## Homelab Network Plan: <home or lab name>
|
||||
|
||||
### What You Are Building
|
||||
<short description of the target network>
|
||||
|
||||
### Hardware Role Summary
|
||||
| Device | Role | Notes |
|
||||
| --- | --- | --- |
|
||||
|
||||
### Capability Check
|
||||
| Goal | Supported now? | Requirement or upgrade |
|
||||
| --- | --- | --- |
|
||||
|
||||
### Addressing And Segmentation
|
||||
| Network | Purpose | Example range | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
### DNS, DHCP, And Local Services
|
||||
<resolver plan, static reservations, fallback, and service placement>
|
||||
|
||||
### Firewall And Access Rules
|
||||
- <plain-English rule>
|
||||
- <plain-English rule>
|
||||
|
||||
### Implementation Order
|
||||
1. <safe first step>
|
||||
2. <validation before next step>
|
||||
3. <rollback point>
|
||||
|
||||
### Quick Wins
|
||||
1. <small, high-value step>
|
||||
2. <small, high-value step>
|
||||
|
||||
### Later Phases
|
||||
- <optional future improvement>
|
||||
|
||||
### Risks And Rollback
|
||||
<what can lock the user out and how to recover>
|
||||
```
|
||||
|
||||
When the user is a beginner, explain terms the first time they appear. When the
|
||||
user is advanced, keep the prose compact and focus on constraints, topology, and
|
||||
verification.
|
||||
@@ -1,97 +0,0 @@
|
||||
---
|
||||
name: network-architect
|
||||
description: Designs enterprise or multi-site network architecture from requirements, using existing network skills for focused routing, validation, automation, and troubleshooting detail.
|
||||
tools: ["Read", "Grep"]
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are a senior network architecture planner. Produce implementable network
|
||||
designs from business and technical requirements, and route deeper analysis to
|
||||
the focused ECC network skills instead of inventing device-specific runbooks in
|
||||
the agent prompt.
|
||||
|
||||
## Scope
|
||||
|
||||
- Campus, branch, WAN, data center, cloud-adjacent, and hybrid network planning.
|
||||
- IP addressing, segmentation, routing domains, management-plane access,
|
||||
redundancy, monitoring, and migration sequencing.
|
||||
- Design and review only. Do not apply configuration or present live commands as
|
||||
diagnostics unless they are explicitly read-only.
|
||||
|
||||
Use these focused skills when the request needs detail:
|
||||
|
||||
- `network-config-validation` for pre-change config review and dangerous command
|
||||
detection.
|
||||
- `network-bgp-diagnostics` for BGP neighbor, route-policy, and prefix evidence.
|
||||
- `network-interface-health` for link, counter, CRC, drop, and flap analysis.
|
||||
- `cisco-ios-patterns` for IOS/IOS-XE syntax and safe show-command workflows.
|
||||
- `netmiko-ssh-automation` for bounded read-only network automation patterns.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Restate the objective, constraints, and non-goals.
|
||||
2. Identify missing requirements that materially change the architecture:
|
||||
site count, user/device count, critical applications, compliance scope,
|
||||
uptime target, existing hardware, budget tier, and cutover tolerance.
|
||||
3. Pick the topology and explain why it fits the constraints.
|
||||
4. Design routing and segmentation before discussing hardware.
|
||||
5. Define the management plane, logging, monitoring, backup, and rollback model.
|
||||
6. Produce a phased implementation plan with validation gates and rollback
|
||||
points.
|
||||
7. List residual risks and the evidence still needed from operators.
|
||||
|
||||
## Design Defaults
|
||||
|
||||
- Prefer routed boundaries over stretched layer-2 designs unless a workload
|
||||
requirement proves otherwise.
|
||||
- Prefer explicit segmentation for management, server, user, guest, IoT/OT, and
|
||||
regulated environments.
|
||||
- Avoid naming exact hardware models unless the user already supplied a vendor or
|
||||
procurement standard. Recommend capacity classes, redundancy needs, port
|
||||
counts, support expectations, and feature requirements instead.
|
||||
- Do not assume BGP, OSPF, EVPN, SD-WAN, or microsegmentation are required. Pick
|
||||
the simplest design that satisfies scale, operations, and risk.
|
||||
- Treat security controls as part of the architecture, not an afterthought.
|
||||
|
||||
## Output Format
|
||||
|
||||
```text
|
||||
## Network Architecture: <project or environment>
|
||||
|
||||
### Objective
|
||||
<what this design is for>
|
||||
|
||||
### Assumptions And Required Follow-Up
|
||||
- <assumption>
|
||||
- <question that would change the design>
|
||||
|
||||
### Recommended Topology
|
||||
<topology choice and reasoning>
|
||||
|
||||
### Addressing And Segmentation
|
||||
| Zone / domain | Purpose | Routing boundary | Allowed flows |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
### Routing And Connectivity
|
||||
<protocols, route boundaries, summarization, failover, and cloud/WAN notes>
|
||||
|
||||
### Management, Observability, And Backup
|
||||
<management access, logging, config backup, monitoring, and alerting>
|
||||
|
||||
### Implementation Phases
|
||||
1. <phase with validation gate>
|
||||
2. <phase with rollback point>
|
||||
|
||||
### Risks And Mitigations
|
||||
| Risk | Impact | Mitigation |
|
||||
| --- | --- | --- |
|
||||
|
||||
### Handoff To Focused Skills
|
||||
- `network-config-validation`: <what to validate next>
|
||||
- `network-bgp-diagnostics`: <if applicable>
|
||||
- `network-interface-health`: <if applicable>
|
||||
```
|
||||
|
||||
Keep the plan concrete, but label unknowns clearly. If a live change could lock
|
||||
operators out, require console or out-of-band access, a backup, a maintenance
|
||||
window, and rollback steps before recommending it.
|
||||
@@ -99,7 +99,7 @@ If PR not found, stop with error. Store PR metadata for later phases.
|
||||
Build review context:
|
||||
|
||||
1. **Project rules** — Read `CLAUDE.md`, `.claude/docs/`, and any contributing guidelines
|
||||
2. **Planning artifacts** — Check `.claude/prds/`, `.claude/plans/`, `.claude/reviews/`, and legacy `.claude/PRPs/{prds,plans,reports,reviews}/` for context related to this PR
|
||||
2. **PRP artifacts** — Check `.claude/PRPs/reports/` and `.claude/PRPs/plans/` for implementation context related to this PR
|
||||
3. **PR intent** — Parse PR description for goals, linked issues, test plans
|
||||
4. **Changed files** — List all modified files and categorize by type (source, test, config, docs)
|
||||
|
||||
@@ -188,7 +188,7 @@ Special cases:
|
||||
|
||||
### Phase 6 — REPORT
|
||||
|
||||
Create review artifact at `.claude/reviews/pr-<NUMBER>-review.md` unless the repo already uses legacy `.claude/PRPs/reviews/` for this workstream:
|
||||
Create review artifact at `.claude/PRPs/reviews/pr-<NUMBER>-review.md`:
|
||||
|
||||
```markdown
|
||||
# PR Review: #<NUMBER> — <TITLE>
|
||||
@@ -273,7 +273,7 @@ Issues: <critical_count> critical, <high_count> high, <medium_count> medium, <lo
|
||||
Validation: <pass_count>/<total_count> checks passed
|
||||
|
||||
Artifacts:
|
||||
Review: .claude/reviews/pr-<NUMBER>-review.md
|
||||
Review: .claude/PRPs/reviews/pr-<NUMBER>-review.md
|
||||
GitHub: <PR URL>
|
||||
|
||||
Next steps:
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
---
|
||||
description: Navigate ECC's current agents, skills, commands, hooks, install profiles, and docs from the live repository surface.
|
||||
---
|
||||
|
||||
# /ecc-guide
|
||||
|
||||
Use this command as a conversational map of Everything Claude Code. It should help the user discover the right ECC surface for their task without dumping the entire README or stale catalog counts.
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
/ecc-guide
|
||||
/ecc-guide setup
|
||||
/ecc-guide skills
|
||||
/ecc-guide commands
|
||||
/ecc-guide hooks
|
||||
/ecc-guide install
|
||||
/ecc-guide find: <query>
|
||||
/ecc-guide <feature-or-file-name>
|
||||
```
|
||||
|
||||
## Operating Rules
|
||||
|
||||
1. Read current repository files before answering when the checkout is available.
|
||||
2. Prefer current filesystem/catalog data over hard-coded counts.
|
||||
3. Keep the first answer short, then offer specific drill-down paths.
|
||||
4. Link users to canonical files instead of copying long sections.
|
||||
5. Do not invent commands, skills, agents, or install profiles that are not present.
|
||||
|
||||
## What To Inspect
|
||||
|
||||
Use these files as the canonical map:
|
||||
|
||||
- `README.md` for install paths, reset/uninstall guidance, and high-level positioning
|
||||
- `AGENTS.md` for contributor and project-structure guidance
|
||||
- `agent.yaml` for exported agent and command surface
|
||||
- `commands/` for maintained slash-command shims
|
||||
- `skills/*/SKILL.md` for reusable skill workflows
|
||||
- `agents/*.md` for delegated agent roles
|
||||
- `hooks/README.md` and `hooks/hooks.json` for hook behavior
|
||||
- `manifests/install-*.json` for selective install modules, components, and profiles
|
||||
- `scripts/ci/catalog.js --json` for live catalog counts when running inside ECC
|
||||
|
||||
## Response Patterns
|
||||
|
||||
### No Arguments
|
||||
|
||||
Give a compact menu:
|
||||
|
||||
- setup and install
|
||||
- choosing skills
|
||||
- command compatibility shims
|
||||
- agents and delegation
|
||||
- hooks and safety
|
||||
- troubleshooting an install
|
||||
- finding a specific feature
|
||||
|
||||
Then ask what they want to do next.
|
||||
|
||||
### Topic Lookup
|
||||
|
||||
For topics like `skills`, `commands`, `hooks`, `install`, or `agents`:
|
||||
|
||||
1. Summarize the current surface in 3-6 bullets.
|
||||
2. Point to the canonical directories/files.
|
||||
3. Suggest one or two commands that can verify the state.
|
||||
4. Avoid exhaustive lists unless the user asks for one.
|
||||
|
||||
### Search Mode
|
||||
|
||||
For `find: <query>`:
|
||||
|
||||
1. Search the relevant files with `rg`.
|
||||
2. Group results by surface: skills, commands, agents, rules, docs, hooks.
|
||||
3. Return the strongest matches first with file paths.
|
||||
4. Recommend the next action for each match.
|
||||
|
||||
### Feature Lookup
|
||||
|
||||
For a specific feature name:
|
||||
|
||||
1. Check exact paths first, such as `skills/<name>/SKILL.md`, `commands/<name>.md`, and `agents/<name>.md`.
|
||||
2. If exact lookup fails, search with `rg`.
|
||||
3. Explain what the feature does, when to use it, and what file is canonical.
|
||||
4. Mention adjacent features only when they reduce confusion.
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/project-init` for stack-aware ECC onboarding of a target project
|
||||
- `/harness-audit` for deterministic repo readiness scoring
|
||||
- `/skill-health` for skill quality checks
|
||||
- `/skill-create` for extracting a new skill from local git history
|
||||
- `/security-scan` for Claude/OpenCode configuration security review
|
||||
@@ -1,160 +0,0 @@
|
||||
---
|
||||
description: "Generate a lean, problem-first PRD and hand off to /plan for implementation planning."
|
||||
argument-hint: "[product/feature idea] (blank = start with questions)"
|
||||
---
|
||||
|
||||
# PRD Command
|
||||
|
||||
Produces a **Product Requirements Document** — the requirements-phase artifact of the SDLC. Captures *what* must be true for success and *why*, and stops before *how*. Implementation decomposition is delegated to `/plan`.
|
||||
|
||||
**Input**: `$ARGUMENTS`
|
||||
|
||||
## Scope of this command
|
||||
|
||||
| This command does | This command does NOT do |
|
||||
|---|---|
|
||||
| Frame the problem and users | Design the architecture |
|
||||
| Capture success criteria and scope | Pick files or write patterns |
|
||||
| List open questions and risks | Enumerate implementation tasks |
|
||||
| Write `.claude/prds/{name}.prd.md` | Produce an implementation plan — that's `/plan` |
|
||||
|
||||
If you find yourself writing implementation detail, stop and cut it. It belongs in `/plan`.
|
||||
|
||||
**Anti-fluff rule**: When information is missing, write `TBD — needs validation via {method}`. Never invent plausible-sounding requirements.
|
||||
|
||||
## Workflow
|
||||
|
||||
Four phases. Each phase is a single gate — ask the questions, wait for the user, then move on. No nested loops, no parallel research ceremony.
|
||||
|
||||
### Phase 1 — FRAME
|
||||
|
||||
If `$ARGUMENTS` is empty, ask:
|
||||
|
||||
> What do you want to build? One or two sentences.
|
||||
|
||||
If provided, restate in one sentence and ask:
|
||||
|
||||
> I understand: *{restated}*. Correct, or should I adjust?
|
||||
|
||||
Then ask the framing questions in a single set:
|
||||
|
||||
> 1. **Who** has this problem? (specific role or segment)
|
||||
> 2. **What** is the observable pain? (describe behavior, not assumed needs)
|
||||
> 3. **Why** can't they solve it with what exists today?
|
||||
> 4. **Why now?** — what changed that makes this worth doing?
|
||||
|
||||
Wait for the user. Do not proceed without answers (or explicit "skip").
|
||||
|
||||
### Phase 2 — GROUND
|
||||
|
||||
Ask for evidence. This is the shortest phase and the most load-bearing:
|
||||
|
||||
> What evidence do you have that this problem is real and worth solving? (user quotes, support tickets, metrics, observed behavior, failed workarounds — anything concrete)
|
||||
|
||||
If the user has none, record the PRD's Evidence section as `Assumption — needs validation via {user research | analytics | prototype}`. This keeps the PRD honest.
|
||||
|
||||
### Phase 3 — DECIDE
|
||||
|
||||
Scope and hypothesis in a single set:
|
||||
|
||||
> 1. **Hypothesis** — Complete: *We believe **{capability}** will **{solve problem}** for **{users}**. We'll know we're right when **{measurable outcome}**.*
|
||||
> 2. **MVP** — The minimum needed to test the hypothesis?
|
||||
> 3. **Out of scope** — What are you explicitly **not** building (even if users ask)?
|
||||
> 4. **Open questions** — Uncertainties that could change the approach?
|
||||
|
||||
Wait for responses.
|
||||
|
||||
### Phase 4 — GENERATE & HAND OFF
|
||||
|
||||
Create the directory if needed, write the PRD, and report.
|
||||
|
||||
```bash
|
||||
mkdir -p .claude/prds
|
||||
```
|
||||
|
||||
**Output path**: `.claude/prds/{kebab-case-name}.prd.md`
|
||||
|
||||
#### PRD Template
|
||||
|
||||
```markdown
|
||||
# {Product / Feature Name}
|
||||
|
||||
## Problem
|
||||
{2–3 sentences: who has what problem, and what's the cost of leaving it unsolved?}
|
||||
|
||||
## Evidence
|
||||
- {User quote, data point, or observation}
|
||||
- {OR: "Assumption — needs validation via {method}"}
|
||||
|
||||
## Users
|
||||
- **Primary**: {role, context, what triggers the need}
|
||||
- **Not for**: {who this explicitly excludes}
|
||||
|
||||
## Hypothesis
|
||||
We believe **{capability}** will **{solve problem}** for **{users}**.
|
||||
We'll know we're right when **{measurable outcome}**.
|
||||
|
||||
## Success Metrics
|
||||
| Metric | Target | How measured |
|
||||
|---|---|---|
|
||||
| {primary} | {number} | {method} |
|
||||
|
||||
## Scope
|
||||
**MVP** — {the minimum to test the hypothesis}
|
||||
|
||||
**Out of scope**
|
||||
- {item} — {why deferred}
|
||||
|
||||
## Delivery Milestones
|
||||
<!-- Business outcomes, not engineering tasks. /plan turns each into a plan. -->
|
||||
<!-- Status: pending | in-progress | complete -->
|
||||
|
||||
| # | Milestone | Outcome | Status | Plan |
|
||||
|---|---|---|---|---|
|
||||
| 1 | {name} | {user-visible change} | pending | — |
|
||||
| 2 | {name} | {user-visible change} | pending | — |
|
||||
|
||||
## Open Questions
|
||||
- [ ] {question that could change scope or approach}
|
||||
|
||||
## Risks
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|---|---|---|---|
|
||||
|
||||
---
|
||||
*Status: DRAFT — requirements only. Implementation planning pending via /plan.*
|
||||
```
|
||||
|
||||
#### Report to user
|
||||
|
||||
```
|
||||
PRD created: .claude/prds/{name}.prd.md
|
||||
|
||||
Problem: {one line}
|
||||
Hypothesis: {one line}
|
||||
MVP: {one line}
|
||||
|
||||
Validation status:
|
||||
Problem {validated | assumption}
|
||||
Users {concrete | generic — refine}
|
||||
Metrics {defined | TBD}
|
||||
|
||||
Open questions: {count}
|
||||
|
||||
Next step: /plan .claude/prds/{name}.prd.md
|
||||
→ /plan will pick the next pending milestone and produce an implementation plan.
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
- `/plan <prd-path>` — consume the PRD and produce an implementation plan for the next pending milestone.
|
||||
- `tdd-workflow` skill — implement the plan test-first.
|
||||
- `/pr` — open a PR that references the PRD and plan.
|
||||
|
||||
## Success criteria
|
||||
|
||||
- **PROBLEM_CLEAR**: problem is specific and evidenced (or flagged as assumption).
|
||||
- **USER_CONCRETE**: primary user is a specific role, not "users".
|
||||
- **HYPOTHESIS_TESTABLE**: measurable outcome included.
|
||||
- **SCOPE_BOUNDED**: explicit MVP and explicit out-of-scope.
|
||||
- **NO_IMPLEMENTATION_DETAIL**: file paths, libraries, or task breakdowns are absent — if they appeared, move them to the `/plan` step.
|
||||
+7
-86
@@ -1,11 +1,10 @@
|
||||
---
|
||||
description: Restate requirements, assess risks, and create step-by-step implementation plan. WAIT for user CONFIRM before touching any code.
|
||||
argument-hint: "[feature description | path/to/*.prd.md]"
|
||||
---
|
||||
|
||||
# Plan Command
|
||||
|
||||
This command creates a comprehensive implementation plan before writing any code. It accepts either free-form requirements or a PRD markdown file.
|
||||
This command creates a comprehensive implementation plan before writing any code.
|
||||
|
||||
Run inline by default. Do not call the Task tool or any subagent by default. This keeps `/plan` usable from plugin installs that ship commands without agent files.
|
||||
|
||||
@@ -30,86 +29,11 @@ Use `/plan` when:
|
||||
The assistant will:
|
||||
|
||||
1. **Analyze the request** and restate requirements in clear terms
|
||||
2. **Ground the plan** in relevant codebase patterns when the repo is available
|
||||
3. **Break down into phases** with specific, actionable steps
|
||||
4. **Identify dependencies** between components
|
||||
5. **Assess risks** and potential blockers
|
||||
6. **Estimate complexity** (High/Medium/Low)
|
||||
7. **Present the plan** and WAIT for your explicit confirmation
|
||||
|
||||
## Input Modes
|
||||
|
||||
| Input | Mode | Behavior |
|
||||
|---|---|---|
|
||||
| `path/to/name.prd.md` | PRD artifact mode | Read the PRD, pick the next pending delivery milestone or implementation phase, and write `.claude/plans/{name}.plan.md` |
|
||||
| Any other markdown path | Reference mode | Read the file as context and produce an inline plan |
|
||||
| Free-form text | Conversational mode | Produce an inline plan |
|
||||
| Empty input | Clarification mode | Ask what should be planned |
|
||||
|
||||
In PRD artifact mode, create `.claude/plans/` if needed. If the PRD contains a `Delivery Milestones` table, update only the selected row from `pending` to `in-progress` and set its `Plan` cell to the generated plan path. If the PRD uses the legacy `.claude/PRPs/prds/` format with `Implementation Phases`, read it without migrating paths.
|
||||
|
||||
## Pattern Grounding
|
||||
|
||||
Before writing the plan, search the codebase for conventions the implementation should mirror. Capture the top example for each relevant category with file references:
|
||||
|
||||
| Category | What to capture |
|
||||
|---|---|
|
||||
| Naming | File, function, type, command, or script naming in the affected area |
|
||||
| Error handling | How failures are raised, returned, logged, or handled gracefully |
|
||||
| Logging | Levels, format, and what gets logged |
|
||||
| Data access | Repository, service, query, or filesystem patterns |
|
||||
| Tests | Test file location, framework, fixtures, and assertion style |
|
||||
|
||||
If no similar code exists, state that explicitly. Do not invent a pattern.
|
||||
|
||||
## PRD Artifact Output
|
||||
|
||||
When called with a `.prd.md` file, write the plan to `.claude/plans/{kebab-case-name}.plan.md` using this structure:
|
||||
|
||||
````markdown
|
||||
# Plan: {Feature Name}
|
||||
|
||||
**Source PRD**: {path}
|
||||
**Selected Milestone**: {milestone or phase name}
|
||||
**Complexity**: {Small | Medium | Large}
|
||||
|
||||
## Summary
|
||||
{2-3 sentences}
|
||||
|
||||
## Patterns to Mirror
|
||||
| Category | Source | Pattern |
|
||||
|---|---|---|
|
||||
| Naming | `path:line` | {short description} |
|
||||
| Errors | `path:line` | {short description} |
|
||||
| Tests | `path:line` | {short description} |
|
||||
|
||||
## Files to Change
|
||||
| File | Action | Why |
|
||||
|---|---|---|
|
||||
| `path` | CREATE / UPDATE / DELETE | {reason} |
|
||||
|
||||
## Tasks
|
||||
### Task 1: {name}
|
||||
- **Action**: {what to do}
|
||||
- **Mirror**: {pattern to follow}
|
||||
- **Validate**: {command that proves correctness}
|
||||
|
||||
## Validation
|
||||
```bash
|
||||
{project-specific validation commands}
|
||||
```
|
||||
|
||||
## Risks
|
||||
| Risk | Likelihood | Mitigation |
|
||||
|---|---|---|
|
||||
|
||||
## Acceptance
|
||||
- [ ] All tasks complete
|
||||
- [ ] Validation passes
|
||||
- [ ] Patterns mirrored, not reinvented
|
||||
````
|
||||
|
||||
After writing the artifact, report its path and WAIT for confirmation before writing code.
|
||||
2. **Break down into phases** with specific, actionable steps
|
||||
3. **Identify dependencies** between components
|
||||
4. **Assess risks** and potential blockers
|
||||
5. **Estimate complexity** (High/Medium/Low)
|
||||
6. **Present the plan** and WAIT for your explicit confirmation
|
||||
|
||||
## Example Usage
|
||||
|
||||
@@ -184,11 +108,8 @@ After planning:
|
||||
- Use the `tdd-workflow` skill to implement with test-driven development
|
||||
- Use `/build-fix` if build errors occur
|
||||
- Use `/code-review` to review completed implementation
|
||||
- Use `/pr` or `/prp-pr` to open a pull request
|
||||
|
||||
> **Need requirements first?** Use `/plan-prd` for a lean PRD at `.claude/prds/{name}.prd.md`.
|
||||
>
|
||||
> **Need the legacy PRP flow?** Use `/prp-plan` for deep PRP planning with `.claude/PRPs/` artifacts. Use `/prp-implement` to execute those plans with rigorous validation loops.
|
||||
> **Need deeper planning?** Use `/prp-plan` for artifact-producing planning with PRD integration, codebase analysis, and pattern extraction. Use `/prp-implement` to execute those plans with rigorous validation loops.
|
||||
|
||||
## Optional Planner Agent
|
||||
|
||||
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
---
|
||||
description: "Create a GitHub PR from current branch with unpushed commits — discovers templates, analyzes changes, pushes"
|
||||
argument-hint: "[base-branch] (default: main)"
|
||||
---
|
||||
|
||||
# Create Pull Request
|
||||
|
||||
**Input**: `$ARGUMENTS` — optional, may contain a base branch name and/or flags (e.g., `--draft`).
|
||||
|
||||
**Parse `$ARGUMENTS`**:
|
||||
- Extract any recognized flags (`--draft`)
|
||||
- Treat remaining non-flag text as the base branch name
|
||||
- Default base branch to `main` if none specified
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — VALIDATE
|
||||
|
||||
Check preconditions:
|
||||
|
||||
```bash
|
||||
git branch --show-current
|
||||
git status --short
|
||||
git log origin/<base>..HEAD --oneline
|
||||
```
|
||||
|
||||
| Check | Condition | Action if Failed |
|
||||
|---|---|---|
|
||||
| Not on base branch | Current branch ≠ base | Stop: "Switch to a feature branch first." |
|
||||
| Clean working directory | No uncommitted changes | Warn: "You have uncommitted changes. Commit or stash first." |
|
||||
| Has commits ahead | `git log origin/<base>..HEAD` not empty | Stop: "No commits ahead of `<base>`. Nothing to PR." |
|
||||
| No existing PR | `gh pr list --head <branch> --json number` is empty | Stop: "PR already exists: #<number>. Use `gh pr view <number> --web` to open it." |
|
||||
|
||||
If all checks pass, proceed.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — DISCOVER
|
||||
|
||||
### PR Template
|
||||
|
||||
Search for PR template in order:
|
||||
|
||||
1. `.github/PULL_REQUEST_TEMPLATE/` directory — if exists, list files and let user choose (or use `default.md`)
|
||||
2. `.github/PULL_REQUEST_TEMPLATE.md`
|
||||
3. `.github/pull_request_template.md`
|
||||
4. `docs/pull_request_template.md`
|
||||
|
||||
If found, read it and use its structure for the PR body.
|
||||
|
||||
### Commit Analysis
|
||||
|
||||
```bash
|
||||
git log origin/<base>..HEAD --format="%h %s" --reverse
|
||||
```
|
||||
|
||||
Analyze commits to determine:
|
||||
- **PR title**: Use conventional commit format with type prefix — `feat: ...`, `fix: ...`, etc.
|
||||
- If multiple types, use the dominant one
|
||||
- If single commit, use its message as-is
|
||||
- **Change summary**: Group commits by type/area
|
||||
|
||||
### File Analysis
|
||||
|
||||
```bash
|
||||
git diff origin/<base>..HEAD --stat
|
||||
git diff origin/<base>..HEAD --name-only
|
||||
```
|
||||
|
||||
Categorize changed files: source, tests, docs, config, migrations.
|
||||
|
||||
### Planning Artifacts
|
||||
|
||||
Check for related artifacts produced by `/plan-prd`, `/plan`, or the legacy PRP workflow:
|
||||
- `.claude/prds/` — PRDs this PR implements a milestone of
|
||||
- `.claude/plans/` — Plans executed by this PR
|
||||
- `.claude/PRPs/prds/` — legacy PRP PRDs
|
||||
- `.claude/PRPs/plans/` — legacy PRP implementation plans
|
||||
- `.claude/PRPs/reports/` — legacy PRP implementation reports
|
||||
|
||||
Reference these in the PR body if they exist.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — PUSH
|
||||
|
||||
```bash
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
If push fails due to divergence:
|
||||
```bash
|
||||
git fetch origin
|
||||
git rebase origin/<base>
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
If rebase conflicts occur, stop and inform the user.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — CREATE
|
||||
|
||||
### With Template
|
||||
|
||||
If a PR template was found in Phase 2, fill in each section using the commit and file analysis. Preserve all template sections — leave sections as "N/A" if not applicable rather than removing them.
|
||||
|
||||
### Without Template
|
||||
|
||||
Use this default format:
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
|
||||
<1-2 sentence description of what this PR does and why>
|
||||
|
||||
## Changes
|
||||
|
||||
<bulleted list of changes grouped by area>
|
||||
|
||||
## Files Changed
|
||||
|
||||
<table or list of changed files with change type: Added/Modified/Deleted>
|
||||
|
||||
## Testing
|
||||
|
||||
<description of how changes were tested, or "Needs testing">
|
||||
|
||||
## Related Issues
|
||||
|
||||
<linked issues with Closes/Fixes/Relates to #N, or "None">
|
||||
```
|
||||
|
||||
### Create the PR
|
||||
|
||||
```bash
|
||||
gh pr create \
|
||||
--title "<PR title>" \
|
||||
--base <base-branch> \
|
||||
--body "<PR body>"
|
||||
# Add --draft if the --draft flag was parsed from $ARGUMENTS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — VERIFY
|
||||
|
||||
```bash
|
||||
gh pr view --json number,url,title,state,baseRefName,headRefName,additions,deletions,changedFiles
|
||||
gh pr checks --json name,status,conclusion 2>/dev/null || true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — OUTPUT
|
||||
|
||||
Report to user:
|
||||
|
||||
```
|
||||
PR #<number>: <title>
|
||||
URL: <url>
|
||||
Branch: <head> → <base>
|
||||
Changes: +<additions> -<deletions> across <changedFiles> files
|
||||
|
||||
CI Checks: <status summary or "pending" or "none configured">
|
||||
|
||||
Artifacts referenced:
|
||||
- <any PRDs/plans linked in PR body>
|
||||
|
||||
Next steps:
|
||||
- gh pr view <number> --web → open in browser
|
||||
- /code-review <number> → review the PR
|
||||
- gh pr merge <number> → merge when ready
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Edge Cases
|
||||
|
||||
- **No `gh` CLI**: Stop with: "GitHub CLI (`gh`) is required. Install: <https://cli.github.com/>"
|
||||
- **Not authenticated**: Stop with: "Run `gh auth login` first."
|
||||
- **Force push needed**: If remote has diverged and rebase was done, use `git push --force-with-lease` (never `--force`).
|
||||
- **Multiple PR templates**: If `.github/PULL_REQUEST_TEMPLATE/` has multiple files, list them and ask user to choose.
|
||||
- **Large PR (>20 files)**: Warn about PR size. Suggest splitting if changes are logically separable.
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
description: Detect a project's stack and produce a dry-run ECC onboarding plan using the repository's install manifests and stack mappings.
|
||||
---
|
||||
|
||||
# /project-init
|
||||
|
||||
Create a safe, reviewable ECC onboarding plan for the current project. This command should start in dry-run mode and only write files after explicit user approval.
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
/project-init
|
||||
/project-init --dry-run
|
||||
/project-init --target claude
|
||||
/project-init --target cursor
|
||||
/project-init --skills continuous-learning-v2,security-review
|
||||
/project-init --config ecc-install.json
|
||||
```
|
||||
|
||||
## Safety Rules
|
||||
|
||||
1. Default to dry-run. Do not modify `CLAUDE.md`, settings files, rules, skills, or install state until the user approves the concrete plan.
|
||||
2. Preserve existing project guidance. If `CLAUDE.md`, `.claude/settings.local.json`, `.cursor/`, `.codex/`, `.gemini/`, `.opencode/`, `.codebuddy/`, `.joycode/`, or `.qwen/` already exists, inspect it and propose a merge/append plan instead of overwriting.
|
||||
3. Use ECC's installer and manifest tooling. Do not hand-copy files or clone arbitrary remotes as an install shortcut.
|
||||
4. Keep permissions narrow. Any generated settings should match detected build/test/lint tools and avoid broad shell access.
|
||||
5. Report exactly what would change before applying anything.
|
||||
|
||||
## Detection Inputs
|
||||
|
||||
Read the current project root and detect stack signals from:
|
||||
|
||||
- package manager files: `package.json`, `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`
|
||||
- language manifests: `pyproject.toml`, `requirements.txt`, `go.mod`, `Cargo.toml`, `pom.xml`, `build.gradle`, `build.gradle.kts`
|
||||
- framework files: `next.config.*`, `vite.config.*`, `tailwind.config.*`, `Dockerfile`, `docker-compose.yml`
|
||||
- ECC config: `ecc-install.json`
|
||||
- optional stack map: `config/project-stack-mappings.json` in the ECC repo
|
||||
|
||||
When the ECC checkout is available, use `config/project-stack-mappings.json` as the stack-to-rules/skills reference. If the file is unavailable, fall back to the installed ECC manifests and explicit user choices.
|
||||
|
||||
## Planning Flow
|
||||
|
||||
1. Identify the target harness. Default to `claude` unless the user asks for `cursor`, `codex`, `gemini`, `opencode`, `codebuddy`, `joycode`, or `qwen`.
|
||||
2. Detect stacks from project files and show the evidence for each match.
|
||||
3. Resolve the smallest useful ECC plan:
|
||||
- project has an `ecc-install.json`: `node scripts/install-plan.js --config ecc-install.json --json`
|
||||
- user named a profile: `node scripts/install-plan.js --profile <profile> --target <target> --json`
|
||||
- user named skills: `node scripts/install-plan.js --skills <skill-ids> --target <target> --json`
|
||||
- only language stacks are detected: use the legacy language install dry-run with those language names
|
||||
4. Run a dry-run apply command before writing:
|
||||
|
||||
```bash
|
||||
node scripts/install-apply.js --target <target> --dry-run --json <language-or-profile-args>
|
||||
```
|
||||
|
||||
5. Summarize detected stacks, selected modules/components/skills, target paths, skipped unsupported modules, and files that would be changed.
|
||||
6. Ask for approval before applying the non-dry-run command.
|
||||
|
||||
## Output Contract
|
||||
|
||||
Return:
|
||||
|
||||
1. detected stack evidence
|
||||
2. proposed target harness
|
||||
3. exact dry-run command used
|
||||
4. exact apply command to run after approval
|
||||
5. files/directories that would be created or changed
|
||||
6. warnings about existing files, broad permissions, missing scripts, or unsupported targets
|
||||
|
||||
## CLAUDE.md Guidance
|
||||
|
||||
If the user wants a `CLAUDE.md` starter, generate it separately from the installer plan and keep it minimal:
|
||||
|
||||
- build command, if detected
|
||||
- test command, if detected
|
||||
- lint/typecheck command, if detected
|
||||
- dev server command, if detected
|
||||
- repo-specific notes from existing package scripts or manifests
|
||||
|
||||
Never replace an existing `CLAUDE.md` without showing a diff and receiving approval.
|
||||
|
||||
## Related
|
||||
|
||||
- `config/project-stack-mappings.json` for stack-to-surface hints
|
||||
- `scripts/install-plan.js` for deterministic plan resolution
|
||||
- `scripts/install-apply.js` for dry-run and apply operations
|
||||
- `/ecc-guide` for interactive feature discovery before installing
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
description: Run AgentShield against agent, hook, MCP, permission, and secret surfaces.
|
||||
agent: everything-claude-code:security-reviewer
|
||||
subtask: true
|
||||
---
|
||||
|
||||
# Security Scan Command
|
||||
|
||||
Run AgentShield against the current project or a target path, then turn the findings into a prioritized remediation plan.
|
||||
|
||||
## Usage
|
||||
|
||||
`/security-scan [path] [--format text|json|markdown|html] [--min-severity low|medium|high|critical] [--fix]`
|
||||
|
||||
- `path` (optional): defaults to the current project. Use a `.claude/` path, a repo root, or a checked-in template directory.
|
||||
- `--format`: output format. Use `json` for CI, `markdown` for handoffs, and `html` for standalone review reports.
|
||||
- `--min-severity`: filters lower-priority findings.
|
||||
- `--fix`: applies only AgentShield fixes explicitly marked as safe and auto-fixable.
|
||||
|
||||
## Deterministic Engine
|
||||
|
||||
Prefer the packaged scanner:
|
||||
|
||||
```bash
|
||||
npx ecc-agentshield scan --path "${TARGET_PATH:-.}" --format text
|
||||
```
|
||||
|
||||
For local AgentShield development, run from the AgentShield checkout:
|
||||
|
||||
```bash
|
||||
npm run scan -- --path "${TARGET_PATH:-.}" --format text
|
||||
```
|
||||
|
||||
Do not invent findings. Use AgentShield output as the source of truth and separate scanner facts from follow-up judgment.
|
||||
|
||||
## Review Checklist
|
||||
|
||||
1. Identify active runtime findings first:
|
||||
- hardcoded secrets
|
||||
- broad permissions
|
||||
- executable hooks
|
||||
- MCP servers with shell, filesystem, remote transport, or unpinned `npx`
|
||||
- agent prompts that handle untrusted content without defenses
|
||||
2. Separate lower-confidence inventory:
|
||||
- docs examples
|
||||
- template examples
|
||||
- plugin manifests
|
||||
- project-local optional settings
|
||||
3. For each critical or high finding, return:
|
||||
- file path
|
||||
- severity
|
||||
- runtime confidence
|
||||
- why it matters
|
||||
- exact remediation
|
||||
- whether it is safe to auto-fix
|
||||
4. If `--fix` is requested, state the planned edits before applying fixes.
|
||||
5. Re-run the scan after fixes and report the before/after score.
|
||||
|
||||
## Output Contract
|
||||
|
||||
Return:
|
||||
|
||||
1. Security grade and score.
|
||||
2. Counts by severity and runtime confidence.
|
||||
3. Critical/high findings with exact paths.
|
||||
4. Lower-confidence findings grouped separately.
|
||||
5. A remediation order.
|
||||
6. Commands run and whether the scan was local, CI, or npx-backed.
|
||||
|
||||
## CI Pattern
|
||||
|
||||
Use AgentShield in GitHub Actions for enforced gates:
|
||||
|
||||
```yaml
|
||||
- uses: affaan-m/agentshield@v1
|
||||
with:
|
||||
path: "."
|
||||
min-severity: "medium"
|
||||
fail-on-findings: true
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- Skill: `skills/security-scan/SKILL.md`
|
||||
- Agent: `agents/security-reviewer.md`
|
||||
- Scanner: <https://github.com/affaan-m/agentshield>
|
||||
|
||||
## Arguments
|
||||
|
||||
$ARGUMENTS:
|
||||
- optional target path
|
||||
- optional AgentShield flags
|
||||
@@ -1,539 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"description": "Maps project indicator files to ECC skills, rules, hooks, and default commands. Used by /project-init to auto-configure projects.",
|
||||
"stacks": [
|
||||
{
|
||||
"id": "typescript",
|
||||
"name": "TypeScript / JavaScript",
|
||||
"indicators": [
|
||||
{ "file": "tsconfig.json" },
|
||||
{ "file": "tsconfig.*.json" },
|
||||
{ "file": "package.json", "contains": "typescript" }
|
||||
],
|
||||
"rules": ["common", "typescript"],
|
||||
"skills": [
|
||||
"coding-standards",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["npx tsc --noEmit", "npm run build"],
|
||||
"test": ["npm test", "npx jest", "npx vitest"],
|
||||
"lint": ["npx eslint .", "npx tsc --noEmit"],
|
||||
"format": ["npx prettier --write ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["npx tsc", "npx eslint", "npx prettier", "npm test", "npm run *", "npx jest", "npx vitest"],
|
||||
"deny": ["npm publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "javascript",
|
||||
"name": "JavaScript (Node.js)",
|
||||
"indicators": [
|
||||
{ "file": "package.json" },
|
||||
{ "file": ".eslintrc*" },
|
||||
{ "file": "eslint.config.*" }
|
||||
],
|
||||
"rules": ["common", "typescript"],
|
||||
"skills": [
|
||||
"coding-standards",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["npm run build"],
|
||||
"test": ["npm test", "npx jest", "npx vitest"],
|
||||
"lint": ["npx eslint ."],
|
||||
"format": ["npx prettier --write ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["npx eslint", "npx prettier", "npm test", "npm run *", "npx jest", "npx vitest"],
|
||||
"deny": ["npm publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "react",
|
||||
"name": "React",
|
||||
"indicators": [
|
||||
{ "file": "package.json", "contains": "\"react\":" }
|
||||
],
|
||||
"rules": ["common", "typescript", "web"],
|
||||
"skills": [
|
||||
"coding-standards",
|
||||
"frontend-patterns",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["npm run build"],
|
||||
"test": ["npm test", "npx jest", "npx vitest"],
|
||||
"lint": ["npx eslint ."],
|
||||
"format": ["npx prettier --write ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["npx eslint", "npx prettier", "npm test", "npm run *", "npx jest", "npx vitest"],
|
||||
"deny": ["npm publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "nextjs",
|
||||
"name": "Next.js",
|
||||
"indicators": [
|
||||
{ "file": "next.config.*" },
|
||||
{ "file": "package.json", "contains": "\"next\":" }
|
||||
],
|
||||
"rules": ["common", "typescript", "web"],
|
||||
"skills": [
|
||||
"coding-standards",
|
||||
"frontend-patterns",
|
||||
"backend-patterns",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["npm run build", "npx next build"],
|
||||
"test": ["npm test", "npx jest", "npx vitest"],
|
||||
"lint": ["npx next lint", "npx eslint ."],
|
||||
"format": ["npx prettier --write ."],
|
||||
"dev": ["npm run dev", "npx next dev"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["npx next *", "npx eslint", "npx prettier", "npm test", "npm run *", "npx jest", "npx vitest"],
|
||||
"deny": ["npm publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "golang",
|
||||
"name": "Go",
|
||||
"indicators": [
|
||||
{ "file": "go.mod" },
|
||||
{ "file": "go.sum" }
|
||||
],
|
||||
"rules": ["common", "golang"],
|
||||
"skills": [
|
||||
"golang-patterns",
|
||||
"golang-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["go build ./..."],
|
||||
"test": ["go test ./..."],
|
||||
"lint": ["golangci-lint run", "go vet ./..."],
|
||||
"format": ["gofmt -w ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["go build *", "go test *", "go vet *", "go mod *", "go run *", "golangci-lint *", "gofmt *"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "python",
|
||||
"name": "Python",
|
||||
"indicators": [
|
||||
{ "file": "pyproject.toml" },
|
||||
{ "file": "setup.py" },
|
||||
{ "file": "setup.cfg" },
|
||||
{ "file": "requirements.txt" },
|
||||
{ "file": "Pipfile" },
|
||||
{ "file": "poetry.lock" }
|
||||
],
|
||||
"rules": ["common", "python"],
|
||||
"skills": [
|
||||
"python-patterns",
|
||||
"python-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["python -m build", "pip install -e ."],
|
||||
"test": ["pytest", "python -m pytest"],
|
||||
"lint": ["ruff check .", "flake8", "mypy ."],
|
||||
"format": ["ruff format .", "black ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["python *", "pip install *", "pytest *", "ruff *", "black *", "mypy *", "flake8 *"],
|
||||
"deny": ["pip install --user *"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rust",
|
||||
"name": "Rust",
|
||||
"indicators": [
|
||||
{ "file": "Cargo.toml" },
|
||||
{ "file": "Cargo.lock" }
|
||||
],
|
||||
"rules": ["common", "rust"],
|
||||
"skills": [
|
||||
"rust-patterns",
|
||||
"rust-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["cargo build"],
|
||||
"test": ["cargo test"],
|
||||
"lint": ["cargo clippy -- -D warnings"],
|
||||
"format": ["cargo fmt"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["cargo build *", "cargo test *", "cargo clippy *", "cargo fmt *", "cargo run *", "cargo check *"],
|
||||
"deny": ["cargo publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "java",
|
||||
"name": "Java",
|
||||
"indicators": [
|
||||
{ "file": "pom.xml" },
|
||||
{ "file": "build.gradle" },
|
||||
{ "file": "build.gradle.kts" }
|
||||
],
|
||||
"rules": ["common", "java"],
|
||||
"skills": [
|
||||
"java-coding-standards",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["./mvnw compile", "./gradlew build", "mvn compile", "gradle build"],
|
||||
"test": ["./mvnw test", "./gradlew test", "mvn test", "gradle test"],
|
||||
"lint": ["./mvnw checkstyle:check", "./gradlew checkstyleMain"],
|
||||
"format": ["./mvnw spotless:apply", "./gradlew spotlessApply"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["./mvnw *", "./gradlew *", "mvn *", "gradle *", "java *"],
|
||||
"deny": ["./mvnw deploy", "./gradlew publish", "mvn deploy", "gradle publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "springboot",
|
||||
"name": "Spring Boot (Java/Kotlin)",
|
||||
"indicators": [
|
||||
{ "file": "pom.xml", "contains": "spring-boot" },
|
||||
{ "file": "build.gradle", "contains": "spring-boot" },
|
||||
{ "file": "build.gradle.kts", "contains": "spring-boot" }
|
||||
],
|
||||
"rules": ["common", "java"],
|
||||
"skills": [
|
||||
"springboot-patterns",
|
||||
"springboot-tdd",
|
||||
"springboot-verification",
|
||||
"springboot-security",
|
||||
"java-coding-standards",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["./mvnw compile", "./gradlew build"],
|
||||
"test": ["./mvnw test", "./gradlew test"],
|
||||
"lint": ["./mvnw checkstyle:check"],
|
||||
"format": ["./mvnw spotless:apply"],
|
||||
"dev": ["./mvnw spring-boot:run", "./gradlew bootRun"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["./mvnw *", "./gradlew *", "mvn *", "gradle *", "java *"],
|
||||
"deny": ["./mvnw deploy", "./gradlew publish", "mvn deploy", "gradle publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "kotlin",
|
||||
"name": "Kotlin",
|
||||
"indicators": [
|
||||
{ "file": "build.gradle.kts" },
|
||||
{ "file": "settings.gradle.kts" },
|
||||
{ "file": "build.gradle", "contains": "kotlin" }
|
||||
],
|
||||
"rules": ["common", "kotlin"],
|
||||
"skills": [
|
||||
"kotlin-patterns",
|
||||
"kotlin-testing",
|
||||
"kotlin-coroutines-flows",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["./gradlew build"],
|
||||
"test": ["./gradlew test"],
|
||||
"lint": ["./gradlew ktlintCheck", "./gradlew detekt"],
|
||||
"format": ["./gradlew ktlintFormat"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["./gradlew *", "gradle *", "kotlin *"],
|
||||
"deny": ["./gradlew publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "swift",
|
||||
"name": "Swift / SwiftUI",
|
||||
"indicators": [
|
||||
{ "file": "Package.swift" },
|
||||
{ "file": "*.xcodeproj" },
|
||||
{ "file": "*.xcworkspace" },
|
||||
{ "file": "Podfile" }
|
||||
],
|
||||
"rules": ["common", "swift"],
|
||||
"skills": [
|
||||
"swiftui-patterns",
|
||||
"swift-concurrency-6-2",
|
||||
"swift-actor-persistence",
|
||||
"swift-protocol-di-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["swift build", "xcodebuild build"],
|
||||
"test": ["swift test", "xcodebuild test"],
|
||||
"lint": ["swiftlint"],
|
||||
"format": ["swiftformat ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["swift build *", "swift test *", "swift run *", "xcodebuild *", "swiftlint *", "swiftformat *"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dart-flutter",
|
||||
"name": "Dart / Flutter",
|
||||
"indicators": [
|
||||
{ "file": "pubspec.yaml" },
|
||||
{ "file": "pubspec.lock" }
|
||||
],
|
||||
"rules": ["common", "dart"],
|
||||
"skills": [
|
||||
"dart-flutter-patterns",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["flutter build", "dart compile"],
|
||||
"test": ["flutter test", "dart test"],
|
||||
"lint": ["dart analyze"],
|
||||
"format": ["dart format ."]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["flutter *", "dart *"],
|
||||
"deny": ["flutter pub publish"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "php-laravel",
|
||||
"name": "PHP / Laravel",
|
||||
"indicators": [
|
||||
{ "file": "composer.json" },
|
||||
{ "file": "artisan" },
|
||||
{ "file": "composer.lock" }
|
||||
],
|
||||
"rules": ["common", "php"],
|
||||
"skills": [
|
||||
"laravel-patterns",
|
||||
"laravel-tdd",
|
||||
"laravel-verification",
|
||||
"laravel-security",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["composer install"],
|
||||
"test": ["php artisan test", "vendor/bin/phpunit", "vendor/bin/pest"],
|
||||
"lint": ["vendor/bin/phpstan analyse", "vendor/bin/pint"],
|
||||
"format": ["vendor/bin/pint"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["php artisan *", "composer *", "vendor/bin/*"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ruby",
|
||||
"name": "Ruby / Rails",
|
||||
"indicators": [
|
||||
{ "file": "Gemfile" },
|
||||
{ "file": "Gemfile.lock" },
|
||||
{ "file": "Rakefile" }
|
||||
],
|
||||
"rules": ["common"],
|
||||
"skills": [
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["bundle install"],
|
||||
"test": ["bundle exec rspec", "bundle exec rake test"],
|
||||
"lint": ["bundle exec rubocop"],
|
||||
"format": ["bundle exec rubocop -A"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["bundle exec *", "rails *", "rake *", "ruby *"],
|
||||
"deny": ["gem push"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "csharp-dotnet",
|
||||
"name": "C# / .NET",
|
||||
"indicators": [
|
||||
{ "file": "*.csproj" },
|
||||
{ "file": "*.sln" },
|
||||
{ "file": "global.json" }
|
||||
],
|
||||
"rules": ["common", "csharp"],
|
||||
"skills": [
|
||||
"dotnet-patterns",
|
||||
"csharp-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["dotnet build"],
|
||||
"test": ["dotnet test"],
|
||||
"lint": ["dotnet format --verify-no-changes"],
|
||||
"format": ["dotnet format"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["dotnet build *", "dotnet test *", "dotnet run *", "dotnet format *"],
|
||||
"deny": ["dotnet nuget push"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cpp",
|
||||
"name": "C / C++",
|
||||
"indicators": [
|
||||
{ "file": "CMakeLists.txt" },
|
||||
{ "file": "Makefile" },
|
||||
{ "file": "meson.build" },
|
||||
{ "file": "*.vcxproj" }
|
||||
],
|
||||
"rules": ["common", "cpp"],
|
||||
"skills": [
|
||||
"cpp-coding-standards",
|
||||
"cpp-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["cmake --build build", "make"],
|
||||
"test": ["ctest --test-dir build", "make test"],
|
||||
"lint": ["clang-tidy -p build"],
|
||||
"format": ["clang-format -i **/*.cpp **/*.h **/*.c **/*.hpp"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["cmake *", "make *", "ctest *", "clang-tidy *", "clang-format *", "gcc *", "g++ *"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "perl",
|
||||
"name": "Perl",
|
||||
"indicators": [
|
||||
{ "file": "cpanfile" },
|
||||
{ "file": "Makefile.PL" },
|
||||
{ "file": "Build.PL" },
|
||||
{ "file": "dist.ini" }
|
||||
],
|
||||
"rules": ["common", "perl"],
|
||||
"skills": [
|
||||
"perl-patterns",
|
||||
"perl-testing",
|
||||
"perl-security",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["perl Makefile.PL && make", "perl Build.PL && ./Build"],
|
||||
"test": ["prove -lr t/", "make test"],
|
||||
"lint": ["perlcritic lib/"],
|
||||
"format": ["perltidy -b lib/**/*.pl"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["perl *", "prove *", "make *", "perlcritic *", "perltidy *"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "django",
|
||||
"name": "Django (Python)",
|
||||
"indicators": [
|
||||
{ "file": "manage.py" },
|
||||
{ "file": "requirements.txt", "contains": "django" },
|
||||
{ "file": "pyproject.toml", "contains": "django" }
|
||||
],
|
||||
"rules": ["common", "python"],
|
||||
"skills": [
|
||||
"django-patterns",
|
||||
"django-tdd",
|
||||
"django-verification",
|
||||
"django-security",
|
||||
"python-patterns",
|
||||
"python-testing",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["pip install -e ."],
|
||||
"test": ["python manage.py test", "pytest"],
|
||||
"lint": ["ruff check .", "mypy ."],
|
||||
"format": ["ruff format .", "black ."],
|
||||
"dev": ["python manage.py runserver"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["python *", "pip install *", "pytest *", "ruff *", "black *", "mypy *"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "android",
|
||||
"name": "Android (Kotlin/Java)",
|
||||
"indicators": [
|
||||
{ "file": "settings.gradle.kts", "contains": "android" },
|
||||
{ "file": "build.gradle", "contains": "android" },
|
||||
{ "file": "AndroidManifest.xml" }
|
||||
],
|
||||
"rules": ["common", "kotlin"],
|
||||
"skills": [
|
||||
"android-clean-architecture",
|
||||
"kotlin-patterns",
|
||||
"kotlin-testing",
|
||||
"kotlin-coroutines-flows",
|
||||
"compose-multiplatform-patterns",
|
||||
"tdd-workflow",
|
||||
"verification-loop"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["./gradlew assembleDebug"],
|
||||
"test": ["./gradlew testDebugUnitTest"],
|
||||
"lint": ["./gradlew lint", "./gradlew ktlintCheck"],
|
||||
"format": ["./gradlew ktlintFormat"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["./gradlew *", "adb *"],
|
||||
"deny": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "docker",
|
||||
"name": "Docker / Containerized",
|
||||
"indicators": [
|
||||
{ "file": "Dockerfile" },
|
||||
{ "file": "docker-compose.yml" },
|
||||
{ "file": "docker-compose.yaml" },
|
||||
{ "file": "compose.yml" },
|
||||
{ "file": "compose.yaml" }
|
||||
],
|
||||
"rules": [],
|
||||
"skills": [
|
||||
"docker-patterns",
|
||||
"deployment-patterns"
|
||||
],
|
||||
"commands": {
|
||||
"build": ["docker compose build", "docker build ."],
|
||||
"test": ["docker compose run --rm app test"],
|
||||
"dev": ["docker compose up"]
|
||||
},
|
||||
"permissions": {
|
||||
"allow": ["docker compose *", "docker build *"],
|
||||
"deny": ["docker push"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
# ECC 2.0 GA Roadmap
|
||||
|
||||
This roadmap is the durable repo mirror for the Linear project:
|
||||
|
||||
<https://linear.app/ecctools/project/ecc-20-ga-harness-os-security-platform-de2a0ecace6f>
|
||||
|
||||
Linear issue creation is currently blocked by the workspace active issue limit,
|
||||
so the live execution truth is split across:
|
||||
|
||||
- the Linear project description, status updates, and milestones;
|
||||
- this repo document;
|
||||
- merged PR evidence;
|
||||
- handoffs under `~/.cluster-swarm/handoffs/`.
|
||||
|
||||
## Current Evidence
|
||||
|
||||
As of 2026-05-12:
|
||||
|
||||
- Public GitHub queues are clean across `everything-claude-code`,
|
||||
`agentshield`, `JARVIS`, `ECC-Tools`, and `ECC-website`.
|
||||
- `npm run harness:audit -- --format json` reports 70/70 on current `main`.
|
||||
- `npm run observability:ready` reports 14/14 readiness on current `main`.
|
||||
- `docs/architecture/harness-adapter-compliance.md` maps Claude Code, Codex,
|
||||
OpenCode, Cursor, Gemini, Zed-adjacent, dmux, Orca, Superset, Ghast, and
|
||||
terminal-only support to install paths, verification commands, and risk
|
||||
notes.
|
||||
- `npm run harness:adapters -- --check` validates that the public adapter
|
||||
matrix still matches the source data in
|
||||
`scripts/lib/harness-adapter-compliance.js`.
|
||||
- `docs/releases/2.0.0-rc.1/publication-readiness.md` gates GitHub release,
|
||||
npm dist-tag, Claude plugin, Codex plugin, OpenCode package, billing, and
|
||||
announcement publication on fresh evidence fields.
|
||||
- `docs/legacy-artifact-inventory.md` records that no `_legacy-documents-*`
|
||||
directories exist in the current checkout, inventories the two sibling
|
||||
workspace-level `_legacy-documents-*` repos as sanitized extraction sources,
|
||||
and classifies `legacy-command-shims/` as an opt-in archive/no-action
|
||||
surface.
|
||||
- `docs/stale-pr-salvage-ledger.md` records stale PR salvage outcomes,
|
||||
skipped PRs, superseded work, and the remaining #1687 translator/manual
|
||||
review tail.
|
||||
- AgentShield PR #53 reduced two context-rule false positives and closed the
|
||||
remaining AgentShield issues.
|
||||
- AgentShield PR #55 added GitHub Action organization-policy enforcement with
|
||||
`policy` / `fail-on-policy` inputs, `policy-status` /
|
||||
`policy-violations` outputs, job-summary evidence, and policy violation
|
||||
annotations.
|
||||
- AgentShield PR #56 added SARIF/code-scanning output for organization-policy
|
||||
violations as `agentshield-policy/*` results.
|
||||
- AgentShield PR #57 added OSS, team, enterprise, regulated,
|
||||
high-risk-hooks/MCP, and CI-enforcement policy-pack presets plus
|
||||
`agentshield policy init --pack`.
|
||||
- AgentShield PR #58 added MCP package provenance fields and report-level
|
||||
counts for npm vs git, pinned vs unpinned, known-good, and registry-backed
|
||||
supply-chain evidence.
|
||||
- AgentShield PR #59 added self-contained HTML executive summaries with risk
|
||||
posture, critical/high priority findings, category exposure, README/API
|
||||
docs, built-CLI smoke validation, and 1,704-test coverage.
|
||||
- AgentShield PR #60 added category-level built-in corpus benchmark output,
|
||||
a `readyForRegressionGate` signal, terminal `--corpus` category coverage,
|
||||
README/API docs, built-CLI smoke validation, and 1,705-test coverage.
|
||||
- ECC PR #1778 recovered the useful stale #1413 network/homelab architect-agent
|
||||
concepts.
|
||||
- ECC-Tools PR #26 added cost/token-risk predictive follow-ups for AI routing,
|
||||
Claude/model calls, usage limits, quota, and analysis-budget changes that lack
|
||||
budget, quota, rate-limit, or cost validation evidence.
|
||||
- ECC-Tools PR #27 added the non-blocking `ECC Tools / PR Risk Taxonomy`
|
||||
check-run for Security Evidence, Harness Drift, Install Manifest Integrity,
|
||||
CI/CD Recommendation, Cost/Token Risk, and Agent Config Review buckets.
|
||||
- ECC-Tools PR #28 added billing readiness audit checks for plan limits,
|
||||
entitlements, Marketplace plan shape, subscription source, seats, and
|
||||
overage metering.
|
||||
- ECC-Tools PR #29 added deterministic Reference Set Validation signals for
|
||||
analyzer, skill, agent, command, and harness-guidance changes that lack eval,
|
||||
golden trace, benchmark, or reference-set evidence.
|
||||
- ECC-Tools PR #30 capped follow-up generation to three new GitHub issues and
|
||||
one draft PR per run, then emits the remaining deterministic findings as a
|
||||
project sync backlog for Linear/status tracking without flooding trackers.
|
||||
|
||||
## Operating Rules
|
||||
|
||||
- Keep public PRs and issues below 20, with zero as the preferred release-lane
|
||||
target.
|
||||
- Maintain 70/70 harness audit and 14/14 observability readiness after every
|
||||
GA-readiness batch.
|
||||
- Do not publish release or social announcements until the GitHub release,
|
||||
npm/package state, billing state, and plugin submission surfaces are verified
|
||||
with fresh evidence.
|
||||
- Do not treat closed stale PRs as discarded. Pair each cleanup batch with a
|
||||
salvage pass: inspect the closed diffs, port useful compatible work on
|
||||
maintainer-owned branches, and credit the source PR.
|
||||
- Do not create new Linear issues until the active issue limit is cleared.
|
||||
|
||||
## Reference Pressure
|
||||
|
||||
The GA roadmap is informed by these reference surfaces:
|
||||
|
||||
- `stablyai/orca` and `superset-sh/superset` for worktree-native parallel agent
|
||||
UX, review loops, and workspace presets.
|
||||
- `standardagents/dmux` and `aidenybai/ghast` for terminal/worktree
|
||||
multiplexing, session grouping, and lifecycle hooks.
|
||||
- `jarrodwatts/claude-hud` for always-visible status, tool, agent, todo, and
|
||||
context telemetry.
|
||||
- `stanford-iris-lab/meta-harness` and `greyhaven-ai/autocontext` for
|
||||
evaluation-driven harness improvement, traces, playbooks, and promotion
|
||||
loops.
|
||||
- `NousResearch/hermes-agent` for operator shell, gateway, memory, skills, and
|
||||
multi-platform command patterns.
|
||||
- `anthropics/claude-code`, active `sst/opencode` / `anomalyco/opencode`, Zed,
|
||||
Codex, Cursor, Gemini, and terminal-only workflows for adapter expectations.
|
||||
|
||||
The output of this reference work should be concrete ECC deltas, not a second
|
||||
strategy memo.
|
||||
|
||||
## Milestones
|
||||
|
||||
### 1. GA Release, Naming, And Plugin Publication Readiness
|
||||
|
||||
Target: 2026-05-24
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Naming matrix covers product name, npm package, Claude plugin, Codex plugin,
|
||||
OpenCode package, marketplace metadata, docs, and migration copy.
|
||||
- GitHub release, npm dist-tag, plugin publication, and announcement gates are
|
||||
mapped to fresh command evidence.
|
||||
- Release notes, migration guide, known issues, quickstart, X thread, LinkedIn
|
||||
post, and GitHub release copy are ready but not posted before release URLs
|
||||
exist.
|
||||
- Plugin publication/contact paths for Claude and Codex are documented with
|
||||
owner, required artifacts, and submission status.
|
||||
|
||||
### 2. Harness Adapter Compliance Matrix And Scorecard Onramp
|
||||
|
||||
Target: 2026-05-31
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Adapter matrix covers Claude Code, Codex, OpenCode, Cursor, Gemini,
|
||||
Zed-adjacent surfaces, dmux, Orca, Superset, Ghast, and terminal-only use.
|
||||
- Each adapter has supported assets, unsupported surfaces, install path,
|
||||
verification command, and risk notes.
|
||||
- Harness audit remains 70/70 and gains a public onramp that explains how teams
|
||||
use the scorecard.
|
||||
- Reference findings are converted into concrete adapter, observability, or
|
||||
operator-surface deltas.
|
||||
|
||||
### 3. Local Observability, HUD/Status, And Session Control Plane
|
||||
|
||||
Target: 2026-06-07
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Observability readiness remains 14/14 and is backed by JSONL traces, status
|
||||
snapshots, risk ledger, and exportable handoff contracts.
|
||||
- HUD/status model covers context, tool calls, active agents, todos, checks,
|
||||
cost, risk, and queue state.
|
||||
- Worktree/session controls cover create, resume, status, stop, diff, PR,
|
||||
merge queue, and conflict queue.
|
||||
- Linear/GitHub/handoff sync model is explicit enough for real-time progress
|
||||
tracking.
|
||||
|
||||
### 4. Self-Improving Harness Evaluation Loop
|
||||
|
||||
Target: 2026-06-10
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Scenario specs, verifier contracts, traces, playbooks, and regression gates
|
||||
are documented and at least one read-only prototype exists.
|
||||
- The loop separates observation, proposal, verification, and promotion.
|
||||
- Team and individual setups can be scored and improved without blindly
|
||||
mutating configs.
|
||||
- RAG/reference-set design covers vetted ECC patterns, team history, CI
|
||||
failures, diffs, review outcomes, and harness config quality.
|
||||
|
||||
### 5. AgentShield Enterprise Security Platform
|
||||
|
||||
Target: 2026-06-14
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Formal policy schema exists for org baselines, exceptions, owners,
|
||||
expiration, severity, and audit trails.
|
||||
- SARIF/code-scanning output is implemented and tested.
|
||||
- GitHub Action policy gates expose organization policy status and violation
|
||||
counts for branch-protection and CI evidence.
|
||||
- Policy packs are defined for OSS, team, enterprise, regulated, high-risk
|
||||
hooks/MCP, and CI enforcement.
|
||||
- Supply-chain intelligence covers MCP package provenance and has an extension
|
||||
path for npm/pip reputation, CVEs, typosquats, and dependency risk.
|
||||
- Prompt-injection corpus and regression benchmark are ready for continuous
|
||||
rule hardening with category-level coverage and regression-gate output.
|
||||
- Enterprise reports include JSON plus self-contained HTML executive output
|
||||
with risk posture, priority findings, and category exposure.
|
||||
|
||||
### 6. ECC Tools Billing, Deep Analysis, PR Checks, And Linear Sync
|
||||
|
||||
Target: 2026-06-21
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Native GitHub Marketplace billing announcement is backed by verified
|
||||
implementation and docs.
|
||||
- Internal billing readiness audit covers plan limits, seats, entitlement
|
||||
mapping, Marketplace plan shape, subscription state, overage hooks, and
|
||||
failure modes.
|
||||
- Deep analyzer covers diff patterns, CI/CD workflows, dependency/security
|
||||
surface, PR review behavior, failure history, harness config, skill quality,
|
||||
and reference-set/RAG comparison.
|
||||
- PR check suite taxonomy includes Security Evidence, Harness Drift, Install
|
||||
Manifest Integrity, CI/CD Recommendation, Cost/Token Risk, and Agent Config
|
||||
Review.
|
||||
- Cost/token-risk predictive follow-ups flag AI routing, model-call, usage,
|
||||
quota, and budget changes when budget evidence is missing.
|
||||
- Reference-set validation follow-ups flag analyzer, skill, agent, command, and
|
||||
harness-guidance changes that lack eval, golden trace, benchmark, or
|
||||
maintained reference-set evidence.
|
||||
- Linear sync design maps findings to issues/status without flooding the
|
||||
workspace.
|
||||
- Follow-up generation caps automatic GitHub object creation and keeps overflow
|
||||
findings in a copy-ready project sync backlog.
|
||||
|
||||
### 7. Legacy Audit And Stale-Work Salvage Closure
|
||||
|
||||
Target: 2026-06-15
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Legacy directories and orphaned handoffs are inventoried.
|
||||
- Each useful artifact is marked landed, Linear/project-tracked, salvage
|
||||
branch, or archive/no-action.
|
||||
- Workspace-level legacy repos are mined only through sanitized maintainer
|
||||
branches; raw context, secrets, personal paths, local settings, and private
|
||||
drafts are never imported wholesale.
|
||||
- Stale PR salvage policy stays in force: close stale/conflicted PRs first,
|
||||
record a salvage ledger item, then port useful compatible content on
|
||||
maintainer branches with attribution.
|
||||
- #1687 localization leftovers are handled only by translator/manual review,
|
||||
not blind cherry-pick.
|
||||
|
||||
## Next Engineering Slices
|
||||
|
||||
1. Decide whether AgentShield PDF export adds value beyond the merged HTML
|
||||
executive report and corpus benchmark output.
|
||||
2. Extend ECC Tools deep analysis and Linear/project sync without flooding the
|
||||
workspace.
|
||||
@@ -1,238 +1,54 @@
|
||||
# ECC 2.0 Reference Architecture
|
||||
|
||||
Current execution mirror:
|
||||
[`ECC-2.0-GA-ROADMAP.md`](ECC-2.0-GA-ROADMAP.md).
|
||||
Research summary from competitor/reference analysis (2026-03-22).
|
||||
|
||||
This document turns the May 2026 reference sweep into concrete ECC backlog
|
||||
shape. It is not a second strategy memo: every reference pressure below should
|
||||
land as an adapter, check, observable signal, security policy, PR review
|
||||
surface, or release-readiness gate.
|
||||
## Competitive Landscape
|
||||
|
||||
## Reference Baseline
|
||||
| Project | Stars | Language | Type | Multi-Agent | Worktrees | Terminal-native |
|
||||
|---------|-------|----------|------|-------------|-----------|-----------------|
|
||||
| **ECC 2.0** | - | Rust | TUI | Yes | Yes | **Yes (SSH)** |
|
||||
| superset-sh/superset | 7.7K | TypeScript | Electron | Yes | Yes | No (desktop) |
|
||||
| standardagents/dmux | 1.2K | TypeScript | TUI (Ink) | Yes | Yes | Yes |
|
||||
| opencode-ai/opencode | 11.5K | Go | TUI | No | No | Yes |
|
||||
| smtg-ai/claude-squad | 6.5K | Go | TUI | Yes | Yes | Yes |
|
||||
|
||||
Snapshot date: 2026-05-12.
|
||||
## Three-Layer Architecture
|
||||
|
||||
| Reference | Primary pressure on ECC 2.0 | Concrete ECC delta |
|
||||
| --- | --- | --- |
|
||||
| [`stablyai/orca`](https://github.com/stablyai/orca) | Worktree-native multi-agent IDE with terminals, source control, GitHub integration, SSH, notifications, design/browser mode, account switching, and per-worktree context. | Treat worktree lifecycle, review state, notification state, and account/provider identity as first-class adapter signals. |
|
||||
| [`superset-sh/superset`](https://github.com/superset-sh/superset) | Desktop AI-agent workspace with parallel execution, worktree isolation, diff review, workspace presets, and broad CLI-agent compatibility. | Add workspace preset taxonomy and make ECC2 session/worktree state exportable enough for external editors to consume. |
|
||||
| [`standardagents/dmux`](https://github.com/standardagents/dmux) | Tmux/worktree orchestration, lifecycle hooks, multi-select agent control, smart merging, file browser, notifications, and cleanup. | Add lifecycle-hook coverage to the harness matrix and define merge/conflict queue events. |
|
||||
| [`aidenybai/ghast`](https://github.com/aidenybai/ghast) | Native macOS terminal multiplexer with cwd-grouped workspaces, panes, tabs, drag/drop, search, and notifications. | Preserve terminal-native ergonomics while adding cwd/session grouping and searchable handoff/session records. |
|
||||
| [`jarrodwatts/claude-hud`](https://github.com/jarrodwatts/claude-hud) | Always-visible Claude Code statusline for context, tools, agents, todos, and transcript-backed activity. | Formalize the ECC HUD/status payload for context, cost, tool calls, active agents, todos, queue state, checks, and risk. |
|
||||
| [`stanford-iris-lab/meta-harness`](https://github.com/stanford-iris-lab/meta-harness) | Automated search over task-specific harness design: what to store, retrieve, and show. | Split ECC improvement loops into scenario spec, proposer trace, verifier result, and promoted playbook. |
|
||||
| [`greyhaven-ai/autocontext`](https://github.com/greyhaven-ai/autocontext) | Recursive harness improvement using traces, reports, artifacts, datasets, playbooks, and role-separated evaluators. | Store reusable traces and playbooks before mutating installed harness assets. |
|
||||
| [`NousResearch/hermes-agent`](https://github.com/NousResearch/hermes-agent) | Self-improving operator shell with memories, skills, scheduler, gateways, subagents, terminal backends, and migration tooling. | Keep ECC portable across local, SSH, container, and hosted terminal backends without hiding the underlying commands. |
|
||||
| [`anthropics/claude-code`](https://github.com/anthropics/claude-code), [`sst/opencode`](https://github.com/sst/opencode), Zed, Codex, Cursor, Gemini | Different agent harnesses expose different hooks, plugin surfaces, session stores, config files, and review loops. | Maintain a public adapter compliance matrix instead of treating one harness as the canonical UX. |
|
||||
| Local Claude Code source review | Session, tool, permission, hook, remote, analytics, task, and context-suggestion surfaces are more structured than the public CLI UX suggests. | Model status and risk events around session messages, permission requests, tool progress, context pressure, and summary state. |
|
||||
|
||||
## Architecture Shape
|
||||
|
||||
ECC 2.0 should be a harness operating system, not only a catalog of commands,
|
||||
agents, and skills.
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Operator Surface │
|
||||
│ CLI, plugin, TUI, HUD/statusline, release gates, PR checks │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ Harness Adapter Layer │
|
||||
│ Claude Code, Codex, OpenCode, Cursor, Gemini, Zed, dmux, │
|
||||
│ Orca, Superset, Ghast, terminal-only │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ Worktree, Session, And Queue Runtime │
|
||||
│ worktrees, panes, sessions, todos, checks, merge/conflict │
|
||||
│ queues, notification state, ownership, handoff exports │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ Observability And Evaluation Loop │
|
||||
│ JSONL traces, status snapshots, risk ledger, harness audit, │
|
||||
│ scenario specs, verifiers, promoted playbooks, RAG sets │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ Security And Commercial Platform │
|
||||
│ AgentShield policies/SARIF, ECC Tools checks, billing, │
|
||||
│ Linear/GitHub sync, enterprise reports │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ TUI Layer (ratatui) │ User-facing dashboard
|
||||
│ Panes, diff viewer, hotkeys │ Communicates via Unix socket
|
||||
├─────────────────────────────────┤
|
||||
│ Runtime Layer (library) │ Workspace runtime, agent registry,
|
||||
│ State persistence, detection │ status detection, SQLite
|
||||
├─────────────────────────────────┤
|
||||
│ Daemon Layer (process) │ Persistent across TUI restarts
|
||||
│ Terminal sessions, git ops, │ PTY management, heartbeats
|
||||
│ agent process supervision │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Reference-To-Backlog Map
|
||||
## Patterns to Adopt
|
||||
|
||||
### Worktree And Session Orchestration
|
||||
### From Superset (Electron, 7.7K stars)
|
||||
- **Workspace Runtime Registry** — trait-based abstraction with capability flags
|
||||
- **Persistent daemon terminal** — sessions survive restarts via IPC
|
||||
- **Per-project mutex** for git operations (prevents race conditions)
|
||||
- **Port allocation** per workspace for dev servers
|
||||
- **Cold restore** from serialized terminal scrollback
|
||||
|
||||
Adopt from Orca, Superset, dmux, and Ghast:
|
||||
### From dmux (Ink TUI, 1.2K stars)
|
||||
- **Worker-per-pane status detection** — fingerprint terminal output + LLM classification
|
||||
- **Agent Registry** — centralized agent definitions (install check, launch cmd, permissions)
|
||||
- **Retry strategies** — different policies for destructive vs read-only operations
|
||||
- **PaneLifecycleManager** — exclusive locks preventing concurrent pane races
|
||||
- **Lifecycle hooks** — worktree_created, pre_merge, post_merge
|
||||
- **Background cleanup queue** — async worktree deletion
|
||||
|
||||
- Worktree lifecycle events: create, resume, pause, stop, diff, review, PR,
|
||||
merge-ready, conflict, stale, close, salvage.
|
||||
- Session grouping by repo, branch, cwd, task, owner, and harness.
|
||||
- Workspace presets for release lane, PR triage lane, docs lane, security lane,
|
||||
and test-writer lane.
|
||||
- Notifications for blocked CI, dirty worktrees, merge conflicts, stale review,
|
||||
and finished autonomous runs.
|
||||
- Review loops that can annotate diffs and PRs without taking ownership away
|
||||
from maintainers.
|
||||
|
||||
Repo work:
|
||||
|
||||
- `everything-claude-code`: extend the adapter compliance matrix and public
|
||||
scorecard onramp.
|
||||
- `ecc2`: surface session/worktree state through a stable local payload before
|
||||
adding hosted telemetry.
|
||||
- `ECC-Tools`: consume the same lifecycle events for PR checks, issue routing,
|
||||
and Linear sync.
|
||||
|
||||
Verification:
|
||||
|
||||
- `npm run harness:audit -- --format json`
|
||||
- `npm run observability:ready`
|
||||
- targeted adapter matrix tests once the matrix moves from docs to data
|
||||
|
||||
### HUD, Status, And Observability
|
||||
|
||||
Adopt from Claude HUD and the Claude Code source review:
|
||||
|
||||
- Context pressure: usage, compaction risk, large-result warnings, and summary
|
||||
state.
|
||||
- Tool activity: active tool, recent tools, duration, risky operations, and
|
||||
permission requests.
|
||||
- Agent activity: active subagents, delegated task, branch/worktree, and wait
|
||||
state.
|
||||
- Queue activity: open PRs/issues, CI state, stale/conflict batches, review
|
||||
state, and closed-stale salvage backlog.
|
||||
- Cost/risk: token cost estimate, destructive-operation risk, hook/MCP risk,
|
||||
and security scan state.
|
||||
|
||||
Repo work:
|
||||
|
||||
- Keep `docs/architecture/observability-readiness.md` as the operator-facing
|
||||
readiness gate.
|
||||
- Define a versioned HUD/status JSON contract that both ECC2 and ECC Tools can
|
||||
consume.
|
||||
- Add sample exports from `loop-status`, `session-inspect`, harness audit, and
|
||||
risk ledger into a fixture directory before building visual UI.
|
||||
|
||||
Verification:
|
||||
|
||||
- `npm run observability:ready`
|
||||
- fixture validation for every status payload
|
||||
- cross-platform smoke test for commands that read session history
|
||||
|
||||
### Self-Improving Harness Loop
|
||||
|
||||
Adopt from Meta-Harness, Autocontext, and Hermes Agent:
|
||||
|
||||
- Separate the loop into observation, proposal, verification, promotion, and
|
||||
rollback.
|
||||
- Store every proposed improvement as trace plus artifact, not only as a final
|
||||
changed file.
|
||||
- Promote playbooks only after a verifier proves that they improve a scenario
|
||||
without widening blast radius.
|
||||
- Use RAG/reference sets for vetted ECC patterns, team history, CI failures,
|
||||
review outcomes, harness config quality, and security decisions.
|
||||
|
||||
Repo work:
|
||||
|
||||
- `everything-claude-code`: document scenario specs, verifier contracts, and
|
||||
playbook promotion rules.
|
||||
- `ECC-Tools`: map analyzer findings to PR comments, check runs, and Linear
|
||||
tasks without flooding the workspace.
|
||||
- `agentshield`: feed prompt-injection and config-risk findings into regression
|
||||
suites.
|
||||
|
||||
Verification:
|
||||
|
||||
- read-only prototype that emits a trace, report, candidate playbook, and
|
||||
verifier result
|
||||
- regression fixture proving a bad proposal is rejected
|
||||
|
||||
### AgentShield Enterprise Security Platform
|
||||
|
||||
AgentShield should move from useful scanner to enterprise security platform.
|
||||
|
||||
Backlog shape:
|
||||
|
||||
- Policy schema for org baseline, rule severity, owner, exception, expiration,
|
||||
evidence, and audit trail.
|
||||
- SARIF output for GitHub code scanning.
|
||||
- Policy packs for OSS, team, enterprise, regulated, high-risk hooks/MCP, and
|
||||
CI enforcement.
|
||||
- Supply-chain intelligence for MCP packages, npm/pip provenance, CVEs,
|
||||
typosquats, and dependency reputation.
|
||||
- Prompt-injection corpus and regression benchmark.
|
||||
- JSON plus executive HTML/PDF report output.
|
||||
|
||||
Verification:
|
||||
|
||||
- schema unit tests
|
||||
- SARIF fixture tests
|
||||
- policy-pack golden tests
|
||||
- false-positive regression tests from the public issue history
|
||||
|
||||
### ECC Tools Commercial And Review Platform
|
||||
|
||||
ECC Tools should become the GitHub-native layer for billing, deep analysis,
|
||||
PR checks, and Linear progress tracking.
|
||||
|
||||
Backlog shape:
|
||||
|
||||
- Native GitHub Marketplace billing audit before any payments announcement:
|
||||
plans, seats, org/account mapping, subscription state, overage behavior,
|
||||
downgrade/cancel behavior, and failure modes.
|
||||
- Deep analyzer comparable in scope to the useful parts of GitGuardian,
|
||||
Dependabot, CodeRabbit, and Greptile: security evidence, dependency risk,
|
||||
CI/CD recommendations, PR review behavior, config quality, token/cost risk,
|
||||
and harness drift.
|
||||
- RAG/reference set over vetted ECC patterns, historical PR outcomes,
|
||||
dependency advisories, CI failures, review decisions, and team-specific
|
||||
conventions.
|
||||
- Linear sync that maps findings to project status, milestone evidence, and
|
||||
owner-ready issues without exhausting issue limits.
|
||||
|
||||
Verification:
|
||||
|
||||
- check-run fixture tests
|
||||
- billing webhook replay tests
|
||||
- analyzer golden PR fixtures
|
||||
- Linear sync dry-run fixture
|
||||
|
||||
### Closed-Stale Salvage Lane
|
||||
|
||||
Closing stale PRs keeps the public queue usable, but useful work should not be
|
||||
lost because a contributor no longer has time to rebase.
|
||||
|
||||
Execution rule:
|
||||
|
||||
1. Close stale, conflicted, or obsolete PRs with a clear courtesy comment.
|
||||
2. Record them in a salvage ledger with source PR, author, reason closed,
|
||||
useful files/concepts, risk, and recommended maintainer action.
|
||||
3. After the cleanup batch, inspect each closed PR diff manually.
|
||||
4. Cherry-pick only when the patch still applies cleanly and preserves current
|
||||
architecture. Otherwise reimplement the useful idea in a fresh maintainer
|
||||
branch.
|
||||
5. Preserve attribution in the commit body or PR body.
|
||||
6. Comment back on the source PR when useful work lands, linking the maintainer
|
||||
PR or merged commit.
|
||||
7. Mark the ledger item as landed, superseded, Linear-tracked, or no-action.
|
||||
|
||||
Required safeguards:
|
||||
|
||||
- Never blind cherry-pick generated churn, bulk localization, or dependency
|
||||
major-version changes.
|
||||
- Prefer small maintainer PRs over one salvage megabranch.
|
||||
- Run the same validation gates as normal code, docs, or catalog changes.
|
||||
- Keep contributor credit even when the final implementation is rewritten.
|
||||
|
||||
## Near-Term Implementation Order
|
||||
|
||||
1. Extend the harness adapter matrix and public scorecard onramp.
|
||||
2. Add the release/name/plugin publication checklist with evidence fields.
|
||||
3. Define the HUD/status JSON contract and fixture directory.
|
||||
4. Start AgentShield policy schema plus SARIF fixtures.
|
||||
5. Audit ECC Tools billing and check-run surfaces.
|
||||
6. Inventory legacy folders and closed-stale PRs into the salvage ledger.
|
||||
7. Port useful stale work in small attributed maintainer PRs.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Hosted telemetry before the local event model is useful and testable.
|
||||
- Automatic mutation of user harness configs without verifier evidence.
|
||||
- Treating any one agent harness as the canonical interface.
|
||||
- Release or payments announcements before command, package, marketplace, and
|
||||
billing evidence is fresh.
|
||||
## ECC 2.0 Advantages
|
||||
- Terminal-native (works over SSH, unlike Superset)
|
||||
- Integrates with 116-skill ecosystem
|
||||
- AgentShield security scanning
|
||||
- Self-improving skill evolution (continuous-learning-v2)
|
||||
- Rust single binary (3.4MB, no runtime deps)
|
||||
- First Rust-based agentic IDE TUI in open source
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
# Plan-PRD Pattern: Markdown-Staged Planning Flow
|
||||
|
||||
A lightweight, SDLC-aligned planning workflow where each phase of the lifecycle produces a committable markdown **staging file** that the next command consumes.
|
||||
|
||||
> Short version: `/plan-prd` writes a PRD, `/plan` writes a plan, the `tdd-workflow` skill implements it, and `/pr` ships it. Each arrow is a file on disk, not a conversation in memory.
|
||||
|
||||
## Feature: Markdown Staging Files
|
||||
|
||||
Every planning artifact is a plain `.md` file under `.claude/`:
|
||||
|
||||
```
|
||||
.claude/
|
||||
prds/ # Product Requirements Documents from /plan-prd
|
||||
plans/ # Implementation plans from /plan
|
||||
reviews/ # Code review artifacts from /code-review
|
||||
```
|
||||
|
||||
These files are:
|
||||
|
||||
- **Plain markdown** — readable by humans, diffable in PRs, grep-able at CLI.
|
||||
- **Committable** — check them in alongside code so the intent travels with the implementation.
|
||||
- **Composable** — each command accepts the previous stage's file as its `$ARGUMENTS`, so the toolchain composes via paths rather than in-context state.
|
||||
- **Resumable** — close the session, open a new one tomorrow, pass the file path back in.
|
||||
|
||||
## Flow
|
||||
|
||||
```
|
||||
┌───────────────────────────┐
|
||||
│ /plan-prd "<idea>" │ Requirements phase
|
||||
│ → .claude/prds/X.prd.md │ Problem · Users · Hypothesis · Scope
|
||||
└─────────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────┐
|
||||
│ /plan <prd-path> │ Design phase
|
||||
│ → .claude/plans/X.plan.md│ Patterns · Files · Tasks · Validation
|
||||
└─────────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────┐
|
||||
│ tdd-workflow skill │ Implementation phase
|
||||
│ → code + tests │ Test-first, minimal diff
|
||||
└─────────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────┐
|
||||
│ /pr │ Delivery phase
|
||||
│ → GitHub PR │ Links back to PRD + plan
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
Each box is a **gate**. You can:
|
||||
|
||||
- Stop between gates — the artifact persists.
|
||||
- Restart from any gate using the artifact path.
|
||||
- Skip gates for small work — feed `/plan` free-form text and ignore `/plan-prd`.
|
||||
- Run a gate standalone — `/plan "refactor X"` produces a conversational plan with no artifact.
|
||||
|
||||
## Why `/plan-prd` Is Additional to `/plan`
|
||||
|
||||
They answer different questions. Mixing them causes scope creep.
|
||||
|
||||
| Command | Answers | SDLC Phase | Artifact |
|
||||
|---|---|---|---|
|
||||
| `/plan-prd` | *What problem? For whom? How do we know we're done?* | Requirements | `.claude/prds/{name}.prd.md` |
|
||||
| `/plan` | *What files, patterns, and tasks satisfy the requirement?* | Design + Implementation strategy | `.claude/plans/{name}.plan.md` (PRD mode) or inline (text mode) |
|
||||
|
||||
### Why not combine them?
|
||||
|
||||
- **Separation of concerns.** PRDs ask *why*; plans ask *how*. Bundling them creates one oversized command that does both poorly, as the old `/prp-prd` → `/prp-plan` pair demonstrated (8-phase interrogation with implementation-phase tables mixed into requirements).
|
||||
- **Different audiences.** A stakeholder reviewing a PRD does not care about file paths or type-check commands. An engineer reading a plan does not need the market-research phase.
|
||||
- **Different lifespans.** A PRD can remain stable while its plan is rewritten multiple times as implementation assumptions change.
|
||||
- **Optional step.** Many changes (bug fixes, small refactors, single-file additions) don't need a PRD. `/plan` alone is enough. Forcing a PRD on every change is bureaucracy.
|
||||
|
||||
### When to use each
|
||||
|
||||
Use `/plan-prd` when:
|
||||
|
||||
- Scope is unclear or contested.
|
||||
- Multiple stakeholders need to align on the problem before solutioning.
|
||||
- The change is large enough that writing down the hypothesis is cheaper than relitigating scope mid-implementation.
|
||||
|
||||
Use `/plan` directly when:
|
||||
|
||||
- Requirements are already clear (a bug report, a scoped refactor, a known migration).
|
||||
- The work is small enough that a conversational plan + confirmation gate is sufficient.
|
||||
- You already have a PRD — pass it to `/plan` and skip `/plan-prd`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Full flow (feature with unclear scope)
|
||||
|
||||
```bash
|
||||
# 1. Draft the PRD
|
||||
/plan-prd "Per-user rate limits on the public API"
|
||||
|
||||
# → .claude/prds/per-user-rate-limits.prd.md created
|
||||
# Answer the framing questions, provide evidence, define hypothesis and scope.
|
||||
|
||||
# 2. Pick the next pending milestone and produce a plan
|
||||
/plan .claude/prds/per-user-rate-limits.prd.md
|
||||
|
||||
# → .claude/plans/per-user-rate-limits.plan.md created
|
||||
# The plan includes patterns to mirror, files to change, and validation commands.
|
||||
# PRD's Delivery Milestones table updates the selected row to `in-progress`.
|
||||
|
||||
# 3. Implement test-first
|
||||
Use the tdd-workflow skill
|
||||
|
||||
# 4. Open the PR
|
||||
/pr
|
||||
# → PR body auto-references .claude/prds/... and .claude/plans/...
|
||||
```
|
||||
|
||||
### Quick flow (scope already clear)
|
||||
|
||||
```bash
|
||||
/plan "Add retry with exponential backoff to the notifier"
|
||||
# Conversational planning, no artifact.
|
||||
# Confirm, then use the tdd-workflow skill.
|
||||
```
|
||||
|
||||
### Reference an existing PRD from elsewhere
|
||||
|
||||
```bash
|
||||
# PRD was written by someone else, lives in your repo
|
||||
/plan docs/rfcs/0042-rate-limiting.prd.md
|
||||
```
|
||||
|
||||
`/plan` detects any `.prd.md` path and switches to artifact mode, parsing the Delivery Milestones table.
|
||||
|
||||
## Why staging files beat in-context state
|
||||
|
||||
- **Transferable**: drop the PRD path into a fresh session and you're caught up — no replaying a long conversation.
|
||||
- **Auditable**: the PR reviewer sees *what you intended* next to *what you built*.
|
||||
- **Versioned**: the staging file evolves in git history, same as code.
|
||||
- **Machine-parseable**: `/plan` programmatically picks the next pending milestone; `/pr` programmatically links artifacts in the PR body. No prompt engineering required.
|
||||
|
||||
## Related commands
|
||||
|
||||
- `/plan-prd` — requirements (this pattern entry point).
|
||||
- `/plan` — planning (consumes PRDs or free-form text).
|
||||
- `tdd-workflow` skill — test-first implementation.
|
||||
- `/pr` — open a PR that references PRDs and plans.
|
||||
- `/code-review` — reviews local diffs or PRs; auto-detects `.claude/prds/` and `.claude/plans/` as context.
|
||||
|
||||
## Compatibility
|
||||
|
||||
This pattern adds ECC-native staging-file commands alongside the existing `prp-*` command set. The legacy PRP commands remain available for deeper PRP workflows and for users who already have `.claude/PRPs/` artifacts.
|
||||
|
||||
- `/plan-prd` is the lean requirements entry point for `.claude/prds/`.
|
||||
- `/plan` can consume `.prd.md` files and produce `.claude/plans/` artifacts without requiring the legacy PRP directory layout.
|
||||
- `/pr` is the ECC-native PR creation command and can reference `.claude/prds/` and `.claude/plans/`.
|
||||
- `/prp-prd`, `/prp-plan`, `/prp-implement`, `/prp-commit`, and `/prp-pr` remain valid legacy/deep workflow commands.
|
||||
@@ -13,9 +13,6 @@ The goal is to keep the durable parts of agentic work in one repo:
|
||||
|
||||
Claude Code, Codex, OpenCode, Cursor, Gemini, and future harnesses should adapt those assets at the edge instead of requiring a new workflow model for every tool.
|
||||
|
||||
For the operator-facing support matrix and scorecard workflow, see
|
||||
[Harness Adapter Compliance Matrix](harness-adapter-compliance.md).
|
||||
|
||||
## Portability Model
|
||||
|
||||
| Surface | Shared Source | Harness Adapter | Current Status |
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
# Harness Adapter Compliance Matrix
|
||||
|
||||
This matrix is the public onramp for teams that want to use ECC across more
|
||||
than one coding harness. It turns the cross-harness architecture into a
|
||||
practical scorecard: what works today, what is instruction-only, what needs an
|
||||
adapter, and what evidence an operator should collect before trusting a setup.
|
||||
|
||||
ECC's durable units stay in shared sources:
|
||||
|
||||
- `skills/*/SKILL.md`
|
||||
- `rules/`
|
||||
- `commands/`
|
||||
- `hooks/hooks.json`
|
||||
- `scripts/hooks/`
|
||||
- MCP reference configs
|
||||
- session and observability contracts
|
||||
|
||||
Harness-specific files should only adapt loading, event shape, command names,
|
||||
or platform limits.
|
||||
|
||||
## Compliance States
|
||||
|
||||
| State | Meaning |
|
||||
| --- | --- |
|
||||
| Native | ECC can install or verify the surface directly for this harness. |
|
||||
| Adapter-backed | ECC has a thin adapter, plugin, or package surface, but parity differs by harness. |
|
||||
| Instruction-backed | ECC can provide the guidance and files, but the harness does not expose the runtime hook/session surface ECC needs for enforcement. |
|
||||
| Reference-only | The tool is useful as a design pressure or external runtime, but ECC does not yet ship a direct installer or adapter for it. |
|
||||
|
||||
## Matrix
|
||||
|
||||
The matrix below is rendered from
|
||||
`scripts/lib/harness-adapter-compliance.js` and verified by
|
||||
`npm run harness:adapters -- --check`.
|
||||
|
||||
<!-- harness-adapter-compliance:matrix-start -->
|
||||
| Harness or runtime | State | Supported assets | Unsupported or different surfaces | Install or onramp | Verification command | Risk notes |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| Claude Code | Native | Claude plugin assets; skills; commands; hooks; MCP config; local rules; statusline-oriented workflows | Claude-native hooks do not imply parity in other harnesses | `./install.sh --profile minimal --target claude`; Claude plugin install | `npm run harness:audit -- --format json`; `node scripts/session-inspect.js --list-adapters` | Avoid loading every skill by default; keep hooks opt-in and inspectable. |
|
||||
| Codex | Instruction-backed | `AGENTS.md`; Codex plugin metadata; skills; MCP reference config; command patterns | Native hook enforcement and Claude slash-command semantics are not equivalent | `./install.sh --profile minimal --target codex`; repo-local `AGENTS.md` review | `npm run harness:audit -- --format json` | Treat hooks as policy text unless a native Codex hook surface exists. |
|
||||
| OpenCode | Adapter-backed | OpenCode package/plugin metadata; shared skills; MCP config; event adapter patterns | Event names, plugin packaging, and command dispatch differ from Claude Code | OpenCode package or plugin surface from this repo | `node tests/scripts/build-opencode.test.js`; `npm run harness:audit -- --format json` | Keep hook logic in shared scripts and adapt only event shape at the edge. |
|
||||
| Cursor | Adapter-backed | Cursor rules; project-local skills; hook adapter; shared scripts | Cursor hook events and rule loading differ from Claude Code | `./install.sh --profile minimal --target cursor` | `node tests/lib/install-targets.test.js`; `npm run harness:audit -- --format json` | Cursor adapters must preserve existing project rules and avoid silent overwrite. |
|
||||
| Gemini | Instruction-backed | Gemini project-local instructions; shared skills; rules; compatibility docs | No full ECC hook parity; ecosystem ports must document drift from upstream ECC | `./install.sh --profile minimal --target gemini` | `node tests/lib/install-targets.test.js` | Treat Gemini ports as ecosystem adapters until validated end to end inside Gemini CLI. |
|
||||
| Zed-adjacent workflows | Instruction-backed | shared skills; `AGENTS.md` style project instructions; verification loops | Zed agent surfaces vary; no first-party ECC installer is shipped today | Manual copy from shared ECC sources until adapter requirements settle | `npm run harness:audit -- --format json` | Do not claim native Zed support before a real adapter and verification path exist. |
|
||||
| dmux | Adapter-backed | session snapshots; tmux/worktree orchestration status; handoff exports | dmux is an orchestration runtime, not an install target for skills/rules | `node scripts/session-inspect.js --list-adapters`; dmux session target inspection | `node tests/lib/session-adapters.test.js` | Treat dmux events as session/runtime signals, not as a replacement for repo validation. |
|
||||
| Orca | Reference-only | worktree lifecycle; review state; notification; provider-identity design pressure | No ECC installer or direct adapter today | Use as a comparison target for worktree/session state requirements | `npm run observability:ready` | Do not import product-specific assumptions; convert lessons into ECC event fields. |
|
||||
| Superset | Reference-only | workspace presets; parallel-agent review loops; worktree isolation design pressure | No ECC installer or direct adapter today | Use as a comparison target for workspace preset taxonomy | `npm run observability:ready` | Keep ECC portable; do not require a desktop workspace to get basic value. |
|
||||
| Ghast | Reference-only | terminal-native pane grouping; cwd grouping; search; notifications | No ECC installer or direct adapter today | Use as a comparison target for terminal-first session grouping | `node scripts/session-inspect.js --list-adapters` | Preserve terminal ergonomics before adding visual UI assumptions. |
|
||||
| Terminal-only | Native | skills; rules; commands; scripts; harness audit; observability readiness; handoffs | No external UI, no automatic session control unless scripts are run explicitly | Clone repo; run commands directly; use minimal profile for project installs | `npm run harness:audit -- --format json`; `npm run observability:ready` | This is the fallback contract; every higher-level adapter should degrade to it. |
|
||||
<!-- harness-adapter-compliance:matrix-end -->
|
||||
|
||||
## Scorecard Onramp
|
||||
|
||||
Use this sequence before asking ECC to make a team or repo setup more
|
||||
autonomous:
|
||||
|
||||
```bash
|
||||
npm run harness:adapters -- --check
|
||||
npm run harness:audit -- --format json
|
||||
npm run observability:ready
|
||||
node scripts/session-inspect.js --list-adapters
|
||||
node scripts/loop-status.js --json --write-dir .ecc/loop-status
|
||||
```
|
||||
|
||||
Read the result as a setup scorecard, not a product badge:
|
||||
|
||||
- `harness:adapters -- --check` proves this public matrix still matches the
|
||||
adapter source data and required evidence fields.
|
||||
- `harness:audit` scores tool coverage, context efficiency, quality gates,
|
||||
memory persistence, eval coverage, security guardrails, and cost efficiency.
|
||||
- `observability:ready` proves the repo still exposes the local status,
|
||||
session, tool-activity, risk-ledger, and release-onramp signals.
|
||||
- `session-inspect --list-adapters` shows which session surfaces are actually
|
||||
inspectable in the current environment.
|
||||
- `loop-status --json` creates a machine-readable handoff/status payload for
|
||||
longer autonomous runs.
|
||||
|
||||
## Data-Backed Scorecard Contract
|
||||
|
||||
Each adapter record exposes:
|
||||
|
||||
- `id`
|
||||
- `state`
|
||||
- `supported_assets`
|
||||
- `unsupported_surfaces`
|
||||
- `install_or_onramp`
|
||||
- `verification_commands`
|
||||
- `risk_notes`
|
||||
- `last_verified_at`
|
||||
- `owner`
|
||||
- `source_docs`
|
||||
|
||||
The validator fails if a public adapter claim has no install path,
|
||||
verification command, risk note, owner, source doc, or verification date.
|
||||
|
||||
## Operating Rules
|
||||
|
||||
- Prefer small, additive adapters over harness-specific forks of the same
|
||||
workflow.
|
||||
- Do not call a harness native until the adapter has an install path and a
|
||||
verification command.
|
||||
- Keep Codex, Gemini, and Zed surfaces honest when enforcement is
|
||||
instruction-backed rather than runtime-backed.
|
||||
- Treat reference-only tools as design pressure until ECC has a direct adapter.
|
||||
- Keep the terminal-only path healthy; it is the portability floor.
|
||||
@@ -1,66 +0,0 @@
|
||||
# ECC 2.0 Observability Readiness
|
||||
|
||||
ECC 2.0 should be observable before it becomes more autonomous. The local
|
||||
default is an opt-in, repo-owned readiness gate that checks whether the core
|
||||
signals are present without sending telemetry anywhere.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
npm run observability:ready
|
||||
node scripts/observability-readiness.js --format json
|
||||
```
|
||||
|
||||
The gate is deterministic and safe to run in CI. It only checks repository
|
||||
files and reports whether the release surface can expose the signals an
|
||||
operator needs.
|
||||
|
||||
## Signal Model
|
||||
|
||||
- Live status: `scripts/loop-status.js` can emit JSON, watch active loops, and
|
||||
write snapshots for dashboards or handoffs.
|
||||
- Session traces: `scripts/session-inspect.js` can inspect Claude, dmux, and
|
||||
adapter-backed sessions, then write canonical snapshots.
|
||||
- Harness baseline: `scripts/harness-audit.js` provides a repeatable scorecard
|
||||
for tool coverage, context efficiency, quality gates, memory persistence,
|
||||
eval coverage, security guardrails, and cost efficiency.
|
||||
- Tool activity: `scripts/hooks/session-activity-tracker.js` records local
|
||||
`tool-usage.jsonl` events that ECC2 can sync.
|
||||
- Risk ledger: `ecc2/src/observability/mod.rs` scores tool calls and stores a
|
||||
paginated ledger for review.
|
||||
|
||||
## Reference Pressure
|
||||
|
||||
The current agent-tooling ecosystem is converging on the same operating needs:
|
||||
|
||||
- dmux, Orca, and Superset emphasize isolated worktrees plus one place to see
|
||||
agent state and merge/review work.
|
||||
- Claude HUD makes context, tool activity, agent activity, and todo progress
|
||||
visible inside the coding loop.
|
||||
- Autocontext records every run as durable traces, reports, artifacts, and
|
||||
reusable improvements.
|
||||
- Meta-Harness treats the harness itself as something to evaluate and improve,
|
||||
which requires clean logs of proposer behavior and outcomes.
|
||||
- Zed and OpenCode emphasize agent control surfaces, reviewable changes, and
|
||||
harness-specific configuration that should still preserve portable project
|
||||
knowledge.
|
||||
|
||||
ECC's answer is not a hosted analytics dependency by default. The first
|
||||
release-candidate gate is local and file-backed. Hosted telemetry can come
|
||||
later, but only after the local event model is useful enough to trust.
|
||||
|
||||
## Operator Workflow
|
||||
|
||||
1. Run `npm run observability:ready`.
|
||||
2. Run `npm run harness:audit -- --format json` for the broader harness
|
||||
scorecard.
|
||||
3. Run `node scripts/loop-status.js --json --write-dir .ecc/loop-status`
|
||||
during longer autonomous batches.
|
||||
4. Run `node scripts/session-inspect.js --list-adapters` to confirm which
|
||||
session surfaces are available.
|
||||
5. Use ECC2 tool logs for risky operations, conflict analysis, and handoff
|
||||
review before increasing autonomy.
|
||||
|
||||
The end-state is practical: before asking ECC to run larger multi-agent loops,
|
||||
the operator can prove the system has live status, durable session traces,
|
||||
baseline scorecards, and a local risk ledger.
|
||||
@@ -1,4 +1,4 @@
|
||||
**言語:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md)
|
||||
**言語:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md)
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**言語 / Language / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
**言語 / Language / 語言 / Dil**
|
||||
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md)
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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)
|
||||
**언어:** [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)
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Language / 语言 / 語言 / 언어 / Dil / Язык / 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) | [한국어](README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/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)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
# Legacy Artifact Inventory
|
||||
|
||||
This inventory keeps legacy and stale-work cleanup from becoming implicit. Each
|
||||
artifact should be classified as landed, milestone-tracked, salvage branch, or
|
||||
archive/no-action before release work treats the queue as clean.
|
||||
|
||||
## Classification States
|
||||
|
||||
| State | Meaning |
|
||||
| --- | --- |
|
||||
| Landed | Useful work has already been ported to current `main` and verified. |
|
||||
| Milestone-tracked | Useful work remains, but belongs to a named roadmap milestone. |
|
||||
| Salvage branch | Useful work should be ported through a fresh maintainer branch with attribution. |
|
||||
| Translator/manual review | Content may be useful, but cannot be safely imported automatically. |
|
||||
| Archive/no-action | Artifact is intentionally retained or skipped; no active port is planned. |
|
||||
|
||||
## Current Repository Scan
|
||||
|
||||
As of 2026-05-12, the tracked repo has no `_legacy-documents-*` directories.
|
||||
|
||||
Fresh check:
|
||||
|
||||
```sh
|
||||
find . -type d -name '_legacy-documents-*' -print
|
||||
```
|
||||
|
||||
Expected result: no output.
|
||||
|
||||
The only tracked legacy directory currently found by filename scan is
|
||||
`legacy-command-shims/`.
|
||||
|
||||
The umbrella ECC workspace also contains sibling legacy git repositories outside
|
||||
this tracked checkout. These are intentionally inventoried separately because
|
||||
they can contain raw operator context, local settings, private drafts, or
|
||||
untracked files that should not be copied into the public repo wholesale.
|
||||
|
||||
Fresh workspace-level check from the ECC umbrella directory:
|
||||
|
||||
```sh
|
||||
find .. -maxdepth 1 -type d -name '_legacy-documents-*' -print | sort
|
||||
```
|
||||
|
||||
Expected result:
|
||||
|
||||
```text
|
||||
../_legacy-documents-ecc-context-2026-04-30
|
||||
../_legacy-documents-ecc-everything-claude-code-2026-04-30
|
||||
```
|
||||
|
||||
## Inventory
|
||||
|
||||
| Artifact | State | Evidence | Action |
|
||||
| --- | --- | --- | --- |
|
||||
| `_legacy-documents-*` directories | Archive/no-action | No matching directories exist in the tracked checkout as of 2026-05-12. | Re-run the scan before release. If any appear, add each directory to this table before publishing. |
|
||||
| `legacy-command-shims/` | Archive/no-action | `legacy-command-shims/README.md` states these retired short-name shims are opt-in and no longer loaded by the default plugin command surface. | Keep as an explicit compatibility archive. Do not move these back into the default plugin surface without a migration decision. |
|
||||
| Closed-stale PR salvage ledger | Landed | `docs/stale-pr-salvage-ledger.md` records useful stale work recovered through maintainer PRs. | Continue using the ledger pattern for future stale closures. |
|
||||
| #1687 zh-CN localization tail | Translator/manual review | Large safe subsets landed in #1746-#1752; remaining pieces require translator/manual review per salvage ledger. | Do not blindly cherry-pick. Split by docs, commands, agents, and skills if a translator review lane opens. |
|
||||
|
||||
## Workspace-Level Legacy Repos
|
||||
|
||||
These sibling repositories live outside the tracked `everything-claude-code`
|
||||
checkout. They are source material for future salvage passes, not installable
|
||||
release assets.
|
||||
|
||||
| Artifact | State | Evidence | Action |
|
||||
| --- | --- | --- | --- |
|
||||
| `../_legacy-documents-ecc-everything-claude-code-2026-04-30` | Archive/no-action | Separate legacy checkout on `fix/configure-ecc-skill-copy-paths-1483` at `b78ddbd0`; useful configure-ecc and install-path concepts have been superseded by current install docs and tests. The checkout also has untracked localized project-guidelines examples and a Finder duplicate `skills/social-graph-ranker/SKILL 2.md`. | Do not import wholesale. If configure-ecc copy-root regressions reappear, use this branch only as source-attributed archaeology and port through a fresh maintainer branch. Leave Finder duplicates out of source control. |
|
||||
| `../_legacy-documents-ecc-context-2026-04-30` | Milestone-tracked | Archived `ECC-context` repo is four commits ahead of its origin and contains context, gameplan, knowledge, marketing, AgentShield, and ECC Tools planning material. It also contains local/private surfaces such as `.env` and local settings. | Keep as a sanitized extraction source for roadmap, launch, AgentShield, and ECC Tools work. Never copy raw context, secrets, personal paths, private settings, or unpublished drafts into this repo. Port only focused, public-safe content with attribution. |
|
||||
|
||||
## Workspace Legacy Import Rules
|
||||
|
||||
When mining workspace-level legacy repos:
|
||||
|
||||
1. Do not read, print, stage, or copy `.env` files, tokens, OAuth secrets,
|
||||
local settings, personal paths, or private operator context.
|
||||
2. Do not import raw marketing drafts, gameplans, or chat/context dumps.
|
||||
3. Extract only focused, public-safe ideas into current docs or code.
|
||||
4. Attribute the source legacy repo, branch, commit, or stale PR in the new PR.
|
||||
5. Validate the result with the same tests and release checks as native work.
|
||||
|
||||
## Legacy Command Shim Contents
|
||||
|
||||
The compatibility archive currently contains 12 retired command shims:
|
||||
|
||||
| Shim | Preferred current direction |
|
||||
| --- | --- |
|
||||
| `agent-sort.md` | Use maintained command or skill routing where available. |
|
||||
| `claw.md` | Use maintained `scripts/claw.js` / `npm run claw` surfaces. |
|
||||
| `context-budget.md` | Use maintained token/context budgeting skills. |
|
||||
| `devfleet.md` | Use maintained agent/harness orchestration docs and skills. |
|
||||
| `docs.md` | Use current documentation and release checklist workflows. |
|
||||
| `e2e.md` | Use maintained E2E testing skills and test scripts. |
|
||||
| `eval.md` | Use eval-harness and verification-loop skills. |
|
||||
| `orchestrate.md` | Use maintained orchestration status and worktree scripts. |
|
||||
| `prompt-optimize.md` | Use prompt-optimizer skill. |
|
||||
| `rules-distill.md` | Use current rules and skill extraction workflows. |
|
||||
| `tdd.md` | Use tdd-workflow and language-specific testing skills. |
|
||||
| `verify.md` | Use verification-loop and package-specific verification skills. |
|
||||
|
||||
## Release Rule
|
||||
|
||||
Before any GA or rc publication pass:
|
||||
|
||||
1. Re-run the `_legacy-documents-*` scan.
|
||||
2. Re-run the closed-stale salvage ledger check.
|
||||
3. Confirm every newly discovered legacy artifact is represented in this file.
|
||||
4. Port useful work through fresh maintainer PRs with source attribution.
|
||||
5. Leave archive/no-action artifacts out of default install and plugin loading.
|
||||
@@ -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)
|
||||
**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)
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Idioma / Language / 语言 / Dil / Язык / Ngôn ngữ**
|
||||
**Idioma / Language / 语言 / Dil**
|
||||
|
||||
[**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)
|
||||
[**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)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Working Title
|
||||
|
||||
Turning ECC Into a Cross-Harness Operating System
|
||||
Turning ECC Into a Cross-Harness Operator System
|
||||
|
||||
## Core Argument
|
||||
|
||||
@@ -58,4 +58,4 @@ The leverage comes from treating the harness, reusable workflow layer, and opera
|
||||
|
||||
The goal is not to copy one exact stack.
|
||||
|
||||
The goal is to build an operating system around the agent that turns repeated work into reusable, measurable surfaces.
|
||||
The goal is to build an operator system that turns repeated work into reusable, measurable surfaces.
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
## Repo
|
||||
|
||||
- verify local `main` is synced to `origin/main`
|
||||
- verify `docs/ECC-2.0-GA-ROADMAP.md` reflects the current Linear milestone plan
|
||||
- verify `docs/HERMES-SETUP.md` is present
|
||||
- verify `docs/architecture/cross-harness.md` is present
|
||||
- verify this release directory is committed
|
||||
@@ -13,7 +12,6 @@
|
||||
|
||||
- verify package, plugin, marketplace, OpenCode, and agent metadata stays at `2.0.0-rc.1`
|
||||
- verify `ecc2/Cargo.toml` stays at `0.1.0` for rc.1; `ecc2/` remains an alpha control-plane scaffold
|
||||
- complete `publication-readiness.md` with fresh evidence before any GitHub release, npm publish, plugin submission, or announcement post
|
||||
- update release metadata in one dedicated release-version PR
|
||||
- run the root test suite
|
||||
- run `cd ecc2 && cargo test`
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# ECC v2.0.0-rc.1 Publication Readiness
|
||||
|
||||
This checklist is the release gate for public publication surfaces. Do not use
|
||||
it as evidence by itself. Fill the evidence fields with fresh command output or
|
||||
URLs from the exact commit being released.
|
||||
|
||||
## Release Identity Matrix
|
||||
|
||||
| Surface | Expected value | Source of truth | Fresh check | Evidence artifact | Owner | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| Product name | Everything Claude Code / ECC | `README.md`, `CHANGELOG.md`, release notes | `rg -n "Everything Claude Code" README.md CHANGELOG.md docs/releases/2.0.0-rc.1` | Pending | Release owner | Pending |
|
||||
| GitHub repo | `affaan-m/everything-claude-code` | Git remote and release URLs | `git remote get-url origin` | Pending | Release owner | Pending |
|
||||
| Git tag | `v2.0.0-rc.1` | GitHub releases | `gh release view v2.0.0-rc.1 --repo affaan-m/everything-claude-code` | Pending | Release owner | Pending |
|
||||
| npm package | `ecc-universal` | `package.json` | `node -p "require('./package.json').name"` | Pending | Package owner | Pending |
|
||||
| npm version | `2.0.0-rc.1` | `VERSION`, `package.json`, lockfiles | `node -p "require('./package.json').version"` | Pending | Package owner | Pending |
|
||||
| npm dist-tag | `next` for rc, `latest` only for GA | npm registry | `npm view ecc-universal dist-tags --json` | Pending | Package owner | Pending |
|
||||
| Claude plugin slug | `ecc` / `ecc@ecc` install path | `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json` | `node tests/hooks/hooks.test.js` | Pending | Plugin owner | Pending |
|
||||
| Claude plugin manifest | `2.0.0-rc.1`, no unsupported `agents` or explicit `hooks` fields | `.claude-plugin/plugin.json`, `.claude-plugin/PLUGIN_SCHEMA_NOTES.md` | `claude plugin validate .claude-plugin/plugin.json` | Pending | Plugin owner | Pending |
|
||||
| Codex plugin manifest | `2.0.0-rc.1` with shared skill source | `.codex-plugin/plugin.json` | `node tests/docs/ecc2-release-surface.test.js` | Pending | Plugin owner | Pending |
|
||||
| OpenCode package | `ecc-universal` plugin module | `.opencode/package.json`, `.opencode/index.ts` | `npm run build:opencode` | Pending | Package owner | Pending |
|
||||
| Agent metadata | `2.0.0-rc.1` | `agent.yaml`, `.agents/plugins/marketplace.json` | `node tests/scripts/catalog.test.js` | Pending | Release owner | Pending |
|
||||
| Migration copy | rc.1 upgrade path, not GA claim | `release-notes.md`, `quickstart.md`, `HERMES-SETUP.md` | `npx markdownlint-cli docs/releases/2.0.0-rc.1/*.md` | Pending | Docs owner | Pending |
|
||||
|
||||
## Publication Gates
|
||||
|
||||
| Gate | Required evidence | Fresh check | Blocker field | Owner | Status |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| GitHub release | Tag exists, release notes use final URLs, assets attached if needed | `gh release view v2.0.0-rc.1 --json tagName,url,isPrerelease` | `Blocker:` | Release owner | Pending |
|
||||
| npm package | `npm pack --dry-run` has expected files, version matches, rc goes to `next` | `npm pack --dry-run` and `npm publish --tag next --dry-run` where supported | `Blocker:` | Package owner | Pending |
|
||||
| Claude plugin | Manifest validates, marketplace JSON points to public repo, install docs match slug | `claude plugin validate .claude-plugin/plugin.json` | `Blocker:` | Plugin owner | Pending |
|
||||
| Codex plugin | Manifest version matches package and docs, hook limitations are explicit | `node tests/docs/ecc2-release-surface.test.js` | `Blocker:` | Plugin owner | Pending |
|
||||
| OpenCode package | Build output is regenerated from source and package metadata is current | `npm run build:opencode` | `Blocker:` | Package owner | Pending |
|
||||
| ECC Tools billing reference | Any billing claim links to verified Marketplace/App state | `gh api repos/ECC-Tools/ECC-Tools` plus app/marketplace URL check | `Blocker:` | ECC Tools owner | Pending |
|
||||
| Announcement copy | X, LinkedIn, GitHub release, and longform copy point to live URLs | `rg -n "TODO" docs/releases/2.0.0-rc.1` and repeat for `TBD` | `Blocker:` | Release owner | Pending |
|
||||
|
||||
## Required Command Evidence
|
||||
|
||||
Record the exact commit SHA and command output before any publication action:
|
||||
|
||||
| Evidence | Command | Required result | Recorded output |
|
||||
| --- | --- | --- | --- |
|
||||
| Clean release branch | `git status --short --branch` | On intended release commit; no unrelated files | Pending |
|
||||
| Harness audit | `npm run harness:audit -- --format json` | 70/70 passing | Pending |
|
||||
| Adapter scorecard | `npm run harness:adapters -- --check` | PASS | Pending |
|
||||
| Observability readiness | `npm run observability:ready` | 14/14 passing | Pending |
|
||||
| Root suite | `node tests/run-all.js` | 0 failures | Pending |
|
||||
| Markdown lint | `npx markdownlint-cli '**/*.md' --ignore node_modules` | 0 failures | Pending |
|
||||
| Package surface | `node tests/scripts/npm-publish-surface.test.js` | 0 failures | Pending |
|
||||
| Release surface | `node tests/docs/ecc2-release-surface.test.js` | 0 failures | Pending |
|
||||
| Optional Rust surface | `cd ecc2 && cargo test` | 0 failures or explicit deferral | Pending |
|
||||
|
||||
## Do Not Publish If
|
||||
|
||||
- `main` has unreviewed release-surface changes after the evidence was recorded.
|
||||
- `npm view ecc-universal dist-tags --json` contradicts the intended rc/GA tag.
|
||||
- Claude plugin validation is unavailable and no manual install smoke test is
|
||||
recorded.
|
||||
- Release notes or announcement drafts still contain placeholder URLs,
|
||||
`TODO`, `TBD`, private workspace paths, or personal operator references.
|
||||
- Billing, Marketplace, or plugin-submission copy claims a live surface before
|
||||
the live URL exists.
|
||||
- Stale PR salvage work is mid-flight on the same branch.
|
||||
|
||||
## Announcement Order
|
||||
|
||||
1. Merge the release-version PR.
|
||||
2. Record the required command evidence from the release commit.
|
||||
3. Create or verify the GitHub prerelease.
|
||||
4. Publish npm with the rc dist-tag.
|
||||
5. Submit or update plugin marketplace surfaces.
|
||||
6. Update release notes with final live URLs.
|
||||
7. Publish GitHub release copy.
|
||||
8. Publish X, LinkedIn, and longform copy only after the public URLs work.
|
||||
@@ -31,15 +31,6 @@ Expected result: every test passes with zero failures. For release-specific drif
|
||||
node tests/docs/ecc2-release-surface.test.js
|
||||
```
|
||||
|
||||
Then check the local observability surface:
|
||||
|
||||
```bash
|
||||
npm run observability:ready
|
||||
```
|
||||
|
||||
This runs the [observability readiness gate](../../architecture/observability-readiness.md)
|
||||
for loop status, session traces, harness audit, and ECC2 tool-risk logs.
|
||||
|
||||
## First Skill
|
||||
|
||||
Read `skills/hermes-imports/SKILL.md` first.
|
||||
|
||||
@@ -13,7 +13,6 @@ Claude Code remains a core target. Codex, OpenCode, Cursor, Gemini, and other ha
|
||||
- Clarified the split between ECC as the reusable substrate and Hermes as the operator shell.
|
||||
- Documented the cross-harness portability model for skills, hooks, MCPs, rules, and instructions.
|
||||
- Added a Hermes import playbook for turning local operator patterns into publishable ECC skills.
|
||||
- Added a local [observability readiness gate](../../architecture/observability-readiness.md) for loop status, session traces, harness audit, and ECC2 tool-risk logs.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
@@ -51,7 +50,6 @@ What stays local:
|
||||
1. Follow the [rc.1 quickstart](quickstart.md).
|
||||
2. Read the [Hermes setup guide](../../HERMES-SETUP.md).
|
||||
3. Review the [cross-harness architecture](../../architecture/cross-harness.md).
|
||||
4. Run the [observability readiness gate](../../architecture/observability-readiness.md).
|
||||
5. Start with one workflow lane: engineering, research, content, or outreach.
|
||||
6. Import only sanitized operator patterns into ECC skills.
|
||||
7. Treat `ecc2/` as an alpha control plane until release packaging and installer behavior are finalized.
|
||||
4. Start with one workflow lane: engineering, research, content, or outreach.
|
||||
5. Import only sanitized operator patterns into ECC skills.
|
||||
6. Treat `ecc2/` as an alpha control plane until release packaging and installer behavior are finalized.
|
||||
|
||||
+3
-3
@@ -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)
|
||||
**Язык:** [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) | **Русский**
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Язык / 语言 / 語言 / Dil / Ngôn ngữ**
|
||||
**Язык / 语言 / 語言 / 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) | **Русский** | [Tiếng Việt](../vi-VN/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) | **Русский**
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
# Stale PR Salvage Ledger
|
||||
|
||||
This ledger records useful work recovered from stale, conflicted, or closed PRs.
|
||||
The rule is simple: queue cleanup closes stale PRs, but it does not discard
|
||||
useful work. Maintainers should inspect the closed diff, port compatible pieces
|
||||
on fresh branches, and credit the source PR.
|
||||
|
||||
## Classification States
|
||||
|
||||
| State | Meaning |
|
||||
| --- | --- |
|
||||
| Salvaged | Useful work was ported to current `main` through a maintainer PR. |
|
||||
| Already present | Current `main` already contained the useful work before salvage. |
|
||||
| Superseded | Current `main` solved the same problem differently. |
|
||||
| Skipped | The PR was accidental, too broad, unsafe, or too low-signal to port. |
|
||||
| Translator/manual review | Content may be useful, but needs human language/domain review before import. |
|
||||
|
||||
## Salvaged Into Current Main
|
||||
|
||||
| Source PR | Original contribution | Salvage result |
|
||||
| --- | --- | --- |
|
||||
| #1309 | Trading/community project material | Salvaged in #1761 as a neutral community-project README listing. |
|
||||
| #1322 | Vietnamese README translation | Salvaged in #1764 as `docs/vi-VN/README.md` plus selector updates. |
|
||||
| #1326 | Angular developer skill and rules | Salvaged in #1763 with current skill, rules, install wiring, and catalog updates. |
|
||||
| #1328 | Continuous-learning Windows UTF-8 stdout fix | Salvaged in #1761. |
|
||||
| #1329 | Plugin install detection hardening | Salvaged in #1761 through current harness audit detection support. |
|
||||
| #1334 | Windows desktop E2E skill | Salvaged in #1762 with install, package, and catalog wiring. |
|
||||
| #1352 | Qwen install target | Salvaged in #1738 through the current Qwen install target. |
|
||||
| #1413 | Network and homelab skills/agents | Salvaged through #1729, #1731, #1745, and #1778. |
|
||||
| #1429 | JoyCode install target | Salvaged in #1737 through the current JoyCode install target. |
|
||||
| #1467 | Scientific skills and OpenCode discovery work | Useful USPTO and gget pieces salvaged in #1740; stale generated claims were not copied. |
|
||||
| #1493 | SessionStart context scoping | Salvaged in #1774 with current hook semantics and tests. |
|
||||
| #1498 | PRD planning flow | Salvaged in #1777. |
|
||||
| #1504 | Statusline/context monitor hooks | Salvaged in #1776 with current hook manifest structure and tests. |
|
||||
| #1528/#1529/#1547 | Astraflow and UModelVerse provider support | Salvaged in #1775 with current provider wiring and defensive tool-call parsing. |
|
||||
| #1558 | `agentic-os` skill | Salvaged in #1772. |
|
||||
| #1559 | `error-handling` skill | Salvaged in #1772. |
|
||||
| #1566 | Agent architecture audit skill | Salvaged in #1772. |
|
||||
| #1578 | OpenCode file-probe hardening | Salvaged in #1773. |
|
||||
| #1674 | Production audit skill | Salvaged in #1732 after supply-chain/privacy review and rewrite. |
|
||||
| #1687 | zh-CN localization sync | Large safe subsets salvaged in #1746-#1752; remaining pieces require translator/manual review. |
|
||||
| #1694 | Portfolio curation | Useful focused curation updates salvaged in #1723 and #1724. |
|
||||
| #1695 | Russian README translation | Ported in #1722. |
|
||||
| #1697 | Saved LLM selector config | Salvaged as part of provider config/tool schema work in #1720. |
|
||||
| #1699 | Windows post-edit-format path guard | Ported in #1719. |
|
||||
| #1700 | Provider tool serialization | Ported in #1720. |
|
||||
| #1705/#1780 | Production UI motion system | Salvaged in #1772, #1781, and #1782 with examples fixed before merge. |
|
||||
| #1713 | Swift language support | Ported in #1721. |
|
||||
| #1715 | CI personal-path validator hardening | Ported through CI validator hardening in #1717. |
|
||||
| #1727 | MySQL patterns skill | Salvaged in #1733. |
|
||||
| #1757 | Machine-learning engineering workflow | Salvaged in #1758 and tuned in #1759. |
|
||||
|
||||
## Already Present Or Superseded
|
||||
|
||||
| Source PR | Disposition |
|
||||
| --- | --- |
|
||||
| #1306 | Hook bug workarounds already exist on `main` as `docs/hook-bug-workarounds.md`. |
|
||||
| #1318 | Gemini agent adaptation utility was already present on current `main`. |
|
||||
| #1323 | Hook config update was already present on current `main`. |
|
||||
| #1337 | Catalog count update was superseded by current catalog-count sync. |
|
||||
| #1682/#1701 | Strategic compact hook-path fixes were merged directly or superseded by current docs fixes. |
|
||||
| JARVIS #4/#5/#6 | Stale failing dependency-only PRs; future dependency state should be regenerated by Dependabot. |
|
||||
|
||||
## Skipped
|
||||
|
||||
| Source PR | Reason |
|
||||
| --- | --- |
|
||||
| #1308 | Stale zh-CN sync would rewind or delete too much current tree state; concrete selector-link fix was already present. |
|
||||
| #1320 | Package-manager removal conflicts with the current npm/pnpm/yarn/bun CI policy. |
|
||||
| #1341 | Very large low-signal generated change with no safe focused salvage unit. |
|
||||
| #1416/#1465 | Accidental fork-sync PRs with no focused contribution. |
|
||||
| #1475 | One-line Gemini CLI bridge idea was too stale and underspecified to port safely. |
|
||||
|
||||
## Remaining Manual-Review Backlog
|
||||
|
||||
Only the #1687 localization tail remains plausibly useful but unsafe to
|
||||
auto-port.
|
||||
|
||||
Handling rule:
|
||||
|
||||
1. Keep #1687 in translator/manual review.
|
||||
2. Split any future work by surface: agents, commands, top-level docs, release
|
||||
and count surfaces, then skills.
|
||||
3. Do not import stale top-level docs that carry old version or catalog-count
|
||||
facts.
|
||||
4. Do not reopen old PRs unless the original author returns with a current
|
||||
rebase; maintainer-side salvage should happen on fresh branches with
|
||||
attribution.
|
||||
|
||||
## Future Cleanup Rule
|
||||
|
||||
For every stale/conflicted PR cleanup batch:
|
||||
|
||||
1. Close or comment on the PR based on the queue policy.
|
||||
2. Add the source PR to this ledger or a dated successor ledger.
|
||||
3. Classify it as salvaged, already present, superseded, skipped, or
|
||||
translator/manual review.
|
||||
4. If useful, port a small compatible slice on a fresh maintainer branch.
|
||||
5. Credit the source PR and author in the maintainer PR body.
|
||||
+2
-2
@@ -21,9 +21,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Dil / Language / 语言 / 語言 / Язык / Ngôn ngữ**
|
||||
**Dil / Language / 语言 / 語言**
|
||||
|
||||
[**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)
|
||||
[**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)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
**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**
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||

|
||||
|
||||
[](https://github.com/affaan-m/everything-claude-code/stargazers)
|
||||
[](https://github.com/affaan-m/everything-claude-code/network/members)
|
||||
[](https://github.com/affaan-m/everything-claude-code/graphs/contributors)
|
||||
[](https://www.npmjs.com/package/ecc-universal)
|
||||
[](../../LICENSE)
|
||||
|
||||
> **140K+ sao** | **21K+ fork** | **170+ contributor** | **12+ hệ sinh thái ngôn ngữ** | **Anthropic Hackathon Winner**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**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**
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
**Everything Claude Code là hệ thống tối ưu hiệu năng cho AI agent harness.**
|
||||
|
||||
ECC không chỉ là một bộ cấu hình. Repo này đóng gói agents, skills, hooks, rules, MCP config, selective install, kiểm tra bảo mật, và workflow vận hành cho Claude Code, Codex, Cursor, OpenCode, Gemini và các harness agent khác.
|
||||
|
||||
Trang tiếng Việt này là bản onboarding gọn, được phục hồi từ đóng góp cộng đồng trong PR [#1322](https://github.com/affaan-m/everything-claude-code/pull/1322) và cập nhật để khớp mặt cài đặt hiện tại. README tiếng Anh vẫn là nguồn chuẩn đầy đủ nhất.
|
||||
|
||||
---
|
||||
|
||||
## Bắt Đầu Nhanh
|
||||
|
||||
### Chọn một đường cài đặt duy nhất
|
||||
|
||||
Với Claude Code, phần lớn người dùng nên chọn đúng **một** trong hai đường:
|
||||
|
||||
- **Khuyến nghị:** cài plugin Claude Code, sau đó copy thủ công chỉ những thư mục `rules/` bạn thật sự cần.
|
||||
- **Dùng installer thủ công** nếu bạn muốn kiểm soát chi tiết hơn, muốn tránh plugin, hoặc bản Claude Code của bạn không resolve được marketplace tự host.
|
||||
- **Không chồng nhiều cách cài lên nhau.** Cấu hình dễ hỏng nhất là `/plugin install` trước, rồi chạy tiếp `install.sh --profile full` hoặc `npx ecc-install --profile full`.
|
||||
|
||||
Nếu bạn đã cài chồng nhiều lần và thấy skill/hook bị trùng, xem [Reset / Gỡ ECC](#reset--gỡ-ecc).
|
||||
|
||||
### Cài plugin Claude Code
|
||||
|
||||
```bash
|
||||
# Thêm marketplace
|
||||
/plugin marketplace add https://github.com/affaan-m/everything-claude-code
|
||||
|
||||
# Cài plugin
|
||||
/plugin install ecc@ecc
|
||||
```
|
||||
|
||||
ECC có ba định danh công khai khác nhau:
|
||||
|
||||
- Repo GitHub: `affaan-m/everything-claude-code`
|
||||
- Plugin Claude marketplace: `ecc@ecc`
|
||||
- Gói npm: `ecc-universal`
|
||||
|
||||
Các tên này cố ý khác nhau. Plugin Claude Code dùng `ecc@ecc`; npm vẫn dùng `ecc-universal`.
|
||||
|
||||
### Copy rules nếu cần
|
||||
|
||||
Plugin Claude Code không tự phân phối `rules/`. Nếu bạn đã cài bằng plugin, **đừng** chạy thêm full installer. Hãy copy riêng rule pack bạn muốn:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/affaan-m/everything-claude-code.git
|
||||
cd everything-claude-code
|
||||
|
||||
mkdir -p ~/.claude/rules/ecc
|
||||
cp -R rules/common ~/.claude/rules/ecc/
|
||||
cp -R rules/typescript ~/.claude/rules/ecc/
|
||||
```
|
||||
|
||||
```powershell
|
||||
git clone https://github.com/affaan-m/everything-claude-code.git
|
||||
cd everything-claude-code
|
||||
|
||||
New-Item -ItemType Directory -Force -Path "$HOME/.claude/rules/ecc" | Out-Null
|
||||
Copy-Item -Recurse rules/common "$HOME/.claude/rules/ecc/"
|
||||
Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/ecc/"
|
||||
```
|
||||
|
||||
Copy cả thư mục ngôn ngữ, ví dụ `rules/common` hoặc `rules/golang`, thay vì copy từng file riêng lẻ.
|
||||
|
||||
### Cài thủ công nếu không dùng plugin
|
||||
|
||||
Chỉ dùng đường này nếu bạn cố ý bỏ qua plugin:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
./install.sh --profile full
|
||||
```
|
||||
|
||||
```powershell
|
||||
npm install
|
||||
.\install.ps1 --profile full
|
||||
# hoặc
|
||||
npx ecc-install --profile full
|
||||
```
|
||||
|
||||
Nếu chọn đường thủ công, dừng ở đó. Đừng chạy thêm `/plugin install`.
|
||||
|
||||
### Đường low-context / không hooks
|
||||
|
||||
Nếu bạn chỉ muốn rules, agents, commands và core workflow skills, dùng profile tối thiểu:
|
||||
|
||||
```bash
|
||||
./install.sh --profile minimal --target claude
|
||||
```
|
||||
|
||||
```powershell
|
||||
.\install.ps1 --profile minimal --target claude
|
||||
# hoặc
|
||||
npx ecc-install --profile minimal --target claude
|
||||
```
|
||||
|
||||
Profile này cố ý không cài `hooks-runtime`.
|
||||
|
||||
---
|
||||
|
||||
## Reset / Gỡ ECC
|
||||
|
||||
Nếu ECC bị trùng, quá xâm lấn, hoặc hoạt động sai, đừng tiếp tục cài đè lên chính nó.
|
||||
|
||||
- **Đường plugin:** gỡ plugin trong Claude Code, rồi xoá các rule folder bạn đã copy thủ công dưới `~/.claude/rules/ecc/`.
|
||||
- **Đường installer/CLI:** từ root repo, preview trước:
|
||||
|
||||
```bash
|
||||
node scripts/uninstall.js --dry-run
|
||||
```
|
||||
|
||||
Sau đó gỡ các file do ECC quản lý:
|
||||
|
||||
```bash
|
||||
node scripts/uninstall.js
|
||||
```
|
||||
|
||||
Bạn cũng có thể dùng lifecycle wrapper:
|
||||
|
||||
```bash
|
||||
node scripts/ecc.js list-installed
|
||||
node scripts/ecc.js doctor
|
||||
node scripts/ecc.js repair
|
||||
node scripts/ecc.js uninstall --dry-run
|
||||
```
|
||||
|
||||
ECC chỉ xoá file có trong install-state của nó. Nó không xoá file không liên quan.
|
||||
|
||||
---
|
||||
|
||||
## Tài Liệu Quan Trọng
|
||||
|
||||
- [README tiếng Anh](../../README.md) - nguồn chuẩn đầy đủ nhất
|
||||
- [Hướng dẫn Hermes](../HERMES-SETUP.md)
|
||||
- [Release notes v2.0.0-rc.1](../releases/2.0.0-rc.1/release-notes.md)
|
||||
- [Kiến trúc cross-harness](../architecture/cross-harness.md)
|
||||
- [Troubleshooting](../TROUBLESHOOTING.md)
|
||||
- [Hook bug workarounds](../hook-bug-workarounds.md)
|
||||
|
||||
---
|
||||
|
||||
## Dùng Thử
|
||||
|
||||
```bash
|
||||
# Plugin install dùng namespace đầy đủ
|
||||
/ecc:plan "Thêm xác thực người dùng"
|
||||
|
||||
# Manual install giữ dạng slash ngắn
|
||||
# /plan "Thêm xác thực người dùng"
|
||||
|
||||
# Xem plugin đang cài
|
||||
/plugin list ecc@ecc
|
||||
```
|
||||
|
||||
ECC hiện cung cấp hàng chục agent, hơn 200 skill và legacy command shim cho các workflow agent khác nhau. Kiểm tra README tiếng Anh để xem danh sách và hướng dẫn chi tiết nhất.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Everything Claude Code (ECC) — 智能体指令
|
||||
|
||||
这是一个**生产就绪的 AI 编码插件**,提供 58 个专业代理、220 项技能、74 条命令以及自动化钩子工作流,用于软件开发。
|
||||
这是一个**生产就绪的 AI 编码插件**,提供 54 个专业代理、204 项技能、69 条命令以及自动化钩子工作流,用于软件开发。
|
||||
|
||||
**版本:** 2.0.0-rc.1
|
||||
|
||||
@@ -146,9 +146,9 @@
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
agents/ — 58 个专业子代理
|
||||
skills/ — 220 个工作流技能和领域知识
|
||||
commands/ — 74 个斜杠命令
|
||||
agents/ — 54 个专业子代理
|
||||
skills/ — 204 个工作流技能和领域知识
|
||||
commands/ — 69 个斜杠命令
|
||||
hooks/ — 基于触发的自动化
|
||||
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
||||
scripts/ — 跨平台 Node.js 实用工具
|
||||
|
||||
+10
-10
@@ -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)
|
||||
**语言:** [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)
|
||||
|
||||
# Everything Claude Code
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**语言 / Language / 語言 / Dil / Язык / 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](../vi-VN/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)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -224,7 +224,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
||||
/plugin list ecc@ecc
|
||||
```
|
||||
|
||||
**搞定!** 你现在可以使用 58 个智能体、220 项技能和 74 个命令了。
|
||||
**搞定!** 你现在可以使用 54 个智能体、204 项技能和 69 个命令了。
|
||||
|
||||
***
|
||||
|
||||
@@ -1132,9 +1132,9 @@ opencode
|
||||
|
||||
| 功能特性 | Claude Code | OpenCode | 状态 |
|
||||
|---------|-------------|----------|--------|
|
||||
| 智能体 | PASS: 58 个 | PASS: 12 个 | **Claude Code 领先** |
|
||||
| 命令 | PASS: 74 个 | PASS: 35 个 | **Claude Code 领先** |
|
||||
| 技能 | PASS: 220 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||
| 智能体 | PASS: 54 个 | PASS: 12 个 | **Claude Code 领先** |
|
||||
| 命令 | PASS: 69 个 | PASS: 31 个 | **Claude Code 领先** |
|
||||
| 技能 | PASS: 204 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
||||
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
||||
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
||||
@@ -1240,9 +1240,9 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|
||||
|
||||
| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||
|---------|------------|------------|-----------|----------|
|
||||
| **智能体** | 58 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||
| **命令** | 74 | 共享 | 基于指令 | 35 |
|
||||
| **技能** | 220 | 共享 | 10 (原生格式) | 37 |
|
||||
| **智能体** | 54 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||
| **命令** | 69 | 共享 | 基于指令 | 31 |
|
||||
| **技能** | 204 | 共享 | 10 (原生格式) | 37 |
|
||||
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
||||
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
||||
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
**Language / 语言 / 語言 / Dil**
|
||||
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](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)
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -908,10 +908,11 @@ pub async fn drain_inbox(
|
||||
use_worktree: bool,
|
||||
limit: usize,
|
||||
) -> Result<Vec<InboxDrainOutcome>> {
|
||||
let repo_root =
|
||||
std::env::current_dir().context("Failed to resolve current working directory")?;
|
||||
let runner_program =
|
||||
std::env::current_exe().context("Failed to resolve ECC executable path")?;
|
||||
let lead = resolve_session(db, lead_id)?;
|
||||
let repo_root = lead.working_dir.clone();
|
||||
let messages = db.unread_task_handoffs_for_session(&lead.id, limit)?;
|
||||
let mut outcomes = Vec::new();
|
||||
|
||||
@@ -1056,10 +1057,11 @@ pub async fn rebalance_team_backlog(
|
||||
use_worktree: bool,
|
||||
limit: usize,
|
||||
) -> Result<Vec<RebalanceOutcome>> {
|
||||
let repo_root =
|
||||
std::env::current_dir().context("Failed to resolve current working directory")?;
|
||||
let runner_program =
|
||||
std::env::current_exe().context("Failed to resolve ECC executable path")?;
|
||||
let lead = resolve_session(db, lead_id)?;
|
||||
let repo_root = lead.working_dir.clone();
|
||||
let mut outcomes = Vec::new();
|
||||
|
||||
if limit == 0 {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
# HarmonyOS App Project CLAUDE.md
|
||||
|
||||
This is a project-level CLAUDE.md example for HarmonyOS applications. Place it at your project root.
|
||||
|
||||
## Project Overview
|
||||
|
||||
[Briefly describe your app - features, target devices, API level]
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Tech Stack Constraints
|
||||
|
||||
- Platform: HarmonyOS (ArkTS/TypeScript), prefer latest stable official APIs
|
||||
- State Management: **V2 only** (`@ComponentV2`, `@Local`, `@Param`, `@Event`, `@Provider`, `@Consumer`, `@Monitor`, `@Computed`)
|
||||
- Routing: **Navigation only** (`Navigation` + `NavPathStack` + `NavDestination`)
|
||||
- Architecture: MVVM with modular layers - View renders only, all business logic in ViewModel
|
||||
- Component priority: in-module reusable components > cross-module shared components > third-party libraries
|
||||
|
||||
### 2. Code Organization
|
||||
|
||||
- Prefer many small files over few large files
|
||||
- High cohesion, low coupling
|
||||
- Target 200-400 lines per file, max 800 lines
|
||||
- Organize by feature/domain, not by type
|
||||
|
||||
### 3. Code Style
|
||||
|
||||
- No emojis in code, comments, or documentation
|
||||
- Immutability - never mutate objects directly
|
||||
- Double quotes for strings; semicolons required
|
||||
- Never use `var` - prefer `const`, then `let`
|
||||
- No `any` type - complete type annotations for all methods, parameters, return values
|
||||
- Naming: `camelCase` for variables/functions, `PascalCase` for classes/interfaces, `UPPER_SNAKE_CASE` for constants
|
||||
- File header: `@file` + `@author`; all methods need JSDoc with `@param` and `@returns`
|
||||
|
||||
### 4. Layout & Interaction
|
||||
|
||||
- Use `layoutWeight(1)` for even distribution - avoid `SpaceAround`/`SpaceBetween`
|
||||
- Use percentages / layout weights / adaptive units - no hardcoded fixed dimensions (except icons)
|
||||
- Define UI constants as resources, reference via `$r()`
|
||||
- Support both light and dark themes for new color resources
|
||||
|
||||
### 5. Build & Validation
|
||||
|
||||
```bash
|
||||
# Build HAP package
|
||||
hvigorw assembleHap -p product=default
|
||||
```
|
||||
|
||||
- Run build after every implementation to verify compilation
|
||||
- Refer to official Huawei developer docs for uncertain API usage - never guess
|
||||
|
||||
### 6. Testing
|
||||
|
||||
- TDD: write tests first
|
||||
- Unit tests for utility functions and ViewModels
|
||||
- UI tests for critical user flows
|
||||
- Minimum 80% coverage for business logic
|
||||
|
||||
### 7. Security
|
||||
|
||||
- No hardcoded secrets
|
||||
- Verify permissions in `module.json5` before using system APIs
|
||||
- Validate all user input
|
||||
- Use HTTPS for all network requests
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
src/
|
||||
|-- entry/ # App entry, framework initialization
|
||||
|-- core/ # Core framework layer
|
||||
|-- shared/ # Shared contracts layer
|
||||
|-- packages/ # Business feature packages
|
||||
```
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/plan` - Create implementation plan
|
||||
- `/code-review` - Code quality review
|
||||
- `/build-fix` - Fix build errors
|
||||
|
||||
## Git Workflow
|
||||
|
||||
- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
|
||||
- No direct commits to main branch
|
||||
- PRs require review
|
||||
- All tests must pass before merge
|
||||
@@ -1,20 +1,19 @@
|
||||
{
|
||||
"statusLine": {
|
||||
"type": "command",
|
||||
"command": "node \"<plugin-root>/scripts/hooks/ecc-statusline.js\"",
|
||||
"description": "ECC statusline: model | task | $cost tools files duration | dir | context bar"
|
||||
"command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo",
|
||||
"description": "Custom status line showing: user:path branch* ctx:% model time todos:N"
|
||||
},
|
||||
"_comments": {
|
||||
"setup": "Replace <plugin-root> with your ECC installation path. For plugin installs, use the resolved path from CLAUDE_PLUGIN_ROOT.",
|
||||
"display": "Shows model name, current task, session cost, tool count, files modified, session duration, directory, and context usage bar with color thresholds.",
|
||||
"colors": {
|
||||
"green": "Context used < 50%",
|
||||
"yellow": "Context used < 65%",
|
||||
"orange": "Context used < 80%",
|
||||
"red_blink": "Context used >= 80%"
|
||||
"B": "Blue - directory path",
|
||||
"G": "Green - git branch",
|
||||
"Y": "Yellow - dirty status, time",
|
||||
"M": "Magenta - context remaining",
|
||||
"C": "Cyan - username, todos",
|
||||
"T": "Gray - model name"
|
||||
},
|
||||
"output_example": "Opus 4.6 | Fixing auth bug | $1.23 47t 5f 15m | myproject ███████░░░ 68%",
|
||||
"dependencies": "Reads bridge file from ecc-metrics-bridge.js PostToolUse hook. Both must be installed for full metrics display.",
|
||||
"output_example": "affoon:~/projects/myapp main* ctx:73% sonnet-4.6 14:30 todos:3",
|
||||
"usage": "Copy the statusLine object to your ~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes
|
||||
|
||||
## Hooks in This Plugin
|
||||
|
||||
Memory persistence lifecycle definitions live in `hooks/memory-persistence/`.
|
||||
The executable hook graph remains `hooks/hooks.json`; the memory persistence directory is the stable contract for SessionStart, PreCompact, observation, activity tracking, and SessionEnd behavior.
|
||||
|
||||
## Installing These Hooks Manually
|
||||
|
||||
For Claude Code manual installs, do not paste the raw repo `hooks.json` into `~/.claude/settings.json` or copy it directly into `~/.claude/hooks/hooks.json`. The checked-in file is plugin/repo-oriented and is meant to be installed through the ECC installer or loaded as a plugin.
|
||||
|
||||
@@ -219,30 +219,6 @@
|
||||
],
|
||||
"description": "Capture tool use results for continuous learning",
|
||||
"id": "post:observe:continuous-learning"
|
||||
},
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node -e \"const p=require('path');const r=(()=>{var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var p=require('path'),f=require('fs'),h=require('os').homedir(),d=p.join(h,'.claude'),q=p.join('scripts','lib','utils.js');if(f.existsSync(p.join(d,q)))return d;for(var s of [[\\\"ecc\\\"],[\\\"ecc@ecc\\\"],[\\\"marketplaces\\\",\\\"ecc\\\"],[\\\"everything-claude-code\\\"],[\\\"everything-claude-code@everything-claude-code\\\"],[\\\"marketplaces\\\",\\\"everything-claude-code\\\"]]){var l=p.join(d,'plugins',...s);if(f.existsSync(p.join(l,q)))return l}try{for(var g of [\\\"ecc\\\",\\\"everything-claude-code\\\"]){var b=p.join(d,'plugins','cache',g);for(var o of f.readdirSync(b,{withFileTypes:true})){if(!o.isDirectory())continue;for(var v of f.readdirSync(p.join(b,o.name),{withFileTypes:true})){if(!v.isDirectory())continue;var c=p.join(b,o.name,v.name);if(f.existsSync(p.join(c,q)))return c}}}}catch(x){}return d})();const s=p.join(r,'scripts/hooks/plugin-hook-bootstrap.js');process.env.CLAUDE_PLUGIN_ROOT=r;process.argv.splice(1,0,s);require(s)\" node scripts/hooks/run-with-flags.js post:ecc-metrics-bridge scripts/hooks/ecc-metrics-bridge.js minimal,standard,strict",
|
||||
"timeout": 10
|
||||
}
|
||||
],
|
||||
"description": "Maintain running session metrics aggregate for statusline and context monitor",
|
||||
"id": "post:ecc-metrics-bridge"
|
||||
},
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node -e \"const p=require('path');const r=(()=>{var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var p=require('path'),f=require('fs'),h=require('os').homedir(),d=p.join(h,'.claude'),q=p.join('scripts','lib','utils.js');if(f.existsSync(p.join(d,q)))return d;for(var s of [[\\\"ecc\\\"],[\\\"ecc@ecc\\\"],[\\\"marketplaces\\\",\\\"ecc\\\"],[\\\"everything-claude-code\\\"],[\\\"everything-claude-code@everything-claude-code\\\"],[\\\"marketplaces\\\",\\\"everything-claude-code\\\"]]){var l=p.join(d,'plugins',...s);if(f.existsSync(p.join(l,q)))return l}try{for(var g of [\\\"ecc\\\",\\\"everything-claude-code\\\"]){var b=p.join(d,'plugins','cache',g);for(var o of f.readdirSync(b,{withFileTypes:true})){if(!o.isDirectory())continue;for(var v of f.readdirSync(p.join(b,o.name),{withFileTypes:true})){if(!v.isDirectory())continue;var c=p.join(b,o.name,v.name);if(f.existsSync(p.join(c,q)))return c}}}}catch(x){}return d})();const s=p.join(r,'scripts/hooks/plugin-hook-bootstrap.js');process.env.CLAUDE_PLUGIN_ROOT=r;process.argv.splice(1,0,s);require(s)\" node scripts/hooks/run-with-flags.js post:ecc-context-monitor scripts/hooks/ecc-context-monitor.js standard,strict",
|
||||
"timeout": 10
|
||||
}
|
||||
],
|
||||
"description": "Inject agent warnings on context exhaustion, high cost, scope creep, or tool loops",
|
||||
"id": "post:ecc-context-monitor"
|
||||
}
|
||||
],
|
||||
"PostToolUseFailure": [
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# Memory Persistence Hooks
|
||||
|
||||
These lifecycle hook definitions document ECC's memory persistence contract for Claude Code plugin and manual installs.
|
||||
|
||||
The executable implementations live in `scripts/hooks/`:
|
||||
|
||||
- `session-start.js` loads bounded prior context, detects project state, and prepares session metadata.
|
||||
- `pre-compact.js` captures state before context compaction.
|
||||
- `session-end.js` persists session-end summaries when transcript metadata is available.
|
||||
- `observe-runner.js` records tool-use observations for continuous learning.
|
||||
- `session-activity-tracker.js` records tool usage and file activity for ECC2 status and observability.
|
||||
|
||||
The installed hook graph is still `hooks/hooks.json`. This directory is the stable, human-readable lifecycle definition surface referenced by the harness audit and longform docs.
|
||||
|
||||
## Lifecycle Contract
|
||||
|
||||
| Event | Hook | Purpose | Blocking |
|
||||
|---|---|---|---|
|
||||
| `SessionStart` | `session:start` | Load bounded prior context and project metadata | no |
|
||||
| `PreCompact` | `pre:compact` | Save state before compaction | no |
|
||||
| `PreToolUse` | `pre:observe:continuous-learning` | Capture tool intent for learning signals | no |
|
||||
| `PostToolUse` | `post:observe:continuous-learning` | Capture tool result for learning signals | no |
|
||||
| `PostToolUse` | `post:session-activity-tracker` | Record tool and file activity for ECC2 metrics | no |
|
||||
| `Stop` | `stop:format-typecheck` | Batch quality gate after edits | yes on hook failure |
|
||||
| `Stop` | `stop:check-console-log` | Audit modified files for debug logging | warn/error by hook output |
|
||||
|
||||
## Operator Expectations
|
||||
|
||||
- Keep persistence local by default.
|
||||
- Avoid sending transcripts or tool traces to hosted services unless a user explicitly enables an integration.
|
||||
- Bound context loaded at session start with `ECC_SESSION_START_MAX_CHARS`.
|
||||
- Allow opt-out with `ECC_SESSION_START_CONTEXT=off`.
|
||||
- Keep lifecycle hooks profile-gated through `ECC_HOOK_PROFILE` and `ECC_DISABLED_HOOKS`.
|
||||
|
||||
## Related Files
|
||||
|
||||
- `hooks/hooks.json`
|
||||
- `hooks/README.md`
|
||||
- `scripts/hooks/session-start.js`
|
||||
- `scripts/hooks/pre-compact.js`
|
||||
- `scripts/hooks/session-end.js`
|
||||
- `scripts/hooks/observe-runner.js`
|
||||
- `scripts/hooks/session-activity-tracker.js`
|
||||
- `docs/architecture/observability-readiness.md`
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"description": "Reference lifecycle hook definitions for ECC memory persistence. The production hook graph is hooks/hooks.json.",
|
||||
"events": [
|
||||
{
|
||||
"event": "SessionStart",
|
||||
"id": "session:start",
|
||||
"script": "scripts/hooks/session-start-bootstrap.js",
|
||||
"purpose": "Load bounded prior context and detect project state at session start.",
|
||||
"blocking": false
|
||||
},
|
||||
{
|
||||
"event": "PreCompact",
|
||||
"id": "pre:compact",
|
||||
"script": "scripts/hooks/pre-compact.js",
|
||||
"purpose": "Persist session state before context compaction.",
|
||||
"blocking": false
|
||||
},
|
||||
{
|
||||
"event": "PreToolUse",
|
||||
"id": "pre:observe:continuous-learning",
|
||||
"script": "scripts/hooks/observe-runner.js",
|
||||
"purpose": "Record tool intent for continuous learning signals.",
|
||||
"blocking": false
|
||||
},
|
||||
{
|
||||
"event": "PostToolUse",
|
||||
"id": "post:observe:continuous-learning",
|
||||
"script": "scripts/hooks/observe-runner.js",
|
||||
"purpose": "Record tool results for continuous learning signals.",
|
||||
"blocking": false
|
||||
},
|
||||
{
|
||||
"event": "PostToolUse",
|
||||
"id": "post:session-activity-tracker",
|
||||
"script": "scripts/hooks/session-activity-tracker.js",
|
||||
"purpose": "Record per-session tool calls and file activity for ECC2 metrics.",
|
||||
"blocking": false
|
||||
},
|
||||
{
|
||||
"event": "SessionEnd",
|
||||
"id": "session:end",
|
||||
"script": "scripts/hooks/session-end.js",
|
||||
"purpose": "Persist session-end summaries when transcript metadata is available.",
|
||||
"blocking": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -81,14 +81,6 @@
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "framework:angular",
|
||||
"family": "framework",
|
||||
"description": "Angular-focused engineering guidance and rules. Currently resolves through the shared framework-language module.",
|
||||
"modules": [
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "framework:react",
|
||||
"family": "framework",
|
||||
@@ -121,15 +113,6 @@
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "framework:quarkus",
|
||||
"family": "framework",
|
||||
"description": "Quarkus-focused engineering guidance for REST, Panache, security, testing, and verification.",
|
||||
"modules": [
|
||||
"framework-language",
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "capability:database",
|
||||
"family": "capability",
|
||||
@@ -226,14 +209,6 @@
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lang:arkts",
|
||||
"family": "language",
|
||||
"description": "HarmonyOS, ArkTS, and ArkUI development guidance including V2 state management, Navigation routing, and HarmonyOS API best practices.",
|
||||
"modules": [
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lang:perl",
|
||||
"family": "language",
|
||||
@@ -259,14 +234,6 @@
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lang:fsharp",
|
||||
"family": "language",
|
||||
"description": "F# functional patterns and testing guidance. Currently resolves through the shared framework-language module.",
|
||||
"modules": [
|
||||
"framework-language"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "framework:laravel",
|
||||
"family": "framework",
|
||||
@@ -372,22 +339,6 @@
|
||||
"agents-core"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "agent:harmonyos-app-resolver",
|
||||
"family": "agent",
|
||||
"description": "HarmonyOS application development expert agent.",
|
||||
"modules": [
|
||||
"agents-core"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "agent:fsharp-reviewer",
|
||||
"family": "agent",
|
||||
"description": "F# code review agent for functional idioms, type safety, and .NET testing.",
|
||||
"modules": [
|
||||
"agents-core"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "agent:refactor-cleaner",
|
||||
"family": "agent",
|
||||
@@ -444,14 +395,6 @@
|
||||
"workflow-quality"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skill:windows-desktop-e2e",
|
||||
"family": "skill",
|
||||
"description": "E2E testing for Windows native desktop apps with pywinauto and Windows UI Automation.",
|
||||
"modules": [
|
||||
"workflow-quality"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skill:strategic-compact",
|
||||
"family": "skill",
|
||||
|
||||
@@ -122,13 +122,11 @@
|
||||
"description": "Core framework, language, and application-engineering skills.",
|
||||
"paths": [
|
||||
"skills/android-clean-architecture",
|
||||
"skills/angular-developer",
|
||||
"skills/api-design",
|
||||
"skills/backend-patterns",
|
||||
"skills/coding-standards",
|
||||
"skills/compose-multiplatform-patterns",
|
||||
"skills/csharp-testing",
|
||||
"skills/fsharp-testing",
|
||||
"skills/cpp-coding-standards",
|
||||
"skills/cpp-testing",
|
||||
"skills/dart-flutter-patterns",
|
||||
@@ -139,7 +137,6 @@
|
||||
"skills/fastapi-patterns",
|
||||
"skills/frontend-patterns",
|
||||
"skills/frontend-slides",
|
||||
"skills/motion-ui",
|
||||
"skills/golang-patterns",
|
||||
"skills/golang-testing",
|
||||
"skills/java-coding-standards",
|
||||
@@ -158,9 +155,6 @@
|
||||
"skills/perl-testing",
|
||||
"skills/python-patterns",
|
||||
"skills/python-testing",
|
||||
"skills/quarkus-patterns",
|
||||
"skills/quarkus-tdd",
|
||||
"skills/quarkus-verification",
|
||||
"skills/rust-patterns",
|
||||
"skills/rust-testing",
|
||||
"skills/springboot-patterns",
|
||||
@@ -230,7 +224,6 @@
|
||||
"skills/continuous-learning-v2",
|
||||
"skills/council",
|
||||
"skills/e2e-testing",
|
||||
"skills/error-handling",
|
||||
"skills/eval-harness",
|
||||
"skills/hookify-rules",
|
||||
"skills/iterative-retrieval",
|
||||
@@ -239,8 +232,7 @@
|
||||
"skills/skill-stocktake",
|
||||
"skills/strategic-compact",
|
||||
"skills/tdd-workflow",
|
||||
"skills/verification-loop",
|
||||
"skills/windows-desktop-e2e"
|
||||
"skills/verification-loop"
|
||||
],
|
||||
"targets": [
|
||||
"claude",
|
||||
@@ -272,7 +264,6 @@
|
||||
"skills/llm-trading-agent-security",
|
||||
"skills/nodejs-keccak256",
|
||||
"skills/perl-security",
|
||||
"skills/quarkus-security",
|
||||
"skills/security-review",
|
||||
"skills/security-scan",
|
||||
"skills/security-bounty-hunter",
|
||||
@@ -515,10 +506,8 @@
|
||||
"kind": "skills",
|
||||
"description": "Agentic engineering, autonomous loops, agent harness construction, and LLM pipeline optimization skills.",
|
||||
"paths": [
|
||||
"skills/agent-architecture-audit",
|
||||
"skills/agent-harness-construction",
|
||||
"skills/agentic-engineering",
|
||||
"skills/agentic-os",
|
||||
"skills/ai-first-engineering",
|
||||
"skills/autonomous-loops",
|
||||
"skills/blueprint",
|
||||
@@ -598,9 +587,7 @@
|
||||
"antigravity",
|
||||
"codex",
|
||||
"opencode",
|
||||
"codebuddy",
|
||||
"joycode",
|
||||
"qwen"
|
||||
"codebuddy"
|
||||
],
|
||||
"dependencies": [
|
||||
"framework-language",
|
||||
|
||||
@@ -41,11 +41,6 @@
|
||||
"args": ["omega-memory", "serve"],
|
||||
"description": "Persistent agent memory with semantic search, multi-agent coordination, and knowledge graphs — run via uvx (richer than the basic memory store)"
|
||||
},
|
||||
"longhand": {
|
||||
"command": "longhand",
|
||||
"args": ["mcp-server"],
|
||||
"description": "Lossless Claude Code session history — indexes raw tool calls, file edits, and thinking blocks from ~/.claude/projects/*.jsonl into local SQLite + ChromaDB before Claude Code rotates them. Complements memory/omega-memory (synthesized) with verbatim recall. Install: pip install longhand && longhand setup"
|
||||
},
|
||||
"sequential-thinking": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking"],
|
||||
|
||||
@@ -71,9 +71,7 @@
|
||||
"scripts/doctor.js",
|
||||
"scripts/ecc.js",
|
||||
"scripts/gemini-adapt-agents.js",
|
||||
"scripts/harness-adapter-compliance.js",
|
||||
"scripts/harness-audit.js",
|
||||
"scripts/observability-readiness.js",
|
||||
"scripts/hooks/",
|
||||
"scripts/install-apply.js",
|
||||
"scripts/install-plan.js",
|
||||
@@ -91,16 +89,13 @@
|
||||
"scripts/status.js",
|
||||
"scripts/work-items.js",
|
||||
"scripts/uninstall.js",
|
||||
"skills/agent-architecture-audit/",
|
||||
"skills/agent-harness-construction/",
|
||||
"skills/agent-introspection-debugging/",
|
||||
"skills/agent-sort/",
|
||||
"skills/agentic-engineering/",
|
||||
"skills/agentic-os/",
|
||||
"skills/ai-first-engineering/",
|
||||
"skills/ai-regression-testing/",
|
||||
"skills/android-clean-architecture/",
|
||||
"skills/angular-developer/",
|
||||
"skills/api-connector-builder/",
|
||||
"skills/api-design/",
|
||||
"skills/article-writing/",
|
||||
@@ -150,7 +145,6 @@
|
||||
"skills/email-ops/",
|
||||
"skills/energy-procurement/",
|
||||
"skills/enterprise-agent-ops/",
|
||||
"skills/error-handling/",
|
||||
"skills/eval-harness/",
|
||||
"skills/evm-token-decimals/",
|
||||
"skills/exa-search/",
|
||||
@@ -160,7 +154,6 @@
|
||||
"skills/foundation-models-on-device/",
|
||||
"skills/frontend-patterns/",
|
||||
"skills/frontend-slides/",
|
||||
"skills/fsharp-testing/",
|
||||
"skills/github-ops/",
|
||||
"skills/golang-patterns/",
|
||||
"skills/golang-testing/",
|
||||
@@ -197,7 +190,6 @@
|
||||
"skills/mcp-server-patterns/",
|
||||
"skills/messages-ops/",
|
||||
"skills/mle-workflow/",
|
||||
"skills/motion-ui/",
|
||||
"skills/mysql-patterns/",
|
||||
"skills/nanoclaw-repl/",
|
||||
"skills/nestjs-patterns/",
|
||||
@@ -220,10 +212,6 @@
|
||||
"skills/python-patterns/",
|
||||
"skills/python-testing/",
|
||||
"skills/quality-nonconformance/",
|
||||
"skills/quarkus-patterns/",
|
||||
"skills/quarkus-security/",
|
||||
"skills/quarkus-tdd/",
|
||||
"skills/quarkus-verification/",
|
||||
"skills/ralphinho-rfc-pipeline/",
|
||||
"skills/regex-vs-llm-structured-text/",
|
||||
"skills/remotion-video-creation/",
|
||||
@@ -263,7 +251,6 @@
|
||||
"skills/video-editing/",
|
||||
"skills/videodb/",
|
||||
"skills/visa-doc-translate/",
|
||||
"skills/windows-desktop-e2e/",
|
||||
"skills/workspace-surface-audit/",
|
||||
"skills/x-api/",
|
||||
"the-security-guide.md"
|
||||
@@ -277,9 +264,7 @@
|
||||
"catalog:check": "node scripts/ci/catalog.js --text",
|
||||
"catalog:sync": "node scripts/ci/catalog.js --write --text",
|
||||
"lint": "eslint . && markdownlint '**/*.md' --ignore node_modules",
|
||||
"harness:adapters": "node scripts/harness-adapter-compliance.js",
|
||||
"harness:audit": "node scripts/harness-audit.js",
|
||||
"observability:ready": "node scripts/observability-readiness.js",
|
||||
"claw": "node scripts/claw.js",
|
||||
"orchestrate:status": "node scripts/orchestration-status.js",
|
||||
"orchestrate:worker": "bash scripts/orchestrate-codex-worker.sh",
|
||||
|
||||
+1
-7
@@ -15,13 +15,11 @@ rules/
|
||||
│ ├── agents.md
|
||||
│ └── security.md
|
||||
├── typescript/ # TypeScript/JavaScript specific
|
||||
├── angular/ # Angular specific
|
||||
├── python/ # Python specific
|
||||
├── golang/ # Go specific
|
||||
├── web/ # Web and frontend specific
|
||||
├── swift/ # Swift specific
|
||||
├── php/ # PHP specific
|
||||
└── arkts/ # HarmonyOS / ArkTS specific
|
||||
└── php/ # PHP specific
|
||||
```
|
||||
|
||||
- **common/** contains universal principles — no language-specific code examples.
|
||||
@@ -34,13 +32,11 @@ rules/
|
||||
```bash
|
||||
# Install common + one or more language-specific rule sets
|
||||
./install.sh typescript
|
||||
./install.sh angular
|
||||
./install.sh python
|
||||
./install.sh golang
|
||||
./install.sh web
|
||||
./install.sh swift
|
||||
./install.sh php
|
||||
./install.sh arkts
|
||||
|
||||
# Install multiple languages at once
|
||||
./install.sh typescript python
|
||||
@@ -60,13 +56,11 @@ cp -r rules/common ~/.claude/rules/common
|
||||
|
||||
# Install language-specific rules based on your project's tech stack
|
||||
cp -r rules/typescript ~/.claude/rules/typescript
|
||||
cp -r rules/angular ~/.claude/rules/angular
|
||||
cp -r rules/python ~/.claude/rules/python
|
||||
cp -r rules/golang ~/.claude/rules/golang
|
||||
cp -r rules/web ~/.claude/rules/web
|
||||
cp -r rules/swift ~/.claude/rules/swift
|
||||
cp -r rules/php ~/.claude/rules/php
|
||||
cp -r rules/arkts ~/.claude/rules/arkts
|
||||
|
||||
# Attention ! ! ! Configure according to your actual project requirements; the configuration here is for reference only.
|
||||
```
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.directive.ts"
|
||||
- "**/*.pipe.ts"
|
||||
- "**/*.guard.ts"
|
||||
- "**/*.resolver.ts"
|
||||
- "**/*.module.ts"
|
||||
---
|
||||
# Angular Coding Style
|
||||
|
||||
> This file extends [common/coding-style.md](../common/coding-style.md) with Angular specific content.
|
||||
|
||||
## Version Awareness
|
||||
|
||||
Always check the project's Angular version before writing code — features differ significantly between versions. Run `ng version` or inspect `package.json`. When creating a new project, do not pin a version unless the user specifies one.
|
||||
|
||||
After generating or modifying Angular code, always run `ng build` to catch errors before finishing.
|
||||
|
||||
## File Naming
|
||||
|
||||
Follow Angular CLI conventions — one artifact per file:
|
||||
|
||||
- `user-profile.component.ts` + `user-profile.component.html` + `user-profile.component.spec.ts`
|
||||
- `user.service.ts`, `auth.guard.ts`, `date-format.pipe.ts`
|
||||
- Feature folders: `features/users/`, `features/auth/`
|
||||
- Generate with the CLI: `ng generate component features/users/user-card`
|
||||
|
||||
## Components
|
||||
|
||||
Prefer standalone components (v17+ default). Use `OnPush` change detection on all new components.
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'app-user-card',
|
||||
standalone: true,
|
||||
imports: [RouterModule],
|
||||
templateUrl: './user-card.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UserCardComponent {
|
||||
user = input.required<User>();
|
||||
select = output<string>();
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Use `inject()` over constructor injection. Keep constructors empty or remove them entirely.
|
||||
|
||||
```typescript
|
||||
// CORRECT
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UserService {
|
||||
private http = inject(HttpClient);
|
||||
private router = inject(Router);
|
||||
}
|
||||
|
||||
// WRONG: Constructor injection is verbose and harder to tree-shake
|
||||
constructor(private http: HttpClient, private router: Router) {}
|
||||
```
|
||||
|
||||
Use `InjectionToken` for non-class dependencies:
|
||||
|
||||
```typescript
|
||||
const API_URL = new InjectionToken<string>('API_URL');
|
||||
|
||||
// Provide:
|
||||
{ provide: API_URL, useValue: 'https://api.example.com' }
|
||||
|
||||
// Consume:
|
||||
private apiUrl = inject(API_URL);
|
||||
```
|
||||
|
||||
## Signals
|
||||
|
||||
### Core Primitives
|
||||
|
||||
```typescript
|
||||
count = signal(0);
|
||||
doubled = computed(() => this.count() * 2);
|
||||
|
||||
increment() {
|
||||
this.count.update(n => n + 1);
|
||||
}
|
||||
```
|
||||
|
||||
### `linkedSignal` — Writable Derived State
|
||||
|
||||
Use `linkedSignal` when a signal must reset or adapt when a source changes, but also be independently writable:
|
||||
|
||||
```typescript
|
||||
selectedOption = linkedSignal(() => this.options()[0]);
|
||||
// Resets to first option when options changes, but user can override
|
||||
```
|
||||
|
||||
### `resource` — Async Data into Signals
|
||||
|
||||
Use `resource()` to fetch async data reactively without manual subscriptions:
|
||||
|
||||
```typescript
|
||||
userResource = resource({
|
||||
request: () => ({ id: this.userId() }),
|
||||
loader: ({ request }) => fetch(`/api/users/${request.id}`).then(r => r.json()),
|
||||
});
|
||||
|
||||
// Access: userResource.value(), userResource.isLoading(), userResource.error()
|
||||
```
|
||||
|
||||
### `effect` Usage
|
||||
|
||||
Use `effect()` only for side effects that must react to signal changes (logging, third-party DOM manipulation). Never use effects to synchronize signals — use `computed` or `linkedSignal` instead. For DOM work after render, use `afterRenderEffect`.
|
||||
|
||||
```typescript
|
||||
// CORRECT: Side effect
|
||||
effect(() => console.log('User changed:', this.user()));
|
||||
|
||||
// WRONG: Use computed instead
|
||||
effect(() => { this.fullName.set(`${this.first()} ${this.last()}`); });
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
Use v17+ block syntax. Always provide `track` in `@for`:
|
||||
|
||||
```html
|
||||
@for (item of items(); track item.id) {
|
||||
<app-item [item]="item" />
|
||||
}
|
||||
|
||||
@if (isLoading()) {
|
||||
<app-spinner />
|
||||
} @else if (error()) {
|
||||
<app-error [message]="error()" />
|
||||
} @else {
|
||||
<app-content [data]="data()" />
|
||||
}
|
||||
```
|
||||
|
||||
No logic in templates beyond simple conditionals — move to component methods or pipes.
|
||||
|
||||
## Forms
|
||||
|
||||
Choose the form strategy that matches the project's existing approach:
|
||||
|
||||
- **Signal Forms** (v21+): Preferred for new projects on v21+. Signal-based form state.
|
||||
- **Reactive Forms**: `FormBuilder` + `FormGroup` + `FormControl`. Best for complex forms with dynamic validation.
|
||||
- **Template-Driven Forms**: `ngModel`. Suitable for simple forms only.
|
||||
|
||||
```typescript
|
||||
// Reactive Forms — standard approach for most apps
|
||||
export class LoginComponent {
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
form = this.fb.group({
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
password: ['', [Validators.required, Validators.minLength(8)]],
|
||||
});
|
||||
|
||||
submit() {
|
||||
if (this.form.valid) {
|
||||
// use this.form.value
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Component Styles
|
||||
|
||||
Use component-level styles with `ViewEncapsulation.Emulated` (default). Avoid `ViewEncapsulation.None` unless building a design system that intentionally bleeds styles.
|
||||
|
||||
- Scope styles to the component — do not use global class names inside component stylesheets
|
||||
- Use `:host` for host element styling
|
||||
- Prefer CSS custom properties for themeable values
|
||||
|
||||
## Change Detection
|
||||
|
||||
- Default to `ChangeDetectionStrategy.OnPush` on all new components
|
||||
- Signals and `async` pipe handle detection automatically — avoid `markForCheck()` and `detectChanges()`
|
||||
- Never mutate `@Input()` objects in place when using OnPush
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.directive.ts"
|
||||
- "**/*.pipe.ts"
|
||||
- "**/*.spec.ts"
|
||||
---
|
||||
# Angular Hooks
|
||||
|
||||
> This file extends [common/hooks.md](../common/hooks.md) with Angular specific content.
|
||||
|
||||
## PostToolUse Hooks
|
||||
|
||||
Configure in `~/.claude/settings.json`:
|
||||
|
||||
- **Prettier**: Auto-format `.ts` and `.html` files after edit
|
||||
- **ESLint / ng lint**: Run `ng lint` after editing Angular source files to catch decorator misuse, template errors, and style violations
|
||||
- **TypeScript check**: Run `tsc --noEmit` after editing `.ts` files
|
||||
- **Build check**: Run `ng build` after generating or significantly changing Angular code to catch template and type errors early
|
||||
|
||||
## Stop Hooks
|
||||
|
||||
- **Lint audit**: Run `ng lint` across modified files before session ends to catch any outstanding violations
|
||||
@@ -1,249 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.store.ts"
|
||||
- "**/*.routes.ts"
|
||||
---
|
||||
# Angular Patterns
|
||||
|
||||
> This file extends [common/patterns.md](../common/patterns.md) with Angular specific content.
|
||||
|
||||
## Smart / Dumb Component Split
|
||||
|
||||
Smart (container) components own data fetching and state. Dumb (presentational) components receive inputs and emit outputs only — no service injection.
|
||||
|
||||
```typescript
|
||||
// Smart — owns data
|
||||
@Component({ standalone: true, changeDetection: ChangeDetectionStrategy.OnPush })
|
||||
export class UserPageComponent {
|
||||
private userService = inject(UserService);
|
||||
user = toSignal(this.userService.getUser(this.userId));
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Dumb — pure presentation -->
|
||||
<app-user-card [user]="user()" (select)="onSelect($event)" />
|
||||
```
|
||||
|
||||
## Service Layer
|
||||
|
||||
Services own all data access and business logic. Components delegate — no `HttpClient` in components.
|
||||
|
||||
```typescript
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UserService {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
getUsers(): Observable<User[]> {
|
||||
return this.http.get<User[]>('/api/users');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Async Data with `resource`
|
||||
|
||||
Use `resource()` for reactive async fetching. Prefer over manual RxJS pipelines for simple data loading:
|
||||
|
||||
```typescript
|
||||
export class UserDetailComponent {
|
||||
userId = input.required<string>();
|
||||
|
||||
userResource = resource({
|
||||
request: () => ({ id: this.userId() }),
|
||||
loader: ({ request }) =>
|
||||
firstValueFrom(inject(UserService).getUser(request.id)),
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Access state: `userResource.value()`, `userResource.isLoading()`, `userResource.error()`, `userResource.reload()`.
|
||||
|
||||
## Signal State Patterns
|
||||
|
||||
```typescript
|
||||
// Local mutable state
|
||||
count = signal(0);
|
||||
|
||||
// Derived (never duplicated)
|
||||
doubled = computed(() => this.count() * 2);
|
||||
|
||||
// Writable derived state that resets with source
|
||||
selectedItem = linkedSignal(() => this.items()[0]);
|
||||
|
||||
// Bridge Observable to signal
|
||||
users = toSignal(this.userService.getUsers(), { initialValue: [] });
|
||||
```
|
||||
|
||||
Never store derived values in separate signals — use `computed`. Never use `effect` to sync signals — use `computed` or `linkedSignal`.
|
||||
|
||||
## Subscription Cleanup
|
||||
|
||||
Use `takeUntilDestroyed()` for all manual subscriptions. Never use manual `ngOnDestroy` + `Subject` + `takeUntil` on new code.
|
||||
|
||||
```typescript
|
||||
export class UserComponent {
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
ngOnInit() {
|
||||
this.userService.updates$
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(update => this.handleUpdate(update));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Routing
|
||||
|
||||
### Route Definition
|
||||
|
||||
```typescript
|
||||
// app.routes.ts
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{
|
||||
path: 'admin',
|
||||
canMatch: [authGuard], // CanMatch prevents loading the chunk at all
|
||||
loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
|
||||
},
|
||||
{
|
||||
path: 'users/:id',
|
||||
resolve: { user: userResolver },
|
||||
component: UserDetailComponent,
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
- Use `canMatch` over `canActivate` when the route module should not load for unauthorized users
|
||||
- Lazy-load all feature modules with `loadChildren`
|
||||
- Pre-fetch data with `resolve` to avoid loading states in components
|
||||
|
||||
### Functional Guards
|
||||
|
||||
```typescript
|
||||
export const authGuard: CanActivateFn = () => {
|
||||
const auth = inject(AuthService);
|
||||
return auth.isAuthenticated()
|
||||
? true
|
||||
: inject(Router).createUrlTree(['/login']);
|
||||
};
|
||||
```
|
||||
|
||||
### Data Resolvers
|
||||
|
||||
```typescript
|
||||
export const userResolver: ResolveFn<User> = (route) => {
|
||||
return inject(UserService).getUser(route.paramMap.get('id')!);
|
||||
};
|
||||
```
|
||||
|
||||
### View Transitions
|
||||
|
||||
Enable smooth route transitions with the View Transitions API:
|
||||
|
||||
```typescript
|
||||
// app.config.ts
|
||||
provideRouter(routes, withViewTransitions())
|
||||
```
|
||||
|
||||
## Dependency Injection Patterns
|
||||
|
||||
### Scoped Providers
|
||||
|
||||
Provide services at component or route level when they should not be singletons:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
providers: [UserEditService], // scoped to this component subtree
|
||||
})
|
||||
export class UserEditComponent {}
|
||||
```
|
||||
|
||||
### `InjectionToken`
|
||||
|
||||
```typescript
|
||||
export const CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');
|
||||
|
||||
// In providers:
|
||||
{ provide: CONFIG, useValue: appConfig }
|
||||
{ provide: CONFIG, useFactory: () => loadConfig(), deps: [] }
|
||||
|
||||
// Consume:
|
||||
private config = inject(CONFIG);
|
||||
```
|
||||
|
||||
### `viewProviders` vs `providers`
|
||||
|
||||
- `providers`: Available to the component and all its content children
|
||||
- `viewProviders`: Available only to the component's own view (not projected content)
|
||||
|
||||
## HTTP Interceptors
|
||||
|
||||
Use functional interceptors (v15+) for auth, error handling, and retries:
|
||||
|
||||
```typescript
|
||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const token = inject(AuthService).token();
|
||||
if (!token) return next(req);
|
||||
return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
|
||||
};
|
||||
```
|
||||
|
||||
Register in `app.config.ts`:
|
||||
|
||||
```typescript
|
||||
provideHttpClient(withInterceptors([authInterceptor, errorInterceptor]))
|
||||
```
|
||||
|
||||
## RxJS Operators
|
||||
|
||||
- `switchMap` — search, navigation (cancels previous)
|
||||
- `mergeMap` — independent parallel requests
|
||||
- `exhaustMap` — form submissions (ignores until complete)
|
||||
- Always handle errors with `catchError` — never let streams die silently
|
||||
|
||||
```typescript
|
||||
search$ = this.query$.pipe(
|
||||
debounceTime(300),
|
||||
distinctUntilChanged(),
|
||||
switchMap(q => this.service.search(q).pipe(catchError(() => of([])))),
|
||||
);
|
||||
```
|
||||
|
||||
## Forms
|
||||
|
||||
Match the project's existing form strategy. For new v21+ apps, prefer signal forms.
|
||||
|
||||
```typescript
|
||||
// Reactive Forms — standard for complex forms
|
||||
export class UserFormComponent {
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
form = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Rendering Strategies
|
||||
|
||||
- **CSR** (default): Standard SPA
|
||||
- **SSR + Hydration**: `ng add @angular/ssr` — improves FCP and SEO
|
||||
- **SSG (Prerendering)**: Static pages at build time for content-heavy routes
|
||||
|
||||
When using SSR, avoid `window`, `document`, `localStorage` directly — use `isPlatformBrowser` or `DOCUMENT` token.
|
||||
|
||||
## Accessibility
|
||||
|
||||
Use Angular CDK for headless, accessible components (Accordion, Listbox, Combobox, Menu, Tabs, Toolbar, Tree, Grid). Style ARIA attributes rather than managing them manually:
|
||||
|
||||
```css
|
||||
[aria-selected="true"] { background: var(--color-selected); }
|
||||
```
|
||||
|
||||
## Skill Reference
|
||||
|
||||
See skill: `angular-developer` for deep guidance on signals, forms, routing, DI, SSR, and accessibility patterns.
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.interceptor.ts"
|
||||
---
|
||||
# Angular Security
|
||||
|
||||
> This file extends [common/security.md](../common/security.md) with Angular specific content.
|
||||
|
||||
## XSS Prevention
|
||||
|
||||
Angular auto-sanitizes bound values. Never bypass the sanitizer on user-controlled input.
|
||||
|
||||
```typescript
|
||||
// WRONG: Bypasses sanitization — XSS risk
|
||||
this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(userInput);
|
||||
|
||||
// CORRECT: Sanitize explicitly before trusting
|
||||
this.safeHtml = this.sanitizer.sanitize(SecurityContext.HTML, userInput);
|
||||
```
|
||||
|
||||
- Never use `bypassSecurityTrust*` methods without a documented, reviewed reason
|
||||
- Avoid `[innerHTML]` with untrusted content — use `innerText` or a sanitizing pipe
|
||||
- Never bind `[href]` to user input — Angular does not block `javascript:` URLs in all contexts
|
||||
- Never construct template strings from user data
|
||||
|
||||
## HTTP Security
|
||||
|
||||
Use `HttpClient` exclusively — never raw `fetch()` or `XHR` unless no alternative exists.
|
||||
|
||||
```typescript
|
||||
// WRONG: Bypasses interceptors (auth headers, error handling, logging)
|
||||
const res = await fetch('/api/users');
|
||||
|
||||
// CORRECT
|
||||
users$ = this.http.get<User[]>('/api/users');
|
||||
```
|
||||
|
||||
- Attach auth tokens via interceptors — never hardcode in individual service calls
|
||||
- Type and validate API responses — treat external data as `unknown` at the boundary
|
||||
- Never log HTTP responses that may contain tokens, PII, or credentials
|
||||
|
||||
## Secret Management
|
||||
|
||||
```typescript
|
||||
// WRONG: Hardcoded secret in source
|
||||
const apiKey = 'sk-live-xxxx';
|
||||
|
||||
// CORRECT: Injected via environment
|
||||
import { environment } from '../environments/environment';
|
||||
const apiKey = environment.apiKey;
|
||||
```
|
||||
|
||||
- Treat `environment.ts` as a config shape — never store real secrets in source-controlled environment files
|
||||
- Inject production secrets via CI/CD (environment variables, secret managers)
|
||||
|
||||
## Route Guards
|
||||
|
||||
Every authenticated or role-restricted route must have a guard. Never rely on hiding UI elements alone.
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: 'admin',
|
||||
canMatch: [authGuard, roleGuard('admin')],
|
||||
loadChildren: () => import('./admin/admin.routes'),
|
||||
}
|
||||
```
|
||||
|
||||
Use `canMatch` for sensitive routes — it prevents the route module from loading at all for unauthorized users.
|
||||
|
||||
## SSR Security
|
||||
|
||||
When using Angular SSR:
|
||||
|
||||
- Never expose server-side environment variables to the client via `TransferState` unless they are intentionally public
|
||||
- Sanitize all inputs before server-side rendering — DOM-based XSS can occur server-side too
|
||||
- Avoid `window`, `document`, `localStorage` on the server — gate with `isPlatformBrowser` or inject via `DOCUMENT` token
|
||||
|
||||
## Content Security Policy
|
||||
|
||||
Configure CSP headers server-side. Avoid `unsafe-inline` in `script-src`. When using SSR with inline scripts, use nonces via Angular's CSP support.
|
||||
|
||||
## Agent Support
|
||||
|
||||
- Use **security-reviewer** skill for comprehensive security audits
|
||||
@@ -1,164 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.test.ts"
|
||||
---
|
||||
# Angular Testing
|
||||
|
||||
> This file extends [common/testing.md](../common/testing.md) with Angular specific content.
|
||||
|
||||
## Test Runner
|
||||
|
||||
Use the test runner configured by the project. Check `angular.json` and `package.json`; Angular projects commonly use Vitest, Jest, or Jasmine + Karma.
|
||||
|
||||
```bash
|
||||
ng test # watch mode
|
||||
ng test --no-watch # CI mode
|
||||
```
|
||||
|
||||
## TestBed Setup
|
||||
|
||||
For standalone components, import the component directly. Call `compileComponents()` for components with external templates.
|
||||
|
||||
```typescript
|
||||
describe('UserCardComponent', () => {
|
||||
let fixture: ComponentFixture<UserCardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserCardComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserCardComponent);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Signal Inputs
|
||||
|
||||
Set signal-based inputs via `fixture.componentRef.setInput()`:
|
||||
|
||||
```typescript
|
||||
fixture.componentRef.setInput('user', mockUser);
|
||||
fixture.detectChanges();
|
||||
```
|
||||
|
||||
## Component Harnesses
|
||||
|
||||
Prefer Angular CDK component harnesses over direct DOM queries for UI interaction. Harnesses are more resilient to markup changes.
|
||||
|
||||
```typescript
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatButtonHarness } from '@angular/material/button/testing';
|
||||
|
||||
let loader: HarnessLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
});
|
||||
|
||||
it('triggers save on button click', async () => {
|
||||
const button = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
|
||||
await button.click();
|
||||
expect(saveSpy).toHaveBeenCalled();
|
||||
});
|
||||
```
|
||||
|
||||
## Router Testing
|
||||
|
||||
Use `RouterTestingHarness` for components that depend on the router:
|
||||
|
||||
```typescript
|
||||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
|
||||
it('renders user on navigation', async () => {
|
||||
const harness = await RouterTestingHarness.create();
|
||||
const component = await harness.navigateByUrl('/users/1', UserDetailComponent);
|
||||
expect(component.userId()).toBe('1');
|
||||
});
|
||||
```
|
||||
|
||||
## Async Testing
|
||||
|
||||
Use `fakeAsync` + `tick` for controlled async. Use `waitForAsync` for real async with `fixture.whenStable()`.
|
||||
|
||||
```typescript
|
||||
it('loads user after delay', fakeAsync(() => {
|
||||
const service = TestBed.inject(UserService);
|
||||
vi.spyOn(service, 'getUser').mockReturnValue(of(mockUser));
|
||||
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('.name').textContent).toBe(mockUser.name);
|
||||
}));
|
||||
```
|
||||
|
||||
## HTTP Testing
|
||||
|
||||
```typescript
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { HttpTestingController } from '@angular/common/http/testing';
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClient(), provideHttpClientTesting()],
|
||||
});
|
||||
httpMock = TestBed.inject(HttpTestingController);
|
||||
});
|
||||
|
||||
afterEach(() => httpMock.verify());
|
||||
```
|
||||
|
||||
## Service Testing
|
||||
|
||||
Inject services directly without a component fixture:
|
||||
|
||||
```typescript
|
||||
describe('UserService', () => {
|
||||
let service: UserService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClient(), provideHttpClientTesting()],
|
||||
});
|
||||
service = TestBed.inject(UserService);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## What to Test
|
||||
|
||||
- **Services**: All public methods, error paths, HTTP interactions
|
||||
- **Components**: Input/output bindings, rendered output for key states, user interactions via harnesses
|
||||
- **Pipes**: Pure transformation — plain unit tests, no TestBed needed
|
||||
- **Guards/Resolvers**: Return values for allowed and denied states using `RouterTestingHarness`
|
||||
|
||||
## E2E Testing
|
||||
|
||||
Use the project's configured E2E framework, such as Cypress or Playwright, for critical user flows.
|
||||
|
||||
```typescript
|
||||
describe('Login flow', () => {
|
||||
it('redirects to dashboard on valid credentials', () => {
|
||||
cy.visit('/login');
|
||||
cy.get('[data-cy=email]').type('user@example.com');
|
||||
cy.get('[data-cy=password]').type('password123');
|
||||
cy.get('[data-cy=submit]').click();
|
||||
cy.url().should('include', '/dashboard');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- Add `data-cy` attributes to interactive elements for stable selectors
|
||||
- Do not rely on CSS classes or text content for selectors in E2E tests
|
||||
|
||||
## Coverage
|
||||
|
||||
Target ≥80% for services and pipes. Components: test behaviour, not implementation details.
|
||||
|
||||
## Skill Reference
|
||||
|
||||
See skill: `angular-developer` for comprehensive testing patterns, harness usage, and async best practices.
|
||||
@@ -1,153 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.ets"
|
||||
- "**/*.ts"
|
||||
- "**/module.json5"
|
||||
- "**/oh-package.json5"
|
||||
- "**/build-profile.json5"
|
||||
---
|
||||
# HarmonyOS / ArkTS Coding Style
|
||||
|
||||
> This file extends [common/coding-style.md](../common/coding-style.md) with HarmonyOS and ArkTS-specific content.
|
||||
|
||||
## ArkTS Language Constraints
|
||||
|
||||
ArkTS is a strict, statically-typed subset of TypeScript. Violating these constraints causes **compilation failures**.
|
||||
|
||||
### Type System
|
||||
|
||||
- No `any` or `unknown` types - always use explicit types
|
||||
- No index access types - use type names directly
|
||||
- No conditional type aliases or `infer` keyword
|
||||
- No intersection types - use inheritance
|
||||
- No mapped types - use classes and regular idioms
|
||||
- No `typeof` for type annotations - use explicit type declarations
|
||||
- No `as const` assertions - use explicit type annotations
|
||||
- No structural typing - use inheritance, interfaces, or type aliases
|
||||
- No TypeScript utility types except `Partial`, `Required`, `Readonly`, `Record`
|
||||
- For `Record<K, V>`, index expression type is `V | undefined`
|
||||
- Omit type annotations in `catch` clauses (ArkTS does not support `any`/`unknown`)
|
||||
|
||||
### Functions & Classes
|
||||
|
||||
- No function expressions - use arrow functions
|
||||
- No nested functions - use lambdas
|
||||
- No generator functions - use `async`/`await` for multitasking
|
||||
- No `Function.apply`, `Function.call`, `Function.bind` - follow traditional OOP for `this`
|
||||
- No constructor type expressions - use lambdas
|
||||
- No constructor signatures in interfaces or object types - use methods or classes
|
||||
- No declaring class fields in constructors - declare in class body
|
||||
- No `this` in standalone functions or static methods - only in instance methods
|
||||
- No `new.target`
|
||||
- No definite assignment assertions (`let v!: T`) - use initialized declarations
|
||||
- No class literals - introduce named class types
|
||||
- No using classes as objects (assigning to variables) - class declarations introduce types, not values
|
||||
- Only one static block per class - merge all static statements
|
||||
|
||||
### Object & Property Access
|
||||
|
||||
- No dynamic field declaration or `obj["field"]` access - use `obj.field` syntax
|
||||
- No `delete` operator - use nullable type with `null` to mark absence
|
||||
- No prototype assignment - use classes and interfaces
|
||||
- No `in` operator - use `instanceof`
|
||||
- No reassigning object methods - use wrapper functions or inheritance
|
||||
- No `Symbol()` API (except `Symbol.iterator`)
|
||||
- No `globalThis` or global scope - use explicit module exports/imports
|
||||
- No namespaces as objects - use classes or modules
|
||||
- No statements inside namespaces - use functions
|
||||
|
||||
### Destructuring & Spread
|
||||
|
||||
- No destructuring assignments or variable declarations - use intermediate objects and field-by-field access
|
||||
- No destructuring parameter declarations - pass parameters directly, assign local names manually
|
||||
- Spread operator only for expanding arrays (or array-derived classes) into rest parameters or array literals
|
||||
|
||||
### Modules & Imports
|
||||
|
||||
- No `require()` - use regular `import` syntax
|
||||
- No `export = ...` - use normal export/import
|
||||
- No import assertions - imports are compile-time in ArkTS
|
||||
- No UMD modules
|
||||
- No wildcards in module names
|
||||
- All `import` statements must appear before all other statements
|
||||
- TypeScript codebases must not depend on ArkTS codebases via import (reverse is supported)
|
||||
|
||||
### Other Restrictions
|
||||
|
||||
- No `var` - use `let`
|
||||
- No `for...in` loops - use regular `for` loops for arrays
|
||||
- No `with` statements
|
||||
- No JSX expressions
|
||||
- No `#` private identifiers - use `private` keyword
|
||||
- No declaration merging (classes, interfaces, enums) - keep definitions compact
|
||||
- No index signatures - use arrays
|
||||
- Comma operator only in `for` loops
|
||||
- Unary operators `+`, `-`, `~` only for numeric types (no implicit string conversion)
|
||||
- Enum members: only same-type compile-time expressions for explicit initializers
|
||||
- Function return type inference is limited - specify return types explicitly when calling functions with omitted return types
|
||||
|
||||
### Object Literals
|
||||
|
||||
- Supported only when compiler can infer the corresponding class or interface
|
||||
- NOT supported for: `any`/`Object`/`object` types, classes/interfaces with methods, classes with parameterized constructors, classes with `readonly` fields
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- Variables / functions: `camelCase` (e.g., `getUserInfo`, `goodsList`)
|
||||
- Classes / interfaces: `PascalCase` (e.g., `UserViewModel`, `IGoodsModel`)
|
||||
- Constants: `UPPER_SNAKE_CASE` (e.g., `MAX_PAGE_SIZE`, `COLOR_PRIMARY`)
|
||||
- File names: `PascalCase` for components (e.g., `HomePage.ets`), `camelCase` for utilities
|
||||
|
||||
## Formatting
|
||||
|
||||
- Prefer double quotes for strings
|
||||
- Semicolons at end of statements
|
||||
- Never use `var` - prefer `const`, then `let`
|
||||
- All methods, parameters, return values must have complete type annotations
|
||||
|
||||
## File Organization
|
||||
|
||||
- Component files (`.ets`): one `@ComponentV2` per file
|
||||
- ViewModel files: one ViewModel class per file
|
||||
- Model files: related data models may share a file
|
||||
- Keep files under 400 lines; extract helpers for files approaching 800 lines
|
||||
|
||||
## Comments
|
||||
|
||||
- File header: `@file` (file purpose) + `@author` (developer), if the project already uses file headers
|
||||
- Public methods: JSDoc with `@param`, `@returns`; add `@example` for complex methods
|
||||
- Match the project's existing documentation language; use English unless the repository has already standardized on Chinese comments
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
// Use try/catch with proper error handling
|
||||
try {
|
||||
const result = await riskyOperation()
|
||||
return result
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'TAG', 'Operation failed: %{public}s', error)
|
||||
throw new Error('User-friendly error message')
|
||||
}
|
||||
```
|
||||
|
||||
## Immutability
|
||||
|
||||
Follow the common immutability principles - create new objects instead of mutating:
|
||||
|
||||
```typescript
|
||||
// BAD: mutation
|
||||
function updateUser(user: UserModel, name: string): UserModel {
|
||||
user.name = name // direct mutation
|
||||
return user
|
||||
}
|
||||
|
||||
// GOOD: immutable - create new instance
|
||||
function updateUser(user: UserModel, name: string): UserModel {
|
||||
const updated = new UserModel()
|
||||
updated.id = user.id
|
||||
updated.name = name
|
||||
updated.email = user.email
|
||||
return updated
|
||||
}
|
||||
```
|
||||
@@ -1,135 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.ets"
|
||||
- "**/*.ts"
|
||||
- "**/module.json5"
|
||||
- "**/oh-package.json5"
|
||||
---
|
||||
# HarmonyOS / ArkTS Hooks
|
||||
|
||||
> This file extends [common/hooks.md](../common/hooks.md) with HarmonyOS-specific build and validation hooks.
|
||||
|
||||
## Build Commands
|
||||
|
||||
### HAP Package Build
|
||||
|
||||
```bash
|
||||
# Build HAP package (global hvigor environment)
|
||||
hvigorw assembleHap -p product=default
|
||||
|
||||
# Build with specific module
|
||||
hvigorw assembleHap -p module=entry -p product=default
|
||||
|
||||
# Clean build
|
||||
hvigorw clean
|
||||
```
|
||||
|
||||
### DevEco Studio CLI
|
||||
|
||||
```bash
|
||||
# Check project structure
|
||||
hvigorw --version
|
||||
|
||||
# Install dependencies
|
||||
ohpm install
|
||||
|
||||
# Update dependencies
|
||||
ohpm update
|
||||
```
|
||||
|
||||
## Recommended PostToolUse Hooks
|
||||
|
||||
### After Editing .ets/.ts Files
|
||||
|
||||
Run hvigor build to check for ArkTS compilation errors:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "PostToolUse",
|
||||
"matcher": {
|
||||
"tool": ["Edit", "Write"],
|
||||
"filePath": ["**/*.ets", "**/*.ts"]
|
||||
},
|
||||
"hooks": [
|
||||
{
|
||||
"command": "hvigorw assembleHap -p product=default 2>&1 | tail -20",
|
||||
"async": true,
|
||||
"timeout": 60000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### After Editing module.json5
|
||||
|
||||
Validate permission and ability declarations:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "PostToolUse",
|
||||
"matcher": {
|
||||
"tool": "Edit",
|
||||
"filePath": "**/module.json5"
|
||||
},
|
||||
"hooks": [
|
||||
{
|
||||
"command": "echo '[HarmonyOS] module.json5 modified - verify permissions and abilities'",
|
||||
"async": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### After Editing oh-package.json5
|
||||
|
||||
Reinstall dependencies:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "PostToolUse",
|
||||
"matcher": {
|
||||
"tool": "Edit",
|
||||
"filePath": "**/oh-package.json5"
|
||||
},
|
||||
"hooks": [
|
||||
{
|
||||
"command": "ohpm install 2>&1 | tail -10",
|
||||
"async": true,
|
||||
"timeout": 30000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## PreToolUse Hooks
|
||||
|
||||
### V1 Decorator Guard
|
||||
|
||||
Warn when code contains V1 state management decorators:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "PreToolUse",
|
||||
"matcher": {
|
||||
"tool": ["Write", "Edit"],
|
||||
"filePath": "**/*.ets"
|
||||
},
|
||||
"hooks": [
|
||||
{
|
||||
"command": "echo '[HarmonyOS] Reminder: Use @ComponentV2 / @Local / @Param - V1 decorators (@State, @Prop, @Link) are prohibited'"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
After each implementation cycle, verify:
|
||||
|
||||
- [ ] `hvigorw assembleHap` completes without errors
|
||||
- [ ] No V1 decorators in new or modified `.ets` files
|
||||
- [ ] No `@ohos.router` imports in new or modified files
|
||||
- [ ] All API permissions declared in `module.json5`
|
||||
- [ ] All dependencies listed in `oh-package.json5`
|
||||
- [ ] Resource strings added to all i18n directories
|
||||
- [ ] Dark theme colors provided for new color resources
|
||||
@@ -1,236 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.ets"
|
||||
- "**/*.ts"
|
||||
---
|
||||
# HarmonyOS / ArkTS Patterns
|
||||
|
||||
> This file extends [common/patterns.md](../common/patterns.md) with HarmonyOS and ArkTS-specific patterns.
|
||||
|
||||
## State Management: V2 Only
|
||||
|
||||
**MUST use** ArkUI State Management V2. V1 decorators are deprecated and must not be used.
|
||||
|
||||
### V2 Decorators
|
||||
|
||||
| Decorator | Purpose |
|
||||
|-----------|---------|
|
||||
| `@ComponentV2` | Marks a struct as a V2 component |
|
||||
| `@Local` | Local state within a component |
|
||||
| `@Param` | Props received from parent (read-only) |
|
||||
| `@Event` | Callback events from child to parent |
|
||||
| `@Provider` | Provides state to descendant components |
|
||||
| `@Consumer` | Consumes state from ancestor `@Provider` |
|
||||
| `@Monitor` | Watches for state changes (replaces V1 `@Watch`) |
|
||||
| `@Computed` | Derived/computed values |
|
||||
| `@ObservedV2` | Makes a class observable for V2 state management |
|
||||
| `@Trace` | Marks observable properties in `@ObservedV2` classes |
|
||||
|
||||
### Prohibited V1 Decorators
|
||||
|
||||
Never use: `@State`, `@Prop`, `@Link`, `@ObjectLink`, `@Observed`, `@Provide`, `@Consume`, `@Watch`, `@Component` (use `@ComponentV2` instead).
|
||||
|
||||
### V2 Component Example
|
||||
|
||||
```typescript
|
||||
@ObservedV2
|
||||
class UserModel {
|
||||
@Trace name: string = ''
|
||||
@Trace age: number = 0
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
struct UserCard {
|
||||
@Param user: UserModel = new UserModel()
|
||||
@Event onDelete: () => void = () => {}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text(this.user.name)
|
||||
.fontSize($r('app.float.font_size_title'))
|
||||
Text(`${this.user.age}`)
|
||||
.fontSize($r('app.float.font_size_body'))
|
||||
Button($r('app.string.delete'))
|
||||
.onClick(() => this.onDelete())
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### State Synchronization
|
||||
|
||||
```typescript
|
||||
@ComponentV2
|
||||
struct ParentPage {
|
||||
@Provider('userState') userModel: UserModel = new UserModel()
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
ChildComponent() // automatically receives @Consumer('userState')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
struct ChildComponent {
|
||||
@Consumer('userState') userModel: UserModel = new UserModel()
|
||||
|
||||
build() {
|
||||
Text(this.userModel.name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Routing: Navigation Only
|
||||
|
||||
**MUST use** `Navigation` component with `NavPathStack`. Never use `@ohos.router`.
|
||||
|
||||
### Navigation Setup
|
||||
|
||||
```typescript
|
||||
@ComponentV2
|
||||
struct MainPage {
|
||||
@Local navPathStack: NavPathStack = new NavPathStack()
|
||||
|
||||
build() {
|
||||
Navigation(this.navPathStack) {
|
||||
// Home content
|
||||
}
|
||||
.navDestination(this.routerMap)
|
||||
}
|
||||
|
||||
@Builder
|
||||
routerMap(name: string, param: ESObject) {
|
||||
if (name === 'detail') {
|
||||
DetailPage()
|
||||
} else if (name === 'settings') {
|
||||
SettingsPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Page Navigation
|
||||
|
||||
```typescript
|
||||
// Push a new page
|
||||
this.navPathStack.pushPath({ name: 'detail', param: { id: '123' } })
|
||||
|
||||
// Replace current page
|
||||
this.navPathStack.replacePath({ name: 'settings' })
|
||||
|
||||
// Pop back
|
||||
this.navPathStack.pop()
|
||||
|
||||
// Pop to root
|
||||
this.navPathStack.clear()
|
||||
```
|
||||
|
||||
### NavDestination Sub-page
|
||||
|
||||
```typescript
|
||||
@ComponentV2
|
||||
struct DetailPage {
|
||||
build() {
|
||||
NavDestination() {
|
||||
Column() {
|
||||
Text($r('app.string.detail_title'))
|
||||
}
|
||||
}
|
||||
.title($r('app.string.detail_nav_title'))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture Pattern: MVVM
|
||||
|
||||
Recommended architecture for HarmonyOS applications:
|
||||
|
||||
```
|
||||
feature/
|
||||
|-- model/ # Data models (@ObservedV2 classes)
|
||||
|-- viewmodel/ # Business logic (ViewModel classes)
|
||||
|-- view/ # UI components (@ComponentV2 structs)
|
||||
|-- service/ # API calls, data access
|
||||
```
|
||||
|
||||
- **View**: Only rendering logic, no business logic in `build()`
|
||||
- **ViewModel**: All business logic encapsulated here
|
||||
- **Model**: Pure data classes with `@ObservedV2` and `@Trace`
|
||||
- **Service**: Network requests, database operations, file I/O
|
||||
|
||||
## ArkUI Animation Patterns
|
||||
|
||||
### State-Driven Animation
|
||||
|
||||
```typescript
|
||||
@ComponentV2
|
||||
struct AnimatedCard {
|
||||
@Local isExpanded: boolean = false
|
||||
@Local cardScale: number = 0.8
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
// Content
|
||||
}
|
||||
.scale({ x: this.cardScale, y: this.cardScale })
|
||||
.animation({ duration: 300, curve: Curve.EaseInOut })
|
||||
.onClick(() => {
|
||||
this.isExpanded = !this.isExpanded
|
||||
this.cardScale = this.isExpanded ? 1.0 : 0.8
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Animation Rules
|
||||
|
||||
- Prefer native HarmonyOS animation APIs and advanced templates
|
||||
- Use declarative UI with state-driven animations (change state variables to trigger animations)
|
||||
- Set `renderGroup(true)` for complex sub-component animations to reduce render batches
|
||||
- **NEVER** frequently change `width`, `height`, `padding`, `margin` during animations - severe performance impact
|
||||
- Use `animateTo` for explicit animation control
|
||||
- Prefer `transform` (translate, scale, rotate) and `opacity` for performant animations
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
### LazyForEach for Large Lists
|
||||
|
||||
```typescript
|
||||
@ComponentV2
|
||||
struct LargeList {
|
||||
@Local dataSource: MyDataSource = new MyDataSource()
|
||||
|
||||
build() {
|
||||
List() {
|
||||
LazyForEach(this.dataSource, (item: ItemModel) => {
|
||||
ListItem() {
|
||||
ItemComponent({ item: item })
|
||||
}
|
||||
}, (item: ItemModel) => item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Component Reuse
|
||||
|
||||
- Extract reusable components into separate files
|
||||
- Use `@Builder` for lightweight UI fragments within a component
|
||||
- Use `@Param` for configurable components
|
||||
|
||||
## Resource References
|
||||
|
||||
Always define UI constants as resources and reference via `$r()`:
|
||||
|
||||
```typescript
|
||||
// BAD: hardcoded values
|
||||
Text('Hello')
|
||||
.fontSize(16)
|
||||
.fontColor('#333333')
|
||||
|
||||
// GOOD: resource references
|
||||
Text($r('app.string.greeting'))
|
||||
.fontSize($r('app.float.font_size_body'))
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
```
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.ets"
|
||||
- "**/*.ts"
|
||||
- "**/module.json5"
|
||||
---
|
||||
# HarmonyOS / ArkTS Security
|
||||
|
||||
> This file extends [common/security.md](../common/security.md) with HarmonyOS-specific security practices.
|
||||
|
||||
## Permission Management
|
||||
|
||||
### Declare Permissions in module.json5
|
||||
|
||||
All system API calls requiring permissions must be declared:
|
||||
|
||||
```json5
|
||||
{
|
||||
"module": {
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.INTERNET",
|
||||
"reason": "$string:internet_permission_reason",
|
||||
"usedScene": {
|
||||
"abilities": ["EntryAbility"],
|
||||
"when": "always"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Permission Checklist
|
||||
|
||||
Before calling system APIs, verify:
|
||||
|
||||
- [ ] Permission declared in `module.json5`
|
||||
- [ ] Permission reason string defined in resources (for user-facing permissions)
|
||||
- [ ] Runtime permission request implemented for sensitive permissions (camera, location, etc.)
|
||||
- [ ] Permission check before API call with graceful fallback on denial
|
||||
|
||||
### Runtime Permission Request
|
||||
|
||||
```typescript
|
||||
import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit';
|
||||
|
||||
async function checkAndRequestPermission(permission: Permissions): Promise<boolean> {
|
||||
const atManager = abilityAccessCtrl.createAtManager();
|
||||
const bundleInfo = await bundleManager.getBundleInfoForSelf(
|
||||
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION
|
||||
);
|
||||
const tokenId = bundleInfo.appInfo.accessTokenId;
|
||||
const grantStatus = await atManager.checkAccessToken(tokenId, permission);
|
||||
|
||||
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const result = await atManager.requestPermissionsFromUser(getContext(), [permission]);
|
||||
return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
|
||||
}
|
||||
```
|
||||
|
||||
## Secret Management
|
||||
|
||||
- **NEVER** hardcode API keys, tokens, or passwords in `.ets`/`.ts` source files
|
||||
- Use HarmonyOS Preferences API for non-sensitive configuration
|
||||
- Use HarmonyOS Keystore for sensitive credentials
|
||||
- Environment-specific configs should be managed via build profiles
|
||||
|
||||
```typescript
|
||||
// BAD: hardcoded secret
|
||||
const API_KEY: string = 'sk-xxxxxxxxxxxx';
|
||||
|
||||
// GOOD: from build profile config (non-sensitive)
|
||||
import { BuildProfile } from 'BuildProfile';
|
||||
const endpoint = BuildProfile.API_ENDPOINT;
|
||||
|
||||
// GOOD: use HUKS to encrypt/decrypt data without exposing key material
|
||||
import { huks } from '@kit.UniversalKeystoreKit';
|
||||
async function decryptWithKeystore(alias: string, nonce: Uint8Array, aad: Uint8Array, cipherData: Uint8Array): Promise<Uint8Array> {
|
||||
const options: huks.HuksOptions = {
|
||||
properties: [
|
||||
{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES },
|
||||
{ tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT },
|
||||
{ tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_GCM },
|
||||
{ tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE },
|
||||
{ tag: huks.HuksTag.HUKS_TAG_NONCE, value: nonce },
|
||||
{ tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA, value: aad }
|
||||
],
|
||||
inData: cipherData
|
||||
};
|
||||
const handle = await huks.initSession(alias, options);
|
||||
const result = await huks.finishSession(handle.handle, options);
|
||||
return result.outData;
|
||||
}
|
||||
```
|
||||
|
||||
## Input Validation
|
||||
|
||||
- Validate all user input before processing
|
||||
- Sanitize data before displaying in UI to prevent injection
|
||||
- Validate deep link parameters before navigation
|
||||
|
||||
```typescript
|
||||
// Validate before navigation
|
||||
function handleDeepLink(uri: string): void {
|
||||
const allowedPaths: string[] = ['detail', 'settings', 'profile'];
|
||||
const parsed = new URL(uri);
|
||||
const path = parsed.pathname.replace('/', '');
|
||||
|
||||
if (!allowedPaths.includes(path)) {
|
||||
hilog.warn(0x0000, 'DeepLink', 'Invalid deep link path: %{public}s', path);
|
||||
return;
|
||||
}
|
||||
|
||||
navPathStack.pushPath({ name: path });
|
||||
}
|
||||
```
|
||||
|
||||
## Network Security
|
||||
|
||||
- Always use HTTPS for network requests
|
||||
- Validate server certificates
|
||||
- Implement request timeout and retry policies
|
||||
- Never log sensitive data (tokens, user credentials) in network request/response logs
|
||||
|
||||
## Data Storage Security
|
||||
|
||||
- Use encrypted preferences for sensitive local data
|
||||
- Clear sensitive data from memory when no longer needed
|
||||
- Implement proper data lifecycle management
|
||||
- Consider data classification (public, internal, confidential) when choosing storage mechanisms
|
||||
|
||||
## Dependency Security
|
||||
|
||||
- Only use dependencies from trusted sources (official ohpm registry)
|
||||
- Verify dependency versions in `oh-package.json5`
|
||||
- Regularly check for known vulnerabilities in third-party libraries
|
||||
- Pin dependency versions to avoid unexpected updates
|
||||
@@ -1,126 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.ets"
|
||||
- "**/*.ts"
|
||||
- "**/ohosTest/**"
|
||||
---
|
||||
# HarmonyOS / ArkTS Testing
|
||||
|
||||
> This file extends [common/testing.md](../common/testing.md) with HarmonyOS-specific testing practices.
|
||||
|
||||
## Test Framework
|
||||
|
||||
HarmonyOS uses the built-in test framework with `@ohos.test` capabilities:
|
||||
|
||||
- **Unit tests**: Located in `src/ohosTest/ets/test/`
|
||||
- **UI tests**: Use `@ohos.UiTest` for component testing
|
||||
- **Instrument tests**: Run on device/emulator
|
||||
|
||||
## Test Directory Structure
|
||||
|
||||
```
|
||||
module/
|
||||
|-- src/
|
||||
| |-- main/ets/ # Production code
|
||||
| |-- ohosTest/ets/ # Test code
|
||||
| |-- test/
|
||||
| | |-- Ability.test.ets
|
||||
| | |-- List.test.ets
|
||||
| |-- TestAbility.ets
|
||||
| |-- TestRunner.ets
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests for a module
|
||||
hvigorw testHap -p product=default
|
||||
|
||||
# Run tests on connected device
|
||||
hdc shell aa test -b com.example.app -m entry_test -s unittest /ets/TestRunner/OpenHarmonyTestRunner
|
||||
```
|
||||
|
||||
## Unit Test Example
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect } from '@ohos/hypium';
|
||||
|
||||
export default function UserViewModelTest() {
|
||||
describe('UserViewModel', () => {
|
||||
it('should_initialize_with_empty_state', 0, () => {
|
||||
const vm = new UserViewModel();
|
||||
expect(vm.userName).assertEqual('');
|
||||
expect(vm.isLoading).assertFalse();
|
||||
});
|
||||
|
||||
it('should_update_user_name', 0, () => {
|
||||
const vm = new UserViewModel();
|
||||
vm.updateUserName('Alice');
|
||||
expect(vm.userName).assertEqual('Alice');
|
||||
});
|
||||
|
||||
it('should_handle_empty_input', 0, () => {
|
||||
const vm = new UserViewModel();
|
||||
vm.updateUserName('');
|
||||
expect(vm.userName).assertEqual('');
|
||||
expect(vm.hasError).assertFalse();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## UI Test Example
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect } from '@ohos/hypium';
|
||||
import { Driver, ON } from '@ohos.UiTest';
|
||||
|
||||
export default function HomePageUITest() {
|
||||
describe('HomePage_UI', () => {
|
||||
it('should_display_title', 0, async () => {
|
||||
const driver = Driver.create();
|
||||
await driver.delayMs(1000);
|
||||
|
||||
const title = await driver.findComponent(ON.text('Home'));
|
||||
expect(title !== null).assertTrue();
|
||||
});
|
||||
|
||||
it('should_navigate_to_detail_on_click', 0, async () => {
|
||||
const driver = Driver.create();
|
||||
const button = await driver.findComponent(ON.id('detailButton'));
|
||||
await button.click();
|
||||
await driver.delayMs(500);
|
||||
|
||||
const detailTitle = await driver.findComponent(ON.text('Detail'));
|
||||
expect(detailTitle !== null).assertTrue();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## TDD Workflow for HarmonyOS
|
||||
|
||||
Follow the standard TDD cycle adapted for HarmonyOS:
|
||||
|
||||
1. **RED**: Write a failing test in `ohosTest/ets/test/`
|
||||
2. **GREEN**: Implement minimal code in `main/ets/` to pass
|
||||
3. **REFACTOR**: Clean up while keeping tests green
|
||||
4. **BUILD**: Run `hvigorw assembleHap` to verify compilation
|
||||
5. **VERIFY**: Run tests on device/emulator
|
||||
|
||||
## Test Coverage Requirements
|
||||
|
||||
- Minimum 80% coverage for all critical application code (ViewModels, services, utilities)
|
||||
- **Unit tests**: All utility functions, ViewModel logic, data models
|
||||
- **Integration tests**: API calls, database operations, cross-module interactions
|
||||
- **E2E / UI tests**: Critical user flows (login, navigation, data submission)
|
||||
- Test edge cases: empty data, network errors, permission denials
|
||||
|
||||
## Testing Best Practices
|
||||
|
||||
- Keep tests independent - no shared mutable state between tests
|
||||
- Mock network calls and system APIs in unit tests
|
||||
- Use meaningful test names: `should_[expected_behavior]_when_[condition]`
|
||||
- Test V2 state management reactivity: verify `@Trace` properties trigger UI updates
|
||||
- Test Navigation flows: verify `NavPathStack` push/pop/replace operations
|
||||
- Avoid testing framework internals - focus on business logic and user-visible behavior
|
||||
@@ -16,7 +16,6 @@ Located in `~/.claude/agents/`:
|
||||
| refactor-cleaner | Dead code cleanup | Code maintenance |
|
||||
| doc-updater | Documentation | Updating docs |
|
||||
| rust-reviewer | Rust code review | Rust projects |
|
||||
| harmonyos-app-resolver | HarmonyOS app development | HarmonyOS/ArkTS projects |
|
||||
|
||||
## Immediate Agent Usage
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.fs"
|
||||
- "**/*.fsx"
|
||||
---
|
||||
# F# Coding Style
|
||||
|
||||
> This file extends [common/coding-style.md](../common/coding-style.md) with F#-specific content.
|
||||
|
||||
## Standards
|
||||
|
||||
- Follow standard F# conventions and leverage the type system for correctness
|
||||
- Prefer immutability by default; use `mutable` only when justified by performance
|
||||
- Keep modules focused and cohesive
|
||||
|
||||
## Types and Models
|
||||
|
||||
- Prefer discriminated unions for domain modeling over class hierarchies
|
||||
- Use records for data with named fields
|
||||
- Use single-case unions for type-safe wrappers around primitives
|
||||
- Avoid classes unless interop or mutable state requires them
|
||||
|
||||
```fsharp
|
||||
type EmailAddress = EmailAddress of string
|
||||
|
||||
type OrderStatus =
|
||||
| Pending
|
||||
| Confirmed of confirmedAt: DateTimeOffset
|
||||
| Shipped of trackingNumber: string
|
||||
| Cancelled of reason: string
|
||||
|
||||
type Order =
|
||||
{ Id: Guid
|
||||
CustomerId: string
|
||||
Status: OrderStatus
|
||||
Items: OrderItem list }
|
||||
```
|
||||
|
||||
## Immutability
|
||||
|
||||
- Records are immutable by default; use `with` expressions for updates
|
||||
- Prefer `list`, `map`, `set` over mutable collections
|
||||
- Avoid `ref` cells and mutable fields in domain logic
|
||||
|
||||
```fsharp
|
||||
let rename (profile: UserProfile) newName =
|
||||
{ profile with Name = newName }
|
||||
```
|
||||
|
||||
## Function Style
|
||||
|
||||
- Prefer small, composable functions over large methods
|
||||
- Use the pipe operator `|>` to build readable data pipelines
|
||||
- Prefer pattern matching over if/else chains
|
||||
- Use `Option` instead of null; use `Result` for operations that can fail
|
||||
|
||||
```fsharp
|
||||
let processOrder order =
|
||||
order
|
||||
|> validateItems
|
||||
|> Result.bind calculateTotal
|
||||
|> Result.map applyDiscount
|
||||
|> Result.mapError OrderError
|
||||
```
|
||||
|
||||
## Async and Error Handling
|
||||
|
||||
- Use `task { }` for interop with .NET async APIs
|
||||
- Use `async { }` for F#-native async workflows
|
||||
- Propagate `CancellationToken` through public async APIs
|
||||
- Prefer `Result` and railway-oriented programming over exceptions for expected failures
|
||||
|
||||
```fsharp
|
||||
let loadOrderAsync (orderId: Guid) (ct: CancellationToken) =
|
||||
task {
|
||||
let! order = repository.FindAsync(orderId, ct)
|
||||
return
|
||||
order
|
||||
|> Option.defaultWith (fun () ->
|
||||
failwith $"Order {orderId} was not found.")
|
||||
}
|
||||
```
|
||||
|
||||
## Formatting
|
||||
|
||||
- Use `fantomas` for automatic formatting
|
||||
- Prefer significant whitespace; avoid unnecessary parentheses
|
||||
- Remove unused `open` declarations
|
||||
|
||||
### Open Declaration Order
|
||||
|
||||
Group `open` statements into four sections separated by a blank line, each section sorted lexically within itself:
|
||||
|
||||
1. `System.*`
|
||||
2. `Microsoft.*`
|
||||
3. Third-party namespaces
|
||||
4. First-party / project namespaces
|
||||
|
||||
```fsharp
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open System.Threading.Tasks
|
||||
|
||||
open Microsoft.AspNetCore.Http
|
||||
open Microsoft.Extensions.Logging
|
||||
|
||||
open FsCheck.Xunit
|
||||
open Swensen.Unquote
|
||||
|
||||
open MyApp.Domain
|
||||
open MyApp.Infrastructure
|
||||
```
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.fs"
|
||||
- "**/*.fsx"
|
||||
- "**/*.fsproj"
|
||||
- "**/*.sln"
|
||||
- "**/*.slnx"
|
||||
- "**/Directory.Build.props"
|
||||
- "**/Directory.Build.targets"
|
||||
---
|
||||
# F# Hooks
|
||||
|
||||
> This file extends [common/hooks.md](../common/hooks.md) with F#-specific content.
|
||||
|
||||
## PostToolUse Hooks
|
||||
|
||||
Configure in `~/.claude/settings.json`:
|
||||
|
||||
- **fantomas**: Auto-format edited F# files
|
||||
- **dotnet build**: Verify the solution or project still compiles after edits
|
||||
- **dotnet test --no-build**: Re-run the nearest relevant test project after behavior changes
|
||||
|
||||
## Stop Hooks
|
||||
|
||||
- Run a final `dotnet build` before ending a session with broad F# changes
|
||||
- Warn on modified `appsettings*.json` files so secrets do not get committed
|
||||
@@ -1,111 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.fs"
|
||||
- "**/*.fsx"
|
||||
---
|
||||
# F# Patterns
|
||||
|
||||
> This file extends [common/patterns.md](../common/patterns.md) with F#-specific content.
|
||||
|
||||
## Result Type for Error Handling
|
||||
|
||||
Use `Result<'T, 'TError>` with railway-oriented programming instead of exceptions for expected failures.
|
||||
|
||||
```fsharp
|
||||
type OrderError =
|
||||
| InvalidCustomer of string
|
||||
| EmptyItems
|
||||
| ItemOutOfStock of sku: string
|
||||
|
||||
let validateOrder (request: CreateOrderRequest) : Result<ValidatedOrder, OrderError> =
|
||||
if String.IsNullOrWhiteSpace request.CustomerId then
|
||||
Error(InvalidCustomer "CustomerId is required")
|
||||
elif request.Items |> List.isEmpty then
|
||||
Error EmptyItems
|
||||
else
|
||||
Ok { CustomerId = request.CustomerId; Items = request.Items }
|
||||
```
|
||||
|
||||
## Option for Missing Values
|
||||
|
||||
Prefer `Option<'T>` over null. Use `Option.map`, `Option.bind`, and `Option.defaultValue` to transform.
|
||||
|
||||
```fsharp
|
||||
let findUser (id: Guid) : User option =
|
||||
users |> Map.tryFind id
|
||||
|
||||
let getUserEmail userId =
|
||||
findUser userId
|
||||
|> Option.map (fun u -> u.Email)
|
||||
|> Option.defaultValue "unknown@example.com"
|
||||
```
|
||||
|
||||
## Discriminated Unions for Domain Modeling
|
||||
|
||||
Model business states explicitly. The compiler enforces exhaustive handling.
|
||||
|
||||
```fsharp
|
||||
type PaymentState =
|
||||
| AwaitingPayment of amount: decimal
|
||||
| Paid of paidAt: DateTimeOffset * transactionId: string
|
||||
| Refunded of refundedAt: DateTimeOffset * reason: string
|
||||
| Failed of error: string
|
||||
|
||||
let describePayment = function
|
||||
| AwaitingPayment amount -> $"Awaiting payment of {amount:C}"
|
||||
| Paid (at, txn) -> $"Paid at {at} (txn: {txn})"
|
||||
| Refunded (at, reason) -> $"Refunded at {at}: {reason}"
|
||||
| Failed error -> $"Payment failed: {error}"
|
||||
```
|
||||
|
||||
## Computation Expressions
|
||||
|
||||
Use computation expressions to simplify sequential operations that may fail.
|
||||
|
||||
```fsharp
|
||||
let placeOrder request =
|
||||
result {
|
||||
let! validated = validateOrder request
|
||||
let! inventory = checkInventory validated.Items
|
||||
let! order = createOrder validated inventory
|
||||
return order
|
||||
}
|
||||
```
|
||||
|
||||
## Module Organization
|
||||
|
||||
- Group related functions in modules rather than classes
|
||||
- Use `[<RequireQualifiedAccess>]` to prevent name collisions
|
||||
- Keep modules small and focused on a single responsibility
|
||||
|
||||
```fsharp
|
||||
[<RequireQualifiedAccess>]
|
||||
module Order =
|
||||
let create customerId items = { Id = Guid.NewGuid(); CustomerId = customerId; Items = items; Status = Pending }
|
||||
let confirm order = { order with Status = Confirmed(DateTimeOffset.UtcNow) }
|
||||
let cancel reason order = { order with Status = Cancelled reason }
|
||||
```
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
- Define dependencies as function parameters or record-of-functions
|
||||
- Use interfaces sparingly, primarily at the boundary with .NET libraries
|
||||
- Prefer partial application for injecting dependencies into pipelines
|
||||
|
||||
```fsharp
|
||||
type OrderDeps =
|
||||
{ FindOrder: Guid -> Task<Order option>
|
||||
SaveOrder: Order -> Task<unit>
|
||||
SendNotification: Order -> Task<unit> }
|
||||
|
||||
let processOrder (deps: OrderDeps) orderId =
|
||||
task {
|
||||
match! deps.FindOrder orderId with
|
||||
| None -> return Error "Order not found"
|
||||
| Some order ->
|
||||
let confirmed = Order.confirm order
|
||||
do! deps.SaveOrder confirmed
|
||||
do! deps.SendNotification confirmed
|
||||
return Ok confirmed
|
||||
}
|
||||
```
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.fs"
|
||||
- "**/*.fsx"
|
||||
- "**/*.fsproj"
|
||||
- "**/appsettings*.json"
|
||||
---
|
||||
# F# Security
|
||||
|
||||
> This file extends [common/security.md](../common/security.md) with F#-specific content.
|
||||
|
||||
## Secret Management
|
||||
|
||||
- Never hardcode API keys, tokens, or connection strings in source code
|
||||
- Use environment variables, user secrets for local development, and a secret manager in production
|
||||
- Keep `appsettings.*.json` free of real credentials
|
||||
|
||||
```fsharp
|
||||
// BAD
|
||||
let apiKey = "sk-live-123"
|
||||
|
||||
// GOOD
|
||||
let apiKey =
|
||||
configuration["OpenAI:ApiKey"]
|
||||
|> Option.ofObj
|
||||
|> Option.defaultWith (fun () -> failwith "OpenAI:ApiKey is not configured.")
|
||||
```
|
||||
|
||||
## SQL Injection Prevention
|
||||
|
||||
- Always use parameterized queries with ADO.NET, Dapper, or EF Core
|
||||
- Never concatenate user input into SQL strings
|
||||
- Validate sort fields and filter operators before using dynamic query composition
|
||||
|
||||
```fsharp
|
||||
let findByCustomer (connection: IDbConnection) customerId =
|
||||
task {
|
||||
let sql = "SELECT * FROM Orders WHERE CustomerId = @customerId"
|
||||
return! connection.QueryAsync<Order>(sql, {| customerId = customerId |})
|
||||
}
|
||||
```
|
||||
|
||||
## Input Validation
|
||||
|
||||
- Validate inputs at the application boundary using types
|
||||
- Use single-case discriminated unions for validated values
|
||||
- Reject invalid input before it enters domain logic
|
||||
|
||||
```fsharp
|
||||
type ValidatedEmail = private ValidatedEmail of string
|
||||
|
||||
module ValidatedEmail =
|
||||
let create (input: string) =
|
||||
if System.Text.RegularExpressions.Regex.IsMatch(input, @"^[^@]+@[^@]+\.[^@]+$") then
|
||||
Ok(ValidatedEmail input)
|
||||
else
|
||||
Error "Invalid email address"
|
||||
|
||||
let value (ValidatedEmail v) = v
|
||||
```
|
||||
|
||||
## Authentication and Authorization
|
||||
|
||||
- Prefer framework auth handlers instead of custom token parsing
|
||||
- Enforce authorization policies at endpoint or handler boundaries
|
||||
- Never log raw tokens, passwords, or PII
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Return safe client-facing messages
|
||||
- Log detailed exceptions with structured context server-side
|
||||
- Do not expose stack traces, SQL text, or filesystem paths in API responses
|
||||
|
||||
## References
|
||||
|
||||
See skill: `security-review` for broader application security review checklists.
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.fs"
|
||||
- "**/*.fsx"
|
||||
- "**/*.fsproj"
|
||||
---
|
||||
# F# Testing
|
||||
|
||||
> This file extends [common/testing.md](../common/testing.md) with F#-specific content.
|
||||
|
||||
## Test Framework
|
||||
|
||||
- Prefer **xUnit** with **FsUnit.xUnit** for F#-friendly assertions
|
||||
- Use **Unquote** for quotation-based assertions with clear failure messages
|
||||
- Use **FsCheck.xUnit** for property-based testing
|
||||
- Use **NSubstitute** or function stubs for mocking dependencies
|
||||
- Use **Testcontainers** when integration tests need real infrastructure
|
||||
|
||||
## Test Organization
|
||||
|
||||
- Mirror `src/` structure under `tests/`
|
||||
- Separate unit, integration, and end-to-end coverage clearly
|
||||
- Name tests by behavior, not implementation details
|
||||
|
||||
```fsharp
|
||||
open Xunit
|
||||
open Swensen.Unquote
|
||||
|
||||
[<Fact>]
|
||||
let ``PlaceOrder returns success when request is valid`` () =
|
||||
let request = { CustomerId = "cust-123"; Items = [ validItem ] }
|
||||
let result = OrderService.placeOrder request
|
||||
test <@ Result.isOk result @>
|
||||
|
||||
[<Fact>]
|
||||
let ``PlaceOrder returns error when items are empty`` () =
|
||||
let request = { CustomerId = "cust-123"; Items = [] }
|
||||
let result = OrderService.placeOrder request
|
||||
test <@ Result.isError result @>
|
||||
```
|
||||
|
||||
## Property-Based Testing with FsCheck
|
||||
|
||||
```fsharp
|
||||
open FsCheck.Xunit
|
||||
|
||||
[<Property>]
|
||||
let ``order total is never negative`` (items: OrderItem list) =
|
||||
let total = Order.calculateTotal items
|
||||
total >= 0m
|
||||
```
|
||||
|
||||
## ASP.NET Core Integration Tests
|
||||
|
||||
- Use `WebApplicationFactory<TEntryPoint>` for API integration coverage
|
||||
- Test auth, validation, and serialization through HTTP, not by bypassing middleware
|
||||
|
||||
## Coverage
|
||||
|
||||
- Target 80%+ line coverage
|
||||
- Focus coverage on domain logic, validation, auth, and failure paths
|
||||
- Run `dotnet test` in CI with coverage collection enabled where available
|
||||
@@ -143,5 +143,4 @@ public record ApiResponse<T>(boolean success, T data, String error) {
|
||||
## References
|
||||
|
||||
See skill: `springboot-patterns` for Spring Boot architecture patterns.
|
||||
See skill: `quarkus-patterns` for Quarkus architecture patterns with REST, Panache, and messaging.
|
||||
See skill: `jpa-patterns` for entity design and query optimization.
|
||||
|
||||
@@ -97,5 +97,4 @@ try {
|
||||
## References
|
||||
|
||||
See skill: `springboot-security` for Spring Security authentication and authorization patterns.
|
||||
See skill: `quarkus-security` for Quarkus security with JWT/OIDC, RBAC, and CDI.
|
||||
See skill: `security-review` for general security checklists.
|
||||
|
||||
@@ -112,7 +112,6 @@ class OrderRepositoryIT {
|
||||
```
|
||||
|
||||
For Spring Boot integration tests, see skill: `springboot-tdd`.
|
||||
For Quarkus integration tests, see skill: `quarkus-tdd`.
|
||||
|
||||
## Test Naming
|
||||
|
||||
@@ -129,5 +128,4 @@ Use descriptive names with `@DisplayName`:
|
||||
## References
|
||||
|
||||
See skill: `springboot-tdd` for Spring Boot TDD patterns with MockMvc and Testcontainers.
|
||||
See skill: `quarkus-tdd` for Quarkus TDD patterns with REST Assured and Dev Services.
|
||||
See skill: `java-coding-standards` for testing expectations.
|
||||
|
||||
@@ -141,7 +141,7 @@ function parseReadmeExpectations(readmeContent) {
|
||||
},
|
||||
{
|
||||
category: 'commands',
|
||||
regex: /^\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*(\d+)\s*\|\s*Shared\s*\|\s*Instruction-based\s*\|\s*\d+\s*\|$/im,
|
||||
regex: /^\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*(\d+)\s*\|\s*Shared\s*\|\s*Instruction-based\s*\|\s*31\s*\|$/im,
|
||||
source: 'README.md parity table'
|
||||
},
|
||||
{
|
||||
@@ -223,7 +223,7 @@ function parseZhDocsReadmeExpectations(readmeContent) {
|
||||
},
|
||||
{
|
||||
category: 'commands',
|
||||
regex: /^\|\s*(?:\*\*)?命令(?:\*\*)?\s*\|\s*(\d+)\s*\|\s*共享\s*\|\s*基于指令\s*\|\s*\d+\s*\|$/im,
|
||||
regex: /^\|\s*(?:\*\*)?命令(?:\*\*)?\s*\|\s*(\d+)\s*\|\s*共享\s*\|\s*基于指令\s*\|\s*31\s*\|$/im,
|
||||
source: 'docs/zh-CN/README.md parity table'
|
||||
},
|
||||
{
|
||||
@@ -447,7 +447,7 @@ function syncEnglishReadme(content, catalog) {
|
||||
);
|
||||
nextContent = replaceOrThrow(
|
||||
nextContent,
|
||||
/^(\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*)(\d+)(\s*\|\s*Shared\s*\|\s*Instruction-based\s*\|\s*\d+\s*\|)$/im,
|
||||
/^(\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*)(\d+)(\s*\|\s*Shared\s*\|\s*Instruction-based\s*\|\s*31\s*\|)$/im,
|
||||
(_, prefix, __, suffix) => `${prefix}${catalog.commands.count}${suffix}`,
|
||||
'README.md parity table (commands)'
|
||||
);
|
||||
@@ -539,7 +539,7 @@ function syncZhDocsReadme(content, catalog) {
|
||||
);
|
||||
nextContent = replaceOrThrow(
|
||||
nextContent,
|
||||
/^(\|\s*(?:\*\*)?命令(?:\*\*)?\s*\|\s*)(\d+)(\s*\|\s*共享\s*\|\s*基于指令\s*\|\s*\d+\s*\|)$/im,
|
||||
/^(\|\s*(?:\*\*)?命令(?:\*\*)?\s*\|\s*)(\d+)(\s*\|\s*共享\s*\|\s*基于指令\s*\|\s*31\s*\|)$/im,
|
||||
(_, prefix, __, suffix) => `${prefix}${catalog.commands.count}${suffix}`,
|
||||
'docs/zh-CN/README.md parity table (commands)'
|
||||
);
|
||||
|
||||
@@ -14,9 +14,7 @@ const ignoredDirs = new Set([
|
||||
'node_modules',
|
||||
'.dmux',
|
||||
'.next',
|
||||
'.venv',
|
||||
'coverage',
|
||||
'venv',
|
||||
]);
|
||||
|
||||
const textExtensions = new Set([
|
||||
|
||||
+1
-29
@@ -11,21 +11,6 @@ const DEFAULT_TARGET = 'claude';
|
||||
const DEFAULT_LIMIT = 5;
|
||||
const MAX_LIMIT = 20;
|
||||
const SCHEMA_VERSION = 'ecc.consult.v1';
|
||||
const FUZZY_EXCLUDED_TOKENS = new Set(['review']);
|
||||
const MACHINE_LEARNING_CONTEXT_TOKENS = new Set([
|
||||
'data-science',
|
||||
'evals',
|
||||
'evaluation',
|
||||
'inference',
|
||||
'ml',
|
||||
'mle',
|
||||
'mlops',
|
||||
'model',
|
||||
'models',
|
||||
'pytorch',
|
||||
'serving',
|
||||
'training',
|
||||
]);
|
||||
|
||||
const STOP_WORDS = new Set([
|
||||
'a',
|
||||
@@ -89,7 +74,6 @@ const COMPONENT_ALIASES = Object.freeze({
|
||||
'mlops',
|
||||
'model',
|
||||
'models',
|
||||
'pytorch',
|
||||
'training',
|
||||
'inference',
|
||||
'serving',
|
||||
@@ -268,7 +252,6 @@ function scoreAgainstQuery(queryTokens, corpusTokens, options = {}) {
|
||||
|
||||
if (
|
||||
token.length >= 4
|
||||
&& !FUZZY_EXCLUDED_TOKENS.has(token)
|
||||
&& [...corpus].some(corpusToken => (
|
||||
corpusToken.length >= 4
|
||||
&& (corpusToken.includes(token) || token.includes(corpusToken))
|
||||
@@ -289,7 +272,6 @@ function scoreAgainstQuery(queryTokens, corpusTokens, options = {}) {
|
||||
function preferredComponentBonus(component, queryTokens) {
|
||||
let bonus = 0;
|
||||
const suffix = component.id.split(':')[1];
|
||||
const hasMachineLearningContext = queryTokens.some(token => MACHINE_LEARNING_CONTEXT_TOKENS.has(token));
|
||||
|
||||
if (queryTokens[0] === suffix) {
|
||||
bonus += 5;
|
||||
@@ -299,17 +281,7 @@ function preferredComponentBonus(component, queryTokens) {
|
||||
bonus += 3;
|
||||
}
|
||||
|
||||
if (component.id === 'agent:mle-reviewer' && hasMachineLearningContext) {
|
||||
bonus += 2;
|
||||
}
|
||||
|
||||
if (
|
||||
component.id === 'capability:security'
|
||||
&& (
|
||||
queryTokens.some(token => ['audit', 'security', 'threat', 'vulnerability'].includes(token))
|
||||
|| (!hasMachineLearningContext && queryTokens.includes('review'))
|
||||
)
|
||||
) {
|
||||
if (component.id === 'capability:security' && queryTokens.some(token => ['audit', 'review', 'security'].includes(token))) {
|
||||
bonus += 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const {
|
||||
ADAPTER_RECORDS,
|
||||
renderMarkdownTable,
|
||||
validateAdapterRecords,
|
||||
validateDocumentation,
|
||||
} = require('./lib/harness-adapter-compliance');
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = argv.slice(2);
|
||||
const parsed = {
|
||||
check: false,
|
||||
format: 'text',
|
||||
help: false,
|
||||
root: process.cwd(),
|
||||
};
|
||||
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
|
||||
if (arg === '--help' || arg === '-h') {
|
||||
parsed.help = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--check') {
|
||||
parsed.check = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--format') {
|
||||
parsed.format = String(args[index + 1] || '').toLowerCase();
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--format=')) {
|
||||
parsed.format = arg.slice('--format='.length).toLowerCase();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--root') {
|
||||
parsed.root = path.resolve(args[index + 1] || process.cwd());
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--root=')) {
|
||||
parsed.root = path.resolve(arg.slice('--root='.length));
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (!['text', 'json', 'markdown'].includes(parsed.format)) {
|
||||
throw new Error(`Invalid format: ${parsed.format}. Use text, json, or markdown.`);
|
||||
}
|
||||
|
||||
parsed.root = path.resolve(parsed.root);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
console.log([
|
||||
'Usage: node scripts/harness-adapter-compliance.js [options]',
|
||||
'',
|
||||
'Validate or render the ECC harness adapter compliance scorecard.',
|
||||
'',
|
||||
'Options:',
|
||||
' --check Fail if adapter records or docs are out of sync',
|
||||
' --format <text|json|markdown>',
|
||||
' --root <path> Repository root, defaults to cwd',
|
||||
' -h, --help Show this help',
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
function buildPayload(root) {
|
||||
const recordErrors = validateAdapterRecords();
|
||||
const documentationErrors = validateDocumentation({ repoRoot: root });
|
||||
|
||||
return {
|
||||
schema_version: 'ecc.harness-adapter-compliance.v1',
|
||||
generated_from: 'scripts/lib/harness-adapter-compliance.js',
|
||||
adapter_count: ADAPTER_RECORDS.length,
|
||||
valid: recordErrors.length === 0 && documentationErrors.length === 0,
|
||||
errors: [...recordErrors, ...documentationErrors],
|
||||
adapters: ADAPTER_RECORDS,
|
||||
};
|
||||
}
|
||||
|
||||
function renderText(payload) {
|
||||
const lines = [
|
||||
`Harness Adapter Compliance: ${payload.valid ? 'PASS' : 'FAIL'}`,
|
||||
`Adapters: ${payload.adapter_count}`,
|
||||
];
|
||||
|
||||
if (payload.errors.length > 0) {
|
||||
lines.push('Errors:');
|
||||
for (const error of payload.errors) {
|
||||
lines.push(`- ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function main() {
|
||||
let parsed;
|
||||
|
||||
try {
|
||||
parsed = parseArgs(process.argv);
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (parsed.help) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = buildPayload(parsed.root);
|
||||
|
||||
if (parsed.format === 'json') {
|
||||
console.log(JSON.stringify(payload, null, 2));
|
||||
} else if (parsed.format === 'markdown') {
|
||||
console.log(renderMarkdownTable());
|
||||
} else {
|
||||
console.log(renderText(payload));
|
||||
}
|
||||
|
||||
if (parsed.check && !payload.valid) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildPayload,
|
||||
parseArgs,
|
||||
};
|
||||
|
||||
+19
-150
@@ -187,157 +187,28 @@ function detectTargetMode(rootDir) {
|
||||
return 'consumer';
|
||||
}
|
||||
|
||||
const ECC_PLUGIN_KEY_PATTERNS = [
|
||||
/^ecc@/i,
|
||||
/^everything-claude-code@/i,
|
||||
];
|
||||
|
||||
const ECC_LEGACY_PLUGIN_DIRS = [
|
||||
'ecc',
|
||||
'ecc@ecc',
|
||||
'everything-claude-code',
|
||||
'everything-claude-code@everything-claude-code',
|
||||
];
|
||||
|
||||
const ECC_CACHE_MARKETPLACES = ['everything-claude-code', 'ecc'];
|
||||
const ECC_CACHE_PLUGIN_NAMES = ['ecc', 'everything-claude-code'];
|
||||
|
||||
function uniquePaths(paths) {
|
||||
return [...new Set(paths.filter(Boolean))];
|
||||
}
|
||||
|
||||
function compareVersionDesc(a, b) {
|
||||
const partsA = String(a).split('.').map(part => parseInt(part, 10) || 0);
|
||||
const partsB = String(b).split('.').map(part => parseInt(part, 10) || 0);
|
||||
const length = Math.max(partsA.length, partsB.length);
|
||||
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
const valueA = partsA[index] || 0;
|
||||
const valueB = partsB[index] || 0;
|
||||
if (valueA !== valueB) {
|
||||
return valueB - valueA;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function findPluginJsonUnder(installRoot) {
|
||||
const pluginJson = path.join(installRoot, '.claude-plugin', 'plugin.json');
|
||||
if (fs.existsSync(pluginJson)) {
|
||||
return pluginJson;
|
||||
}
|
||||
|
||||
const fallback = path.join(installRoot, 'plugin.json');
|
||||
return fs.existsSync(fallback) ? fallback : null;
|
||||
}
|
||||
|
||||
function findPluginInstallFromManifest(installedPluginsPaths) {
|
||||
for (const installedPath of installedPluginsPaths) {
|
||||
if (!fs.existsSync(installedPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const manifest = safeParseJson(safeRead(path.dirname(installedPath), path.basename(installedPath)));
|
||||
if (!manifest || !manifest.plugins) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(manifest.plugins)) {
|
||||
if (!ECC_PLUGIN_KEY_PATTERNS.some(pattern => pattern.test(key))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const entries = Array.isArray(value) ? value : [];
|
||||
for (const entry of entries) {
|
||||
if (!entry || typeof entry.installPath !== 'string' || !entry.installPath.trim()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const installRoot = path.isAbsolute(entry.installPath)
|
||||
? entry.installPath
|
||||
: path.resolve(path.dirname(installedPath), entry.installPath);
|
||||
const hit = findPluginJsonUnder(installRoot);
|
||||
if (hit) {
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findPluginInstallFlatLayout(candidateRoots) {
|
||||
for (const pluginsDir of candidateRoots) {
|
||||
for (const pluginDir of ECC_LEGACY_PLUGIN_DIRS) {
|
||||
const hit = findPluginJsonUnder(path.join(pluginsDir, pluginDir));
|
||||
if (hit) {
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findPluginInstallMarketplaceCache(candidateRoots) {
|
||||
for (const pluginsDir of candidateRoots) {
|
||||
for (const marketplace of ECC_CACHE_MARKETPLACES) {
|
||||
for (const pluginName of ECC_CACHE_PLUGIN_NAMES) {
|
||||
const pluginRoot = path.join(pluginsDir, 'cache', marketplace, pluginName);
|
||||
if (!fs.existsSync(pluginRoot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let versions = [];
|
||||
try {
|
||||
versions = fs
|
||||
.readdirSync(pluginRoot, { withFileTypes: true })
|
||||
.filter(entry => entry.isDirectory())
|
||||
.map(entry => entry.name)
|
||||
.sort(compareVersionDesc);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const version of versions) {
|
||||
const hit = findPluginJsonUnder(path.join(pluginRoot, version));
|
||||
if (hit) {
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findPluginInstall(rootDir) {
|
||||
const homeDirs = uniquePaths([
|
||||
process.env.HOME,
|
||||
process.env.USERPROFILE,
|
||||
os.homedir(),
|
||||
]);
|
||||
const pluginRoots = uniquePaths([
|
||||
const homeDir = process.env.HOME || process.env.USERPROFILE || os.homedir() || '';
|
||||
const pluginDirs = [
|
||||
'ecc',
|
||||
'ecc@ecc',
|
||||
'everything-claude-code',
|
||||
'everything-claude-code@everything-claude-code',
|
||||
];
|
||||
const candidateRoots = [
|
||||
path.join(rootDir, '.claude', 'plugins'),
|
||||
...homeDirs.map(homeDir => path.join(homeDir, '.claude', 'plugins')),
|
||||
]);
|
||||
const installedPluginsPaths = uniquePaths([
|
||||
path.join(rootDir, '.claude', 'plugins', 'installed_plugins.json'),
|
||||
...homeDirs.map(homeDir => path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json')),
|
||||
]);
|
||||
const flatRoots = uniquePaths([
|
||||
...pluginRoots,
|
||||
...pluginRoots.map(pluginsDir => path.join(pluginsDir, 'marketplaces')),
|
||||
]);
|
||||
|
||||
return (
|
||||
findPluginInstallFromManifest(installedPluginsPaths)
|
||||
|| findPluginInstallFlatLayout(flatRoots)
|
||||
|| findPluginInstallMarketplaceCache(pluginRoots)
|
||||
path.join(rootDir, '.claude', 'plugins', 'marketplaces'),
|
||||
homeDir && path.join(homeDir, '.claude', 'plugins'),
|
||||
homeDir && path.join(homeDir, '.claude', 'plugins', 'marketplaces'),
|
||||
].filter(Boolean);
|
||||
const candidates = candidateRoots.flatMap((pluginsDir) =>
|
||||
pluginDirs.flatMap((pluginDir) => [
|
||||
path.join(pluginsDir, pluginDir, '.claude-plugin', 'plugin.json'),
|
||||
path.join(pluginsDir, pluginDir, 'plugin.json'),
|
||||
])
|
||||
);
|
||||
|
||||
return candidates.find(candidate => fs.existsSync(candidate)) || null;
|
||||
}
|
||||
|
||||
function getRepoChecks(rootDir) {
|
||||
@@ -864,6 +735,4 @@ if (require.main === module) {
|
||||
module.exports = {
|
||||
buildReport,
|
||||
parseArgs,
|
||||
findPluginInstall,
|
||||
compareVersionDesc,
|
||||
};
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const { ensureDir, appendFile, getClaudeDir } = require('../lib/utils');
|
||||
const { estimateCost } = require('../lib/cost-estimate');
|
||||
const { sanitizeSessionId } = require('../lib/session-bridge');
|
||||
const {
|
||||
ensureDir,
|
||||
appendFile,
|
||||
getClaudeDir,
|
||||
} = require('../lib/utils');
|
||||
|
||||
const MAX_STDIN = 1024 * 1024;
|
||||
let raw = '';
|
||||
@@ -20,6 +22,23 @@ function toNumber(value) {
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
}
|
||||
|
||||
function estimateCost(model, inputTokens, outputTokens) {
|
||||
// Approximate per-1M-token blended rates. Conservative defaults.
|
||||
const table = {
|
||||
'haiku': { in: 0.8, out: 4.0 },
|
||||
'sonnet': { in: 3.0, out: 15.0 },
|
||||
'opus': { in: 15.0, out: 75.0 },
|
||||
};
|
||||
|
||||
const normalized = String(model || '').toLowerCase();
|
||||
let rates = table.sonnet;
|
||||
if (normalized.includes('haiku')) rates = table.haiku;
|
||||
if (normalized.includes('opus')) rates = table.opus;
|
||||
|
||||
const cost = (inputTokens / 1_000_000) * rates.in + (outputTokens / 1_000_000) * rates.out;
|
||||
return Math.round(cost * 1e6) / 1e6;
|
||||
}
|
||||
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', chunk => {
|
||||
if (raw.length < MAX_STDIN) {
|
||||
@@ -36,11 +55,7 @@ process.stdin.on('end', () => {
|
||||
const outputTokens = toNumber(usage.output_tokens || usage.completion_tokens || 0);
|
||||
|
||||
const model = String(input.model || input._cursor?.model || process.env.CLAUDE_MODEL || 'unknown');
|
||||
const sessionId =
|
||||
sanitizeSessionId(input.session_id) ||
|
||||
sanitizeSessionId(process.env.ECC_SESSION_ID) ||
|
||||
sanitizeSessionId(process.env.CLAUDE_SESSION_ID) ||
|
||||
'default';
|
||||
const sessionId = String(process.env.ECC_SESSION_ID || process.env.CLAUDE_SESSION_ID || 'default');
|
||||
|
||||
const metricsDir = path.join(getClaudeDir(), 'metrics');
|
||||
ensureDir(metricsDir);
|
||||
@@ -51,7 +66,7 @@ process.stdin.on('end', () => {
|
||||
model,
|
||||
input_tokens: inputTokens,
|
||||
output_tokens: outputTokens,
|
||||
estimated_cost_usd: estimateCost(model, inputTokens, outputTokens)
|
||||
estimated_cost_usd: estimateCost(model, inputTokens, outputTokens),
|
||||
};
|
||||
|
||||
appendFile(path.join(metricsDir, 'costs.jsonl'), `${JSON.stringify(row)}\n`);
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ECC Context Monitor — PostToolUse hook
|
||||
*
|
||||
* Reads bridge file from ecc-metrics-bridge.js and injects agent-facing
|
||||
* warnings when thresholds are crossed: context exhaustion, high cost,
|
||||
* scope creep, or tool loops.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { sanitizeSessionId, readBridge } = require('../lib/session-bridge');
|
||||
|
||||
const CONTEXT_WARNING_PCT = 35;
|
||||
const CONTEXT_CRITICAL_PCT = 25;
|
||||
const COST_NOTICE_USD = 5;
|
||||
const COST_WARNING_USD = 10;
|
||||
const COST_CRITICAL_USD = 50;
|
||||
const FILES_WARNING_COUNT = 20;
|
||||
const LOOP_THRESHOLD = 3;
|
||||
const STALE_SECONDS = 60;
|
||||
const DEBOUNCE_CALLS = 5;
|
||||
|
||||
/**
|
||||
* Get debounce state file path.
|
||||
* @param {string} sessionId
|
||||
* @returns {string}
|
||||
*/
|
||||
function getWarnPath(sessionId) {
|
||||
return path.join(os.tmpdir(), `ecc-ctx-warn-${sessionId}.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read debounce state.
|
||||
* @param {string} sessionId
|
||||
* @returns {object}
|
||||
*/
|
||||
function readWarnState(sessionId) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(getWarnPath(sessionId), 'utf8'));
|
||||
} catch {
|
||||
return { callsSinceWarn: 0, lastSeverity: null };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write debounce state.
|
||||
* @param {string} sessionId
|
||||
* @param {object} state
|
||||
*/
|
||||
function writeWarnState(sessionId, state) {
|
||||
const target = getWarnPath(sessionId);
|
||||
const tmp = `${target}.tmp`;
|
||||
fs.writeFileSync(tmp, JSON.stringify(state), 'utf8');
|
||||
fs.renameSync(tmp, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect tool loops from recent_tools ring buffer.
|
||||
* @param {Array} recentTools
|
||||
* @returns {{detected: boolean, tool: string, count: number}}
|
||||
*/
|
||||
function detectLoop(recentTools) {
|
||||
if (!Array.isArray(recentTools) || recentTools.length < LOOP_THRESHOLD) {
|
||||
return { detected: false, tool: '', count: 0 };
|
||||
}
|
||||
const counts = {};
|
||||
for (const entry of recentTools) {
|
||||
const key = `${entry.tool}:${entry.hash}`;
|
||||
counts[key] = (counts[key] || 0) + 1;
|
||||
}
|
||||
for (const [key, count] of Object.entries(counts)) {
|
||||
if (count >= LOOP_THRESHOLD) {
|
||||
return { detected: true, tool: key.split(':')[0], count };
|
||||
}
|
||||
}
|
||||
return { detected: false, tool: '', count: 0 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate all warning conditions against bridge data.
|
||||
* Returns array of {severity, type, message} sorted by severity desc.
|
||||
*/
|
||||
function evaluateConditions(bridge) {
|
||||
const warnings = [];
|
||||
const remaining = bridge.context_remaining_pct;
|
||||
|
||||
// Context warnings (skip if no context data)
|
||||
if (remaining !== null && remaining !== undefined) {
|
||||
if (remaining <= CONTEXT_CRITICAL_PCT) {
|
||||
warnings.push({
|
||||
severity: 3,
|
||||
type: 'context',
|
||||
message:
|
||||
`CONTEXT CRITICAL: ${remaining}% remaining. Context nearly exhausted. ` +
|
||||
'Inform the user that context is low and ask how they want to proceed. ' +
|
||||
'Do NOT autonomously save state or write handoff files unless the user asks.'
|
||||
});
|
||||
} else if (remaining <= CONTEXT_WARNING_PCT) {
|
||||
warnings.push({
|
||||
severity: 2,
|
||||
type: 'context',
|
||||
message: `CONTEXT WARNING: ${remaining}% remaining. ` + 'Be aware that context is getting limited. Avoid starting new complex work.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Cost warnings
|
||||
const cost = bridge.total_cost_usd || 0;
|
||||
if (cost > COST_CRITICAL_USD) {
|
||||
warnings.push({
|
||||
severity: 3,
|
||||
type: 'cost',
|
||||
message: `COST CRITICAL: Session cost is $${cost.toFixed(2)}. ` + 'Stop and inform the user about high cost before continuing.'
|
||||
});
|
||||
} else if (cost > COST_WARNING_USD) {
|
||||
warnings.push({
|
||||
severity: 2,
|
||||
type: 'cost',
|
||||
message: `COST WARNING: Session cost is $${cost.toFixed(2)}. ` + 'Review whether the current approach justifies the expense.'
|
||||
});
|
||||
} else if (cost > COST_NOTICE_USD) {
|
||||
warnings.push({
|
||||
severity: 1,
|
||||
type: 'cost',
|
||||
message: `COST NOTICE: Session cost is $${cost.toFixed(2)}. ` + 'Consider whether the current approach is efficient.'
|
||||
});
|
||||
}
|
||||
|
||||
// File scope warning
|
||||
const fileCount = bridge.files_modified_count || 0;
|
||||
if (fileCount > FILES_WARNING_COUNT) {
|
||||
warnings.push({
|
||||
severity: 2,
|
||||
type: 'scope',
|
||||
message: `SCOPE WARNING: ${fileCount} files modified this session. ` + 'Consider whether changes are too scattered.'
|
||||
});
|
||||
}
|
||||
|
||||
// Loop detection
|
||||
const loop = detectLoop(bridge.recent_tools);
|
||||
if (loop.detected) {
|
||||
warnings.push({
|
||||
severity: 2,
|
||||
type: 'loop',
|
||||
message: `LOOP WARNING: Tool '${loop.tool}' called ${loop.count} times ` + 'with same parameters in last 5 calls. This may indicate a stuck loop.'
|
||||
});
|
||||
}
|
||||
|
||||
return warnings.sort((a, b) => b.severity - a.severity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map numeric severity to label.
|
||||
*/
|
||||
function severityLabel(n) {
|
||||
if (n >= 3) return 'critical';
|
||||
if (n >= 2) return 'warning';
|
||||
return 'notice';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} rawInput - Raw JSON string from stdin
|
||||
* @returns {string} JSON output with additionalContext or pass-through
|
||||
*/
|
||||
function run(rawInput) {
|
||||
try {
|
||||
const input = rawInput.trim() ? JSON.parse(rawInput) : {};
|
||||
|
||||
const sessionId = sanitizeSessionId(input.session_id) || sanitizeSessionId(process.env.ECC_SESSION_ID) || sanitizeSessionId(process.env.CLAUDE_SESSION_ID);
|
||||
|
||||
if (!sessionId) return rawInput;
|
||||
|
||||
const bridge = readBridge(sessionId);
|
||||
if (!bridge) return rawInput;
|
||||
|
||||
// Stale check for context warnings
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const lastTs = bridge.last_timestamp ? Math.floor(new Date(bridge.last_timestamp).getTime() / 1000) : 0;
|
||||
const isStale = lastTs > 0 && now - lastTs > STALE_SECONDS;
|
||||
|
||||
// If bridge is stale, null out context data (still check cost/scope/loop)
|
||||
const evalBridge = isStale ? { ...bridge, context_remaining_pct: null } : bridge;
|
||||
|
||||
const warnings = evaluateConditions(evalBridge);
|
||||
if (warnings.length === 0) return rawInput;
|
||||
|
||||
// Debounce logic
|
||||
const warnState = readWarnState(sessionId);
|
||||
warnState.callsSinceWarn = (warnState.callsSinceWarn || 0) + 1;
|
||||
|
||||
const topSeverity = severityLabel(warnings[0].severity);
|
||||
const severityEscalated = topSeverity === 'critical' && warnState.lastSeverity !== 'critical';
|
||||
|
||||
const isFirst = !warnState.lastSeverity;
|
||||
if (!isFirst && warnState.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
|
||||
writeWarnState(sessionId, warnState);
|
||||
return rawInput;
|
||||
}
|
||||
|
||||
// Reset debounce, emit warning
|
||||
warnState.callsSinceWarn = 0;
|
||||
warnState.lastSeverity = topSeverity;
|
||||
writeWarnState(sessionId, warnState);
|
||||
|
||||
// Combine top 2 warnings
|
||||
const message = warnings
|
||||
.slice(0, 2)
|
||||
.map(w => w.message)
|
||||
.join('\n');
|
||||
|
||||
const output = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'PostToolUse',
|
||||
additionalContext: message
|
||||
}
|
||||
};
|
||||
|
||||
return JSON.stringify(output);
|
||||
} catch {
|
||||
// Never block tool execution
|
||||
return rawInput;
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
let data = '';
|
||||
const MAX_STDIN = 1024 * 1024;
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', chunk => {
|
||||
if (data.length < MAX_STDIN) data += chunk.substring(0, MAX_STDIN - data.length);
|
||||
});
|
||||
process.stdin.on('end', () => {
|
||||
process.stdout.write(run(data));
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { run, evaluateConditions, detectLoop, severityLabel };
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ECC Metrics Bridge — PostToolUse hook
|
||||
*
|
||||
* Maintains a running session aggregate in /tmp/ecc-metrics-{session}.json.
|
||||
* This bridge file is read by ecc-statusline.js and ecc-context-monitor.js,
|
||||
* avoiding the need to scan large JSONL logs on every invocation.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { sanitizeSessionId, readBridge, writeBridgeAtomic } = require('../lib/session-bridge');
|
||||
const { getClaudeDir } = require('../lib/utils');
|
||||
|
||||
const MAX_STDIN = 1024 * 1024;
|
||||
const MAX_FILES_TRACKED = 200;
|
||||
const RECENT_TOOLS_SIZE = 5;
|
||||
const HASH_INPUT_LIMIT = 2048;
|
||||
|
||||
function toNumber(value) {
|
||||
const n = Number(value);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
}
|
||||
|
||||
function stableStringify(value, depth = 0) {
|
||||
if (depth > 4) return '[depth-limit]';
|
||||
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
||||
if (Array.isArray(value)) {
|
||||
return `[${value.map(item => stableStringify(item, depth + 1)).join(',')}]`;
|
||||
}
|
||||
return `{${Object.keys(value)
|
||||
.sort()
|
||||
.map(key => `${JSON.stringify(key)}:${stableStringify(value[key], depth + 1)}`)
|
||||
.join(',')}}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash tool call for loop detection.
|
||||
* Uses tool name + a key parameter when available, otherwise a stable input digest.
|
||||
*/
|
||||
function hashToolCall(toolName, toolInput) {
|
||||
const name = String(toolName || '');
|
||||
let key = '';
|
||||
if (name === 'Bash') {
|
||||
key = String(toolInput?.command || '').slice(0, 160);
|
||||
} else if (toolInput?.file_path) {
|
||||
key = String(toolInput.file_path);
|
||||
} else {
|
||||
key = stableStringify(toolInput || {}).slice(0, HASH_INPUT_LIMIT);
|
||||
}
|
||||
return crypto.createHash('sha256').update(`${name}:${key}`).digest('hex').slice(0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract modified file paths from tool input.
|
||||
*/
|
||||
function extractFilePaths(toolName, toolInput) {
|
||||
const paths = [];
|
||||
if (!toolInput || typeof toolInput !== 'object') return paths;
|
||||
|
||||
const fp = toolInput.file_path;
|
||||
if (fp && typeof fp === 'string') paths.push(fp);
|
||||
|
||||
const edits = toolInput.edits;
|
||||
if (Array.isArray(edits)) {
|
||||
for (const edit of edits) {
|
||||
if (edit?.file_path && typeof edit.file_path === 'string') {
|
||||
paths.push(edit.file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read cumulative cost for a session from the tail of costs.jsonl.
|
||||
* Reads last 8KB to avoid scanning entire file.
|
||||
*/
|
||||
function readSessionCost(sessionId) {
|
||||
try {
|
||||
const costsPath = path.join(getClaudeDir(), 'metrics', 'costs.jsonl');
|
||||
const stat = fs.statSync(costsPath);
|
||||
const readSize = Math.min(stat.size, 8192);
|
||||
const fd = fs.openSync(costsPath, 'r');
|
||||
try {
|
||||
const buf = Buffer.alloc(readSize);
|
||||
fs.readSync(fd, buf, 0, readSize, Math.max(0, stat.size - readSize));
|
||||
const lines = buf.toString('utf8').split('\n').filter(Boolean);
|
||||
|
||||
let totalCost = 0;
|
||||
let totalIn = 0;
|
||||
let totalOut = 0;
|
||||
for (const line of lines) {
|
||||
try {
|
||||
const row = JSON.parse(line);
|
||||
if (row.session_id === sessionId) {
|
||||
totalCost += toNumber(row.estimated_cost_usd);
|
||||
totalIn += toNumber(row.input_tokens);
|
||||
totalOut += toNumber(row.output_tokens);
|
||||
}
|
||||
} catch {
|
||||
/* skip malformed lines */
|
||||
}
|
||||
}
|
||||
return { totalCost, totalIn, totalOut };
|
||||
} finally {
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
} catch {
|
||||
return { totalCost: 0, totalIn: 0, totalOut: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} rawInput - Raw JSON string from stdin
|
||||
* @returns {string} Pass-through
|
||||
*/
|
||||
function run(rawInput) {
|
||||
try {
|
||||
const input = rawInput.trim() ? JSON.parse(rawInput) : {};
|
||||
const toolName = String(input.tool_name || '');
|
||||
const toolInput = input.tool_input || {};
|
||||
|
||||
const sessionId = sanitizeSessionId(input.session_id) || sanitizeSessionId(process.env.ECC_SESSION_ID) || sanitizeSessionId(process.env.CLAUDE_SESSION_ID);
|
||||
|
||||
if (!sessionId) return rawInput;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const bridge = readBridge(sessionId) || {
|
||||
session_id: sessionId,
|
||||
total_cost_usd: 0,
|
||||
total_input_tokens: 0,
|
||||
total_output_tokens: 0,
|
||||
tool_count: 0,
|
||||
files_modified_count: 0,
|
||||
files_modified: [],
|
||||
recent_tools: [],
|
||||
first_timestamp: now,
|
||||
last_timestamp: now,
|
||||
context_remaining_pct: null
|
||||
};
|
||||
|
||||
// Increment tool count
|
||||
bridge.tool_count = (bridge.tool_count || 0) + 1;
|
||||
bridge.last_timestamp = now;
|
||||
if (!bridge.first_timestamp) bridge.first_timestamp = now;
|
||||
|
||||
// Track modified files (Write/Edit/MultiEdit only)
|
||||
const isWriteOp = /^(Write|Edit|MultiEdit)$/i.test(toolName);
|
||||
if (isWriteOp) {
|
||||
const newPaths = extractFilePaths(toolName, toolInput);
|
||||
const existing = new Set(bridge.files_modified || []);
|
||||
for (const p of newPaths) {
|
||||
if (existing.size < MAX_FILES_TRACKED && !existing.has(p)) {
|
||||
existing.add(p);
|
||||
}
|
||||
}
|
||||
bridge.files_modified = [...existing];
|
||||
bridge.files_modified_count = existing.size;
|
||||
}
|
||||
|
||||
// Ring buffer for loop detection
|
||||
const recent = bridge.recent_tools || [];
|
||||
recent.push({ tool: toolName, hash: hashToolCall(toolName, toolInput) });
|
||||
if (recent.length > RECENT_TOOLS_SIZE) recent.shift();
|
||||
bridge.recent_tools = recent;
|
||||
|
||||
// Update cost from costs.jsonl tail
|
||||
const costs = readSessionCost(sessionId);
|
||||
bridge.total_cost_usd = Math.round(costs.totalCost * 1e6) / 1e6;
|
||||
bridge.total_input_tokens = costs.totalIn;
|
||||
bridge.total_output_tokens = costs.totalOut;
|
||||
|
||||
writeBridgeAtomic(sessionId, bridge);
|
||||
} catch {
|
||||
// Never block tool execution
|
||||
}
|
||||
|
||||
return rawInput;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
let data = '';
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', chunk => {
|
||||
if (data.length < MAX_STDIN) data += chunk.substring(0, MAX_STDIN - data.length);
|
||||
});
|
||||
process.stdin.on('end', () => {
|
||||
process.stdout.write(run(data));
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { run, hashToolCall, extractFilePaths, readSessionCost, stableStringify };
|
||||
@@ -1,168 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ECC Statusline — statusLine command
|
||||
*
|
||||
* Displays: model | task | $cost Nt Nf Nm | dir ██░░ N%
|
||||
*
|
||||
* Registered in settings.json under "statusLine", not in hooks.json.
|
||||
* Reads bridge file from ecc-metrics-bridge.js and stdin from Claude Code runtime.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { sanitizeSessionId, readBridge, writeBridgeAtomic } = require('../lib/session-bridge');
|
||||
|
||||
const AUTO_COMPACT_BUFFER_PCT = 16.5;
|
||||
const MAX_STDIN = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Format duration from ISO timestamp to now.
|
||||
* @param {string} isoTimestamp
|
||||
* @returns {string} e.g. "5s", "12m", "1h23m"
|
||||
*/
|
||||
function formatDuration(isoTimestamp) {
|
||||
if (!isoTimestamp) return '?';
|
||||
const elapsed = Math.floor((Date.now() - new Date(isoTimestamp).getTime()) / 1000);
|
||||
if (elapsed < 0) return '?';
|
||||
if (elapsed < 60) return `${elapsed}s`;
|
||||
const mins = Math.floor(elapsed / 60);
|
||||
if (mins < 60) return `${mins}m`;
|
||||
const hours = Math.floor(mins / 60);
|
||||
const remMins = mins % 60;
|
||||
return remMins > 0 ? `${hours}h${remMins}m` : `${hours}h`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build context progress bar with ANSI colors.
|
||||
* @param {number} remaining - Raw remaining percentage from Claude Code
|
||||
* @returns {string} Colored bar string
|
||||
*/
|
||||
function buildContextBar(remaining) {
|
||||
if (remaining === null || remaining === undefined) return '';
|
||||
|
||||
const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
|
||||
const used = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
|
||||
|
||||
const filled = Math.floor(used / 10);
|
||||
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(10 - filled);
|
||||
|
||||
if (used < 50) return ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
||||
if (used < 65) return ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
||||
if (used < 80) return ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
||||
return ` \x1b[5;31m${bar} ${used}%\x1b[0m`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read current in-progress task from todos directory.
|
||||
* @param {string} sessionId
|
||||
* @returns {string} Task activeForm text or empty string
|
||||
*/
|
||||
function readCurrentTask(sessionId) {
|
||||
try {
|
||||
const safeSessionId = sanitizeSessionId(sessionId);
|
||||
if (!safeSessionId) return '';
|
||||
|
||||
const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
||||
const todosDir = path.join(claudeDir, 'todos');
|
||||
if (!fs.existsSync(todosDir)) return '';
|
||||
|
||||
const files = fs
|
||||
.readdirSync(todosDir)
|
||||
.filter(f => f.startsWith(safeSessionId) && f.includes('-agent-') && f.endsWith('.json'))
|
||||
.map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
|
||||
if (files.length === 0) return '';
|
||||
|
||||
const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
|
||||
const inProgress = todos.find(t => t.status === 'in_progress');
|
||||
return inProgress?.activeForm || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function runStatusline() {
|
||||
let input = '';
|
||||
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', chunk => {
|
||||
if (input.length < MAX_STDIN) {
|
||||
input += chunk.substring(0, MAX_STDIN - input.length);
|
||||
}
|
||||
});
|
||||
process.stdin.on('end', () => {
|
||||
clearTimeout(stdinTimeout);
|
||||
try {
|
||||
const data = JSON.parse(input);
|
||||
const model = data.model?.display_name || 'Claude';
|
||||
const dir = data.workspace?.current_dir || process.cwd();
|
||||
const session = data.session_id || '';
|
||||
const remaining = data.context_window?.remaining_percentage;
|
||||
|
||||
const sessionId = sanitizeSessionId(session);
|
||||
const bridge = sessionId ? readBridge(sessionId) : null;
|
||||
|
||||
// Write context % back to bridge for context-monitor
|
||||
if (sessionId && bridge && remaining !== null && remaining !== undefined) {
|
||||
bridge.context_remaining_pct = remaining;
|
||||
try {
|
||||
writeBridgeAtomic(sessionId, bridge);
|
||||
} catch {
|
||||
/* best effort */
|
||||
}
|
||||
}
|
||||
|
||||
// Current task
|
||||
const task = sessionId ? readCurrentTask(sessionId) : '';
|
||||
|
||||
// Metrics from bridge
|
||||
let metricsStr = '';
|
||||
if (bridge) {
|
||||
const parts = [];
|
||||
if (bridge.total_cost_usd > 0) {
|
||||
parts.push(`$${bridge.total_cost_usd.toFixed(2)}`);
|
||||
}
|
||||
if (bridge.tool_count > 0) {
|
||||
parts.push(`${bridge.tool_count}t`);
|
||||
}
|
||||
if (bridge.files_modified_count > 0) {
|
||||
parts.push(`${bridge.files_modified_count}f`);
|
||||
}
|
||||
const dur = formatDuration(bridge.first_timestamp);
|
||||
if (dur !== '?') {
|
||||
parts.push(dur);
|
||||
}
|
||||
if (parts.length > 0) {
|
||||
metricsStr = `\x1b[36m${parts.join(' ')}\x1b[0m`;
|
||||
}
|
||||
}
|
||||
|
||||
// Context bar
|
||||
const ctx = buildContextBar(remaining);
|
||||
|
||||
// Build output
|
||||
const dirname = path.basename(dir);
|
||||
const segments = [`\x1b[2m${model}\x1b[0m`];
|
||||
|
||||
if (task) {
|
||||
segments.push(`\x1b[1m${task}\x1b[0m`);
|
||||
}
|
||||
if (metricsStr) {
|
||||
segments.push(metricsStr);
|
||||
}
|
||||
segments.push(`\x1b[2m${dirname}\x1b[0m`);
|
||||
|
||||
process.stdout.write(segments.join(' \x1b[2m\u2502\x1b[0m ') + ctx);
|
||||
} catch {
|
||||
// Silent fail
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { formatDuration, buildContextBar, readCurrentTask, MAX_STDIN };
|
||||
|
||||
if (require.main === module) runStatusline();
|
||||
@@ -33,8 +33,6 @@ const MAX_INJECTED_LEARNED_SKILLS = 6;
|
||||
const MAX_LEARNED_SKILL_SUMMARY_CHARS = 220;
|
||||
const DEFAULT_SESSION_START_CONTEXT_MAX_CHARS = 8000;
|
||||
const DEFAULT_SESSION_RETENTION_DAYS = 30;
|
||||
const SESSION_START_MODE_INVALID = 'invalid';
|
||||
const SESSION_START_MODE_SKIP = 'skip';
|
||||
|
||||
/**
|
||||
* Resolve a filesystem path to its canonical (real) form.
|
||||
@@ -103,33 +101,6 @@ function getSessionStartMaxContextChars() {
|
||||
return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_SESSION_START_CONTEXT_MAX_CHARS;
|
||||
}
|
||||
|
||||
function getSessionStartMode(rawInput) {
|
||||
const input = String(rawInput || '');
|
||||
if (!input.trim()) return null;
|
||||
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(input);
|
||||
} catch {
|
||||
log(`[SessionStart] Invalid stdin payload; skipping previous session summary injection. Length: ${input.length}`);
|
||||
return SESSION_START_MODE_INVALID;
|
||||
}
|
||||
|
||||
const supportedModes = new Set(['startup', 'resume', 'clear', 'compact']);
|
||||
const hookName = typeof payload.hookName === 'string' ? payload.hookName.trim() : '';
|
||||
if (hookName.startsWith('SessionStart:')) {
|
||||
const mode = hookName.slice('SessionStart:'.length).trim().toLowerCase();
|
||||
return supportedModes.has(mode) ? mode : SESSION_START_MODE_SKIP;
|
||||
}
|
||||
|
||||
if (payload.hook_event_name === 'SessionStart') {
|
||||
const mode = typeof payload.source === 'string' ? payload.source.trim().toLowerCase() : '';
|
||||
return supportedModes.has(mode) ? mode : SESSION_START_MODE_SKIP;
|
||||
}
|
||||
|
||||
return SESSION_START_MODE_SKIP;
|
||||
}
|
||||
|
||||
function limitSessionStartContext(additionalContext, maxChars = getSessionStartMaxContextChars()) {
|
||||
const context = String(additionalContext || '');
|
||||
|
||||
@@ -197,8 +168,8 @@ function pruneExpiredSessions(searchDirs, retentionDays) {
|
||||
*
|
||||
* Priority (highest to lowest):
|
||||
* 1. Exact worktree (cwd) match — most recent
|
||||
* 2. Same project name match for legacy sessions without Worktree metadata
|
||||
* 3. No injection when sessions belong to a different worktree/project
|
||||
* 2. Same project name match — most recent
|
||||
* 3. Fallback to overall most recent (original behavior)
|
||||
*
|
||||
* Sessions are already sorted newest-first, so the first match in each
|
||||
* category wins.
|
||||
@@ -218,12 +189,18 @@ function selectMatchingSession(sessions, cwd, currentProject) {
|
||||
|
||||
let projectMatch = null;
|
||||
let projectMatchContent = null;
|
||||
let readableSessions = 0;
|
||||
let fallbackSession = null;
|
||||
let fallbackContent = null;
|
||||
|
||||
for (const session of sessions) {
|
||||
const content = readFile(session.path);
|
||||
if (!content) continue;
|
||||
readableSessions++;
|
||||
|
||||
// Cache first readable session+content pair for fallback
|
||||
if (!fallbackSession) {
|
||||
fallbackSession = session;
|
||||
fallbackContent = content;
|
||||
}
|
||||
|
||||
// Extract **Worktree:** field
|
||||
const worktreeMatch = content.match(/\*\*Worktree:\*\*\s*(.+)$/m);
|
||||
@@ -235,9 +212,8 @@ function selectMatchingSession(sessions, cwd, currentProject) {
|
||||
return { session, content, matchReason: 'worktree' };
|
||||
}
|
||||
|
||||
// Project name match is only safe for legacy session files written before
|
||||
// Worktree metadata existed. A different explicit Worktree is not a match.
|
||||
if (!projectMatch && currentProject && !sessionWorktree) {
|
||||
// Project name match — keep searching for a worktree match
|
||||
if (!projectMatch && currentProject) {
|
||||
const projectFieldMatch = content.match(/\*\*Project:\*\*\s*(.+)$/m);
|
||||
const sessionProject = projectFieldMatch ? projectFieldMatch[1].trim() : '';
|
||||
if (sessionProject && sessionProject === currentProject) {
|
||||
@@ -251,9 +227,12 @@ function selectMatchingSession(sessions, cwd, currentProject) {
|
||||
return { session: projectMatch, content: projectMatchContent, matchReason: 'project' };
|
||||
}
|
||||
|
||||
log(readableSessions > 0
|
||||
? '[SessionStart] No worktree/project session match found'
|
||||
: '[SessionStart] All session files were unreadable');
|
||||
// Fallback: most recent readable session (original behavior)
|
||||
if (fallbackSession) {
|
||||
return { session: fallbackSession, content: fallbackContent, matchReason: 'recency-fallback' };
|
||||
}
|
||||
|
||||
log('[SessionStart] All session files were unreadable');
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -519,7 +498,6 @@ async function main() {
|
||||
const maxContextChars = getSessionStartMaxContextChars();
|
||||
const explicitContextDisabled = isSessionStartContextDisabled();
|
||||
const shouldInjectContext = !explicitContextDisabled && maxContextChars !== 0;
|
||||
const sessionStartMode = getSessionStartMode(fs.readFileSync(0, 'utf8'));
|
||||
|
||||
// Ensure directories exist
|
||||
ensureDir(sessionsDir);
|
||||
@@ -554,59 +532,50 @@ async function main() {
|
||||
additionalContextParts.push(instinctSummary);
|
||||
}
|
||||
|
||||
if (sessionStartMode && sessionStartMode !== 'startup') {
|
||||
const reason = sessionStartMode === SESSION_START_MODE_INVALID
|
||||
? 'invalid stdin payload'
|
||||
: sessionStartMode === SESSION_START_MODE_SKIP
|
||||
? 'unrecognized SessionStart payload'
|
||||
: `non-startup SessionStart mode: ${sessionStartMode}`;
|
||||
log(`[SessionStart] Skipping previous session summary injection for ${reason}`);
|
||||
} else {
|
||||
// Check for recent session files (last 7 days)
|
||||
const recentSessions = dedupeRecentSessions(sessionSearchDirs);
|
||||
// Check for recent session files (last 7 days)
|
||||
const recentSessions = dedupeRecentSessions(sessionSearchDirs);
|
||||
|
||||
if (recentSessions.length > 0) {
|
||||
log(`[SessionStart] Found ${recentSessions.length} recent session(s)`);
|
||||
if (recentSessions.length > 0) {
|
||||
log(`[SessionStart] Found ${recentSessions.length} recent session(s)`);
|
||||
|
||||
// Prefer a session that matches the current working directory or project.
|
||||
// Session files contain **Project:** and **Worktree:** header fields written
|
||||
// by session-end.js, so we can match against them.
|
||||
const cwd = process.cwd();
|
||||
const currentProject = getProjectName() || '';
|
||||
// Prefer a session that matches the current working directory or project.
|
||||
// Session files contain **Project:** and **Worktree:** header fields written
|
||||
// by session-end.js, so we can match against them.
|
||||
const cwd = process.cwd();
|
||||
const currentProject = getProjectName() || '';
|
||||
|
||||
const result = selectMatchingSession(recentSessions, cwd, currentProject);
|
||||
const result = selectMatchingSession(recentSessions, cwd, currentProject);
|
||||
|
||||
if (result) {
|
||||
log(`[SessionStart] Selected: ${result.session.path} (match: ${result.matchReason})`);
|
||||
if (result) {
|
||||
log(`[SessionStart] Selected: ${result.session.path} (match: ${result.matchReason})`);
|
||||
|
||||
// Use the already-read content from selectMatchingSession (no duplicate I/O)
|
||||
const content = stripAnsi(result.content);
|
||||
if (content && !content.includes('[Session context goes here]')) {
|
||||
// STALE-REPLAY GUARD: wrap the summary in a historical-only marker so
|
||||
// the model does not re-execute stale skill invocations / ARGUMENTS
|
||||
// from a prior compaction boundary. Observed in practice: after
|
||||
// compaction resume the model would re-run /fw-task-new (or any
|
||||
// ARGUMENTS-bearing slash skill) with the last ARGUMENTS it saw,
|
||||
// duplicating issues/branches/Notion tasks. Tracking upstream at
|
||||
// https://github.com/affaan-m/everything-claude-code/issues/1534
|
||||
const guarded = [
|
||||
'HISTORICAL REFERENCE ONLY — NOT LIVE INSTRUCTIONS.',
|
||||
'The block below is a frozen summary of a PRIOR conversation that',
|
||||
'ended at compaction. Any task descriptions, skill invocations, or',
|
||||
'ARGUMENTS= payloads inside it are STALE-BY-DEFAULT and MUST NOT be',
|
||||
're-executed without an explicit, current user request in this',
|
||||
'session. Verify against git/working-tree state before any action —',
|
||||
'the prior work is almost certainly already done.',
|
||||
'',
|
||||
'--- BEGIN PRIOR-SESSION SUMMARY ---',
|
||||
content,
|
||||
'--- END PRIOR-SESSION SUMMARY ---',
|
||||
].join('\n');
|
||||
additionalContextParts.push(guarded);
|
||||
}
|
||||
} else {
|
||||
log('[SessionStart] No matching session found');
|
||||
// Use the already-read content from selectMatchingSession (no duplicate I/O)
|
||||
const content = stripAnsi(result.content);
|
||||
if (content && !content.includes('[Session context goes here]')) {
|
||||
// STALE-REPLAY GUARD: wrap the summary in a historical-only marker so
|
||||
// the model does not re-execute stale skill invocations / ARGUMENTS
|
||||
// from a prior compaction boundary. Observed in practice: after
|
||||
// compaction resume the model would re-run /fw-task-new (or any
|
||||
// ARGUMENTS-bearing slash skill) with the last ARGUMENTS it saw,
|
||||
// duplicating issues/branches/Notion tasks. Tracking upstream at
|
||||
// https://github.com/affaan-m/everything-claude-code/issues/1534
|
||||
const guarded = [
|
||||
'HISTORICAL REFERENCE ONLY — NOT LIVE INSTRUCTIONS.',
|
||||
'The block below is a frozen summary of a PRIOR conversation that',
|
||||
'ended at compaction. Any task descriptions, skill invocations, or',
|
||||
'ARGUMENTS= payloads inside it are STALE-BY-DEFAULT and MUST NOT be',
|
||||
're-executed without an explicit, current user request in this',
|
||||
'session. Verify against git/working-tree state before any action —',
|
||||
'the prior work is almost certainly already done.',
|
||||
'',
|
||||
'--- BEGIN PRIOR-SESSION SUMMARY ---',
|
||||
content,
|
||||
'--- END PRIOR-SESSION SUMMARY ---',
|
||||
].join('\n');
|
||||
additionalContextParts.push(guarded);
|
||||
}
|
||||
} else {
|
||||
log('[SessionStart] No matching session found');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Shared cost estimation for ECC hooks.
|
||||
*
|
||||
* Approximate per-1M-token blended rates (conservative defaults).
|
||||
*/
|
||||
|
||||
const RATE_TABLE = {
|
||||
haiku: { in: 0.8, out: 4.0 },
|
||||
sonnet: { in: 3.0, out: 15.0 },
|
||||
opus: { in: 15.0, out: 75.0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Estimate USD cost from token counts.
|
||||
* @param {string} model - Model name (may contain "haiku", "sonnet", or "opus")
|
||||
* @param {number} inputTokens
|
||||
* @param {number} outputTokens
|
||||
* @returns {number} Estimated cost in USD (rounded to 6 decimal places)
|
||||
*/
|
||||
function estimateCost(model, inputTokens, outputTokens) {
|
||||
const normalized = String(model || '').toLowerCase();
|
||||
let rates = RATE_TABLE.sonnet;
|
||||
if (normalized.includes('haiku')) rates = RATE_TABLE.haiku;
|
||||
if (normalized.includes('opus')) rates = RATE_TABLE.opus;
|
||||
|
||||
const cost = (inputTokens / 1_000_000) * rates.in + (outputTokens / 1_000_000) * rates.out;
|
||||
return Math.round(cost * 1e6) / 1e6;
|
||||
}
|
||||
|
||||
module.exports = { estimateCost, RATE_TABLE };
|
||||
@@ -1,446 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const MATRIX_BLOCK_START = '<!-- harness-adapter-compliance:matrix-start -->';
|
||||
const MATRIX_BLOCK_END = '<!-- harness-adapter-compliance:matrix-end -->';
|
||||
|
||||
const COMPLIANCE_STATES = Object.freeze({
|
||||
Native: 'ECC can install or verify the surface directly for this harness.',
|
||||
'Adapter-backed': 'ECC has a thin adapter, plugin, or package surface, but parity differs by harness.',
|
||||
'Instruction-backed': 'ECC can provide the guidance and files, but the harness does not expose the runtime hook/session surface ECC needs for enforcement.',
|
||||
'Reference-only': 'The tool is useful as a design pressure or external runtime, but ECC does not yet ship a direct installer or adapter for it.',
|
||||
});
|
||||
|
||||
const REQUIRED_FIELDS = Object.freeze([
|
||||
'id',
|
||||
'harness',
|
||||
'state',
|
||||
'supported_assets',
|
||||
'unsupported_surfaces',
|
||||
'install_or_onramp',
|
||||
'verification_commands',
|
||||
'risk_notes',
|
||||
'last_verified_at',
|
||||
'owner',
|
||||
'source_docs',
|
||||
]);
|
||||
|
||||
function freezeRecord(record) {
|
||||
return Object.freeze({
|
||||
...record,
|
||||
supported_assets: Object.freeze(record.supported_assets.slice()),
|
||||
unsupported_surfaces: Object.freeze(record.unsupported_surfaces.slice()),
|
||||
install_or_onramp: Object.freeze(record.install_or_onramp.slice()),
|
||||
verification_commands: Object.freeze(record.verification_commands.slice()),
|
||||
risk_notes: Object.freeze(record.risk_notes.slice()),
|
||||
source_docs: Object.freeze(record.source_docs.slice()),
|
||||
});
|
||||
}
|
||||
|
||||
const ADAPTER_RECORDS = Object.freeze([
|
||||
{
|
||||
id: 'claude-code',
|
||||
harness: 'Claude Code',
|
||||
state: 'Native',
|
||||
supported_assets: [
|
||||
'Claude plugin assets',
|
||||
'skills',
|
||||
'commands',
|
||||
'hooks',
|
||||
'MCP config',
|
||||
'local rules',
|
||||
'statusline-oriented workflows',
|
||||
],
|
||||
unsupported_surfaces: ['Claude-native hooks do not imply parity in other harnesses'],
|
||||
install_or_onramp: [
|
||||
'`./install.sh --profile minimal --target claude`',
|
||||
'Claude plugin install',
|
||||
],
|
||||
verification_commands: [
|
||||
'`npm run harness:audit -- --format json`',
|
||||
'`node scripts/session-inspect.js --list-adapters`',
|
||||
],
|
||||
risk_notes: ['Avoid loading every skill by default; keep hooks opt-in and inspectable.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'.claude-plugin/plugin.json',
|
||||
'docs/architecture/cross-harness.md',
|
||||
'scripts/lib/install-targets/claude-home.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'codex',
|
||||
harness: 'Codex',
|
||||
state: 'Instruction-backed',
|
||||
supported_assets: [
|
||||
'`AGENTS.md`',
|
||||
'Codex plugin metadata',
|
||||
'skills',
|
||||
'MCP reference config',
|
||||
'command patterns',
|
||||
],
|
||||
unsupported_surfaces: ['Native hook enforcement and Claude slash-command semantics are not equivalent'],
|
||||
install_or_onramp: [
|
||||
'`./install.sh --profile minimal --target codex`',
|
||||
'repo-local `AGENTS.md` review',
|
||||
],
|
||||
verification_commands: ['`npm run harness:audit -- --format json`'],
|
||||
risk_notes: ['Treat hooks as policy text unless a native Codex hook surface exists.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'.codex-plugin/plugin.json',
|
||||
'AGENTS.md',
|
||||
'scripts/lib/install-targets/codex-home.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'opencode',
|
||||
harness: 'OpenCode',
|
||||
state: 'Adapter-backed',
|
||||
supported_assets: [
|
||||
'OpenCode package/plugin metadata',
|
||||
'shared skills',
|
||||
'MCP config',
|
||||
'event adapter patterns',
|
||||
],
|
||||
unsupported_surfaces: ['Event names, plugin packaging, and command dispatch differ from Claude Code'],
|
||||
install_or_onramp: ['OpenCode package or plugin surface from this repo'],
|
||||
verification_commands: [
|
||||
'`node tests/scripts/build-opencode.test.js`',
|
||||
'`npm run harness:audit -- --format json`',
|
||||
],
|
||||
risk_notes: ['Keep hook logic in shared scripts and adapt only event shape at the edge.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'.opencode/package.json',
|
||||
'.opencode/plugins/ecc-hooks.ts',
|
||||
'scripts/build-opencode.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'cursor',
|
||||
harness: 'Cursor',
|
||||
state: 'Adapter-backed',
|
||||
supported_assets: [
|
||||
'Cursor rules',
|
||||
'project-local skills',
|
||||
'hook adapter',
|
||||
'shared scripts',
|
||||
],
|
||||
unsupported_surfaces: ['Cursor hook events and rule loading differ from Claude Code'],
|
||||
install_or_onramp: ['`./install.sh --profile minimal --target cursor`'],
|
||||
verification_commands: [
|
||||
'`node tests/lib/install-targets.test.js`',
|
||||
'`npm run harness:audit -- --format json`',
|
||||
],
|
||||
risk_notes: ['Cursor adapters must preserve existing project rules and avoid silent overwrite.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'.cursor/',
|
||||
'scripts/lib/install-targets/cursor-project.js',
|
||||
'tests/lib/install-targets.test.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'gemini',
|
||||
harness: 'Gemini',
|
||||
state: 'Instruction-backed',
|
||||
supported_assets: [
|
||||
'Gemini project-local instructions',
|
||||
'shared skills',
|
||||
'rules',
|
||||
'compatibility docs',
|
||||
],
|
||||
unsupported_surfaces: ['No full ECC hook parity; ecosystem ports must document drift from upstream ECC'],
|
||||
install_or_onramp: ['`./install.sh --profile minimal --target gemini`'],
|
||||
verification_commands: ['`node tests/lib/install-targets.test.js`'],
|
||||
risk_notes: ['Treat Gemini ports as ecosystem adapters until validated end to end inside Gemini CLI.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'.gemini/',
|
||||
'scripts/lib/install-targets/gemini-project.js',
|
||||
'tests/lib/install-targets.test.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'zed-adjacent',
|
||||
harness: 'Zed-adjacent workflows',
|
||||
state: 'Instruction-backed',
|
||||
supported_assets: [
|
||||
'shared skills',
|
||||
'`AGENTS.md` style project instructions',
|
||||
'verification loops',
|
||||
],
|
||||
unsupported_surfaces: ['Zed agent surfaces vary; no first-party ECC installer is shipped today'],
|
||||
install_or_onramp: ['Manual copy from shared ECC sources until adapter requirements settle'],
|
||||
verification_commands: ['`npm run harness:audit -- --format json`'],
|
||||
risk_notes: ['Do not claim native Zed support before a real adapter and verification path exist.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'AGENTS.md',
|
||||
'docs/architecture/cross-harness.md',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dmux',
|
||||
harness: 'dmux',
|
||||
state: 'Adapter-backed',
|
||||
supported_assets: [
|
||||
'session snapshots',
|
||||
'tmux/worktree orchestration status',
|
||||
'handoff exports',
|
||||
],
|
||||
unsupported_surfaces: ['dmux is an orchestration runtime, not an install target for skills/rules'],
|
||||
install_or_onramp: [
|
||||
'`node scripts/session-inspect.js --list-adapters`',
|
||||
'dmux session target inspection',
|
||||
],
|
||||
verification_commands: ['`node tests/lib/session-adapters.test.js`'],
|
||||
risk_notes: ['Treat dmux events as session/runtime signals, not as a replacement for repo validation.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'scripts/lib/session-adapters/dmux-tmux.js',
|
||||
'scripts/orchestration-status.js',
|
||||
'tests/lib/session-adapters.test.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'orca',
|
||||
harness: 'Orca',
|
||||
state: 'Reference-only',
|
||||
supported_assets: [
|
||||
'worktree lifecycle',
|
||||
'review state',
|
||||
'notification',
|
||||
'provider-identity design pressure',
|
||||
],
|
||||
unsupported_surfaces: ['No ECC installer or direct adapter today'],
|
||||
install_or_onramp: ['Use as a comparison target for worktree/session state requirements'],
|
||||
verification_commands: ['`npm run observability:ready`'],
|
||||
risk_notes: ['Do not import product-specific assumptions; convert lessons into ECC event fields.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: ['docs/architecture/cross-harness.md'],
|
||||
},
|
||||
{
|
||||
id: 'superset',
|
||||
harness: 'Superset',
|
||||
state: 'Reference-only',
|
||||
supported_assets: [
|
||||
'workspace presets',
|
||||
'parallel-agent review loops',
|
||||
'worktree isolation design pressure',
|
||||
],
|
||||
unsupported_surfaces: ['No ECC installer or direct adapter today'],
|
||||
install_or_onramp: ['Use as a comparison target for workspace preset taxonomy'],
|
||||
verification_commands: ['`npm run observability:ready`'],
|
||||
risk_notes: ['Keep ECC portable; do not require a desktop workspace to get basic value.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: ['docs/architecture/cross-harness.md'],
|
||||
},
|
||||
{
|
||||
id: 'ghast',
|
||||
harness: 'Ghast',
|
||||
state: 'Reference-only',
|
||||
supported_assets: [
|
||||
'terminal-native pane grouping',
|
||||
'cwd grouping',
|
||||
'search',
|
||||
'notifications',
|
||||
],
|
||||
unsupported_surfaces: ['No ECC installer or direct adapter today'],
|
||||
install_or_onramp: ['Use as a comparison target for terminal-first session grouping'],
|
||||
verification_commands: ['`node scripts/session-inspect.js --list-adapters`'],
|
||||
risk_notes: ['Preserve terminal ergonomics before adding visual UI assumptions.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: ['docs/architecture/cross-harness.md'],
|
||||
},
|
||||
{
|
||||
id: 'terminal-only',
|
||||
harness: 'Terminal-only',
|
||||
state: 'Native',
|
||||
supported_assets: [
|
||||
'skills',
|
||||
'rules',
|
||||
'commands',
|
||||
'scripts',
|
||||
'harness audit',
|
||||
'observability readiness',
|
||||
'handoffs',
|
||||
],
|
||||
unsupported_surfaces: ['No external UI, no automatic session control unless scripts are run explicitly'],
|
||||
install_or_onramp: [
|
||||
'Clone repo',
|
||||
'run commands directly',
|
||||
'use minimal profile for project installs',
|
||||
],
|
||||
verification_commands: [
|
||||
'`npm run harness:audit -- --format json`',
|
||||
'`npm run observability:ready`',
|
||||
],
|
||||
risk_notes: ['This is the fallback contract; every higher-level adapter should degrade to it.'],
|
||||
last_verified_at: '2026-05-12',
|
||||
owner: 'ECC maintainers',
|
||||
source_docs: [
|
||||
'scripts/harness-audit.js',
|
||||
'scripts/observability-readiness.js',
|
||||
'docs/architecture/observability-readiness.md',
|
||||
],
|
||||
},
|
||||
].map(freezeRecord));
|
||||
|
||||
function toTextList(value) {
|
||||
return Array.isArray(value) ? value.join('; ') : String(value || '');
|
||||
}
|
||||
|
||||
function escapeMarkdownCell(value) {
|
||||
return toTextList(value).replace(/\|/g, '\\|').trim();
|
||||
}
|
||||
|
||||
function renderMarkdownTable(records = ADAPTER_RECORDS) {
|
||||
const lines = [
|
||||
'| Harness or runtime | State | Supported assets | Unsupported or different surfaces | Install or onramp | Verification command | Risk notes |',
|
||||
'| --- | --- | --- | --- | --- | --- | --- |',
|
||||
];
|
||||
|
||||
for (const record of records) {
|
||||
lines.push([
|
||||
record.harness,
|
||||
record.state,
|
||||
record.supported_assets,
|
||||
record.unsupported_surfaces,
|
||||
record.install_or_onramp,
|
||||
record.verification_commands,
|
||||
record.risk_notes,
|
||||
].map(escapeMarkdownCell).join(' | ').replace(/^/, '| ').replace(/$/, ' |'));
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function renderStateTable() {
|
||||
const lines = [
|
||||
'| State | Meaning |',
|
||||
'| --- | --- |',
|
||||
];
|
||||
|
||||
for (const [state, meaning] of Object.entries(COMPLIANCE_STATES)) {
|
||||
lines.push(`| ${escapeMarkdownCell(state)} | ${escapeMarkdownCell(meaning)} |`);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function validateAdapterRecords(records = ADAPTER_RECORDS) {
|
||||
const errors = [];
|
||||
const ids = new Set();
|
||||
|
||||
records.forEach((record, index) => {
|
||||
const label = record?.id || `record[${index}]`;
|
||||
|
||||
for (const field of REQUIRED_FIELDS) {
|
||||
if (!Object.prototype.hasOwnProperty.call(record, field)) {
|
||||
errors.push(`${label}: missing required field ${field}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof record.id !== 'string' || !/^[a-z0-9-]+$/.test(record.id)) {
|
||||
errors.push(`${label}: id must be a lowercase slug`);
|
||||
} else if (ids.has(record.id)) {
|
||||
errors.push(`${label}: duplicate id`);
|
||||
} else {
|
||||
ids.add(record.id);
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(COMPLIANCE_STATES, record.state)) {
|
||||
errors.push(`${label}: unknown state ${record.state}`);
|
||||
}
|
||||
|
||||
for (const field of [
|
||||
'supported_assets',
|
||||
'unsupported_surfaces',
|
||||
'install_or_onramp',
|
||||
'verification_commands',
|
||||
'risk_notes',
|
||||
'source_docs',
|
||||
]) {
|
||||
if (!Array.isArray(record[field]) || record[field].length === 0) {
|
||||
errors.push(`${label}: ${field} must be a non-empty array`);
|
||||
continue;
|
||||
}
|
||||
|
||||
record[field].forEach((value, valueIndex) => {
|
||||
if (typeof value !== 'string' || !value.trim()) {
|
||||
errors.push(`${label}: ${field}[${valueIndex}] must be a non-empty string`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof record.harness !== 'string' || !record.harness.trim()) {
|
||||
errors.push(`${label}: harness must be a non-empty string`);
|
||||
}
|
||||
|
||||
if (typeof record.owner !== 'string' || !record.owner.trim()) {
|
||||
errors.push(`${label}: owner must be a non-empty string`);
|
||||
}
|
||||
|
||||
if (typeof record.last_verified_at !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(record.last_verified_at)) {
|
||||
errors.push(`${label}: last_verified_at must be YYYY-MM-DD`);
|
||||
}
|
||||
});
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
function extractMatrixBlock(markdown) {
|
||||
const normalized = String(markdown).replace(/\r\n/g, '\n');
|
||||
const start = normalized.indexOf(MATRIX_BLOCK_START);
|
||||
const end = normalized.indexOf(MATRIX_BLOCK_END);
|
||||
|
||||
if (start < 0 || end < 0 || end <= start) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalized.slice(start + MATRIX_BLOCK_START.length, end).trim();
|
||||
}
|
||||
|
||||
function validateDocumentation(options = {}) {
|
||||
const repoRoot = options.repoRoot || path.resolve(__dirname, '..', '..');
|
||||
const docPath = options.docPath || path.join(repoRoot, 'docs', 'architecture', 'harness-adapter-compliance.md');
|
||||
const errors = [];
|
||||
const source = fs.readFileSync(docPath, 'utf8');
|
||||
const actual = extractMatrixBlock(source);
|
||||
const expected = renderMarkdownTable();
|
||||
|
||||
if (actual === null) {
|
||||
errors.push(`missing matrix block markers in ${path.relative(repoRoot, docPath)}`);
|
||||
} else if (actual !== expected) {
|
||||
errors.push(`matrix block in ${path.relative(repoRoot, docPath)} is not generated from adapter records`);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ADAPTER_RECORDS,
|
||||
COMPLIANCE_STATES,
|
||||
MATRIX_BLOCK_END,
|
||||
MATRIX_BLOCK_START,
|
||||
REQUIRED_FIELDS,
|
||||
extractMatrixBlock,
|
||||
renderMarkdownTable,
|
||||
renderStateTable,
|
||||
validateAdapterRecords,
|
||||
validateDocumentation,
|
||||
};
|
||||
@@ -40,11 +40,8 @@ const LEGACY_LANGUAGE_ALIAS_TO_CANONICAL = Object.freeze({
|
||||
c: 'c',
|
||||
cpp: 'cpp',
|
||||
csharp: 'csharp',
|
||||
fsharp: 'fsharp',
|
||||
go: 'go',
|
||||
golang: 'go',
|
||||
arkts: 'arkts',
|
||||
harmonyos: 'arkts',
|
||||
java: 'java',
|
||||
javascript: 'typescript',
|
||||
kotlin: 'java',
|
||||
@@ -59,9 +56,7 @@ const LEGACY_LANGUAGE_EXTRA_MODULE_IDS = Object.freeze({
|
||||
c: ['framework-language'],
|
||||
cpp: ['framework-language'],
|
||||
csharp: ['framework-language'],
|
||||
fsharp: ['framework-language'],
|
||||
go: ['framework-language'],
|
||||
arkts: ['framework-language'],
|
||||
java: ['framework-language'],
|
||||
perl: [],
|
||||
php: [],
|
||||
|
||||
@@ -60,11 +60,6 @@ const LANGUAGE_RULES = [
|
||||
markers: [],
|
||||
extensions: ['.cs', '.csproj', '.sln']
|
||||
},
|
||||
{
|
||||
type: 'fsharp',
|
||||
markers: [],
|
||||
extensions: ['.fs', '.fsx', '.fsproj']
|
||||
},
|
||||
{
|
||||
type: 'swift',
|
||||
markers: ['Package.swift'],
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Shared session bridge utilities for ECC hooks.
|
||||
*
|
||||
* The bridge file is a small JSON aggregate in /tmp that allows
|
||||
* statusline, metrics-bridge, and context-monitor to share state
|
||||
* without scanning large JSONL logs on every invocation.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const MAX_SESSION_ID_LENGTH = 64;
|
||||
|
||||
/**
|
||||
* Sanitize a session ID for safe use in file paths.
|
||||
* Rejects path traversal, strips unsafe chars, limits length.
|
||||
* @param {string} raw
|
||||
* @returns {string|null} Safe session ID or null if invalid
|
||||
*/
|
||||
function sanitizeSessionId(raw) {
|
||||
if (!raw || typeof raw !== 'string') return null;
|
||||
if (/[/\\]|\.\./.test(raw)) return null;
|
||||
const safe = raw.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, MAX_SESSION_ID_LENGTH);
|
||||
return safe || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bridge file path for a session.
|
||||
* @param {string} sessionId - Already-sanitized session ID
|
||||
* @returns {string}
|
||||
*/
|
||||
function getBridgePath(sessionId) {
|
||||
return path.join(os.tmpdir(), `ecc-metrics-${sessionId}.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bridge data. Returns null on any error.
|
||||
* @param {string} sessionId - Already-sanitized session ID
|
||||
* @returns {object|null}
|
||||
*/
|
||||
function readBridge(sessionId) {
|
||||
try {
|
||||
const raw = fs.readFileSync(getBridgePath(sessionId), 'utf8');
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bridge data atomically (write .tmp then rename).
|
||||
* @param {string} sessionId - Already-sanitized session ID
|
||||
* @param {object} data
|
||||
*/
|
||||
function writeBridgeAtomic(sessionId, data) {
|
||||
const target = getBridgePath(sessionId);
|
||||
const tmp = `${target}.tmp`;
|
||||
fs.writeFileSync(tmp, JSON.stringify(data), 'utf8');
|
||||
fs.renameSync(tmp, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve session ID from environment variables.
|
||||
* @returns {string|null} Sanitized session ID or null
|
||||
*/
|
||||
function resolveSessionId() {
|
||||
const raw = process.env.ECC_SESSION_ID || process.env.CLAUDE_SESSION_ID || '';
|
||||
return sanitizeSessionId(raw);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sanitizeSessionId,
|
||||
getBridgePath,
|
||||
readBridge,
|
||||
writeBridgeAtomic,
|
||||
resolveSessionId,
|
||||
MAX_SESSION_ID_LENGTH
|
||||
};
|
||||
@@ -1,309 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const RUBRIC_VERSION = '2026-05-11';
|
||||
|
||||
function usage() {
|
||||
console.log([
|
||||
'Usage: node scripts/observability-readiness.js [--format <text|json>] [--root <dir>]',
|
||||
'',
|
||||
'Deterministic ECC 2.0 observability readiness gate.',
|
||||
'',
|
||||
'Options:',
|
||||
' --format <text|json> Output format (default: text)',
|
||||
' --root <dir> Repository root to inspect (default: cwd)',
|
||||
' --help, -h Show this help'
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
function readValue(args, index, flagName) {
|
||||
const value = args[index + 1];
|
||||
if (!value || value.startsWith('--')) {
|
||||
throw new Error(`${flagName} requires a value`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = argv.slice(2);
|
||||
const parsed = {
|
||||
format: 'text',
|
||||
help: false,
|
||||
root: path.resolve(process.cwd())
|
||||
};
|
||||
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
|
||||
if (arg === '--help' || arg === '-h') {
|
||||
parsed.help = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--format') {
|
||||
parsed.format = readValue(args, index, arg).toLowerCase();
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--format=')) {
|
||||
parsed.format = arg.slice('--format='.length).toLowerCase();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--root') {
|
||||
parsed.root = path.resolve(readValue(args, index, arg));
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--root=')) {
|
||||
parsed.root = path.resolve(arg.slice('--root='.length));
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (!['text', 'json'].includes(parsed.format)) {
|
||||
throw new Error(`Invalid format: ${parsed.format}. Use text or json.`);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function fileExists(rootDir, relativePath) {
|
||||
return fs.existsSync(path.join(rootDir, relativePath));
|
||||
}
|
||||
|
||||
function readText(rootDir, relativePath) {
|
||||
try {
|
||||
return fs.readFileSync(path.join(rootDir, relativePath), 'utf8');
|
||||
} catch (_error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function safeParseJson(text) {
|
||||
if (!text || !text.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function includesAll(text, needles) {
|
||||
return needles.every(needle => text.includes(needle));
|
||||
}
|
||||
|
||||
function buildChecks(rootDir) {
|
||||
const packageJsonText = readText(rootDir, 'package.json');
|
||||
const packageJson = safeParseJson(packageJsonText) || {};
|
||||
const packageFiles = Array.isArray(packageJson.files) ? packageJson.files : [];
|
||||
const packageScripts = packageJson.scripts || {};
|
||||
const loopStatus = readText(rootDir, 'scripts/loop-status.js');
|
||||
const sessionInspect = readText(rootDir, 'scripts/session-inspect.js');
|
||||
const harnessAudit = readText(rootDir, 'scripts/harness-audit.js');
|
||||
const activityTracker = readText(rootDir, 'scripts/hooks/session-activity-tracker.js');
|
||||
const observabilityRust = readText(rootDir, 'ecc2/src/observability/mod.rs');
|
||||
const sessionStoreRust = readText(rootDir, 'ecc2/src/session/store.rs');
|
||||
const sessionManagerRust = readText(rootDir, 'ecc2/src/session/manager.rs');
|
||||
const readinessDoc = readText(rootDir, 'docs/architecture/observability-readiness.md');
|
||||
const quickstart = readText(rootDir, 'docs/releases/2.0.0-rc.1/quickstart.md');
|
||||
const releaseNotes = readText(rootDir, 'docs/releases/2.0.0-rc.1/release-notes.md');
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'loop-status-live-signal',
|
||||
category: 'Live Status',
|
||||
points: 2,
|
||||
path: 'scripts/loop-status.js',
|
||||
description: 'Loop status supports JSON output, watch mode, and snapshot writes',
|
||||
pass: fileExists(rootDir, 'scripts/loop-status.js')
|
||||
&& includesAll(loopStatus, ['--json', '--watch', '--write-dir']),
|
||||
fix: 'Restore loop-status JSON/watch/write-dir support.'
|
||||
},
|
||||
{
|
||||
id: 'session-inspect-adapter-registry',
|
||||
category: 'Session Trace',
|
||||
points: 2,
|
||||
path: 'scripts/session-inspect.js',
|
||||
description: 'Session inspection exposes registered adapters and writable snapshots',
|
||||
pass: fileExists(rootDir, 'scripts/session-inspect.js')
|
||||
&& fileExists(rootDir, 'scripts/lib/session-adapters/registry.js')
|
||||
&& includesAll(sessionInspect, ['--list-adapters', '--write', 'inspectSessionTarget']),
|
||||
fix: 'Restore session-inspect adapter registry, list-adapters, and write support.'
|
||||
},
|
||||
{
|
||||
id: 'harness-audit-scorecard',
|
||||
category: 'Harness Baseline',
|
||||
points: 2,
|
||||
path: 'scripts/harness-audit.js',
|
||||
description: 'Harness audit emits deterministic text/JSON scorecards',
|
||||
pass: fileExists(rootDir, 'scripts/harness-audit.js')
|
||||
&& packageScripts['harness:audit'] === 'node scripts/harness-audit.js'
|
||||
&& includesAll(harnessAudit, ['Deterministic harness audit', '--format', 'overall_score']),
|
||||
fix: 'Restore the harness:audit package script and deterministic scorecard output.'
|
||||
},
|
||||
{
|
||||
id: 'hook-activity-jsonl',
|
||||
category: 'Tool Activity',
|
||||
points: 2,
|
||||
path: 'scripts/hooks/session-activity-tracker.js',
|
||||
description: 'Hook activity tracker writes tool usage JSONL for later sync',
|
||||
pass: fileExists(rootDir, 'scripts/hooks/session-activity-tracker.js')
|
||||
&& includesAll(activityTracker, ['tool-usage.jsonl', 'session_id', 'tool_name']),
|
||||
fix: 'Restore hook-side tool activity recording to metrics/tool-usage.jsonl.'
|
||||
},
|
||||
{
|
||||
id: 'ecc2-tool-risk-ledger',
|
||||
category: 'Tool Activity',
|
||||
points: 3,
|
||||
path: 'ecc2/src/observability/mod.rs',
|
||||
description: 'ECC2 records tool calls with risk scoring and paginated queries',
|
||||
pass: fileExists(rootDir, 'ecc2/src/observability/mod.rs')
|
||||
&& includesAll(observabilityRust, ['ToolCallEvent', 'RiskAssessment', 'ToolLogger'])
|
||||
&& includesAll(sessionStoreRust, ['insert_tool_log', 'query_tool_logs'])
|
||||
&& includesAll(sessionManagerRust, ['sync_tool_activity_metrics', 'tool-usage.jsonl']),
|
||||
fix: 'Restore ECC2 tool logging, risk scoring, store queries, and metrics sync.'
|
||||
},
|
||||
{
|
||||
id: 'release-observability-onramp',
|
||||
category: 'Operator Onramp',
|
||||
points: 2,
|
||||
path: 'docs/architecture/observability-readiness.md',
|
||||
description: 'Release docs explain the local observability readiness workflow',
|
||||
pass: readinessDoc.includes('node scripts/observability-readiness.js --format json')
|
||||
&& quickstart.includes('observability-readiness.md')
|
||||
&& releaseNotes.includes('observability-readiness.md'),
|
||||
fix: 'Add the observability readiness doc and link it from rc.1 release docs.'
|
||||
},
|
||||
{
|
||||
id: 'package-exposes-readiness-gate',
|
||||
category: 'Packaging',
|
||||
points: 1,
|
||||
path: 'package.json',
|
||||
description: 'Package exposes the observability readiness gate',
|
||||
pass: packageScripts['observability:ready'] === 'node scripts/observability-readiness.js'
|
||||
&& packageFiles.includes('scripts/observability-readiness.js'),
|
||||
fix: 'Add scripts/observability-readiness.js to package files and observability:ready.'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function buildReport(rootDir) {
|
||||
const checks = buildChecks(rootDir);
|
||||
const categories = {};
|
||||
|
||||
for (const check of checks) {
|
||||
if (!categories[check.category]) {
|
||||
categories[check.category] = {
|
||||
score: 0,
|
||||
max_score: 0,
|
||||
passed: 0,
|
||||
total: 0
|
||||
};
|
||||
}
|
||||
|
||||
categories[check.category].max_score += check.points;
|
||||
categories[check.category].total += 1;
|
||||
|
||||
if (check.pass) {
|
||||
categories[check.category].score += check.points;
|
||||
categories[check.category].passed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const overallScore = checks
|
||||
.filter(check => check.pass)
|
||||
.reduce((sum, check) => sum + check.points, 0);
|
||||
const maxScore = checks.reduce((sum, check) => sum + check.points, 0);
|
||||
const failingChecks = checks.filter(check => !check.pass);
|
||||
|
||||
return {
|
||||
schema_version: 'ecc.observability-readiness.v1',
|
||||
rubric_version: RUBRIC_VERSION,
|
||||
deterministic: true,
|
||||
root_dir: fs.realpathSync(rootDir),
|
||||
overall_score: overallScore,
|
||||
max_score: maxScore,
|
||||
ready: overallScore === maxScore,
|
||||
categories,
|
||||
checks,
|
||||
top_actions: failingChecks
|
||||
.sort((left, right) => right.points - left.points || left.id.localeCompare(right.id))
|
||||
.slice(0, 3)
|
||||
.map(check => ({
|
||||
id: check.id,
|
||||
path: check.path,
|
||||
fix: check.fix
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
function renderText(report) {
|
||||
const lines = [
|
||||
`Observability Readiness: ${report.overall_score}/${report.max_score}`,
|
||||
`Ready: ${report.ready ? 'yes' : 'no'}`,
|
||||
'',
|
||||
'Categories:'
|
||||
];
|
||||
|
||||
for (const [name, category] of Object.entries(report.categories)) {
|
||||
lines.push(`- ${name}: ${category.score}/${category.max_score} (${category.passed}/${category.total})`);
|
||||
}
|
||||
|
||||
lines.push('', 'Checks:');
|
||||
for (const check of report.checks) {
|
||||
lines.push(`- ${check.pass ? 'PASS' : 'FAIL'} ${check.id}: ${check.description}`);
|
||||
}
|
||||
|
||||
if (report.top_actions.length > 0) {
|
||||
lines.push('', 'Top Actions:');
|
||||
for (const action of report.top_actions) {
|
||||
lines.push(`- ${action.path}: ${action.fix}`);
|
||||
}
|
||||
}
|
||||
|
||||
return `${lines.join('\n')}\n`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = parseArgs(process.argv);
|
||||
|
||||
if (args.help) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
const report = buildReport(args.root);
|
||||
|
||||
if (args.format === 'json') {
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
} else {
|
||||
process.stdout.write(renderText(report));
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildChecks,
|
||||
buildReport,
|
||||
parseArgs,
|
||||
renderText
|
||||
};
|
||||
@@ -1,256 +0,0 @@
|
||||
---
|
||||
name: agent-architecture-audit
|
||||
description: Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature.
|
||||
origin: oh-my-agent-check
|
||||
tools: Read, Write, Edit, Bash, Grep, Glob
|
||||
---
|
||||
|
||||
# Agent Architecture Audit
|
||||
|
||||
A diagnostic workflow for agent systems that hide failures behind wrapper layers, stale memory, retry loops, or transport/rendering mutations.
|
||||
|
||||
## When to Activate
|
||||
|
||||
**MANDATORY for:**
|
||||
- Releasing any agent or LLM-powered application to production
|
||||
- Shipping features with tool calling, memory, or multi-step workflows
|
||||
- Agent behavior degrades after adding wrapper layers
|
||||
- User reports "the agent is getting worse" or "tools are flaky"
|
||||
- Same model works in playground but breaks inside your wrapper
|
||||
- Debugging agent behavior for more than 15 minutes without finding root cause
|
||||
|
||||
**Especially critical when:**
|
||||
- You've added new prompt layers, tool definitions, or memory systems
|
||||
- Different agents in your system behave inconsistently
|
||||
- The model was fine yesterday but is hallucinating today
|
||||
- You suspect hidden repair/retry loops silently mutating responses
|
||||
|
||||
**Do not use for:**
|
||||
- General code debugging — use `agent-introspection-debugging`
|
||||
- Code review — use language-specific reviewer agents
|
||||
- Security scanning — use `security-review` or `security-review/scan`
|
||||
- Agent performance benchmarking — use `agent-eval`
|
||||
- Writing new features — use the appropriate workflow skill
|
||||
|
||||
## The 12-Layer Stack
|
||||
|
||||
Every agent system has these layers. Any of them can corrupt the answer:
|
||||
|
||||
| # | Layer | What Goes Wrong |
|
||||
|---|-------|----------------|
|
||||
| 1 | System prompt | Conflicting instructions, instruction bloat |
|
||||
| 2 | Session history | Stale context injection from previous turns |
|
||||
| 3 | Long-term memory | Pollution across sessions, old topics in new conversations |
|
||||
| 4 | Distillation | Compressed artifacts re-entering as pseudo-facts |
|
||||
| 5 | Active recall | Redundant re-summary layers wasting context |
|
||||
| 6 | Tool selection | Wrong tool routing, model skips required tools |
|
||||
| 7 | Tool execution | Hallucinated execution — claims to call but doesn't |
|
||||
| 8 | Tool interpretation | Misread or ignored tool output |
|
||||
| 9 | Answer shaping | Format corruption in final response |
|
||||
| 10 | Platform rendering | Transport-layer mutation (UI, API, CLI mutates valid answers) |
|
||||
| 11 | Hidden repair loops | Silent fallback/retry agents running second LLM pass |
|
||||
| 12 | Persistence | Expired state or cached artifacts reused as live evidence |
|
||||
|
||||
## Common Failure Patterns
|
||||
|
||||
### 1. Wrapper Regression
|
||||
|
||||
The base model produces correct answers, but the wrapper layers make it worse.
|
||||
|
||||
**Symptoms:**
|
||||
- Model works fine in playground or direct API call, breaks in your agent
|
||||
- Added a new prompt layer, existing behavior degraded
|
||||
- Agent sounds confident but is confidently wrong
|
||||
- "It was working before the last update"
|
||||
|
||||
### 2. Memory Contamination
|
||||
|
||||
Old topics leak into new conversations through history, memory retrieval, or distillation.
|
||||
|
||||
**Symptoms:**
|
||||
- Agent brings up unrelated past topics
|
||||
- User corrections don't stick (old memory overwrites new)
|
||||
- Same-session artifacts re-enter as pseudo-facts
|
||||
- Memory grows without bound, degrading response quality over time
|
||||
|
||||
### 3. Tool Discipline Failure
|
||||
|
||||
Tools are declared in the prompt but not enforced in code. The model skips them or hallucinates execution.
|
||||
|
||||
**Symptoms:**
|
||||
- "Must use tool X" in prompt, but model answers without calling it
|
||||
- Tool results look correct but were never actually executed
|
||||
- Different tools fight over the same responsibility
|
||||
- Model uses tool when it shouldn't, or skips it when it must
|
||||
|
||||
### 4. Rendering/Transport Corruption
|
||||
|
||||
The agent's internal answer is correct, but the platform layer mutates it during delivery.
|
||||
|
||||
**Symptoms:**
|
||||
- Logs show correct answer, user sees broken output
|
||||
- Markdown rendering, JSON parsing, or streaming fragments corrupt valid responses
|
||||
- Hidden fallback agent quietly replaces the answer before delivery
|
||||
- Output differs between terminal and UI
|
||||
|
||||
### 5. Hidden Agent Layers
|
||||
|
||||
Silent repair, retry, summarization, or recall agents run without explicit contracts.
|
||||
|
||||
**Symptoms:**
|
||||
- Output changes between internal generation and user delivery
|
||||
- "Auto-fix" loops run a second LLM pass the user doesn't know about
|
||||
- Multiple agents modify the same output without coordination
|
||||
- Answers get "smoothed" or "corrected" by invisible layers
|
||||
|
||||
## Audit Workflow
|
||||
|
||||
### Phase 1: Scope
|
||||
|
||||
Define what you're auditing:
|
||||
|
||||
- **Target system** — what agent application?
|
||||
- **Entrypoints** — how do users interact with it?
|
||||
- **Model stack** — which LLM(s) and providers?
|
||||
- **Symptoms** — what does the user report?
|
||||
- **Time window** — when did it start?
|
||||
- **Layers to audit** — which of the 12 layers apply?
|
||||
|
||||
### Phase 2: Evidence Collection
|
||||
|
||||
Gather evidence from the codebase:
|
||||
|
||||
- **Source code** — agent loop, tool router, memory admission, prompt assembly
|
||||
- **Logs** — historical session traces, tool call records
|
||||
- **Config** — prompt templates, tool schemas, provider settings
|
||||
- **Memory files** — SOPs, knowledge bases, session archives
|
||||
|
||||
Use `rg` to search for anti-patterns:
|
||||
|
||||
```bash
|
||||
# Tool requirements expressed only in prompt text (not code)
|
||||
rg "must.*tool|必须.*工具|required.*call" --type md
|
||||
|
||||
# Tool execution without validation
|
||||
rg "tool_call|toolCall|tool_use" --type py --type ts
|
||||
|
||||
# Hidden LLM calls outside main agent loop
|
||||
rg "completion|chat\.create|messages\.create|llm\.invoke"
|
||||
|
||||
# Memory admission without user-correction priority
|
||||
rg "memory.*admit|long.*term.*update|persist.*memory" --type py --type ts
|
||||
|
||||
# Fallback loops that run additional LLM calls
|
||||
rg "fallback|retry.*llm|repair.*prompt|re-?prompt" --type py --type ts
|
||||
|
||||
# Silent output mutation
|
||||
rg "mutate|rewrite.*response|transform.*output|shap" --type py --type ts
|
||||
```
|
||||
|
||||
### Phase 3: Failure Mapping
|
||||
|
||||
For each finding, document:
|
||||
|
||||
- **Symptom** — what the user sees
|
||||
- **Mechanism** — how the wrapper causes it
|
||||
- **Source layer** — which of the 12 layers
|
||||
- **Root cause** — the deepest cause
|
||||
- **Evidence** — file:line or log:row reference
|
||||
- **Confidence** — 0.0 to 1.0
|
||||
|
||||
### Phase 4: Fix Strategy
|
||||
|
||||
Default fix order (code-first, not prompt-first):
|
||||
|
||||
1. **Code-gate tool requirements** — enforce in code, not just prompt text
|
||||
2. **Remove or narrow hidden repair agents** — make fallback explicit with contracts
|
||||
3. **Reduce context duplication** — same info through prompt + history + memory + distillation
|
||||
4. **Tighten memory admission** — user corrections > agent assertions
|
||||
5. **Tighten distillation triggers** — don't compress what shouldn't be compressed
|
||||
6. **Reduce rendering mutation** — pass-through, don't transform
|
||||
7. **Convert to typed JSON envelopes** — structured internal flow, not freeform prose
|
||||
|
||||
## Severity Model
|
||||
|
||||
| Level | Meaning | Action |
|
||||
|-------|---------|--------|
|
||||
| `critical` | Agent can confidently produce wrong operational behavior | Fix before next release |
|
||||
| `high` | Agent frequently degrades correctness or stability | Fix this sprint |
|
||||
| `medium` | Correctness usually survives but output is fragile or wasteful | Plan for next cycle |
|
||||
| `low` | Mostly cosmetic or maintainability issues | Backlog |
|
||||
|
||||
## Output Format
|
||||
|
||||
Present findings to the user in this order:
|
||||
|
||||
1. **Severity-ranked findings** (most critical first)
|
||||
2. **Architecture diagnosis** (which layer corrupted what, and why)
|
||||
3. **Ordered fix plan** (code-first, not prompt-first)
|
||||
|
||||
Do not lead with compliments or summaries. If the system is broken, say so directly.
|
||||
|
||||
## Quick Diagnostic Questions
|
||||
|
||||
When auditing an agent system, answer these:
|
||||
|
||||
| # | Question | If Yes → |
|
||||
|---|----------|----------|
|
||||
| 1 | Can the model skip a required tool and still answer? | Tool not code-gated |
|
||||
| 2 | Does old conversation content appear in new turns? | Memory contamination |
|
||||
| 3 | Is the same info in system prompt AND memory AND history? | Context duplication |
|
||||
| 4 | Does the platform run a second LLM pass before delivery? | Hidden repair loop |
|
||||
| 5 | Does the output differ between internal generation and user delivery? | Rendering corruption |
|
||||
| 6 | Are "must use tool X" rules only in prompt text? | Tool discipline failure |
|
||||
| 7 | Can the agent's own monologue become persistent memory? | Memory poisoning |
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- Avoid blaming the model before falsifying wrapper-layer regressions.
|
||||
- Avoid blaming memory without showing the contamination path.
|
||||
- Do not let a clean current state erase a dirty historical incident.
|
||||
- Do not treat markdown prose as a trustworthy internal protocol.
|
||||
- Do not accept "must use tool" in prompt text when code never enforces it.
|
||||
- Keep findings direct, evidence-backed, and severity-ranked.
|
||||
|
||||
## Report Schema
|
||||
|
||||
Audits should produce structured reports following this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": "ecc.agent-architecture-audit.report.v1",
|
||||
"executive_verdict": {
|
||||
"overall_health": "high_risk",
|
||||
"primary_failure_mode": "string",
|
||||
"most_urgent_fix": "string"
|
||||
},
|
||||
"scope": {
|
||||
"target_name": "string",
|
||||
"model_stack": ["string"],
|
||||
"layers_to_audit": ["string"]
|
||||
},
|
||||
"findings": [
|
||||
{
|
||||
"severity": "critical|high|medium|low",
|
||||
"title": "string",
|
||||
"mechanism": "string",
|
||||
"source_layer": "string",
|
||||
"root_cause": "string",
|
||||
"evidence_refs": ["file:line"],
|
||||
"confidence": 0.0,
|
||||
"recommended_fix": "string"
|
||||
}
|
||||
],
|
||||
"ordered_fix_plan": [
|
||||
{ "order": 1, "goal": "string", "why_now": "string", "expected_effect": "string" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `agent-introspection-debugging` — Debug agent runtime failures (loops, timeouts, state errors)
|
||||
- `agent-eval` — Benchmark agent performance head-to-head
|
||||
- `security-review` — Security audit for code and configuration
|
||||
- `autonomous-agent-harness` — Set up autonomous agent operations
|
||||
- `agent-harness-construction` — Build agent harnesses from scratch
|
||||
@@ -1,40 +1,21 @@
|
||||
---
|
||||
name: agent-payment-x402
|
||||
description: Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol.
|
||||
description: Add x402 payment execution to AI agents — per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents need to pay for APIs, services, or other agents.
|
||||
origin: community
|
||||
---
|
||||
|
||||
# Agent Payment Execution (x402)
|
||||
|
||||
Enable AI agents to make policy-gated payments with built-in spending controls. Uses the x402 HTTP payment protocol and MCP tools so agents can pay for external services, APIs, or other agents without custodial risk.
|
||||
Enable AI agents to make autonomous payments with built-in spending controls. Uses the x402 HTTP payment protocol and MCP tools so agents can pay for external services, APIs, or other agents without custodial risk.
|
||||
|
||||
## When to Use
|
||||
|
||||
Use when: your agent needs to pay for an API call, purchase a service, settle with another agent, enforce per-task spending limits, or manage a non-custodial wallet. Pairs naturally with cost-aware-llm-pipeline and security-review skills.
|
||||
|
||||
## Decision Tree
|
||||
|
||||
Choose the integration path based on whether your agent is buying access to a paid API or charging others for one:
|
||||
|
||||
| Need | Recommended path |
|
||||
|------|------------------|
|
||||
| Agent pays a 402-gated API on Base or another agentwallet-supported chain | Use `agentwallet-sdk` as an MCP payment server with strict spending policy |
|
||||
| Agent pays a 402-gated API on X Layer | Use OKX Agent Payments Protocol from `okx/onchainos-skills`; `okx-x402-payment` is a deprecated legacy alias |
|
||||
| TypeScript API charges agents | Use OKX Payments TypeScript seller SDK docs for Express, Hono, Fastify, or Next.js |
|
||||
| Go API charges agents | Use OKX Payments Go seller SDK docs for Gin, Echo, or `net/http` |
|
||||
| Rust API charges agents | Use OKX Payments Rust seller SDK docs for Axum |
|
||||
| Java API charges agents | Use OKX Payments Java seller SDK docs for Spring Boot 2/3, Java EE, or Jakarta |
|
||||
| Python API charges agents | Check the current OKX Payments repository before implementation; a Python seller guide may not be available |
|
||||
|
||||
## Supported Networks
|
||||
|
||||
- `agentwallet-sdk`: use the package docs to confirm current network coverage before production. Base Sepolia is the safest development default; Base mainnet is the production path called out by the original skill.
|
||||
- OKX Payments / X Layer: current seller docs target X Layer (`eip155:196`) and USDT0 settlement. Fetch current SDK docs before generating production code because payment packages and facilitator behavior can change quickly.
|
||||
|
||||
## How It Works
|
||||
|
||||
### x402 Protocol
|
||||
x402 extends HTTP 402 (Payment Required) into a machine-negotiable flow. When a server returns `402`, the agent's payment tool negotiates price, checks budget, signs a transaction, and retries only inside the policy and confirmation boundary set by the orchestrator.
|
||||
x402 extends HTTP 402 (Payment Required) into a machine-negotiable flow. When a server returns `402`, the agent's payment tool automatically negotiates price, checks budget, signs a transaction, and retries — no human in the loop.
|
||||
|
||||
### Spending Controls
|
||||
Every payment tool call enforces a `SpendingPolicy`:
|
||||
@@ -52,8 +33,6 @@ The payment layer exposes standard MCP tools that slot into any Claude Code or a
|
||||
|
||||
> **Security note**: Always pin the package version. This tool manages private keys — unpinned `npx` installs introduce supply-chain risk.
|
||||
|
||||
### Option A: agentwallet-sdk (Base / multi-chain)
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
@@ -76,28 +55,6 @@ The payment layer exposes standard MCP tools that slot into any Claude Code or a
|
||||
|
||||
> **Note**: Spending policy is set by the **orchestrator** before delegating to the agent — not by the agent itself. This prevents agents from escalating their own spending limits. Configure policy via `set_policy` in your orchestration layer or pre-task hook, never as an agent-callable tool.
|
||||
|
||||
### Option B: OKX Agent Payments Protocol (X Layer)
|
||||
|
||||
Use this path for X Layer x402, Multi-Party Payment (MPP), session payment, charge, and A2A charge flows.
|
||||
|
||||
For buyer-side agent flows:
|
||||
|
||||
1. Install or reference the current `okx/onchainos-skills` repository.
|
||||
2. Use `skills/okx-agent-payments-protocol/SKILL.md` as the dispatcher.
|
||||
3. Treat `skills/okx-x402-payment/SKILL.md` as a deprecated compatibility alias, not as the canonical skill.
|
||||
4. Require explicit user confirmation before wallet status checks or payment actions. Do not hide payment execution behind a generic tool call.
|
||||
|
||||
For seller-side API flows, fetch the latest language-specific guide before generating code:
|
||||
|
||||
| Runtime | Current guide |
|
||||
|---------|---------------|
|
||||
| TypeScript | `https://raw.githubusercontent.com/okx/payments/main/typescript/SELLER.md` |
|
||||
| Go | `https://raw.githubusercontent.com/okx/payments/main/go/x402/SELLER.md` |
|
||||
| Rust | `https://raw.githubusercontent.com/okx/payments/main/rust/x402/SELLER.md` |
|
||||
| Java | `https://raw.githubusercontent.com/okx/payments/main/java/SELLER.md` |
|
||||
|
||||
Do not copy examples from older docs without checking the current OKX repository. Current OKX guidance uses `okx-agent-payments-protocol` as the dispatcher, and Java seller docs are now available.
|
||||
|
||||
## Examples
|
||||
|
||||
### Budget enforcement in an MCP client
|
||||
@@ -219,6 +176,3 @@ main().catch((err) => {
|
||||
- **npm**: [`agentwallet-sdk`](https://www.npmjs.com/package/agentwallet-sdk)
|
||||
- **Merged into NVIDIA NeMo Agent Toolkit**: [PR #17](https://github.com/NVIDIA/NeMo-Agent-Toolkit-Examples/pull/17) — x402 payment tool for NVIDIA's agent examples
|
||||
- **Protocol spec**: [x402.org](https://x402.org)
|
||||
- **OKX Payments SDKs**: [`okx/payments`](https://github.com/okx/payments) — TypeScript, Go, Rust, and Java seller integrations for X Layer x402
|
||||
- **OKX Agent Payments Protocol skill**: [`okx/onchainos-skills`](https://github.com/okx/onchainos-skills/tree/main/skills/okx-agent-payments-protocol)
|
||||
- **OKX Payments overview**: [web3.okx.com/onchainos/dev-docs/payments/overview](https://web3.okx.com/onchainos/dev-docs/payments/overview)
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
---
|
||||
name: agentic-os
|
||||
description: Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases.
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Agentic OS
|
||||
|
||||
Treat Claude Code as a persistent runtime / operating system rather than a chat session. This skill codifies the architecture used by production agentic setups: a kernel config that routes tasks to specialist agents, persistent file-based memory, scheduled automation, and a JSON/markdown data layer.
|
||||
|
||||
## When to Activate
|
||||
|
||||
- Building a multi-agent workflow inside Claude Code
|
||||
- Setting up persistent Claude Code automation that survives session restarts
|
||||
- Creating a "personal OS" or "agentic OS" for recurring tasks
|
||||
- User says "agentic OS", "personal OS", "multi-agent", "agent coordinator", "persistent agent"
|
||||
- Structuring long-running projects where context must survive across sessions
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The Agentic OS has four layers. Each layer is a directory in your project root.
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── CLAUDE.md # Kernel: identity, routing rules, agent registry
|
||||
├── agents/ # Specialist agent definitions (markdown prompts)
|
||||
├── .claude/commands/ # Slash commands: user-facing CLI
|
||||
├── scripts/ # Daemon scripts: scheduled or event-driven tasks
|
||||
└── data/ # State: JSON/markdown filesystem, no external DB
|
||||
```
|
||||
|
||||
### Layer Responsibilities
|
||||
|
||||
| Layer | Purpose | Persistence |
|
||||
|---|---|---|
|
||||
| Kernel (`CLAUDE.md`) | Identity, routing, model policies, agent registry | Git-tracked |
|
||||
| Agents (`agents/`) | Specialist identities with scoped tools and memory | Git-tracked |
|
||||
| Commands (`.claude/commands/`) | User-facing slash commands (`/daily-sync`, `/outreach`) | Git-tracked |
|
||||
| Scripts (`scripts/`) | Python/JS daemons triggered by cron or webhooks | Git-tracked |
|
||||
| State (`data/`) | Append-only logs, project state, decision records | Git-ignored or tracked |
|
||||
|
||||
## The Kernel
|
||||
|
||||
`CLAUDE.md` is the kernel. It acts as the COO / orchestrator. Claude reads it at session start and uses it to route work.
|
||||
|
||||
### Kernel Structure
|
||||
|
||||
```markdown
|
||||
# CLAUDE.md - Agentic OS Kernel
|
||||
|
||||
## Identity
|
||||
You are the COO of [project-name]. You route tasks to specialist agents.
|
||||
You never write code directly. You delegate to the right agent and synthesize results.
|
||||
|
||||
## Agent Registry
|
||||
|
||||
| Agent | Role | Trigger |
|
||||
|---|---|---|
|
||||
| @dev | Code, architecture, debugging | User says "build", "fix", "refactor" |
|
||||
| @writer | Documentation, content, emails | User says "write", "draft", "blog" |
|
||||
| @researcher | Research, analysis, fact-checking | User says "research", "analyze", "compare" |
|
||||
| @ops | DevOps, deployment, infrastructure | User says "deploy", "CI", "server" |
|
||||
|
||||
## Routing Rules
|
||||
1. Parse the user request for intent keywords
|
||||
2. Match to the Agent Registry trigger column
|
||||
3. Load the corresponding agent file from `agents/<name>.md`
|
||||
4. Hand off execution with full context
|
||||
5. Synthesize and present the result back to the user
|
||||
|
||||
## Model Policies
|
||||
- Default model: use the repository or harness default.
|
||||
- @dev tasks: prefer a higher-reasoning model for complex architecture.
|
||||
- @researcher tasks: use the configured research-capable model and approved search tools.
|
||||
- Cost ceiling: warn before exceeding the project's configured spend threshold.
|
||||
```
|
||||
|
||||
### Key Principle
|
||||
|
||||
The kernel should be **small and declarative**. Routing logic lives in plain markdown tables, not code. This makes the system inspectable and editable without debugging.
|
||||
|
||||
## Specialist Agents
|
||||
|
||||
Each agent is a standalone markdown file in `agents/`. Claude loads the relevant agent file when routing a task.
|
||||
|
||||
### Agent Definition Format
|
||||
|
||||
```markdown
|
||||
# @dev - Software Engineer
|
||||
|
||||
## Identity
|
||||
You are a senior software engineer. You write clean, tested, production-grade code.
|
||||
You prefer simple solutions. You ask clarifying questions when requirements are ambiguous.
|
||||
|
||||
## Memory Scope
|
||||
- Read `data/projects/<current-project>.md` for context
|
||||
- Read `data/decisions/` for architectural decisions
|
||||
- Append execution logs to `data/logs/<date>-@dev.md`
|
||||
|
||||
## Tool Access
|
||||
- Full filesystem access within project root
|
||||
- Git operations (status, diff, commit, branch)
|
||||
- Test runner access
|
||||
- MCP servers as configured in `.claude/mcp.json`
|
||||
|
||||
## Constraints
|
||||
- Always write tests for new features
|
||||
- Never commit directly to `main`; use feature branches
|
||||
- Prefer editing existing files over creating new ones
|
||||
- Keep functions under 50 lines when possible
|
||||
```
|
||||
|
||||
### Multi-Agent Collaboration Pattern
|
||||
|
||||
When a task spans multiple agents, the kernel runs them sequentially or in parallel:
|
||||
|
||||
```
|
||||
User: "Build a landing page and write the launch blog post"
|
||||
|
||||
Kernel routing:
|
||||
1. @dev - "Build a landing page with [requirements]"
|
||||
2. @writer - "Write a launch blog post for [product] using the landing page copy"
|
||||
3. Kernel synthesizes both outputs into a unified response
|
||||
```
|
||||
|
||||
For parallel execution, use Claude Code's background task capability or shell scripts that invoke Claude Code with specific agent contexts.
|
||||
|
||||
## Commands and Daily Workflows
|
||||
|
||||
Slash commands are markdown files in `.claude/commands/`. They define reusable workflows.
|
||||
|
||||
### Command Structure
|
||||
|
||||
```markdown
|
||||
# /daily-sync
|
||||
|
||||
Run the morning briefing:
|
||||
|
||||
1. Read `data/logs/last-sync.md` for context
|
||||
2. Check project status: `git status`, pending PRs, CI health
|
||||
3. Review `data/inbox/` for new tasks or decisions needed
|
||||
4. Generate a summary of blockers, priorities, and next actions
|
||||
5. Append the briefing to `data/logs/daily/<date>.md`
|
||||
```
|
||||
|
||||
### Standard Command Set
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `/daily-sync` | Morning briefing: status, blockers, priorities |
|
||||
| `/outreach` | Run outreach workflow (email, LinkedIn, etc.) |
|
||||
| `/research <topic>` | Deep research with citation tracking |
|
||||
| `/apply-jobs` | Tailor resume + cover letter for a target role |
|
||||
| `/analytics` | Pull metrics from Stripe, GitHub, or custom sources |
|
||||
| `/interview-prep` | Generate flashcards or mock interview questions |
|
||||
| `/decision <topic>` | Log a decision with pros/cons and chosen path |
|
||||
|
||||
### Activating Commands
|
||||
|
||||
Place command files in `.claude/commands/<command-name>.md`. Claude Code auto-discovers them. Users invoke them with `/<command-name>`.
|
||||
|
||||
## Persistent Memory
|
||||
|
||||
Memory is file-based. No vector DB, no Redis, no PostgreSQL. JSON and markdown files in `data/` are the database.
|
||||
|
||||
### Memory Directory Structure
|
||||
|
||||
```
|
||||
data/
|
||||
├── daily-logs/ # Append-only daily activity logs
|
||||
├── projects/ # Per-project context files
|
||||
├── decisions/ # Architectural and business decisions (ADR format)
|
||||
├── inbox/ # New tasks or ideas awaiting triage
|
||||
├── contacts/ # People, companies, relationship notes
|
||||
└── templates/ # Reusable prompts and formats
|
||||
```
|
||||
|
||||
### Daily Log Format
|
||||
|
||||
```markdown
|
||||
# 2026-04-22 - Daily Log
|
||||
|
||||
## Sessions
|
||||
- 09:00 - Session 1: Refactored auth module (@dev)
|
||||
- 11:30 - Session 2: Drafted investor update (@writer)
|
||||
|
||||
## Decisions
|
||||
- Switched from JWT to session cookies (see `data/decisions/2026-04-22-auth.md`)
|
||||
|
||||
## Blockers
|
||||
- Waiting on API key from vendor (follow up 2026-04-24)
|
||||
|
||||
## Next Actions
|
||||
- [ ] Merge auth refactor PR
|
||||
- [ ] Send investor update for review
|
||||
```
|
||||
|
||||
### Auto-Reflection Pattern
|
||||
|
||||
At the end of each session, the kernel appends a reflection:
|
||||
|
||||
```markdown
|
||||
## Reflection - Session 3
|
||||
- What worked: Parallel agent execution saved 20 minutes
|
||||
- What didn't: @researcher hit a paywalled source, need better source ranking
|
||||
- What to change: Add `source-tier` field to research notes (A/B/C credibility)
|
||||
```
|
||||
|
||||
This creates a feedback loop that improves the system over time without code changes.
|
||||
|
||||
## Scheduled Automation
|
||||
|
||||
Agentic OS tasks run on a schedule using external cron, not Claude Code's built-in cron (which dies when the session ends).
|
||||
|
||||
### macOS: LaunchAgent
|
||||
|
||||
```xml
|
||||
<!-- ~/Library/LaunchAgents/com.agentic.daily-sync.plist -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ...>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.agentic.daily-sync</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/claude</string>
|
||||
<string>--cwd</string>
|
||||
<string>/path/to/project</string>
|
||||
<string>--command</string>
|
||||
<string>/daily-sync</string>
|
||||
</array>
|
||||
<key>StartCalendarInterval</key>
|
||||
<dict>
|
||||
<key>Hour</key>
|
||||
<integer>8</integer>
|
||||
<key>Minute</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/agentic-daily-sync.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
### Linux: systemd Timer
|
||||
|
||||
```ini
|
||||
# ~/.config/systemd/user/agentic-daily-sync.service
|
||||
[Unit]
|
||||
Description=Agentic OS Daily Sync
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/claude --cwd /path/to/project --command /daily-sync
|
||||
```
|
||||
|
||||
```ini
|
||||
# ~/.config/systemd/user/agentic-daily-sync.timer
|
||||
[Unit]
|
||||
Description=Run daily sync every morning
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 8:00:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
### Cross-Platform: pm2
|
||||
|
||||
```bash
|
||||
# ecosystem.config.js
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'agentic-daily-sync',
|
||||
script: 'claude',
|
||||
args: '--cwd /path/to/project --command /daily-sync',
|
||||
cron_restart: '0 8 * * *',
|
||||
autorestart: false
|
||||
}]
|
||||
};
|
||||
```
|
||||
|
||||
## Data Layer
|
||||
|
||||
The data layer is your filesystem. Use JSON for structured data and markdown for narrative content.
|
||||
|
||||
### JSON for Structured State
|
||||
|
||||
```json
|
||||
// data/projects/website-v2.json
|
||||
{
|
||||
"name": "Website v2",
|
||||
"status": "in-progress",
|
||||
"milestone": "beta-launch",
|
||||
"agents_involved": ["@dev", "@writer"],
|
||||
"files": {
|
||||
"spec": "docs/website-v2-spec.md",
|
||||
"design": "designs/website-v2.fig"
|
||||
},
|
||||
"metrics": {
|
||||
"commits": 47,
|
||||
"last_session": "2026-04-22T11:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Markdown for Narrative
|
||||
|
||||
Use markdown for anything a human reads: decisions, logs, research notes, contact records.
|
||||
|
||||
### Schema Evolution
|
||||
|
||||
Never rename existing fields. Add new fields and mark old ones deprecated:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Website v2",
|
||||
"status": "in-progress",
|
||||
"milestone": "beta-launch",
|
||||
"_deprecated_priority": "high",
|
||||
"priority_v2": { "level": "high", "rationale": "Blocks investor demo" }
|
||||
}
|
||||
```
|
||||
|
||||
This keeps historical data readable without migration scripts.
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### Monolithic Single Agent
|
||||
|
||||
```markdown
|
||||
# BAD - One agent does everything
|
||||
You are a full-stack developer, writer, researcher, and DevOps engineer.
|
||||
```
|
||||
|
||||
Split into specialist agents. The kernel handles routing.
|
||||
|
||||
### Stateless Sessions
|
||||
|
||||
```markdown
|
||||
# BAD - No memory between sessions
|
||||
Starting fresh every time Claude Code opens.
|
||||
```
|
||||
|
||||
Always read `data/` at session start and write back at session end.
|
||||
|
||||
### Hardcoded Credentials
|
||||
|
||||
```markdown
|
||||
# BAD - API keys in agent files or CLAUDE.md
|
||||
Your OpenAI API key is sk-xxxxxxxx
|
||||
```
|
||||
|
||||
Use environment variables or a `.env` file loaded by scripts. Agents reference `process.env.API_KEY`.
|
||||
|
||||
### External Database for Simple State
|
||||
|
||||
```markdown
|
||||
# BAD - PostgreSQL for a solo user's agentic OS
|
||||
```
|
||||
|
||||
Use JSON/markdown files until you have multiple concurrent users or GBs of data.
|
||||
|
||||
### Over-Engineered Routing
|
||||
|
||||
```markdown
|
||||
# BAD - Routing logic in code instead of markdown tables
|
||||
if (intent.includes('deploy')) { agent = opsAgent; }
|
||||
```
|
||||
|
||||
Keep routing declarative in `CLAUDE.md` markdown tables. It is inspectable, editable, and debuggable.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- [ ] `CLAUDE.md` is under 200 lines and fits in context window
|
||||
- [ ] Each agent file is under 100 lines and focused on one domain
|
||||
- [ ] `data/` is git-ignored for sensitive logs, git-tracked for decisions and specs
|
||||
- [ ] Commands use imperative names: `/daily-sync`, not `/run-daily-sync`
|
||||
- [ ] Logs are append-only; never edit past daily logs
|
||||
- [ ] Every agent has a `Memory Scope` section defining what files it reads
|
||||
- [ ] Reflections are written at the end of every session
|
||||
- [ ] Scheduled tasks use external cron (LaunchAgent, systemd, pm2), not Claude Code's session cron
|
||||
- [ ] Cost tracking: log API spend per session in `data/logs/<date>-costs.json`
|
||||
- [ ] One project = one Agentic OS. Do not share a single `CLAUDE.md` across unrelated projects.
|
||||
@@ -1,154 +0,0 @@
|
||||
---
|
||||
name: angular-developer
|
||||
description: Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Angular Developer Guidelines
|
||||
|
||||
## When to Activate
|
||||
|
||||
- Working in any Angular project or codebase
|
||||
- Creating or scaffolding a new Angular project, application, or library
|
||||
- Generating components, services, directives, pipes, guards, or resolvers
|
||||
- Implementing reactivity with Angular Signals, `linkedSignal`, or `resource`
|
||||
- Working with Angular forms (signal forms, reactive forms, or template-driven)
|
||||
- Setting up dependency injection, routing, lazy loading, or route guards
|
||||
- Adding accessibility (ARIA), animations, or component styling
|
||||
- Writing or debugging Angular-specific tests (unit, component harness, E2E)
|
||||
- Configuring Angular CLI tooling or the Angular MCP server
|
||||
|
||||
1. Always analyze the project's Angular version before providing guidance, as best practices and available features can vary significantly between versions. If creating a new project with Angular CLI, do not specify a version unless prompted by the user.
|
||||
|
||||
2. When generating code, follow Angular's style guide and best practices for maintainability and performance. Use the Angular CLI for scaffolding components, services, directives, pipes, and routes to ensure consistency.
|
||||
|
||||
3. Once you finish generating code, run `ng build` to ensure there are no build errors. If there are errors, analyze the error messages and fix them before proceeding. Do not skip this step, as it is critical for ensuring the generated code is correct and functional.
|
||||
|
||||
## Creating New Projects
|
||||
|
||||
If no guidelines are provided by the user, use these defaults when creating a new Angular project:
|
||||
|
||||
1. Use the latest stable version of Angular unless the user specifies otherwise.
|
||||
2. Prefer Signal Forms for new projects only when the target Angular version supports them. [Find out more](references/signal-forms.md).
|
||||
|
||||
**Execution Rules for `ng new`:**
|
||||
When asked to create a new Angular project, you must determine the correct execution command by following these strict steps:
|
||||
|
||||
**Step 1: Check for an explicit user version.**
|
||||
|
||||
- **IF** the user requests a specific version (e.g., Angular 15), bypass local installations and strictly use `npx`.
|
||||
- **Command:** `npx @angular/cli@<requested_version> new <project-name>`
|
||||
|
||||
**Step 2: Check for an existing Angular installation.**
|
||||
|
||||
- **IF** no specific version is requested, run `ng version` in the terminal to check if the Angular CLI is already installed on the system.
|
||||
- **IF** the command succeeds and returns an installed version, use the local/global installation directly.
|
||||
- **Command:** `ng new <project-name>`
|
||||
|
||||
**Step 3: Fallback to Latest.**
|
||||
|
||||
- **IF** no specific version is requested AND the `ng version` command fails (indicating no Angular installation exists), you must use `npx` to fetch the latest version.
|
||||
- **Command:** `npx @angular/cli@latest new <project-name>`
|
||||
|
||||
## Components
|
||||
|
||||
When working with Angular components, consult the following references based on the task:
|
||||
|
||||
- **Fundamentals**: Anatomy, metadata, core concepts, and template control flow (@if, @for, @switch). Read [components.md](references/components.md)
|
||||
- **Inputs**: Signal-based inputs, transforms, and model inputs. Read [inputs.md](references/inputs.md)
|
||||
- **Outputs**: Signal-based outputs and custom event best practices. Read [outputs.md](references/outputs.md)
|
||||
- **Host Elements**: Host bindings and attribute injection. Read [host-elements.md](references/host-elements.md)
|
||||
|
||||
If you require deeper documentation not found in the references above, read the documentation at `https://angular.dev/guide/components`.
|
||||
|
||||
## Reactivity and Data Management
|
||||
|
||||
When managing state and data reactivity, use Angular Signals and consult the following references:
|
||||
|
||||
- **Signals Overview**: Core signal concepts (`signal`, `computed`), reactive contexts, and `untracked`. Read [signals-overview.md](references/signals-overview.md)
|
||||
- **Dependent State (`linkedSignal`)**: Creating writable state linked to source signals. Read [linked-signal.md](references/linked-signal.md)
|
||||
- **Async Reactivity (`resource`)**: Fetching asynchronous data directly into signal state. Read [resource.md](references/resource.md)
|
||||
- **Side Effects (`effect`)**: Logging, third-party DOM manipulation (`afterRenderEffect`), and when NOT to use effects. Read [effects.md](references/effects.md)
|
||||
|
||||
## Forms
|
||||
|
||||
In most cases for new apps, **prefer signal forms**. When making a forms decision, analyze the project and consider the following guidelines:
|
||||
|
||||
- If the application version supports Signal Forms and this is a new form, **prefer signal forms**.
|
||||
- For older applications or existing forms, match the application's current form strategy.
|
||||
|
||||
- **Signal Forms**: Use signals for form state management. Read [signal-forms.md](references/signal-forms.md)
|
||||
- **Template-driven forms**: Use for simple forms. Read [template-driven-forms.md](references/template-driven-forms.md)
|
||||
- **Reactive forms**: Use for complex forms. Read [reactive-forms.md](references/reactive-forms.md)
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
When implementing dependency injection in Angular, follow these guidelines:
|
||||
|
||||
- **Fundamentals**: Overview of Dependency Injection, services, and the `inject()` function. Read [di-fundamentals.md](references/di-fundamentals.md)
|
||||
- **Creating and Using Services**: Creating services, the `providedIn: 'root'` option, and injecting into components or other services. Read [creating-services.md](references/creating-services.md)
|
||||
- **Defining Dependency Providers**: Automatic vs manual provision, `InjectionToken`, `useClass`, `useValue`, `useFactory`, and scopes. Read [defining-providers.md](references/defining-providers.md)
|
||||
- **Injection Context**: Where `inject()` is allowed, `runInInjectionContext`, and `assertInInjectionContext`. Read [injection-context.md](references/injection-context.md)
|
||||
- **Hierarchical Injectors**: The `EnvironmentInjector` vs `ElementInjector`, resolution rules, modifiers (`optional`, `skipSelf`), and `providers` vs `viewProviders`. Read [hierarchical-injectors.md](references/hierarchical-injectors.md)
|
||||
|
||||
## Angular Aria
|
||||
|
||||
When building accessible custom components for any of the following patterns: Accordion, Listbox, Combobox, Menu, Tabs, Toolbar, Tree, Grid, consult the following reference:
|
||||
|
||||
- **Angular Aria Components**: Building headless, accessible components (Accordion, Listbox, Combobox, Menu, Tabs, Toolbar, Tree, Grid) and styling ARIA attributes. Read [angular-aria.md](references/angular-aria.md)
|
||||
|
||||
## Routing
|
||||
|
||||
When implementing navigation in Angular, consult the following references:
|
||||
|
||||
- **Define Routes**: URL paths, static vs dynamic segments, wildcards, and redirects. Read [define-routes.md](references/define-routes.md)
|
||||
- **Route Loading Strategies**: Eager vs lazy loading, and context-aware loading. Read [loading-strategies.md](references/loading-strategies.md)
|
||||
- **Show Routes with Outlets**: Using `<router-outlet>`, nested outlets, and named outlets. Read [show-routes-with-outlets.md](references/show-routes-with-outlets.md)
|
||||
- **Navigate to Routes**: Declarative navigation with `RouterLink` and programmatic navigation with `Router`. Read [navigate-to-routes.md](references/navigate-to-routes.md)
|
||||
- **Control Route Access with Guards**: Implementing `CanActivate`, `CanMatch`, and other guards for security. Read [route-guards.md](references/route-guards.md)
|
||||
- **Data Resolvers**: Pre-fetching data before route activation with `ResolveFn`. Read [data-resolvers.md](references/data-resolvers.md)
|
||||
- **Router Lifecycle and Events**: Chronological order of navigation events and debugging. Read [router-lifecycle.md](references/router-lifecycle.md)
|
||||
- **Rendering Strategies**: CSR, SSG (Prerendering), and SSR with hydration. Read [rendering-strategies.md](references/rendering-strategies.md)
|
||||
- **Route Transition Animations**: Enabling and customizing the View Transitions API. Read [route-animations.md](references/route-animations.md)
|
||||
|
||||
If you require deeper documentation or more context, visit the [official Angular Routing guide](https://angular.dev/guide/routing).
|
||||
|
||||
## Styling and Animations
|
||||
|
||||
When implementing styling and animations in Angular, consult the following references:
|
||||
|
||||
- **Using Tailwind CSS with Angular**: Integrating Tailwind CSS into Angular projects. Read [tailwind-css.md](references/tailwind-css.md)
|
||||
- **Angular Animations**: Using native CSS (recommended) or the legacy DSL for dynamic effects. Read [angular-animations.md](references/angular-animations.md)
|
||||
- **Styling components**: Best practices for component styles and encapsulation. Read [component-styling.md](references/component-styling.md)
|
||||
|
||||
## Testing
|
||||
|
||||
When writing or updating tests, consult the following references based on the task:
|
||||
|
||||
- **Fundamentals**: Best practices for unit testing, async patterns, and `TestBed`. Read [testing-fundamentals.md](references/testing-fundamentals.md)
|
||||
- **Component Harnesses**: Standard patterns for robust component interaction. Read [component-harnesses.md](references/component-harnesses.md)
|
||||
- **Router Testing**: Using `RouterTestingHarness` for reliable navigation tests. Read [router-testing.md](references/router-testing.md)
|
||||
- **End-to-End (E2E) Testing**: Best practices for E2E tests with Cypress or Playwright. Read [e2e-testing.md](references/e2e-testing.md)
|
||||
|
||||
## Tooling
|
||||
|
||||
When working with Angular tooling, consult the following references:
|
||||
|
||||
- **Angular CLI**: Creating applications, generating code (components, routes, services), serving, and building. Read [cli.md](references/cli.md)
|
||||
- **Angular MCP Server**: Available tools, configuration, and experimental features. Read [mcp.md](references/mcp.md)
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- Using `null` or `undefined` as initial signal form field values — use `''`, `0`, or `[]` instead
|
||||
- Accessing form field state flags without calling the field first: `form.field.valid()` — use `form.field().valid()`
|
||||
- Starting new forms with older form APIs when the target Angular version supports Signal Forms
|
||||
- Setting `min`, `max`, `value`, `disabled`, or `readonly` HTML attributes on `[formField]` inputs — define these as schema rules instead
|
||||
- Calling `inject()` outside an injection context — use `runInInjectionContext` when needed
|
||||
- Using `effect()` for derived state that should use `computed()`
|
||||
- Referencing `$parent.$index` in nested `@for` loops — Angular does not support `$parent`; use `let outerIdx = $index` instead
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `tdd-workflow` — test-driven development workflow applicable to Angular components and services
|
||||
- `security-review` — security checklist for web applications including Angular-specific concerns
|
||||
- `frontend-patterns` — general frontend patterns for context on React/Next.js approaches
|
||||
@@ -1,160 +0,0 @@
|
||||
# Angular Animations
|
||||
|
||||
When animating elements in Angular, **first analyze the project's Angular version** in `package.json`.
|
||||
For modern applications (**Angular v20.2 and above**), prefer using native CSS with `animate.enter` and `animate.leave`. For older applications, you may need to use the deprecated `@angular/animations` package.
|
||||
|
||||
## 1. Native CSS Animations (v20.2+ Recommended)
|
||||
|
||||
Modern Angular provides `animate.enter` and `animate.leave` to animate elements as they enter or leave the DOM. They apply CSS classes at the appropriate times.
|
||||
|
||||
### `animate.enter` and `animate.leave`
|
||||
|
||||
Use these directly on elements to apply CSS classes during the enter or leave phase. Angular automatically removes the enter classes when the animation completes. For `animate.leave`, Angular waits for the animation to finish before removing the element from the DOM.
|
||||
|
||||
`animate.enter` example:
|
||||
|
||||
```html
|
||||
@if (isShown()) {
|
||||
<div class="enter-container" animate.enter="enter-animation">
|
||||
<p>The box is entering.</p>
|
||||
</div>
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/* Ensure you have a starting style if using transitions instead of keyframes */
|
||||
.enter-container {
|
||||
border: 1px solid #dddddd;
|
||||
margin-top: 1em;
|
||||
padding: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
.enter-container p {
|
||||
margin: 0;
|
||||
}
|
||||
.enter-animation {
|
||||
animation: slide-fade 1s;
|
||||
}
|
||||
@keyframes slide-fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
_Note: `animate.leave` may be added to child elements being removed._
|
||||
|
||||
### Event Bindings and Third-party Libraries
|
||||
|
||||
You can bind to `(animate.enter)` and `(animate.leave)` to call functions or use JS libraries like GSAP.
|
||||
|
||||
```html
|
||||
@if(show()) {
|
||||
<div (animate.leave)="onLeave($event)">...</div>
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
import { AnimationCallbackEvent } from '@angular/core';
|
||||
|
||||
onLeave(event: AnimationCallbackEvent) {
|
||||
// Custom animation logic here
|
||||
// CRITICAL: You MUST call animationComplete() when done so Angular removes the element!
|
||||
event.animationComplete();
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Advanced CSS Animations
|
||||
|
||||
CSS offers robust tools for advanced animation sequences.
|
||||
|
||||
### Animating State and Styles
|
||||
|
||||
Toggle CSS classes on elements using property binding to trigger transitions.
|
||||
|
||||
```html
|
||||
<div [class.open]="isOpen">...</div>
|
||||
```
|
||||
|
||||
```css
|
||||
div {
|
||||
transition: height 0.3s ease-out;
|
||||
height: 100px;
|
||||
}
|
||||
div.open {
|
||||
height: 200px;
|
||||
}
|
||||
```
|
||||
|
||||
### Animating Auto Height
|
||||
|
||||
You can use `css-grid` to animate to auto height.
|
||||
|
||||
```css
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
transition: grid-template-rows 0.3s;
|
||||
}
|
||||
.container.open {
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
.container > div {
|
||||
overflow: hidden;
|
||||
}
|
||||
```
|
||||
|
||||
### Staggering and Parallel Animations
|
||||
|
||||
- **Staggering**: Use `animation-delay` or `transition-delay` with different values for items in a list.
|
||||
- **Parallel**: Apply multiple animations in the `animation` shorthand (e.g., `animation: rotate 3s, fade-in 2s;`).
|
||||
|
||||
### Programmatic Control
|
||||
|
||||
Retrieve animations directly using standard Web APIs:
|
||||
|
||||
```ts
|
||||
const animations = element.getAnimations();
|
||||
animations.forEach((anim) => anim.pause());
|
||||
```
|
||||
|
||||
## 3. Legacy Animations DSL (Deprecated)
|
||||
|
||||
For older projects (pre v20.2 or where `@angular/animations` is already heavily used), you use the component metadata DSL.
|
||||
|
||||
**Important:** Do not mix legacy animations and `animate.enter`/`leave` in the same component.
|
||||
|
||||
### Setup
|
||||
|
||||
```ts
|
||||
bootstrapApplication(App, {
|
||||
providers: [provideAnimationsAsync()],
|
||||
});
|
||||
```
|
||||
|
||||
### Defining Transitions
|
||||
|
||||
```ts
|
||||
import {signal} from '@angular/core';
|
||||
import {trigger, state, style, animate, transition} from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
animations: [
|
||||
trigger('openClose', [
|
||||
state('open', style({opacity: 1})),
|
||||
state('closed', style({opacity: 0})),
|
||||
transition('open <=> closed', [animate('0.5s')]),
|
||||
]),
|
||||
],
|
||||
template: `<div [@openClose]="isOpen() ? 'open' : 'closed'">...</div>`,
|
||||
})
|
||||
export class OpenClose {
|
||||
isOpen = signal(true);
|
||||
}
|
||||
```
|
||||
@@ -1,410 +0,0 @@
|
||||
# Angular Aria
|
||||
|
||||
Angular Aria (`@angular/aria`) is a collection of headless, accessible directives that implement common WAI-ARIA patterns. These directives handle keyboard interactions, ARIA attributes, focus management, and screen reader support.
|
||||
|
||||
**As an AI Agent, your role is to provide the HTML structure and CSS styling**, while the directives handle the complex accessibility logic.
|
||||
|
||||
## Styling Headless Components
|
||||
|
||||
Because Angular Aria components are headless, they do not come with default styles. You **must** use CSS to style different states based on the ARIA attributes or structural classes the directives automatically apply.
|
||||
|
||||
Common ARIA attributes to target in CSS:
|
||||
|
||||
- `[aria-expanded="true"]` / `[aria-expanded="false"]`
|
||||
- `[aria-selected="true"]`
|
||||
- `[aria-disabled="true"]`
|
||||
- `[aria-current="page"]` (for navigation)
|
||||
|
||||
---
|
||||
|
||||
**CRITICAL**: Before using this package, it must be installed via the package manager. Confirm that it has been installed in the project. Use `npm install @angular/aria` to install if necessary.
|
||||
|
||||
## 1. Accordion
|
||||
|
||||
Organizes related content into expandable/collapsible sections.
|
||||
|
||||
**Usage:** The Accordion is a layout component designed to organize content into logical groups that users can expand one at a time to reduce scrolling on content-heavy pages. Use it for FAQs, long forms, or progressive disclosure of information, but avoid it for primary navigation or scenarios where users must view multiple sections of content simultaneously.
|
||||
|
||||
**Imports:** `import { AccordionContent, AccordionGroup, AccordionPanel, AccordionTrigger } from '@angular/aria/accordion';`
|
||||
|
||||
**Directives:** `ngAccordionGroup`, `ngAccordionTrigger`, `ngAccordionPanel`, `ngAccordionContent` (for lazy loading).
|
||||
|
||||
```ts
|
||||
@Component({
|
||||
selector: 'app-cmp',
|
||||
imports: [AccordionContent, AccordionGroup, AccordionPanel, AccordionTrigger],
|
||||
template: `...`,
|
||||
styles: [],
|
||||
})
|
||||
export class App {
|
||||
protected readonly title = signal('angular-app');
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<div ngAccordionGroup [multiExpandable]="false">
|
||||
<div class="accordion-item">
|
||||
<button ngAccordionTrigger panelId="panel-1" class="accordion-header">
|
||||
Section 1
|
||||
<span class="icon">▼</span>
|
||||
</button>
|
||||
<div ngAccordionPanel panelId="panel-1" class="accordion-panel">
|
||||
<ng-template ngAccordionContent>
|
||||
<p>Lazy loaded content here.</p>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Target the `[aria-expanded]` attribute on the trigger to rotate icons, and style the panel visibility.
|
||||
|
||||
```css
|
||||
.accordion-header[aria-expanded='true'] .icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* The panel directive handles DOM removal, but you can style the transition */
|
||||
.accordion-panel {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Listbox
|
||||
|
||||
A foundational directive for displaying a list of options. Used for visible selection lists (not dropdowns).
|
||||
|
||||
**Usage:** Visible selectable lists (single or multi-select).
|
||||
|
||||
**Imports:** `import {Listbox, Option} from '@angular/aria/listbox';`
|
||||
|
||||
**Directives:** `ngListbox`, `ngOption`.
|
||||
|
||||
```ts
|
||||
@Component({
|
||||
selector: 'app-cmp',
|
||||
imports: [Listbox, Option],
|
||||
template: `...`,
|
||||
styles: [],
|
||||
})
|
||||
export class App {
|
||||
protected readonly title = signal('angular-app');
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- horizontal or vertical orientation -->
|
||||
<ul ngListbox [(values)]="selectedItems" orientation="horizontal" [multi]="true">
|
||||
<li ngOption value="apple" class="option">Apple</li>
|
||||
<li ngOption value="banana" class="option">Banana</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Target `[aria-selected="true"]` for selected state and `:focus-visible` or `[data-active]` for the focused item (Angular Aria uses roving tabindex or activedescendant).
|
||||
|
||||
```css
|
||||
.option {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.option[aria-selected='true'] {
|
||||
background: #e0f7fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
/* Focus state managed by aria */
|
||||
.option:focus-visible {
|
||||
outline: 2px solid blue;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Combobox, Select, and Multiselect
|
||||
|
||||
These patterns combine `ngCombobox` with a popup containing an `ngListbox`.
|
||||
|
||||
- **Combobox**: Text input + popup (used for Autocomplete).
|
||||
- **Select**: Readonly Combobox + single-select Listbox.
|
||||
- **Multiselect**: Readonly Combobox + multi-select Listbox.
|
||||
|
||||
**Usage:** The Combobox is a low-level primitive directive that synchronizes a text input with a popup, serving as the foundational logic for autocomplete, select, and multiselect patterns. Use it specifically for building custom filtering, unique selection requirements, or specialized input-to-popup coordination that deviates from standard, documented components.
|
||||
|
||||
**Imports:**
|
||||
|
||||
```
|
||||
import {Combobox, ComboboxInput, ComboboxPopupContainer} from '@angular/aria/combobox';
|
||||
import {Listbox, Option} from '@angular/aria/listbox';
|
||||
```
|
||||
|
||||
**Directives:** `ngCombobox`, `ngComboboxInput`, `ngComboboxPopupContainer`, `ngListbox`, `ngOption`.
|
||||
|
||||
```html
|
||||
<!-- Example: Standard Select -->
|
||||
<div ngCombobox [readonly]="true">
|
||||
<button ngComboboxInput class="select-trigger">
|
||||
{{ selectedValue() || 'Choose an option' }}
|
||||
</button>
|
||||
|
||||
<ng-template ngComboboxPopupContainer>
|
||||
<ul ngListbox [(values)]="selectedValue" class="dropdown-menu">
|
||||
<li ngOption value="option1">Option 1</li>
|
||||
<li ngOption value="option2">Option 2</li>
|
||||
</ul>
|
||||
</ng-template>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Style the popup container to look like a dropdown floating above content (often paired with CDK Overlay).
|
||||
|
||||
```css
|
||||
.select-trigger {
|
||||
width: 200px;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
.dropdown-menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 1px solid #ccc;
|
||||
background: white;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Menu and Menubar
|
||||
|
||||
For actions, commands, and context menus (not for form selection).
|
||||
|
||||
**Usage:** The Menubar is a high-level navigation pattern designed for building desktop-style application command bars (e.g., File, Edit, View) that stay persistent across an interface. It is best utilized for organizing complex commands into logical top-level categories with full horizontal keyboard support, but it should be avoided for simple standalone action lists or mobile-first layouts where horizontal space is constrained.
|
||||
|
||||
**Imports:** `import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';`
|
||||
|
||||
**Directives:** `ngMenuBar`, `ngMenu`, `ngMenuItem`, `ngMenuTrigger`.
|
||||
|
||||
```html
|
||||
<!-- Menubar Example -->
|
||||
<ul ngMenuBar class="menubar">
|
||||
<li ngMenuItem value="file">
|
||||
<button ngMenuTrigger [menu]="fileMenu">File</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul ngMenu #fileMenu="ngMenu" class="menu">
|
||||
<li ngMenuItem value="new">New</li>
|
||||
<li ngMenuItem value="open">Open</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Use flexbox for the menubar. Hide/show submenus based on the trigger's state.
|
||||
|
||||
```css
|
||||
.menubar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.menu {
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.menu li {
|
||||
padding: 5px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Tabs
|
||||
|
||||
Layered content sections where only one panel is visible.
|
||||
|
||||
**Usage:** The Tabs component is used to organize related content into distinct, navigable sections, allowing users to switch between categories or views without leaving the page. It is ideal for settings panels, multi-topic documentation, or dashboards, but should be avoided for sequential workflows (steppers) or when navigation involves more than 7–8 sections.
|
||||
|
||||
**Imports:** `import {Tab, Tabs, TabList, TabPanel, TabContent} from '@angular/aria/tabs';`
|
||||
|
||||
**Directives:** `ngTabs`, `ngTabList`, `ngTab`, `ngTabPanel`, `ngTabContent`.
|
||||
|
||||
```html
|
||||
<div ngTabs>
|
||||
<ul ngTabList class="tab-list">
|
||||
<li ngTab value="profile" class="tab-btn">Profile</li>
|
||||
<li ngTab value="security" class="tab-btn">Security</li>
|
||||
</ul>
|
||||
|
||||
<div ngTabPanel value="profile" class="tab-panel">
|
||||
<ng-template ngTabContent>Profile Settings</ng-template>
|
||||
</div>
|
||||
<div ngTabPanel value="security" class="tab-panel">
|
||||
<ng-template ngTabContent>Security Settings</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Target `[aria-selected="true"]` on the tab buttons.
|
||||
|
||||
```css
|
||||
.tab-list {
|
||||
display: flex;
|
||||
border-bottom: 2px solid #ccc;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.tab-btn {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.tab-btn[aria-selected='true'] {
|
||||
border-bottom-color: blue;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab-panel {
|
||||
padding: 20px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Toolbar
|
||||
|
||||
Groups related controls (like text formatting).
|
||||
|
||||
**Usage:** The Toolbar is an organizational component designed to group frequently accessed, related controls into a single logical container. It is best used to enhance keyboard efficiency (via arrow-key navigation) and visual structure for workflows requiring repeated actions, such as text formatting or media controls.
|
||||
|
||||
**Imports:** `import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';`
|
||||
|
||||
**Directives:** `ngToolbar`, `ngToolbarWidget`, `ngToolbarWidgetGroup`.
|
||||
|
||||
```html
|
||||
<div ngToolbar class="toolbar">
|
||||
<div ngToolbarWidgetGroup [multi]="true" role="group" aria-label="Formatting">
|
||||
<button ngToolbarWidget value="bold" class="tool-btn">B</button>
|
||||
<button ngToolbarWidget value="italic" class="tool-btn">I</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Target `[aria-pressed="true"]` (for toggle buttons) or `[aria-checked="true"]` (for radio groups) within the toolbar.
|
||||
|
||||
```css
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
padding: 8px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.tool-btn {
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.tool-btn[aria-pressed='true'],
|
||||
.tool-btn[aria-checked='true'] {
|
||||
background: #ddd;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Tree
|
||||
|
||||
Displays hierarchical data (file systems, nested nav).
|
||||
|
||||
**Usage:** The Tree component is designed for navigating and displaying deeply nested, hierarchical data structures like file systems, organization charts, or complex site architectures. It should be used specifically for multi-level relationships where users need to expand or collapse branches, but it should be avoided for flat lists, data tables, or simple selection menus.
|
||||
|
||||
**Imports:** `import {Tree, TreeItem, TreeItemGroup} from '@angular/aria/tree';`
|
||||
|
||||
**Directives:** `ngTree`, `ngTreeItem`, `ngTreeGroup`.
|
||||
|
||||
```html
|
||||
<ul ngTree class="tree">
|
||||
<li ngTreeItem value="documents">
|
||||
<span class="tree-label">Documents</span>
|
||||
<ul ngTreeGroup class="tree-group">
|
||||
<li ngTreeItem value="resume">Resume.pdf</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Target `[aria-expanded]` to show/hide children or rotate chevron icons. Use `padding-left` on nested groups to show hierarchy.
|
||||
|
||||
```css
|
||||
.tree,
|
||||
.tree-group {
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.tree-label::before {
|
||||
content: '> ';
|
||||
display: inline-block;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
li[aria-expanded='true'] > .tree-label::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
```
|
||||
|
||||
## 8. Grid
|
||||
|
||||
A two-dimensional interactive collection of cells enabling navigation via arrow keys.
|
||||
|
||||
**Usage:** Data tables, calendars, spreadsheets, and layout patterns for interactive elements.
|
||||
**Directives:** `ngGrid`, `ngGridRow`, `ngGridCell`, `ngGridCellWidget`.
|
||||
|
||||
```html
|
||||
<table ngGrid [multi]="true" [enableSelection]="true" class="grid-table">
|
||||
<tr ngGridRow>
|
||||
<th ngGridCell role="columnheader">Name</th>
|
||||
<th ngGridCell role="columnheader">Status</th>
|
||||
</tr>
|
||||
<tr ngGridRow>
|
||||
<td ngGridCell>Project A</td>
|
||||
<td ngGridCell [(selected)]="isSelected">
|
||||
<button ngGridCellWidget (activated)="onActivate()">Active</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
**Styling Strategy:**
|
||||
Target `[aria-selected="true"]` for selected cells and `:focus-visible` for the active cell (roving tabindex) or `[aria-activedescendant]` on the container.
|
||||
|
||||
```css
|
||||
.grid-table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
[ngGridCell] {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
[ngGridCell][aria-selected='true'] {
|
||||
background: #e3f2fd;
|
||||
}
|
||||
/* Focus state managed by roving tabindex */
|
||||
[ngGridCell]:focus-visible {
|
||||
outline: 2px solid #2196f3;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
```
|
||||
|
||||
## General Rules for Agents
|
||||
|
||||
1. **Never use native HTML elements like `<select>`** when asked to implement these specific Aria patterns. Use the `ng*` directives.
|
||||
2. **Handle CSS manually**: Remember that `Angular Aria` does NOT provide styles. You must write the CSS, targeting the native ARIA attributes (`aria-expanded`, `aria-selected`, etc.) that the directives automatically toggle.
|
||||
3. **Lazy Loading**: Always use the provided structural directives (`ngAccordionContent`, `ngTabContent`) inside `ng-template` for heavy content panels to ensure they are lazily rendered.
|
||||
@@ -1,86 +0,0 @@
|
||||
# Angular CLI Guide for Agents
|
||||
|
||||
The Angular CLI (`ng`) is the primary tool for managing an Angular workspace. Always prefer CLI commands over manual file creation or generic `npm` commands when modifying project structure or adding Angular-specific dependencies.
|
||||
|
||||
## 1. Managing Dependencies
|
||||
|
||||
**ALWAYS use `ng add` for Angular libraries** instead of `npm install`. `ng add` installs the package AND runs initialization schematics (e.g., configuring `angular.json`, updating root providers).
|
||||
|
||||
```bash
|
||||
ng add @angular/material
|
||||
ng add tailwindcss
|
||||
ng add @angular/fire
|
||||
```
|
||||
|
||||
To update the application and its dependencies (which automatically runs code migrations):
|
||||
|
||||
```bash
|
||||
ng update @angular/core@<latest or specific version> @angular/cli<latest or specific version>
|
||||
```
|
||||
|
||||
## 2. Generating Code (`ng generate` or `ng g`)
|
||||
|
||||
Always use the CLI to generate code to ensure it adheres to Angular standards and updates necessary configuration files automatically.
|
||||
|
||||
| Target | Command | Notes |
|
||||
| :----------- | :-------------------- | :--------------------------------------------------------------------------------------------- |
|
||||
| Component | `ng g c path/to/name` | Generates a component. Use `--inline-style` (`-s`) or `--inline-template` (`-t`) if requested. |
|
||||
| Service | `ng g s path/to/name` | Generates an `@Injectable({providedIn: 'root'})` service. |
|
||||
| Directive | `ng g d path/to/name` | Generates a directive. |
|
||||
| Pipe | `ng g p path/to/name` | Generates a pipe. |
|
||||
| Guard | `ng g g path/to/name` | Generates a functional route guard. |
|
||||
| Environments | `ng g environments` | Scaffolds `src/environments/` and updates `angular.json` with file replacements. |
|
||||
|
||||
_Note: There is no command to generate a single route definition. Generate a component, then manually add it to the `Routes` array in `app.routes.ts`._
|
||||
|
||||
## 3. Development Server & Proxying
|
||||
|
||||
Start the local development server with hot-module replacement (HMR):
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
### Backend API Proxying
|
||||
|
||||
To proxy API requests during development (e.g., rerouting `/api` to a local Node server):
|
||||
|
||||
1. Create `src/proxy.conf.json`:
|
||||
```json
|
||||
{
|
||||
"/api/**": {"target": "http://localhost:3000", "secure": false}
|
||||
}
|
||||
```
|
||||
2. Update `angular.json` under the `serve` target:
|
||||
```json
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"options": { "proxyConfig": "src/proxy.conf.json" }
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Building the Application
|
||||
|
||||
Compile the application into an output directory (default: `dist/<project-name>/browser`). Modern Angular uses the `@angular/build:application` builder (esbuild-based).
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
- `ng build` defaults to the production configuration, which enables Ahead-of-Time (AOT) compilation, minification, and tree-shaking.
|
||||
- Target specific configurations defined in `angular.json` using `--configuration`: `ng build --configuration=staging`.
|
||||
|
||||
## 5. Testing
|
||||
|
||||
- **Unit Tests**: Run `ng test` to execute unit tests via the configured test runner (e.g., Karma or Vitest).
|
||||
- **End-to-End (E2E)**: Run `ng e2e`. If no E2E framework is configured, the CLI will prompt to install one (Cypress, Playwright, Puppeteer, etc.).
|
||||
|
||||
## 6. Deployment
|
||||
|
||||
To deploy an application, you must first add a deployment builder, then run the deploy command:
|
||||
|
||||
```bash
|
||||
# Example for Firebase
|
||||
ng add @angular/fire
|
||||
ng deploy
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user