mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-26 18:35:05 +00:00
feat: integrate twelve reviewed catalog and documentation PRs
This commit is contained in:
76 files changed
+1800
-145
No files matched your search
@@ -11,7 +11,7 @@
|
||||
{
|
||||
"name": "ecc",
|
||||
"source": "./",
|
||||
"description": "Harness-native ECC operator layer - 71 agents, 302 skills, 95 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
|
||||
"description": "Harness-native ECC operator layer - 71 agents, 304 skills, 95 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
|
||||
"version": "2.2.3",
|
||||
"author": {
|
||||
"name": "Affaan Mustafa",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ecc",
|
||||
"version": "2.2.3",
|
||||
"description": "Harness-native ECC plugin for engineering teams - 71 agents, 302 skills, 95 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
|
||||
"description": "Harness-native ECC plugin for engineering teams - 71 agents, 304 skills, 95 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
|
||||
"author": {
|
||||
"name": "Affaan Mustafa",
|
||||
"url": "https://x.com/affaanmustafa"
|
||||
|
||||
@@ -16,7 +16,7 @@ Use with caution:
|
||||
- Enable for trusted, well-defined plans
|
||||
- Disable for exploratory work
|
||||
- Never use dangerously-skip-permissions flag
|
||||
- Configure `allowedTools` in `~/.claude.json` instead
|
||||
- Configure `permissions.allow` rules in `~/.claude/settings.json` (or a project's `.claude/settings.json`) instead
|
||||
|
||||
## TodoWrite Best Practices
|
||||
|
||||
|
||||
@@ -36,16 +36,21 @@ Lower context sensitivity tasks:
|
||||
|
||||
## Extended Thinking + Plan Mode
|
||||
|
||||
Extended thinking is enabled by default, reserving up to 31,999 tokens for internal reasoning.
|
||||
In Claude Code, adaptive reasoning lets the model choose how much to think for each task.
|
||||
For Fable models, Sonnet 5 and later, and Opus 4.7 and later, use effort to control reasoning
|
||||
rather than a fixed token budget. Available levels depend on the model.
|
||||
|
||||
Control extended thinking via:
|
||||
- **Toggle**: Option+T (macOS) / Alt+T (Windows/Linux)
|
||||
- **Config**: Set `alwaysThinkingEnabled` in `~/.claude/settings.json`
|
||||
- **Budget cap**: `export MAX_THINKING_TOKENS=10000` (bash) or `$env:MAX_THINKING_TOKENS = "10000"` (PowerShell)
|
||||
Control thinking via:
|
||||
- **Effort**: Use `/effort` in a session, `--effort <level>` at launch, or `CLAUDE_CODE_EFFORT_LEVEL`. Save a per-model level in `modelSettings` in `~/.claude/settings.json`; the older user-level `effortLevel` key does not apply to Opus 5.5 or newer models.
|
||||
- **Toggle**: Option+T (macOS) / Alt+T (Windows/Linux), or `alwaysThinkingEnabled` in settings. These cannot disable thinking on Opus 5.5, Sonnet 5.5, or Fable models.
|
||||
- **Fixed budget**: Claude Code ignores positive `MAX_THINKING_TOKENS` values on adaptive reasoning models. On Opus 4.6 or Sonnet 4.6, set `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` to use a fixed budget with `MAX_THINKING_TOKENS`.
|
||||
- **Verbose mode**: Ctrl+O to see thinking output
|
||||
|
||||
See [Claude Code model configuration](https://code.claude.com/docs/en/model-config#adjust-effort-level)
|
||||
and [environment variables](https://code.claude.com/docs/en/env-vars) for supported controls.
|
||||
|
||||
For complex tasks requiring deep reasoning:
|
||||
1. Ensure extended thinking is enabled (on by default)
|
||||
1. Choose a higher supported effort level, such as `/effort high`
|
||||
2. Enable **Plan Mode** for structured approach
|
||||
3. Use multiple critique rounds for thorough analysis
|
||||
4. Use split role sub-agents for diverse perspectives
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ This file provides Gemini CLI with the baseline ECC workflow, review standards,
|
||||
|
||||
## Overview
|
||||
|
||||
ECC is a cross-harness coding system with 71 specialized agents, 302 skills, and 95 commands.
|
||||
ECC is a cross-harness coding system with 71 specialized agents, 304 skills, and 95 commands.
|
||||
|
||||
Gemini support is currently focused on a strong project-local instruction layer via `.gemini/GEMINI.md`, plus the shared MCP catalog and package-manager setup assets shipped by the installer.
|
||||
|
||||
|
||||
+25
-23
@@ -158,14 +158,14 @@ Steering files provide always-on rules and context that shape how the agent work
|
||||
|
||||
| File | Inclusion | Description |
|
||||
|------|-----------|-------------|
|
||||
| `coding-style.md` | auto | Core coding style rules: immutability, file organization, error handling, and code quality standards. Loaded in every conversation. |
|
||||
| `security.md` | auto | Security best practices including mandatory checks, secret management, and security response protocol. Loaded in every conversation. |
|
||||
| `testing.md` | auto | Testing requirements: 80% coverage minimum, TDD workflow, and test types (unit, integration, E2E). Loaded in every conversation. |
|
||||
| `development-workflow.md` | auto | Development process, PR workflow, and collaboration patterns. Loaded in every conversation. |
|
||||
| `git-workflow.md` | auto | Git commit conventions, branching strategies, and version control best practices. Loaded in every conversation. |
|
||||
| `patterns.md` | auto | Common design patterns and architectural principles. Loaded in every conversation. |
|
||||
| `performance.md` | auto | Performance optimization guidelines and profiling strategies. Loaded in every conversation. |
|
||||
| `lessons-learned.md` | auto | Project-specific patterns and learnings. Edit this file to capture your team's conventions. Loaded in every conversation. |
|
||||
| `coding-style.md` | always | Core coding style rules: immutability, file organization, error handling, and code quality standards. Loaded in every conversation. |
|
||||
| `security.md` | always | Security best practices including mandatory checks, secret management, and security response protocol. Loaded in every conversation. |
|
||||
| `testing.md` | always | Testing requirements: 80% coverage minimum, TDD workflow, and test types (unit, integration, E2E). Loaded in every conversation. |
|
||||
| `development-workflow.md` | always | Development process, PR workflow, and collaboration patterns. Loaded in every conversation. |
|
||||
| `git-workflow.md` | always | Git commit conventions, branching strategies, and version control best practices. Loaded in every conversation. |
|
||||
| `patterns.md` | always | Common design patterns and architectural principles. Loaded in every conversation. |
|
||||
| `performance.md` | always | Performance optimization guidelines and profiling strategies. Loaded in every conversation. |
|
||||
| `lessons-learned.md` | always | Project-specific patterns and learnings. Edit this file to capture your team's conventions. Loaded in every conversation. |
|
||||
| `typescript-patterns.md` | fileMatch: `*.ts,*.tsx` | TypeScript-specific patterns, type safety, and best practices. Loaded when editing TypeScript files. |
|
||||
| `python-patterns.md` | fileMatch: `*.py` | Python-specific patterns, type hints, and best practices. Loaded when editing Python files. |
|
||||
| `golang-patterns.md` | fileMatch: `*.go` | Go-specific patterns, concurrency, and best practices. Loaded when editing Go files. |
|
||||
@@ -181,15 +181,17 @@ Steering files provide always-on rules and context that shape how the agent work
|
||||
| `review-mode.md` | manual | Code review context mode. Invoke with `#review-mode` for thorough reviews. |
|
||||
| `research-mode.md` | manual | Research context mode. Invoke with `#research-mode` for exploration and learning. |
|
||||
|
||||
Steering files with `auto` inclusion are loaded automatically. No action needed — they apply as soon as you install them.
|
||||
Steering files with `always` inclusion are loaded automatically. No action needed — they apply as soon as you install them.
|
||||
|
||||
> **Upgrading an existing install:** the installer never overwrites steering files that already exist in your project. If you installed before these files switched to `always`, change `inclusion: auto` to `inclusion: always` in the frontmatter of `coding-style.md`, `security.md`, `testing.md`, `development-workflow.md`, `git-workflow.md`, `patterns.md`, `performance.md`, and `lessons-learned.md` in your `.kiro/steering/` directory.
|
||||
|
||||
To create your own, add a markdown file to `.kiro/steering/` with YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
inclusion: auto # auto | fileMatch | manual
|
||||
name: my-steering # required if inclusion is auto
|
||||
description: Brief explanation of what this steering file contains
|
||||
inclusion: always # always | auto | fileMatch | manual
|
||||
name: my-steering # required if inclusion is auto
|
||||
description: Brief explanation of what this steering file contains # required if inclusion is auto
|
||||
fileMatchPattern: "*.ts" # required if inclusion is fileMatch
|
||||
---
|
||||
|
||||
@@ -325,14 +327,14 @@ Shell scripts used by hooks to perform quality checks and formatting.
|
||||
│ ├── ... and 26 more # (testing, deployment, docker, etc.)
|
||||
│ └── (each skill has a SKILL.md with YAML frontmatter)
|
||||
├── steering/ # 22 steering files
|
||||
│ ├── coding-style.md # Auto-loaded coding style rules
|
||||
│ ├── security.md # Auto-loaded security rules
|
||||
│ ├── testing.md # Auto-loaded testing rules
|
||||
│ ├── development-workflow.md # Auto-loaded dev workflow
|
||||
│ ├── git-workflow.md # Auto-loaded git workflow
|
||||
│ ├── patterns.md # Auto-loaded design patterns
|
||||
│ ├── performance.md # Auto-loaded performance rules
|
||||
│ ├── lessons-learned.md # Auto-loaded project patterns
|
||||
│ ├── coding-style.md # Always-loaded coding style rules
|
||||
│ ├── security.md # Always-loaded security rules
|
||||
│ ├── testing.md # Always-loaded testing rules
|
||||
│ ├── development-workflow.md # Always-loaded dev workflow
|
||||
│ ├── git-workflow.md # Always-loaded git workflow
|
||||
│ ├── patterns.md # Always-loaded design patterns
|
||||
│ ├── performance.md # Always-loaded performance rules
|
||||
│ ├── lessons-learned.md # Always-loaded project patterns
|
||||
│ ├── typescript-patterns.md # Loaded for .ts/.tsx files
|
||||
│ ├── typescript-security.md # Loaded for .ts/.tsx files
|
||||
│ ├── python-patterns.md # Loaded for .py files
|
||||
@@ -395,7 +397,7 @@ All files are yours to modify after installation. The installer never overwrites
|
||||
5. **Run quality gate**: Trigger the `quality-gate` hook before committing
|
||||
6. **Verify comprehensively**: Use the `verification-loop` skill before creating PRs
|
||||
|
||||
The auto-loaded steering files (coding-style, security, testing) ensure consistent standards throughout your session.
|
||||
The always-loaded steering files (coding-style, security, testing) ensure consistent standards throughout your session.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
@@ -466,7 +468,7 @@ kiro-cli --agent code-reviewer
|
||||
> /agent swap security-reviewer
|
||||
> "Analyze the API endpoints for security vulnerabilities"
|
||||
|
||||
# 4. The security.md steering file is auto-loaded, ensuring:
|
||||
# 4. The security.md steering file is always-loaded, ensuring:
|
||||
# - No hardcoded secrets
|
||||
# - Proper error handling
|
||||
# - Secure crypto usage
|
||||
@@ -486,7 +488,7 @@ kiro-cli --agent python-reviewer
|
||||
> "Review the type hints and error handling"
|
||||
> /python-patterns # Invoke Python-specific patterns skill
|
||||
|
||||
# Language-specific steering files are auto-loaded:
|
||||
# Language-specific steering files are loaded automatically on file match:
|
||||
# - golang-patterns.md loads when editing .go files
|
||||
# - python-patterns.md loads when editing .py files
|
||||
# - typescript-patterns.md loads when editing .ts/.tsx files
|
||||
|
||||
@@ -41,9 +41,10 @@ Skills provide structured guidance for complex, multi-step processes.
|
||||
|
||||
### 3. Steering Files as Persistent Context
|
||||
|
||||
Steering files inject rules and patterns into every conversation:
|
||||
Steering files inject rules and patterns into conversations:
|
||||
|
||||
- **Auto-inclusion**: Always-on rules (coding style, security, testing)
|
||||
- **Always-inclusion**: Always-on rules (coding style, security, testing)
|
||||
- **Auto-inclusion**: Files are automatically included when your request matches the description.
|
||||
- **File-match**: Conditional rules based on file type (TypeScript patterns for .ts files)
|
||||
- **Manual**: Context modes you invoke explicitly (dev-mode, review-mode)
|
||||
|
||||
@@ -146,7 +147,7 @@ The `lessons-learned.md` steering file acts as your project's evolving knowledge
|
||||
|
||||
```markdown
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
description: Project-specific patterns and decisions
|
||||
---
|
||||
|
||||
@@ -236,9 +237,9 @@ If an agent's output isn't quite right, provide specific feedback and let it ite
|
||||
|
||||
Don't try to describe a complex workflow in a single prompt. Use skills that encode best practices.
|
||||
|
||||
### 8. Combine Auto and Manual Steering
|
||||
### 8. Combine Steering Inclusion Modes
|
||||
|
||||
Use auto-inclusion for universal rules, file-match for language-specific patterns, and manual for context switching.
|
||||
Use always-inclusion for universal rules, auto-inclusion for context-heavy guidance that should only load when relevant, file-match for language-specific patterns, and manual for context switching.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ AI agents are powerful development tools, but they introduce unique security con
|
||||
**Why**: It's easier to maintain security from the start than to retrofit it later.
|
||||
|
||||
**Practice**:
|
||||
- Enable auto-inclusion security steering files
|
||||
- Enable always-inclusion security steering files
|
||||
- Use TDD workflow with security test cases
|
||||
- Include security requirements in planning phase
|
||||
- Document security decisions in lessons-learned
|
||||
@@ -81,7 +81,7 @@ Plan a user authentication feature with these security requirements:
|
||||
**Risk**: May introduce vulnerabilities like SQL injection, XSS, or insecure deserialization.
|
||||
|
||||
**Mitigation**:
|
||||
- Enable security steering files (auto-loaded)
|
||||
- Enable security steering files (always-loaded)
|
||||
- Use git-push-review hook to catch issues before commit
|
||||
- Run security-reviewer agent after implementation
|
||||
- Include security test cases in TDD workflow
|
||||
@@ -203,7 +203,7 @@ Plan a user authentication feature with these security requirements:
|
||||
**Key Rules to Include**:
|
||||
```markdown
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
description: Security best practices and vulnerability prevention
|
||||
---
|
||||
|
||||
@@ -434,7 +434,7 @@ For production:
|
||||
|
||||
### Before Starting Development
|
||||
|
||||
- [ ] Security steering files enabled (auto-inclusion)
|
||||
- [ ] Security steering files enabled (always-inclusion)
|
||||
- [ ] Security-focused hooks enabled (git-push-review, console-log-check)
|
||||
- [ ] MCP servers reviewed and configured securely
|
||||
- [ ] Secrets management strategy in place
|
||||
|
||||
@@ -78,7 +78,7 @@ Type `/` in chat and select from the menu, or use:
|
||||
|
||||
## Steering Files
|
||||
|
||||
### Auto-Loaded (Always Active)
|
||||
### Always-Loaded (Always Active)
|
||||
|
||||
- `coding-style.md` - Code organization and naming
|
||||
- `development-workflow.md` - Dev process and PR workflow
|
||||
@@ -267,9 +267,10 @@ cat .kiro/skills/skill-name/SKILL.md
|
||||
|
||||
### Steering File Not Loading
|
||||
|
||||
1. Check frontmatter: `inclusion: auto` or `fileMatch` or `manual`
|
||||
1. Check frontmatter: `inclusion: always` or `auto` or `fileMatch` or `manual`
|
||||
2. For fileMatch, verify pattern: `fileMatchPattern: "*.ts,*.tsx"`
|
||||
3. For manual, invoke with: `#filename`
|
||||
3. For auto, verify `name` and `description` are set and the description matches your request
|
||||
4. For manual, invoke with: `#filename`
|
||||
|
||||
### Script Not Executing
|
||||
|
||||
@@ -326,7 +327,7 @@ Instructions for the agent...
|
||||
1. Create `.kiro/steering/my-rules.md`:
|
||||
```markdown
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
description: My custom rules
|
||||
---
|
||||
|
||||
@@ -335,7 +336,7 @@ description: My custom rules
|
||||
Rules and patterns...
|
||||
```
|
||||
|
||||
2. Auto-loaded in every conversation
|
||||
2. Always-loaded in every conversation
|
||||
|
||||
### Add Your Own Hook
|
||||
|
||||
|
||||
+1
-1
@@ -138,6 +138,6 @@ echo "Next steps:"
|
||||
echo " 1. Open your project in Kiro"
|
||||
echo " 2. Agents: Automatic in IDE, /agent swap in CLI"
|
||||
echo " 3. Skills: Available via / menu in chat"
|
||||
echo " 4. Steering files with 'auto' inclusion load automatically"
|
||||
echo " 4. Steering files with 'always' inclusion load automatically"
|
||||
echo " 5. Toggle hooks in the Agent Hooks panel"
|
||||
echo " 6. Copy desired MCP servers from .kiro/settings/mcp.json.example to .kiro/settings/mcp.json"
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: coding-style
|
||||
description: Core coding style rules including immutability, file organization, error handling, and code quality standards.
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: development-workflow
|
||||
description: Development workflow guidelines for planning, TDD, code review, and commit pipeline
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: git-workflow
|
||||
description: Git workflow guidelines for conventional commits and pull request process
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: lessons-learned
|
||||
description: Project-specific patterns, preferences, and lessons learned over time (user-editable)
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: patterns
|
||||
description: Common design patterns including repository pattern, API response format, and skeleton project approach
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: performance
|
||||
description: Performance optimization guidelines including model selection strategy, context window management, and build troubleshooting
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: security
|
||||
description: Security best practices including mandatory checks, secret management, and security response protocol.
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
inclusion: auto
|
||||
inclusion: always
|
||||
name: testing
|
||||
description: Testing requirements including 80% coverage, TDD workflow, and test types.
|
||||
---
|
||||
|
||||
@@ -301,7 +301,13 @@ interface Repository<T> {
|
||||
|
||||
## OpenCode-Specific Notes
|
||||
|
||||
Since OpenCode does not support hooks, the following actions that were automated in Claude Code must be done manually:
|
||||
OpenCode supports hooks through its [plugin system](https://opencode.ai/docs/plugins/).
|
||||
The ECC plugin automates selected checks when installed and enabled; see
|
||||
[Hook Runtime Controls](../README.md#hook-runtime-controls) for hook profiles and disabled hook IDs.
|
||||
Formatting and type checking require the `strict` profile. Type checking runs only for
|
||||
qualifying TypeScript edits; check newly written files manually. Confirm which checks
|
||||
actually completed in the plugin output, then run any remaining checks below manually.
|
||||
Always complete the security review, secret review, and full test suite before committing.
|
||||
|
||||
### After Writing/Editing Code
|
||||
- Run `prettier --write <file>` to format JS/TS files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ECC — Agent Instructions
|
||||
|
||||
This is a **production-ready AI coding plugin** providing 71 specialized agents, 302 skills, 95 commands, and automated hook workflows for software development.
|
||||
This is a **production-ready AI coding plugin** providing 71 specialized agents, 304 skills, 95 commands, and automated hook workflows for software development.
|
||||
|
||||
**Version:** 2.2.3
|
||||
|
||||
@@ -202,7 +202,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
|
||||
|
||||
```
|
||||
agents/ — 71 specialized subagents
|
||||
skills/ — 302 workflow skills and domain knowledge
|
||||
skills/ — 304 workflow skills and domain knowledge
|
||||
commands/ — 95 slash commands
|
||||
hooks/ — Trigger-based automations
|
||||
rules/ — Always-follow guidelines (common + per-language)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<p align="center">
|
||||
<strong>Language:</strong>
|
||||
<a href="README.md">English</a> |
|
||||
<a href="docs/ar/README.md">العربية</a> |
|
||||
<a href="docs/pt-BR/README.md">Português (Brasil)</a> |
|
||||
<a href="README.zh-CN.md">简体中文</a> |
|
||||
<a href="docs/zh-TW/README.md">繁體中文</a> |
|
||||
@@ -140,12 +141,12 @@ Instead of rebuilding that process in every prompt, you install it once and make
|
||||
|
||||
ECC is MIT-licensed open source. It works best with Claude Code today, has a supported Codex sync path, and provides capability-limited adapters for Cursor, OpenCode, Gemini, Zed, GitHub Copilot, Antigravity, Qwen, and other harnesses. See the [support status matrix](#platform-support) before assuming feature parity.
|
||||
|
||||
Access to 71 agents, 302 skills, and 95 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work.
|
||||
Access to 71 agents, 304 skills, and 95 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work.
|
||||
|
||||
| Included | Count | What it gives you |
|
||||
| ---------------- | ----------: | ------------------------------------------------------------------------------------ |
|
||||
| Agents | 71 agents | Planning, review, build repair, security, architecture, and domain work |
|
||||
| Skills | 302 skills | TDD, research, security, docs, frontend, data, ML, operations, and more |
|
||||
| Skills | 304 skills | TDD, research, security, docs, frontend, data, ML, operations, and more |
|
||||
| Commands | 95 commands | Convenient entry points while ECC moves to a skills-first surface |
|
||||
| Hooks and memory | Runtime | Enforcement, session summaries, continuous learning, instincts, and context controls |
|
||||
| Rules | Selective | Always-loaded standards you choose by language or project |
|
||||
@@ -827,8 +828,8 @@ Stable graduation of the 2.0 line: control-pane substrate, worktree lifecycle se
|
||||
```text
|
||||
ECC/
|
||||
|-- agents/ # 71 specialized subagents for delegation
|
||||
|-- skills/ # 293 reusable workflows loaded on demand
|
||||
|-- commands/ # 94 maintained slash-command shims
|
||||
|-- skills/ # 304 reusable workflows loaded on demand
|
||||
|-- commands/ # 95 maintained slash-command shims
|
||||
|-- rules/ # opt-in common and language standards
|
||||
|-- hooks/ # runtime automation and enforcement
|
||||
|-- scripts/ # install, repair, sync, orchestration, and checks
|
||||
@@ -1828,20 +1829,27 @@ Add to `~/.claude/settings.json`:
|
||||
{
|
||||
"model": "sonnet",
|
||||
"env": {
|
||||
"MAX_THINKING_TOKENS": "10000",
|
||||
"CLAUDE_CODE_EFFORT_LEVEL": "medium",
|
||||
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "50",
|
||||
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The effort setting requires a model that supports effort controls. On Amazon Bedrock,
|
||||
Google Cloud's Agent Platform, and Microsoft Foundry, the `sonnet` alias currently
|
||||
resolves to Sonnet 4.5, which does not support effort controls. Select an effort-capable
|
||||
model available from your provider, or omit `CLAUDE_CODE_EFFORT_LEVEL`.
|
||||
|
||||
| Setting | Default | Recommended | Impact |
|
||||
|---------|---------|-------------|--------|
|
||||
| `model` | opus | **sonnet** | ~60% cost reduction; handles 80%+ of coding tasks |
|
||||
| `MAX_THINKING_TOKENS` | 31,999 | **10,000** | ~70% reduction in hidden thinking cost per request |
|
||||
| `CLAUDE_CODE_EFFORT_LEVEL` | Model-dependent | **medium**, if supported | Controls adaptive reasoning depth on models that support effort controls; lower effort trades reasoning depth for latency and cost |
|
||||
| `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | 95 | **50** | Compacts earlier, better quality in long sessions |
|
||||
| `ECC_CONTEXT_MONITOR_COST_WARNINGS` | on | **off for subscription users** | Suppresses agent-facing API-rate estimate warnings while keeping context/scope/loop warnings |
|
||||
|
||||
For fixed-budget models, `MAX_THINKING_TOKENS` controls the thinking budget. On Opus 4.6 or Sonnet 4.6, also set `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` to use that mode. A positive token value does not cap adaptive reasoning; see the [performance rule](rules/common/performance.md#extended-thinking--plan-mode).
|
||||
|
||||
Switch to Opus only when you need deep architectural reasoning:
|
||||
```
|
||||
/model opus
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@
|
||||
|
||||
**Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
|
||||
[**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) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Polski](docs/pl/README.md) | [বাংলা](docs/bn/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) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Polski](docs/pl/README.md) | [বাংলা](docs/bn/README.md) | [العربية](docs/ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -200,7 +200,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
||||
/plugin list ecc@ecc
|
||||
```
|
||||
|
||||
**完成!** 你现在可以使用 71 个代理、302 个技能和 95 个命令。
|
||||
**完成!** 你现在可以使用 71 个代理、304 个技能和 95 个命令。
|
||||
|
||||
### multi-* 命令需要额外配置
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Soul
|
||||
|
||||
## Core Identity
|
||||
ECC is a production-ready AI coding plugin with 71 specialized agents, 302 skills, 95 commands, and automated hook workflows for software development.
|
||||
ECC is a production-ready AI coding plugin with 71 specialized agents, 304 skills, 95 commands, and automated hook workflows for software development.
|
||||
|
||||
## Core Principles
|
||||
1. **Agent-First** — route work to the right specialist as early as possible.
|
||||
|
||||
@@ -13,6 +13,7 @@ skills:
|
||||
- agent-eval
|
||||
- agent-harness-construction
|
||||
- agent-payment-x402
|
||||
- agent-runtime-gateway-smoke-test
|
||||
- agentic-engineering
|
||||
- agentic-os
|
||||
- ai-first-engineering
|
||||
@@ -34,6 +35,7 @@ skills:
|
||||
- claude-devfleet
|
||||
- click-path-audit
|
||||
- clickhouse-io
|
||||
- brownfield-retrospective
|
||||
- codebase-onboarding
|
||||
- coding-standards
|
||||
- compose-multiplatform-patterns
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# مسرد المصطلحات / Glossary
|
||||
|
||||
مسرد المصطلحات الموحد للترجمة العربية لمشروع ECC.
|
||||
|
||||
المبدأ التوجيهي: تظل أسماء واجهات ECC الأساسية والمصطلحات التقنية المعتمدة باللغة الإنجليزية كما هي (`agents/`، `skills/`، `hooks/`، `rules/`، `commands/`) عندما يكون تعريبها سبباً في صعوبة ربط النص باسم المجلد أو الأمر البرمجي أو واجهة الاستخدام في بيئة العمل. في السياق الوصفي العام، يمكن استخدام المقابل العربي الموضح أدناه.
|
||||
|
||||
| المصطلح بالإنجليزية (English) | المقابل بالعربية (Arabic) | ملاحظات وسياق الاستخدام |
|
||||
|---|---|---|
|
||||
| Agent | Agent / وكيل | اسم واجهة `agents/` البرمجية؛ الجمع: وكلاء |
|
||||
| Skill | Skill / مهارة | اسم واجهة `skills/`؛ الجمع: مهارات |
|
||||
| Hook | Hook / خُطاف أو معالج أحداث | اسم واجهة `hooks/`؛ معالجات أحداث بيئة العمل |
|
||||
| Command | Command / أمر | اسم واجهة `commands/`؛ عامةً: "أمر برمجيات" |
|
||||
| Rule | Rule / قاعدة | اسم واجهة `rules/`؛ عامةً: "قاعدة توجيهية" |
|
||||
| Harness | بيئة تشغيل الوكيل (Harness) | يُبقى على Harness كمصطلح تقني أصيل لنظام التشغيل |
|
||||
| Instinct | Instinct / غريزة برمجية | مصطلح مستخدم في ميزة Continuous Learning |
|
||||
| Plugin | إضافة (Plugin) | حزمة برمجية متكاملة لبيئة العمل |
|
||||
| Marketplace | سوق الإضافات (Marketplace) | مستودع الإضافات المعتمد |
|
||||
| Worktree | شجرة عمل (Worktree) | مصطلح Git لبيئات العمل المتوازية |
|
||||
| Subagent | وكيل فرعي (Subagent) | وكيل منبثق لمهمة محددة |
|
||||
| Frontmatter | الترويسة التعريفية (Frontmatter) | بيانات YAML الوصفية أعلى الملفات |
|
||||
| Continuous Learning | التعلّم المستمر | ميزة استخلاص التجارب وتطوير الأنماط |
|
||||
| Memory | الذاكرة / نظام الذاكرة | كمفهوم بنيوي في ECC: مستودع الذاكرة |
|
||||
| Context window | نافذة السياق | سعة الرموز المتاحة للنموذج في الجلسة |
|
||||
| Token | رمز / وحدة معالجة (Token) | أصغر وحدة نصية يفهمها النموذج |
|
||||
| Coverage | نسبة التغطية (Test Coverage) | تغطية الاختبارات البرمجية للكود |
|
||||
| Test-Driven Development | التطوير الموجه باختبارات | يُحافظ على الاختصار TDD |
|
||||
| Code review | مراجعة الكود البرمجي | فحص جودة وشروط الكود |
|
||||
| Refactoring | إعادة هيكلة الكود (Refactoring) | تحسين بنية الكود دون تغيير سلوكه |
|
||||
| Pull request | طلب السحب (PR) | طلب دمج التعديلات في المستودع |
|
||||
| Commit | إيداع / تثبيت (Commit) | تسجيل التغييرات في سجل Git |
|
||||
| Branch | فرع (Branch) | مسار تطوير مستقل |
|
||||
| Merge | دمج (Merge) | دمج فرع في آخر |
|
||||
| Build | بناء (Build) | إنتاج الحزمة البرمجية النهائية |
|
||||
| Deploy | نشر (Deploy) | إطلاق البرمجية في بيئة التشغيل |
|
||||
| Pipeline | خط أنابيب المعالجة (Pipeline) | مسار أتمتة العمليات البرمجية |
|
||||
| Orchestration | التنسيق التلقائي (Orchestration) | إدارة تفاعل الوكلاء والمهام |
|
||||
| Repository | مستودع (Repository) | اختصاراً: مستودع (Repo) |
|
||||
| Dependency | تبعية برمجية (Dependency) | الجمع: تبعيات |
|
||||
| Edge case | حالة طرفية / حدّية | سيناريو غير تقليدي في عمل البرنامج |
|
||||
| Best practice | أفضل الممارسات | المعايير الهندسية الفضلى الموصى بها |
|
||||
| Anti-pattern | نمط مضاد (Anti-pattern) | ممارسة برمجية تبدو حلاً ولكنها تخلق مشاكل |
|
||||
| Middleware | برمجية وسيطة (Middleware) | طبقة اعتراض ومعالجة الطلبات |
|
||||
| Endpoint | نقطة نهاية (Endpoint) | رابط واجهة برمجة التطبيقات (API) |
|
||||
| Schema | مخطط هيكلي (Schema) | توصيف بنية البيانات وقواعدها |
|
||||
| Payload | حمولة البيانات (Payload) | البيانات المرسلة في الطلب البرمجي |
|
||||
| Callback | رد نداء (Callback) | دالة تُنفذ كرد على حدث معين |
|
||||
| Checkpoint | نقطة فحص / استعادة | نقطة حفظ مرحلية لحالة العمل |
|
||||
| Linter | أداة فحص الكود (Linter) | مدقق المعايير الصياغية للكود |
|
||||
| Formatter | أداة تنسيق الكود (Formatter) | منظم شكل وترتيب الكود البرمجي |
|
||||
| Staging | بيئة الاختبار والتجهيز (Staging) | بيئة تحاكي بيئة الإنتاج قبل الإطلاق |
|
||||
| Production | بيئة الإنتاج (Production) | البيئة الحية المستخدمة فعلياً |
|
||||
| Debugging | تنقيح الأخطاء (Debugging) | تشخيص وإصلاح العلل البرمجية |
|
||||
| Logging | تسجيل السجلات (Logging) | تدوين أحداث وسجلات النظام |
|
||||
| Monitoring | المراقبة والمتابعة (Monitoring) | رصد أداء واستقرار الخدمات البرمجية |
|
||||
| Rate limit | حد معدل الطلبات (Rate Limit) | السقف الزمني المسموح به للطلبات |
|
||||
| Retry | إعادة المحاولة (Retry) | تكرار تنفيذ عملية فشلت مؤقتاً |
|
||||
| Fallback | خيار بديل / مسار تراجعي | آلية عمل بديلة عند تعذر المسار الأساسي |
|
||||
| Sandboxing | العزل في بيئة آمنة (Sandboxing) | تشغيل الكود في بيئة محكمة معزولة |
|
||||
| Sanitization | تنقية وتطهير البيانات | تنظيف المدخلات من الشوائب والرموز غير الآمنة |
|
||||
| Selective install | التثبيت الانتقائي | تثبيت مكونات محددة وفق الاحتياج |
|
||||
| Profile | ملف تعريف التثبيت (Profile) | إعداد مسبق لحزمة التثبيت |
|
||||
| Component | مكوّن (Component) | عنصر معرف ضمن كتالوج المثبت |
|
||||
| Module | وحدة نمطية (Module) | وحدة ملفات قابلة للتثبيت والنقل |
|
||||
@@ -0,0 +1,174 @@
|
||||
**اللغة:** [English](../../README.md) | **العربية** | [বাংলা](../bn/README.md) | [Deutsch](../de-DE/README.md) | [Español](../es/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||

|
||||
|
||||
> يغطي هذا التوثيق دليل البدء السريع والواجهات والمفاهيم الأساسية لنظام ECC.
|
||||
> مصدر المحتوى الإنجليزي المعتمد هو الفرع main في المستودع الرسمي.
|
||||
> يظل الكتالوج الكامل والمحدّث باستمرار متوفراً في [README باللغة الإنجليزية](../../README.md)؛
|
||||
> وتتم متابعة وتحديث الأقسام دورياً لضمان الدقة والتوافق.
|
||||
|
||||
---
|
||||
|
||||
**نظام تشغيل مهام الوكلاء الأصلي لبيئات العمل (Harness-Native Agent Operating System).**
|
||||
|
||||
مشروع ECC ليس مجرد مجموعة من ملفات الإعداد الثابتة. بل يدمج وكلاء متخصصين (Agents)،
|
||||
ومهارات معيارية (Skills)، ومعالجات أحداث تلقائية (Hooks)، وقواعد توجيهية (Rules)،
|
||||
وتكوينات بروتوكول سياق النموذج (MCP)، وطبقة توافق للأوامر السابقة (Commands).
|
||||
صُمم النظام بناءً على ممارسات عمل حقيقية ويمتد ليعمل بتناسق عبر بيئات متعددة:
|
||||
**Claude Code**، **Codex**، **Cursor**، **OpenCode**، **Gemini**، **Zed**، **GitHub Copilot** وغيرها الكثير.
|
||||
|
||||
## المصادر الرسمية
|
||||
|
||||
ثبّت ECC حصرياً عبر القنوات الرسمية المعتمدة:
|
||||
|
||||
- مستودع المشروع الرسمي: [github.com/affaan-m/ECC](https://github.com/affaan-m/ECC)
|
||||
- حزم npm المعتمدة: [`ecc-universal`](https://www.npmjs.com/package/ecc-universal) و[`ecc-agentshield`](https://www.npmjs.com/package/ecc-agentshield)
|
||||
- تطبيق [ECC Tools على GitHub](https://github.com/apps/ecc-tools)
|
||||
- معرّف الإضافة المعتمد: `ecc@ecc`
|
||||
- الموقع الرسمي: [ecc.tools](https://ecc.tools)
|
||||
|
||||
يُرجى الحذر من النسخ غير الرسمية أو المستودعات البديلة غير المعتمدة حيث لا تخضع للمراجعة الأمنية أو الصيانة من قبل فريق المشروع.
|
||||
|
||||
## البدء السريع
|
||||
|
||||
اختر **طريقة تثبيت واحدة فقط**. الجمع بين تثبيت الإضافة والتثبيت اليدوي الكامل هو السبب الأكثر شيوعاً لتكرار الوكلاء والمهارات والخطافات.
|
||||
|
||||
### الإعداد التفاعلي الموجه (الموصى به)
|
||||
|
||||
```bash
|
||||
npx ecc-universal@2.2.3 setup
|
||||
```
|
||||
|
||||
يمكنك أيضاً استخدام مدير الحزم المفضل لديك:
|
||||
|
||||
```bash
|
||||
pnpm dlx ecc-universal@2.2.3 setup
|
||||
yarn dlx ecc-universal@2.2.3 setup
|
||||
bunx ecc-universal@2.2.3 setup
|
||||
```
|
||||
|
||||
قبل تشغيل كود الحزمة، تحقق من مصدر الإصدار وسلامة السجل المعتمد.
|
||||
|
||||
### Claude Code
|
||||
|
||||
في Claude Code، أضف سوق الإضافات ثم ثبّت الإضافة المعتمدة:
|
||||
|
||||
```text
|
||||
/plugin marketplace add https://github.com/affaan-m/ECC
|
||||
/plugin install ecc@ecc
|
||||
```
|
||||
|
||||
بعد ذلك، ابدأ بـ `rules/common` بالإضافة إلى حزم اللغات وأطر العمل التي تستخدمها بالفعل في مشروعك.
|
||||
تحذير مهم: بعد إتمام تثبيت الإضافة عبر `/plugin install`، لا تقم بتشغيل أمر التثبيت الكامل `./install.sh --profile full` إطلاقاً لتجنب تكرار الوكلاء والمهارات.
|
||||
|
||||
### Codex
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add affaan-m/ECC
|
||||
codex plugin add ecc@ecc
|
||||
```
|
||||
|
||||
في Codex، استخدم `$configure-ecc` لإجراء التكوين المخصص المناسب لمزود الخدمة في بيئتك.
|
||||
|
||||
### البيئات الأخرى (Other Harnesses)
|
||||
|
||||
شغّل الأوامر التالية من المجلد الرئيسي للمستودع المحلي بعد تنزيله. يختار سطر OpenCode الملف التعريفي الكامل ويفعّل الخطافات التلقائية صراحة.
|
||||
|
||||
| بيئة العمل (Harness) | أمر التثبيت من المستودع |
|
||||
|---|---|
|
||||
| Cursor | `./install.sh --profile minimal --target cursor` |
|
||||
| Gemini CLI | `./install.sh --profile minimal --target gemini` |
|
||||
| Zed | `./install.sh --profile minimal --target zed` |
|
||||
| OpenCode | `npm install && npm run build:opencode && ./install.sh --profile full --target opencode --enable-hooks` |
|
||||
| Hermes | `./install.sh --profile minimal --target hermes` |
|
||||
| OpenClaw | `./install.sh --profile minimal --target openclaw` |
|
||||
| Kimi Code CLI | `./install.sh --profile minimal --target kimi` |
|
||||
|
||||
للاطلاع على مصفوفة الدعم الكاملة لكافة المنصات والمتطلبات التفصيلية، راجع [قسم Platform Support في README الأساسي](../../README.md#platform-support).
|
||||
|
||||
## تثبيت التوثيق العربي
|
||||
|
||||
تستخدم اللغة العربية الرمز `ar`، ويُقبل أيضاً الاسم المستعار `ar-SA`. استخدم نسخة محلية من المستودع تحتوي على الدليل `docs/ar/` والمكوّن `locale:ar`. نفّذ الأوامر التالية من المجلد الرئيسي للمستودع بعد تثبيت تبعيات المشروع (يتطلب Node.js 18 أو أحدث). الإصدار الحالي من حزمة `ecc-universal` قد لا يتضمن التوثيق العربي بعد.
|
||||
|
||||
أولاً، عاين خطة التثبيت دون كتابة الملفات (Dry Run):
|
||||
|
||||
```bash
|
||||
node scripts/install-apply.js --target claude --locale ar --dry-run
|
||||
```
|
||||
|
||||
ثم ثبّت التوثيق العربي في المسار `~/.claude/docs/ar/`:
|
||||
|
||||
```bash
|
||||
node scripts/install-apply.js --target claude --locale ar
|
||||
```
|
||||
|
||||
يثبت هذا الخيار وحدة التوثيق `docs-ar` بشكل مستقل؛ ولا يثبت الملف التعريفي الكامل لـ ECC ولا الخطافات التلقائية.
|
||||
|
||||
## مكونات وواجهات ECC
|
||||
|
||||
| الواجهة | الدور البرمجي والوظيفي |
|
||||
|---|---|
|
||||
| `agents/` | وكلاء متخصصون للتخطيط والتنفيذ والمراجعة والتشخيص |
|
||||
| `skills/` | إجراءات ومعارف نمطية متخصصة تُحمّل عند الحاجة إليها في المهمة |
|
||||
| `hooks/` | أتمتة ومعالجات أحداث تُنفّذ عند وقوع أحداث محددة في بيئة العمل |
|
||||
| `rules/` | قواعد توجيهية دائمة ومعايير خاصة باللغات وأطر العمل |
|
||||
| `commands/` | طبقة توافق مع الأوامر السابقة للأوامر المسبوقة بشرطة مائلة |
|
||||
| `mcp-configs/` | تكوينات خوادم بروتوكول سياق النموذج (Model Context Protocol) |
|
||||
| `manifests/` | بيانات تعريفية معيارية للوحدات والمكونات وملفات التثبيت |
|
||||
|
||||
يعتمد مسار المشروع منهجية **المهارات أولاً (skills-first)**: حيث تُضاف مهام سير العمل الجديدة أولاً إلى `skills/`؛ وتبقى `commands/` كواجهة توافق حيثما دعت الحاجة.
|
||||
|
||||
## المفاهيم الأساسية
|
||||
|
||||
### الوكلاء (Agents)
|
||||
|
||||
يمتلك كل وكيل دوراً محدداً، ومجموعة أدوات مخصصة، وأسلوب عمل واضحاً؛ مثل وكيل التخطيط، ومراجعي الكود للغات البرمجية المختلفة، ومتخصصي تشخيص وحل أخطاء البناء والترجمة.
|
||||
|
||||
### المهارات (Skills)
|
||||
|
||||
تحتوي المهارة على إجراء عمل مركز أو معرفة بمجال تقني محدد. بفضل ذلك، يُحمّل السياق فقط عندما يلائم المهمة الحالية، بدلاً من إثقال كل موجّه نظام (System Prompt) بسياق عام غير ضروري.
|
||||
|
||||
### الخطافات (Hooks)
|
||||
|
||||
تستجيب الخطافات للأحداث البرمجية مثل بدء الجلسة أو استدعاء الأدوات. صُممت الخطافات لتكون آمنة وقابلة للنقل؛ ولا يلزم نسخها يدوياً إلى ملف الإعدادات بعد تثبيت الإضافة، حيث تقوم الإصدارات الحديثة من بيئات العمل بتحميل `hooks/hooks.json` تلقائياً.
|
||||
|
||||
### القواعد (Rules)
|
||||
|
||||
تحدد القواعد الأعراف والاصطلاحات البرمجية الواجب اتباعها دائماً. ثبّت النواة المشتركة والحزم المطابقة لاحتياجاتك فقط، لتجنب استهلاك نافذة السياق بقواعد لا صلة لها بالمشروع.
|
||||
|
||||
للاطلاع على تفاصيل تعريب المفاهيم والمصطلحات، راجع [مسرد المصطلحات العربي](GLOSSARY.md).
|
||||
|
||||
## الأمان والخصوصية
|
||||
|
||||
- لا تقم أبداً بتخزين مفاتيح واجهات برمجة التطبيقات (API Keys) أو كلمات المرور أو الرموز السرية داخل المستودع.
|
||||
- راجع السكربتات ومصادر الحزم قبل تشغيلها في بيئتك.
|
||||
- تجنب الجمع بين عدة طرق تثبيت لتفادي تضارب وتكرار الملفات.
|
||||
- لا تنسخ ملف `hooks/hooks.json` الخام مباشرة إلى `~/.claude/settings.json` بعد تثبيت الإضافة.
|
||||
- التزم بمبدأ الحد الأدنى من الصلاحيات وتحقق من سلامة المدخلات عند حدود النظام.
|
||||
|
||||
للمزيد من الإرشادات الأمنية، راجع [قسم Security في README الأساسي](../../README.md#security).
|
||||
|
||||
## تحديث التعريب والمساهمة
|
||||
|
||||
تتم صيانة التوثيق المترجم وفق أفضل الممارسات المستمرة. يُستحسن في كل طلب سحب (PR) توضيح ما يلي:
|
||||
|
||||
1. رقم إيداع المصدر الإنجليزي (Commit SHA) المعتمد.
|
||||
2. النطاق الدقيق للمحتوى المترجم أو المحدّث.
|
||||
3. أي تعديلات أو إضافات على [مسرد المصطلحات](GLOSSARY.md).
|
||||
4. نتائج فحوصات الروابط وسلامة التنسيق ومخططات التثبيت (Manifests).
|
||||
|
||||
يُفضل أن تكون طلبات السحب مركزة ومقسمة حسب المجال (مثل `docs/`، `agents/`، `skills/`) لتسهيل المراجعة والمزامنة مع المستودع الأساسي.
|
||||
|
||||
## المساهمة في المشروع
|
||||
|
||||
قبل البدء في ترجمات موسعة أو ميزات جديدة، راجع المهام وقوائم النقاش المفتوحة لتنسيق الجهود وتجنب العمل المزدوج. للاطلاع على الإرشادات الكاملة للمساهمة، راجع [CONTRIBUTING.md](../../CONTRIBUTING.md).
|
||||
|
||||
## الترخيص
|
||||
|
||||
مرخص بموجب رخصة MIT — يمنحك ذلك حرية الاستخدام والتعديل ومشاركة التحسينات مع مجتمع المطورين.
|
||||
|
||||
---
|
||||
|
||||
**إذا كان ECC يقدم لك قيمة في عملك، شارك بنجمة للمستودع، واقرأ الأدلة، وابنِ برمجيات استثنائية.**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
**ভাষা:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Español](../es/README.md) | [Українська](../uk-UA/README.md) | [اردو](../ur/README.md) | [Polski](../pl/README.md) | বাংলা
|
||||
**ভাষা:** [English](../../README.md) | [العربية](../ar/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Español](../es/README.md) | [Українська](../uk-UA/README.md) | [اردو](../ur/README.md) | [Polski](../pl/README.md) | বাংলা
|
||||
|
||||
# ECC
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**Sprache:** [English](../../README.md) | [Deutsch](README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**Sprache:** [English](../../README.md) | [Deutsch](README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
**Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
|
||||
[English](../../README.md) | [**Deutsch**](README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md)
|
||||
| [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
| [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -76,3 +76,29 @@ Lower-priority review suggestions remain explicit follow-ups: failing projection
|
||||
## Claims deliberately left unobserved
|
||||
|
||||
Native discovery, exact native exclusions, invocation, executable-mode needs, workflow outcomes, activation, hook consent, rollback, automatic routing and actual token savings still require their own gates. Schema validation checks shape; it cannot certify supplied artifact semantics. The independently compiled fixture checks exact layout, selection, file set and bytes. It uses a trusted private temporary parent and does not certify an arbitrary-destination transaction writer against hostile concurrent mutation. No native provider, model, container or VM was launched, and no package was published.
|
||||
|
||||
## October 8 static runtime-closure gate
|
||||
|
||||
Date: October 8, 2026. `scripts/ci/validate-carrier-closure.js` now plans every supported carrier layout for both Lean and Full and checks that each planned JavaScript file's relative `require`/`import` specifiers resolve to a file the same carrier plans. The static require-graph extraction technique is adapted from community PR #2788; that work's staged load smoke, which materializes a carrier and actually loads entry scripts from inside it, is deliberately not ported, and neither is command pruning. Both wait for carrier scope to widen beyond skills.
|
||||
|
||||
RED: `tests/scripts/validate-carrier-closure.test.js` added 8 `node:test` cases; all 8 failed, seven because the validator module was absent and the eighth because the npm gate was unregistered. GREEN: all 8 pass. A second RED for absolute specifiers and package imports failed 3 cases (the absolute path was ignored, packages were ignored, warnings had no kind); GREEN passes them. The supported-layout list is derived by planning every registry target and keeping the ones that return a layout, so a new layout is picked up without editing the validator.
|
||||
|
||||
Against the real registry the gate reports 5 supported layouts of 16 declared targets — `claude-plugin@1`, `codex-plugin@1`, `pi-package@1`, `opencode-project@1` and `cursor-project@1` — giving 10 projections and 2,941 planned files. 65 of those planned files are JavaScript: 13 per Full projection and none in Lean. They declare 35 relative specifiers, all 35 resolving inside their own carrier; every one is a sibling `./shared.mjs` import within `skills/ck`. The validator finds zero escapes and zero dynamic requires.
|
||||
|
||||
Zero escapes is the expected result and not evidence the check is idle. Carriers are skill-only today, so no skill script has a reason to reach past its own directory. The gate exists so that the first command, agent, rule or hook admitted into carrier scope cannot silently ship a script reference that the projection does not contain. Detection is pinned by fixtures rather than by the live inventory: a planted skill script requiring `../../../../../scripts/lib/context-profile-support` fails as `outside-carrier-tree` in all five layouts and names the specifier, a planted sibling-skill reference fails as `unplanned-target`, a planted non-literal `require` warns without failing, and a `require` shape quoted inside a string literal produces neither.
|
||||
|
||||
Absolute specifiers fail as `outside-carrier-tree`. A package import warns rather than fails, because carriers ship no `node_modules`; Node builtins, bare or `node:`-prefixed, are exempt. No skill script imports a package today.
|
||||
|
||||
Limits. Nothing is executed; the check reads bytes and resolves paths. A package warning names the dependency but cannot tell whether the host environment provides it. Resolution assumes Node's CommonJS candidates for `require` in `.js` and `.cjs` (the path, `.js`, `.json`, then a directory's `index.js` or `index.json`), and the exact specifier for `import()`, static `import`/`from`, and anything in `.mjs`; it does not model conditional exports, a nested `package.json` type field, or import maps. A non-literal `require` is reported, not resolved, so a carrier can still fail at load time through a path computed at runtime. Native discovery, executable mode and actual loading remain unobserved, exactly as before.
|
||||
|
||||
The scanner is pinned by twelve edge cases (regex versus division, regex classes and quotes, template interpolations with object braces, block comments, multi-line and re-exported `from`, and `path.join(__dirname, …)`). Across all 655 JavaScript files in the repository it found every relative specifier a plain pattern match finds outside comments and strings, with no scanner errors. The suite validates the real registry once, through the CLI, and shares that receipt between its real-registry cases: 4.8 s per run instead of 9.1 s, which matters because the test job runs in about 33 matrix cells.
|
||||
|
||||
Review follow-up. A third RED added 10 cases from review: spaced calls (`require ('./x')`, `require\n(...)`, `import (...)`, and `path.join (__dirname, ...)`), escaped specifiers (`\u002e`, `\x2e`, `\u{2e}`, escaped quotes and backslashes), Windows drive, UNC and `file:` URL specifiers, and a directory resolving through `index.json`. All 10 failed. GREEN cooks string and template escapes as the parser does, allows whitespace before call parentheses, treats drive, UNC and `file:` specifiers as absolute, normalizes backslash separators in relative specifiers and adds the `index.json` candidate. A fourth RED/GREEN stops `from` and `import` patterns matching across a closed `${...}` interpolation. Rescanning all 802 repository JavaScript files against the previous scanner changed only three results, each a false package warning removed (`${from}` and similar identifiers before a template chunk). The real registry still reports 35/35 resolved, zero escapes, warnings or package imports, in 3.5 s.
|
||||
|
||||
Second review follow-up. Review of that head found four ways the gate still passed a carrier that fails at run time, each reproduced before any change: an extensionless `import('./helper')` and a directory `import('./data')` resolved through the CommonJS candidates, although Node's ESM resolver does neither even from a CommonJS file; `require('./lib\\helper.js')` resolved to `lib/helper.js`, although Linux and macOS read the backslash as part of the filename; and in `if (ok) /['"]/.test(text); require('./missing')` the regex after the condition was read as division, so its quote opened a fake string that swallowed the `require`. A fifth RED added 12 cases, 9 failing: the four findings, the two regex conditions, and the `imports` shape. The three controls already passed: an exact `import()`, an extensionless `require`, and division after `a.if(x)` or a call inside a condition. GREEN records which specifiers arrive through `import` and resolves those by exact path. It reverses the earlier backslash normalization: a relative specifier containing a backslash now fails as `non-portable-separator`. It also tracks whether each closing paren ends an `if`, `while`, `for` or `with` condition. Rescanning all 802 repository JavaScript files against the previous scanner changed no result; 18 of them declare `import` specifiers. The real registry still reports 35/35 resolved and zero escapes, warnings or package imports, unchanged on every receipt count.
|
||||
|
||||
Third review follow-up. Review of that head found four more problems, each reproduced first. Two were older: an extensionless `require` still offered `.cjs` and `.mjs` candidates, which Node's CommonJS search never tries; and a regex directly after a block (`if (ok) {} /"/`) was read as division, so its quote swallowed a later `require`. Two had been introduced by the previous fix. The condition check looked only at the character before `if`, so `obj.` + newline + `if(x) / 2` read as a condition and its division as a regex. Exact import resolution also turned a tagged template named `from` into a failing import. A sixth RED added 9 cases, 7 failing. Its two controls passed: `require` resolving to `.json`, and a template passed to `import()` still counting as a dependency. GREEN limits CommonJS candidates to the path, `.js`, `.json` and a directory's `index.js`/`index.json`. It treats `}` as a statement boundary before a regex, and decides a condition from the token before the keyword, ignoring whitespace. Static `from` and side-effect `import` now accept only quoted strings, never template slots. Rescanning all 802 repository JavaScript files against the previous head changed no result. The real registry is unchanged on every receipt count: 35/35 resolved, zero failures or warnings.
|
||||
|
||||
Two first-round findings had stayed open and still reproduced. A member call such as `records.require('./label')` matched the call pattern, because the dot satisfies a word boundary, so a self-contained object method failed every projection. And the dynamic-argument pattern allowed only one level of nested parentheses, so `require(path.join(__dirname, getName()))` produced no warning while `require(getName())` did. A seventh RED added 3 cases, all failing. GREEN skips calls preceded by a dot, including after whitespace or optional chaining, and finds each call's argument by balanced-paren scanning over the string-free skeleton. Rescanning the 802 files changed 6 results, each a newly reported nested dynamic argument in a docker eval check or a test, none of them in a carrier. The real registry is unchanged.
|
||||
|
||||
Focused verification: 22 carrier cases, 12 compiler cases, 57 closure cases, 3 CI cases and 2 publish-surface cases pass with zero failures or skips. ESLint passes on the validator and the new test. The validator is CI-only and stays out of the packed surface, matching `scripts/ci/validate-context-profiles.js`.
|
||||
@@ -121,3 +121,7 @@ The store recovers actual process exits at five durable boundaries: prepared jou
|
||||
Small authenticated Claude pilots now provide task and token observations, but they are descriptive and the evaluation gate remains `review-required`. Adequately powered task-quality canaries and whole-context measurements need additional evidence. The opt-in interactive bootstrap has local source, discovery and terminal-start evidence, but authenticated task behavior and native skill invocation remain unobserved. Isolated Codex registration, switching, refresh and rollback have local native evidence; changing a live user installation still requires its own ownership and recovery contract. Fresh-install default changes, existing-user migration, other-provider activation, hook plans, ECC Tools compatibility, hosted rollout and package publication remain outside this local preview.
|
||||
|
||||
The optional routing index binds its advisory metadata, including its trigger snapshot, to the current managed receipt. Trigger content is not part of the carrier generation itself, so rebuilding can use newly curated triggers without changing that generation. No skill body or permission is admitted by the index; the resolver re-verifies canonical sources. Suggestions also require an exact name or the fallback's absolute evidence floor, plus the routing policy's name/trigger anchor, so weak ranking matches remain silent.
|
||||
|
||||
### Static carrier dependency gate
|
||||
|
||||
`npm run carrier-closure:check` parses planned JavaScript files with the pinned TypeScript development parser and checks literal module references against each carrier's planned files. It executes no carrier code. Invalid source syntax fails closed; dynamic module expressions and package imports are reported separately. Literal CommonJS and ESM resolution use their distinct path rules, including ESM file URL queries and fragments. Arbitrary loader aliases and shadowed bindings are outside this static check. A successful receipt is a static closure result, while provider-native loading and execution require their own runtime evidence.
|
||||
@@ -134,3 +134,11 @@ Review follow-up, October 9:
|
||||
- Budget: the October 8 decision stands. On the same Windows machine a prototype worker measured 64 to 77 ms against about 55 ms in process, and stopped a simulated 600 ms read at 152 ms. That gap is smaller than the Linux measurement above, so the trade-off may be worth revisiting with numbers from both platforms.
|
||||
|
||||
These boundaries keep the shipped behavior distinct from the M1 release gate. Authenticated outcome observations, a complete Tier 2 disk diff, live-install migration, additional-provider activation, whole-context token truth and release defaults remain unverified until their explicit prerequisites are available.
|
||||
|
||||
## October 10 carrier closure parser follow-up
|
||||
|
||||
Independent review reproduced literal loads hidden after object-literal division and postfix increment, native `module.require` calls being skipped, and malformed source yielding a successful empty scan. The original hand-written lexer returned no dependencies for all four RED fixtures.
|
||||
|
||||
The maintained parser in the existing pinned TypeScript development dependency now reads source without evaluating it. The gate traverses literal `require`, native `module.require`, dynamic `import`, static imports/re-exports, and literal `path.join(__dirname, ...)` calls. Existing regex, template, escape and nested-dynamic cases remain covered. Parse diagnostics fail the projection closed. ESM URL queries and fragments identify the same planned file; encoded separators and malformed escapes fail rather than normalize into a safe path.
|
||||
|
||||
Focused validation passes 67 cases under Node 24.9.0 and Node 18.20.8. The actual CLI projects the current 304-skill registry into 10 profile/layout combinations, with 3,016 planned files, 65 JavaScript scripts and 35/35 literal relative specifiers resolved, zero escapes and zero warnings. Script execution remains deferred. Dynamic expressions are reported as warnings; arbitrary loader aliases, shadowed bindings and provider-native runtime behavior remain outside this literal-dependency gate.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
**Idioma:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | **Español** | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**Idioma:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | **Español** | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Usar con precaución:
|
||||
- Habilitar para planes bien definidos y de confianza
|
||||
- Deshabilitar para trabajo exploratorio
|
||||
- Nunca usar la flag dangerously-skip-permissions
|
||||
- Configurar `allowedTools` en `~/.claude.json` en su lugar
|
||||
- Configurar reglas `permissions.allow` en `~/.claude/settings.json` (o en el `.claude/settings.json` del proyecto) en su lugar
|
||||
|
||||
## Buenas Prácticas de TodoWrite
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<p align="center">
|
||||
<strong>Language:</strong>
|
||||
<a href="../../README.md">English</a> |
|
||||
<a href="../ar/README.md">العربية</a> |
|
||||
<a href="../pt-BR/README.md">Português (Brasil)</a> |
|
||||
<a href="../../README.zh-CN.md">简体中文</a> |
|
||||
<a href="../zh-TW/README.md">繁體中文</a> |
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
- 信頼できる、明確に定義された計画に対して有効化
|
||||
- 探索的な作業では無効化
|
||||
- dangerously-skip-permissions フラグを決して使用しない
|
||||
- 代わりに `~/.claude.json` で `allowedTools` を設定
|
||||
- 代わりに `~/.claude/settings.json`(またはプロジェクトの `.claude/settings.json`)で `permissions.allow` ルールを設定
|
||||
|
||||
## TodoWrite ベストプラクティス
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**언어:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | 한국어 | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**언어:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | 한국어 | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
- 신뢰할 수 있는, 잘 정의된 계획에서만 활성화
|
||||
- 탐색적 작업에서는 비활성화
|
||||
- dangerously-skip-permissions 플래그를 절대 사용하지 않기
|
||||
- 대신 `~/.claude.json`에서 `allowedTools`를 설정
|
||||
- 대신 `~/.claude/settings.json`(또는 프로젝트의 `.claude/settings.json`)에서 `permissions.allow` 규칙을 설정
|
||||
|
||||
## TodoWrite 모범 사례
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
**Język:** [English](../../README.md) | **Polski** | [Deutsch](../de-DE/README.md) | [Español](../es/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md)
|
||||
**Język:** [English](../../README.md) | [العربية](../ar/README.md) | **Polski** | [Deutsch](../de-DE/README.md) | [Español](../es/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**Idioma:** [English](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | Português (Brasil) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**Idioma:** [English](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | Português (Brasil) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Use com cautela:
|
||||
- Habilite para planos confiáveis e bem definidos
|
||||
- Desabilite para trabalho exploratório
|
||||
- Nunca use a flag dangerously-skip-permissions
|
||||
- Configure `allowedTools` em `~/.claude.json` em vez disso
|
||||
- Configure regras `permissions.allow` em `~/.claude/settings.json` (ou no `.claude/settings.json` do projeto) em vez disso
|
||||
|
||||
## Melhores Práticas para TodoWrite
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
**Язык:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | **Русский** | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**Язык:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | **Русский** | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
**ภาษา:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**ภาษา:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
**ภาษา / Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
|
||||
[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | **ไทย** | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Practical settings and habits to reduce token consumption, extend session qualit
|
||||
|
||||
## Recommended Settings
|
||||
|
||||
These are recommended defaults for most users. Power users can tune values further based on their workload — for example, setting `MAX_THINKING_TOKENS` lower for simple tasks or higher for complex architectural work.
|
||||
Tune reasoning effort to your task and model: lower effort can reduce latency and cost, while higher effort suits complex work. Available levels and defaults depend on the model; see [Claude Code model configuration](https://code.claude.com/docs/en/model-config#adjust-effort-level).
|
||||
|
||||
Add to your `~/.claude/settings.json`:
|
||||
|
||||
@@ -16,28 +16,37 @@ Add to your `~/.claude/settings.json`:
|
||||
{
|
||||
"model": "sonnet",
|
||||
"env": {
|
||||
"MAX_THINKING_TOKENS": "10000",
|
||||
"CLAUDE_CODE_EFFORT_LEVEL": "medium",
|
||||
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The effort setting requires a model that supports effort controls. On Amazon Bedrock,
|
||||
Google Cloud's Agent Platform, and Microsoft Foundry, the `sonnet` alias currently
|
||||
resolves to Sonnet 4.5, which does not support effort controls. Select an effort-capable
|
||||
model available from your provider, or omit `CLAUDE_CODE_EFFORT_LEVEL`.
|
||||
|
||||
### What each setting does
|
||||
|
||||
| Setting | Default | Recommended | Effect |
|
||||
|---------|---------|-------------|--------|
|
||||
| `model` | opus | **sonnet** | Sonnet handles ~80% of coding tasks well. Switch to Opus with `/model opus` for complex reasoning. ~60% cost reduction. |
|
||||
| `MAX_THINKING_TOKENS` | 31,999 | **10,000** | Extended thinking reserves up to 31,999 output tokens per request for internal reasoning. Reducing this cuts hidden cost by ~70%. Set to `0` to disable for trivial tasks. |
|
||||
| `CLAUDE_CODE_EFFORT_LEVEL` | Model-dependent | **medium**, if supported | Sets adaptive reasoning effort on models that support effort controls. Adjust it for task complexity; supported levels depend on the model. |
|
||||
| `CLAUDE_CODE_SUBAGENT_MODEL` | _(inherits main)_ | **haiku** | Subagents (Task tool) run on this model. Haiku is ~80% cheaper and sufficient for exploration, file reading, and test running. |
|
||||
| `ECC_CONTEXT_MONITOR_COST_WARNINGS` | on | **off for subscription users** | Suppresses agent-facing API-rate estimate warnings while keeping context exhaustion, scope, and loop warnings. |
|
||||
|
||||
### Fixed thinking budgets
|
||||
|
||||
`MAX_THINKING_TOKENS` applies to fixed-budget thinking. For Opus 4.6 or Sonnet 4.6, set `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` to use this mode before setting a positive budget. A positive value is ignored on adaptive reasoning models, so it does not provide a general cost cap. See the [environment variable reference](https://code.claude.com/docs/en/env-vars).
|
||||
|
||||
### Community note on auto-compaction overrides
|
||||
|
||||
Some recent Claude Code builds have community reports that `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` can only lower the compaction threshold, which means values below the default may compact earlier instead of later. If that happens in your setup, remove the override and rely on manual `/compact` plus ECC's `strategic-compact` guidance. See [Troubleshooting](./TROUBLESHOOTING.md).
|
||||
|
||||
### Toggling extended thinking
|
||||
|
||||
- **Alt+T** (Windows/Linux) or **Option+T** (macOS) — toggle on/off
|
||||
- **Alt+T** (Windows/Linux) or **Option+T** (macOS) — toggle on/off where supported. Opus 5.5, Sonnet 5.5, and Fable models always think.
|
||||
- **Ctrl+O** — see thinking output (verbose mode)
|
||||
|
||||
---
|
||||
@@ -150,7 +159,7 @@ The `configure-ecc` install wizard could offer to set these environment variable
|
||||
/cost # Check spending
|
||||
|
||||
# Environment variables (add to ~/.claude/settings.json "env" block)
|
||||
MAX_THINKING_TOKENS=10000
|
||||
CLAUDE_CODE_EFFORT_LEVEL=medium # Only for models that support effort controls
|
||||
CLAUDE_CODE_SUBAGENT_MODEL=haiku
|
||||
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
|
||||
```
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# ECC — Agent Talimatları
|
||||
|
||||
Bu, yazılım geliştirme için 71 özel agent, 302 skill, 95 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
|
||||
Bu, yazılım geliştirme için 71 özel agent, 304 skill, 95 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
|
||||
|
||||
**Sürüm:** 2.2.3
|
||||
|
||||
@@ -142,7 +142,7 @@ Başarısızlık sorunlarını giderin: test izolasyonunu kontrol edin → mockl
|
||||
|
||||
```
|
||||
agents/ — 71 özel subagent
|
||||
skills/ — 302 iş akışı skillleri ve alan bilgisi
|
||||
skills/ — 304 iş akışı skillleri ve alan bilgisi
|
||||
commands/ — 95 slash command
|
||||
hooks/ — Tetikleyici tabanlı otomasyonlar
|
||||
rules/ — Her zaman uyulması gereken kurallar (ortak + dile özel)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
**Dil / Language / 语言 / 語言 / Язык / Ngôn ngữ**
|
||||
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [**Türkçe**](README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [**Türkçe**](README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Dikkatli kullan:
|
||||
- Güvenilir, iyi tanımlanmış planlar için etkinleştir
|
||||
- Keşifsel çalışmalar için devre dışı bırak
|
||||
- Asla dangerously-skip-permissions flag'i kullanma
|
||||
- Bunun yerine `~/.claude.json` içinde `allowedTools` yapılandır
|
||||
- Bunun yerine `~/.claude/settings.json` (veya projenin `.claude/settings.json` dosyası) içinde `permissions.allow` kurallarını yapılandır
|
||||
|
||||
## TodoWrite En İyi Uygulamalar
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<p align="center">
|
||||
<strong>Мова:</strong>
|
||||
<a href="../../README.md">English</a> |
|
||||
<a href="../ar/README.md">العربية</a> |
|
||||
<a href="../pt-BR/README.md">Português (Brasil)</a> |
|
||||
<a href="../../README.zh-CN.md">简体中文</a> |
|
||||
<a href="../zh-TW/README.md">繁體中文</a> |
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
**زبان:** [English](../../README.md) | [اردو](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**زبان:** [English](../../README.md) | [اردو](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
**زبان / Language / 语言**
|
||||
|
||||
[English](../../README.md) | [**اردو**](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
[English](../../README.md) | [**اردو**](README.md) | [Deutsch](../de-DE/README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**Ngôn ngữ:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**Ngôn ngữ:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
**Ngôn ngữ / Language / 语言 / 語言 / Dil / Язык**
|
||||
|
||||
[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
[English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | **Tiếng Việt** | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ECC — 智能体指令
|
||||
|
||||
这是一个**生产就绪的 AI 编码插件**,提供 71 个专业代理、302 项技能、95 条命令以及自动化钩子工作流,用于软件开发。
|
||||
这是一个**生产就绪的 AI 编码插件**,提供 71 个专业代理、304 项技能、95 条命令以及自动化钩子工作流,用于软件开发。
|
||||
|
||||
**版本:** 2.2.3
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
```
|
||||
agents/ — 71 个专业子代理
|
||||
skills/ — 302 个工作流技能和领域知识
|
||||
skills/ — 304 个工作流技能和领域知识
|
||||
commands/ — 95 个斜杠命令
|
||||
hooks/ — 基于触发的自动化
|
||||
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**语言:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
**语言:** [English](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
# ECC
|
||||
|
||||
@@ -264,7 +264,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
||||
/plugin list ecc@ecc
|
||||
```
|
||||
|
||||
**搞定!** 你现在可以使用 71 个智能体、302 项技能和 95 个命令了。
|
||||
**搞定!** 你现在可以使用 71 个智能体、304 项技能和 95 个命令了。
|
||||
|
||||
***
|
||||
|
||||
@@ -1178,7 +1178,7 @@ opencode
|
||||
|---------|---------------|----------|--------|
|
||||
| 智能体 | PASS: 71 个 | PASS: 12 个 | **Claude Code 领先** |
|
||||
| 命令 | PASS: 95 个 | PASS: 35 个 | **Claude Code 领先** |
|
||||
| 技能 | PASS: 302 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||
| 技能 | PASS: 304 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
||||
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
||||
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
||||
@@ -1286,7 +1286,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|
||||
|---------|-----------------------|------------|-----------|----------|
|
||||
| **智能体** | 71 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||
| **命令** | 95 | 共享 | 基于指令 | 35 |
|
||||
| **技能** | 302 | 共享 | 10 (原生格式) | 37 |
|
||||
| **技能** | 304 | 共享 | 10 (原生格式) | 37 |
|
||||
| **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 |
|
||||
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 |
|
||||
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* 为受信任、定义明确的计划启用
|
||||
* 为探索性工作禁用
|
||||
* 切勿使用 dangerously-skip-permissions 标志
|
||||
* 改为在 `~/.claude.json` 中配置 `allowedTools`
|
||||
* 改为在 `~/.claude/settings.json`(或项目的 `.claude/settings.json`)中配置 `permissions.allow` 规则
|
||||
|
||||
## TodoWrite 最佳实践
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
**Language / 语言 / 語言 / Dil / Язык / Ngôn ngữ**
|
||||
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | **繁體中文** | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md)
|
||||
[**English**](../../README.md) | [Português (Brasil)](../pt-BR/README.md) | [简体中文](../../README.zh-CN.md) | **繁體中文** | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Türkçe](../tr/README.md) | [Русский](../ru/README.md) | [Tiếng Việt](../vi-VN/README.md) | [ไทย](../th/README.md) | [Deutsch](../de-DE/README.md) | [Українська](../uk-UA/README.md) | [Polski](../pl/README.md) | [বাংলা](../bn/README.md) | [العربية](../ar/README.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
- 對受信任、定義明確的計畫啟用
|
||||
- 對探索性工作停用
|
||||
- 絕不使用 dangerously-skip-permissions flag
|
||||
- 改為在 `~/.claude.json` 中設定 `allowedTools`
|
||||
- 改為在 `~/.claude/settings.json`(或專案的 `.claude/settings.json`)中設定 `permissions.allow` 規則
|
||||
|
||||
## TodoWrite 最佳實務
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"coverage":{"skills":302,"withTriggers":32},"generatedAt":"2026-09-24T23:51:22.784Z","id":"skill-triggers@1","model":{"effort":null,"id":"hand-seeded","source":"manual-curation-pending-regeneration"},"registryDigest":"ffac257ab8a4b8e7450ddf1f933e65335542fc444034c8242dd758346417d76e","schemaVersion":1,"triggers":{"skill:api-connector-builder":["add api integration","new provider connector","match existing integration pattern"],"skill:api-design":["rest endpoint design","pagination api","status codes","api versioning","rate limiting api","resource naming","filtering api","api error responses","offset pagination","limit query parameter","pagination defaults"],"skill:backend-patterns":["express api","node backend architecture","nextjs api routes","server side patterns","data access layer","static file server","url path handling","file server"],"skill:browser-qa":["deployed feature test","visual regression screenshots","core web vitals check","axe accessibility audit","ship do not ship","staging verification"],"skill:canary-watch":["post deploy monitoring","smoke test url","production url check","console errors production","sse stream check","after deploy verification"],"skill:code-tour":["onboarding walkthrough","explain subsystem","architecture tour","pr walkthrough","rca tour"],"skill:coding-standards":["code review standards","naming conventions","readability review","immutability conventions","fix naming typo","export naming","consistent exports"],"skill:content-hash-cache-pattern":["cache file processing","content addressed cache","sha256 hash cache"],"skill:database-migrations":["zero downtime migration","schema change production","add column large table","backfill data","expand contract","concurrent index","migration rollback","prisma migration","django migration"],"skill:deployment-patterns":["ci cd setup","dockerize app","health checks","rollback strategy","production readiness","deploy pipeline","containerize application"],"skill:design-system":["design tokens","visual consistency audit","css custom properties","ui audit","design system bootstrap"],"skill:django-patterns":["django orm","drf api","django rest framework","django caching","django signals","django middleware"],"skill:django-security":["django authentication","csrf protection","sql injection prevention","xss prevention","django deployment security","role based access control","authorization middleware","permissions checks"],"skill:docker-patterns":["dockerfile review","docker compose setup","container security","multi service orchestration"],"skill:error-handling":["error types","retry logic","circuit breaker","user facing errors","exception handling patterns","typed errors","error boundaries","go error handling","custom error class","error codes","config validation"],"skill:evm-token-decimals":["token decimals","wei conversion","erc20 balance off","bridge token precision"],"skill:frontend-a11y":["aria attributes","screen reader support","focus management","semantic html","form labeling","keyboard navigation react","a11y lint errors"],"skill:git-workflow":["merge vs rebase","commit conventions","resolve merge conflict","branching strategy","clean up commits","pull request cleanup","git history tidy"],"skill:hexagonal-architecture":["ports and adapters","dependency injection boundaries","decouple domain from io"],"skill:kubernetes-patterns":["kubernetes manifests","kubectl debugging","pod probes","k8s rbac","autoscaling config","configmap secrets"],"skill:orch-fix-defect":["fix a bug","broken behavior","regression fix","reproduce bug","defect repair"],"skill:postgres-patterns":["slow postgres query","query optimization","index design","rls policies","supabase schema","postgres indexing","database performance","schema design postgres","postgres driver","node postgres","query planner"],"skill:python-patterns":["pythonic code","pep 8","type hints python","python code review","idiomatic python"],"skill:python-testing":["pytest fixtures","mocking python","parametrized tests","coverage python","tdd python"],"skill:redis-patterns":["cache aside pattern","distributed lock","redis rate limiting","cache invalidation"],"skill:regex-vs-llm-structured-text":["parse invoice","extract receipt data","text extraction pipeline","parse form fields","cheap document parser","extract table data","parse log lines","parse access logs","common log format","log line parsing"],"skill:rust-patterns":["rust ownership","borrow checker","rust error handling","traits rust","rust concurrency","idiomatic rust"],"skill:search-first":["find existing library","npm package research","before writing custom code","evaluate existing tools","add dependency research"],"skill:security-review":["security audit","authentication review","sanitize user input","secrets handling","payment security checklist","prevent injection attacks","secure api endpoints","authn authz review","vulnerability checklist","input validation security","parameterized queries","sql injection"],"skill:securityLine truncated
|
||||
{"coverage":{"skills":304,"withTriggers":33},"generatedAt":"2026-09-24T23:51:22.784Z","id":"skill-triggers@1","model":{"effort":null,"id":"hand-seeded","source":"manual-curation-pending-regeneration"},"registryDigest":"9b937fa19a9403313d22ed0a1284c563333569fc04958c47defa28d436254815","schemaVersion":1,"triggers":{"skill:agent-runtime-gateway-smoke-test":["test local agent gateway","remote sandbox callback smoke test","agent task stuck after dispatch","verify gateway tunnel","restore web api after agent test"],"skill:api-connector-builder":["add api integration","new provider connector","match existing integration pattern"],"skill:api-design":["rest endpoint design","pagination api","status codes","api versioning","rate limiting api","resource naming","filtering api","api error responses","offset pagination","limit query parameter","pagination defaults"],"skill:backend-patterns":["express api","node backend architecture","nextjs api routes","server side patterns","data access layer","static file server","url path handling","file server"],"skill:browser-qa":["deployed feature test","visual regression screenshots","core web vitals check","axe accessibility audit","ship do not ship","staging verification"],"skill:canary-watch":["post deploy monitoring","smoke test url","production url check","console errors production","sse stream check","after deploy verification"],"skill:code-tour":["onboarding walkthrough","explain subsystem","architecture tour","pr walkthrough","rca tour"],"skill:coding-standards":["code review standards","naming conventions","readability review","immutability conventions","fix naming typo","export naming","consistent exports"],"skill:content-hash-cache-pattern":["cache file processing","content addressed cache","sha256 hash cache"],"skill:database-migrations":["zero downtime migration","schema change production","add column large table","backfill data","expand contract","concurrent index","migration rollback","prisma migration","django migration"],"skill:deployment-patterns":["ci cd setup","dockerize app","health checks","rollback strategy","production readiness","deploy pipeline","containerize application"],"skill:design-system":["design tokens","visual consistency audit","css custom properties","ui audit","design system bootstrap"],"skill:django-patterns":["django orm","drf api","django rest framework","django caching","django signals","django middleware"],"skill:django-security":["django authentication","csrf protection","sql injection prevention","xss prevention","django deployment security","role based access control","authorization middleware","permissions checks"],"skill:docker-patterns":["dockerfile review","docker compose setup","container security","multi service orchestration"],"skill:error-handling":["error types","retry logic","circuit breaker","user facing errors","exception handling patterns","typed errors","error boundaries","go error handling","custom error class","error codes","config validation"],"skill:evm-token-decimals":["token decimals","wei conversion","erc20 balance off","bridge token precision"],"skill:frontend-a11y":["aria attributes","screen reader support","focus management","semantic html","form labeling","keyboard navigation react","a11y lint errors"],"skill:git-workflow":["merge vs rebase","commit conventions","resolve merge conflict","branching strategy","clean up commits","pull request cleanup","git history tidy"],"skill:hexagonal-architecture":["ports and adapters","dependency injection boundaries","decouple domain from io"],"skill:kubernetes-patterns":["kubernetes manifests","kubectl debugging","pod probes","k8s rbac","autoscaling config","configmap secrets"],"skill:orch-fix-defect":["fix a bug","broken behavior","regression fix","reproduce bug","defect repair"],"skill:postgres-patterns":["slow postgres query","query optimization","index design","rls policies","supabase schema","postgres indexing","database performance","schema design postgres","postgres driver","node postgres","query planner"],"skill:python-patterns":["pythonic code","pep 8","type hints python","python code review","idiomatic python"],"skill:python-testing":["pytest fixtures","mocking python","parametrized tests","coverage python","tdd python"],"skill:redis-patterns":["cache aside pattern","distributed lock","redis rate limiting","cache invalidation"],"skill:regex-vs-llm-structured-text":["parse invoice","extract receipt data","text extraction pipeline","parse form fields","cheap document parser","extract table data","parse log lines","parse access logs","common log format","log line parsing"],"skill:rust-patterns":["rust ownership","borrow checker","rust error handling","traits rust","rust concurrency","idiomatic rust"],"skill:search-first":["find existing library","npm package research","before writing custom code","evaluate existing tools","add dependency research"],"skill:security-review":["security audit","authentication review","sanitize user input","secrets handling","payment seLine truncated
|
||||
@@ -213,7 +213,7 @@
|
||||
{
|
||||
"id": "capability:ito-compute",
|
||||
"family": "capability",
|
||||
"description": "Authenticated It\u00f4 GPU inventory, RFQ, status, device revocation, and explicitly gated node-qualification workflows through the separately installed canonical CLI.",
|
||||
"description": "Authenticated Itô GPU inventory, RFQ, status, device revocation, and explicitly gated node-qualification workflows through the separately installed canonical CLI.",
|
||||
"modules": [
|
||||
"ito-compute"
|
||||
]
|
||||
@@ -709,6 +709,14 @@
|
||||
"modules": [
|
||||
"docs-pl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "locale:ar",
|
||||
"family": "locale",
|
||||
"description": "Arabic (ar) translated reference docs installed to ~/.claude/docs/ar/.",
|
||||
"modules": [
|
||||
"docs-ar"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -377,6 +377,7 @@
|
||||
"skills/windows-desktop-e2e",
|
||||
"skills/agent-self-evaluation",
|
||||
"skills/architecture-decision-records",
|
||||
"skills/brownfield-retrospective",
|
||||
"skills/browser-qa",
|
||||
"skills/ck",
|
||||
"skills/click-path-audit",
|
||||
@@ -719,7 +720,7 @@
|
||||
{
|
||||
"id": "ito-compute",
|
||||
"kind": "skills",
|
||||
"description": "Authenticated It\u00f4 GPU inventory, RFQ, status, device revocation, and explicitly gated node-qualification workflows through the separately installed canonical CLI.",
|
||||
"description": "Authenticated Itô GPU inventory, RFQ, status, device revocation, and explicitly gated node-qualification workflows through the separately installed canonical CLI.",
|
||||
"paths": [
|
||||
"skills/ito-compute",
|
||||
"skills/ito-inference",
|
||||
@@ -941,6 +942,7 @@
|
||||
"skills/autonomous-agent-harness",
|
||||
"skills/gan-style-harness",
|
||||
"skills/hermes-imports",
|
||||
"skills/agent-runtime-gateway-smoke-test",
|
||||
"skills/openclaw-persona-forge",
|
||||
"skills/opensource-pipeline",
|
||||
"skills/orch-add-feature",
|
||||
@@ -1301,6 +1303,22 @@
|
||||
"defaultInstall": false,
|
||||
"cost": "heavy",
|
||||
"stability": "stable"
|
||||
},
|
||||
{
|
||||
"id": "docs-ar",
|
||||
"kind": "docs",
|
||||
"description": "Arabic (ar) getting-started and core-concepts guide with a terminology glossary.",
|
||||
"paths": [
|
||||
"docs/ar"
|
||||
],
|
||||
"targets": [
|
||||
"claude",
|
||||
"claude-project"
|
||||
],
|
||||
"dependencies": [],
|
||||
"defaultInstall": false,
|
||||
"cost": "heavy",
|
||||
"stability": "stable"
|
||||
}
|
||||
]
|
||||
}
|
||||
+8
-4
@@ -80,6 +80,7 @@
|
||||
"agents/",
|
||||
"commands/",
|
||||
"docs/bn/",
|
||||
"docs/ar/",
|
||||
"docs/de-DE/",
|
||||
"docs/CODEX-NAVIGATION-GUIDE.md",
|
||||
"docs/COMMAND-AGENT-MAP.md",
|
||||
@@ -473,7 +474,9 @@
|
||||
"!**/*.pyo",
|
||||
"!**/*.pyd",
|
||||
"!**/.pytest_cache/**",
|
||||
"skills/conventional-commit-review/"
|
||||
"skills/conventional-commit-review/",
|
||||
"skills/brownfield-retrospective/",
|
||||
"skills/agent-runtime-gateway-smoke-test/"
|
||||
],
|
||||
"bin": {
|
||||
"ecc": "scripts/ecc.js",
|
||||
@@ -484,9 +487,10 @@
|
||||
"ecc-universal": "scripts/ecc.js"
|
||||
},
|
||||
"scripts": {
|
||||
"welcome": "echo '\\n ecc-universal installed!\\n Run: ecc typescript\\n Compat: ecc-install typescript\\n Docs: https://github.com/affaan-m/ECC\\n Run or self-host any open-source model.\\n Compute: Itô is the preferred compute sponsor — https://compute.itomarkets.com\\n Any GPU provider works. This sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving.\\n Separately, the opt-in ecc ito find bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity.\\n Managed inference through Itô is not live yet.\\n'",
|
||||
"welcome": "echo '\\n ecc-universal installed!\\n Run: ecc typescript\\n Compat: ecc-install typescript\\n Docs: https://github.com/affaan-m/ECC\\n Run or self-host any open-source model.\\n Compute: It\u00f4 is the preferred compute sponsor \u2014 https://compute.itomarkets.com\\n Any GPU provider works. This sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving.\\n Separately, the opt-in ecc ito find bridge invokes the explicitly configured canonical It\u00f4 CLI and submits a live authenticated RFQ; it does not reserve capacity.\\n Managed inference through It\u00f4 is not live yet.\\n'",
|
||||
"catalog:check": "node scripts/ci/catalog.js --text",
|
||||
"context-profiles:check": "node scripts/ci/validate-context-profiles.js",
|
||||
"carrier-closure:check": "node scripts/ci/validate-carrier-closure.js",
|
||||
"catalog:sync": "node scripts/ci/catalog.js --write --text",
|
||||
"command-registry:generate": "node scripts/ci/generate-command-registry.js",
|
||||
"command-registry:write": "node scripts/ci/generate-command-registry.js --write",
|
||||
@@ -510,10 +514,10 @@
|
||||
"orchestrate:status": "node scripts/orchestration-status.js",
|
||||
"orchestrate:worker": "bash scripts/orchestrate-codex-worker.sh",
|
||||
"orchestrate:tmux": "node scripts/orchestrate-worktrees.js",
|
||||
"test:validators": "node scripts/ci/check-unicode-safety.js && node scripts/ci/validate-agents.js && node scripts/ci/validate-commands.js && node scripts/ci/validate-rules.js && node scripts/ci/validate-skills.js && node scripts/ci/validate-hooks.js && node scripts/ci/check-hooks-schema-keys.js && node scripts/ci/validate-install-manifests.js && node scripts/ci/validate-context-profiles.js && node scripts/ci/validate-no-personal-paths.js && npm run catalog:check && npm run command-registry:check",
|
||||
"test:validators": "node scripts/ci/check-unicode-safety.js && node scripts/ci/validate-agents.js && node scripts/ci/validate-commands.js && node scripts/ci/validate-rules.js && node scripts/ci/validate-skills.js && node scripts/ci/validate-hooks.js && node scripts/ci/check-hooks-schema-keys.js && node scripts/ci/validate-install-manifests.js && node scripts/ci/validate-context-profiles.js && node scripts/ci/validate-carrier-closure.js && node scripts/ci/validate-no-personal-paths.js && npm run catalog:check && npm run command-registry:check",
|
||||
"test:js": "node tests/run-all.js",
|
||||
"test:python": "python3 -m pytest tests/ -q",
|
||||
"test": "node scripts/ci/check-unicode-safety.js && node scripts/ci/validate-agents.js && node scripts/ci/validate-commands.js && node scripts/ci/validate-rules.js && node scripts/ci/validate-skills.js && node scripts/ci/validate-hooks.js && node scripts/ci/check-hooks-schema-keys.js && node scripts/ci/validate-install-manifests.js && node scripts/ci/validate-context-profiles.js && node scripts/ci/validate-no-personal-paths.js && npm run catalog:check && npm run command-registry:check && node tests/run-all.js",
|
||||
"test": "node scripts/ci/check-unicode-safety.js && node scripts/ci/validate-agents.js && node scripts/ci/validate-commands.js && node scripts/ci/validate-rules.js && node scripts/ci/validate-skills.js && node scripts/ci/validate-hooks.js && node scripts/ci/check-hooks-schema-keys.js && node scripts/ci/validate-install-manifests.js && node scripts/ci/validate-context-profiles.js && node scripts/ci/validate-carrier-closure.js && node scripts/ci/validate-no-personal-paths.js && npm run catalog:check && npm run command-registry:check && node tests/run-all.js",
|
||||
"coverage:js": "c8 --all --include=\"scripts/**/*.js\" --include=\"scripts/**/*.mjs\" --check-coverage --lines 80 --functions 80 --branches 80 --statements 80 --reporter=text --reporter=lcov node tests/run-all.js",
|
||||
"coverage:python": "python3 -m pytest tests/ -q --cov=src --cov-report=term --cov-fail-under=80",
|
||||
"coverage": "c8 --all --include=\"scripts/**/*.js\" --include=\"scripts/**/*.mjs\" --check-coverage --lines 80 --functions 80 --branches 80 --statements 80 --reporter=text --reporter=lcov node tests/run-all.js",
|
||||
|
||||
@@ -22,6 +22,7 @@ Invoke this skill:
|
||||
|
||||
### Phase 1: Build Verification
|
||||
```bash
|
||||
set -o pipefail # report the build's exit status, not tail's
|
||||
# Check if project builds
|
||||
npm run build 2>&1 | tail -20
|
||||
# OR
|
||||
@@ -34,25 +35,30 @@ If build fails, STOP and fix before continuing.
|
||||
```bash
|
||||
set -o pipefail
|
||||
# TypeScript projects
|
||||
npx --no-install tsc --noEmit 2>&1 | head -30
|
||||
npx --no-install tsc --noEmit 2>&1 | sed -n '1,30p'
|
||||
|
||||
# Python projects
|
||||
pyright . 2>&1 | head -30
|
||||
pyright . 2>&1 | sed -n '1,30p'
|
||||
```
|
||||
|
||||
Report all type errors. Fix critical ones before continuing.
|
||||
|
||||
### Phase 3: Lint Check
|
||||
|
||||
Use `sed` to limit displayed output while consuming the whole stream. This keeps a
|
||||
successful linter from failing on SIGPIPE when `pipefail` is enabled.
|
||||
```bash
|
||||
set -o pipefail
|
||||
# JavaScript/TypeScript
|
||||
npm run lint 2>&1 | head -30
|
||||
npm run lint 2>&1 | sed -n '1,30p'
|
||||
|
||||
# Python
|
||||
ruff check . 2>&1 | head -30
|
||||
ruff check . 2>&1 | sed -n '1,30p'
|
||||
```
|
||||
|
||||
### Phase 4: Test Suite
|
||||
```bash
|
||||
set -o pipefail
|
||||
# Run tests with coverage
|
||||
npm run test -- --coverage 2>&1 | tail -50
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Use with caution:
|
||||
- Enable for trusted, well-defined plans
|
||||
- Disable for exploratory work
|
||||
- Never use dangerously-skip-permissions flag
|
||||
- Configure `allowedTools` in `~/.claude.json` instead
|
||||
- Configure `permissions.allow` rules in `~/.claude/settings.json` (or a project's `.claude/settings.json`) instead
|
||||
|
||||
## TodoWrite Best Practices
|
||||
|
||||
|
||||
@@ -32,16 +32,21 @@ Lower context sensitivity tasks:
|
||||
|
||||
## Extended Thinking + Plan Mode
|
||||
|
||||
Extended thinking is enabled by default, reserving up to 31,999 tokens for internal reasoning.
|
||||
In Claude Code, adaptive reasoning lets the model choose how much to think for each task.
|
||||
For Fable models, Sonnet 5 and later, and Opus 4.7 and later, use effort to control reasoning
|
||||
rather than a fixed token budget. Available levels depend on the model.
|
||||
|
||||
Control extended thinking via:
|
||||
- **Toggle**: Option+T (macOS) / Alt+T (Windows/Linux)
|
||||
- **Config**: Set `alwaysThinkingEnabled` in `~/.claude/settings.json`
|
||||
- **Budget cap**: `export MAX_THINKING_TOKENS=10000` (bash) or `$env:MAX_THINKING_TOKENS = "10000"` (PowerShell)
|
||||
Control thinking via:
|
||||
- **Effort**: Use `/effort` in a session, `--effort <level>` at launch, or `CLAUDE_CODE_EFFORT_LEVEL`. Save a per-model level in `modelSettings` in `~/.claude/settings.json`; the older user-level `effortLevel` key does not apply to Opus 5.5 or newer models.
|
||||
- **Toggle**: Option+T (macOS) / Alt+T (Windows/Linux), or `alwaysThinkingEnabled` in settings. These cannot disable thinking on Opus 5.5, Sonnet 5.5, or Fable models.
|
||||
- **Fixed budget**: Claude Code ignores positive `MAX_THINKING_TOKENS` values on adaptive reasoning models. On Opus 4.6 or Sonnet 4.6, set `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` to use a fixed budget with `MAX_THINKING_TOKENS`.
|
||||
- **Verbose mode**: Ctrl+O to see thinking output
|
||||
|
||||
See [Claude Code model configuration](https://code.claude.com/docs/en/model-config#adjust-effort-level)
|
||||
and [environment variables](https://code.claude.com/docs/en/env-vars) for supported controls.
|
||||
|
||||
For complex tasks requiring deep reasoning:
|
||||
1. Ensure extended thinking is enabled (on by default)
|
||||
1. Choose a higher supported effort level, such as `/effort high`
|
||||
2. Enable **Plan Mode** for structured approach
|
||||
3. Use multiple critique rounds for thorough analysis
|
||||
4. Use split role sub-agents for diverse perspectives
|
||||
|
||||
@@ -11,7 +11,7 @@ paths:
|
||||
## Test Framework
|
||||
|
||||
- Prefer **xUnit** for unit and integration tests
|
||||
- Use **FluentAssertions** for readable assertions
|
||||
- Use **AwesomeAssertions** for readable assertions
|
||||
- Use **Moq** or **NSubstitute** for mocking dependencies
|
||||
- Use **Testcontainers** when integration tests need real infrastructure
|
||||
|
||||
|
||||
+51
-11
@@ -87,6 +87,23 @@ function replaceOrThrow(content, regex, replacer, source) {
|
||||
return content.replace(regex, replacer);
|
||||
}
|
||||
|
||||
function countMarkers(content, regex) {
|
||||
return [...content.matchAll(regex)].length;
|
||||
}
|
||||
|
||||
function replaceAllOrThrow(content, regex, replacer, source, expectedCount) {
|
||||
const found = countMarkers(content, regex);
|
||||
if (found === 0) {
|
||||
throw new Error(`${source} is missing the expected catalog marker`);
|
||||
}
|
||||
|
||||
if (expectedCount !== undefined && found !== expectedCount) {
|
||||
throw new Error(`${source} expected ${expectedCount} marker(s), found ${found}`);
|
||||
}
|
||||
|
||||
return content.replace(regex, replacer);
|
||||
}
|
||||
|
||||
function parseReadmeExpectations(readmeContent) {
|
||||
const expectations = [];
|
||||
|
||||
@@ -103,17 +120,28 @@ function parseReadmeExpectations(readmeContent) {
|
||||
{ category: 'commands', mode: 'exact', expected: Number(quickStartMatch[3]), source: 'README.md quick-start summary' }
|
||||
);
|
||||
|
||||
const projectTreeAgentsMatches = [...readmeContent.matchAll(/^\|\s*--\s*agents\/\s*#\s*(\d+)\s+specialized subagents for delegation\s*$/gim)];
|
||||
if (!projectTreeAgentsMatches.length) {
|
||||
throw new Error('README.md project tree is missing the agents count');
|
||||
}
|
||||
const projectTreePatterns = [
|
||||
{ category: 'agents', regex: /^\|\s*--\s*agents\/\s*#\s*(\d+)\s+specialized subagents for delegation\s*$/gim, source: 'README.md project tree (agents)' },
|
||||
{ category: 'commands', regex: /^\|\s*--\s*commands\/\s*#\s*(\d+)\s+maintained slash-command shims\s*$/gim, source: 'README.md project tree (commands)' },
|
||||
{ category: 'skills', regex: /^\|\s*--\s*skills\/\s*#\s*(\d+)\s+reusable workflows loaded on demand\s*$/gim, source: 'README.md project tree (skills)' }
|
||||
];
|
||||
|
||||
for (const [index, match] of projectTreeAgentsMatches.entries()) expectations.push({
|
||||
category: 'agents',
|
||||
mode: 'exact',
|
||||
expected: Number(match[1]),
|
||||
source: `README.md project tree ${index + 1} (agents)`
|
||||
});
|
||||
for (const pattern of projectTreePatterns) {
|
||||
const matches = [...readmeContent.matchAll(pattern.regex)];
|
||||
|
||||
if (matches.length === 0) {
|
||||
throw new Error(`${pattern.source} is missing the ${pattern.category} count`);
|
||||
}
|
||||
|
||||
for (const match of matches) {
|
||||
expectations.push({
|
||||
category: pattern.category,
|
||||
mode: 'exact',
|
||||
expected: Number(match[1]),
|
||||
source: `${pattern.source} block ${readmeContent.slice(0, match.index).split('\n').length}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const tablePatterns = [
|
||||
{ category: 'agents', regex: /\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+agents\s*\|/i, source: 'README.md comparison table' },
|
||||
@@ -409,12 +437,24 @@ function syncEnglishReadme(content, catalog) {
|
||||
`${prefix}${catalog.agents.count}${agentsSuffix}${catalog.skills.count}${skillsSuffix}${catalog.commands.count} legacy command shims`,
|
||||
'README.md quick-start summary'
|
||||
);
|
||||
nextContent = replaceOrThrow(
|
||||
nextContent = replaceAllOrThrow(
|
||||
nextContent,
|
||||
/^(\|\s*--\s*agents\/\s*#\s*)(\d+)(\s+specialized subagents for delegation\s*)$/gim,
|
||||
(_, prefix, __, suffix) => `${prefix}${catalog.agents.count}${suffix}`,
|
||||
'README.md project tree (agents)'
|
||||
);
|
||||
nextContent = replaceAllOrThrow(
|
||||
nextContent,
|
||||
/^(\|\s*--\s*commands\/\s*#\s*)(\d+)(\s+maintained slash-command shims\s*)$/gim,
|
||||
(_, prefix, __, suffix) => `${prefix}${catalog.commands.count}${suffix}`,
|
||||
'README.md project tree (commands)'
|
||||
);
|
||||
nextContent = replaceAllOrThrow(
|
||||
nextContent,
|
||||
/^(\|\s*--\s*skills\/\s*#\s*)(\d+)(\s+reusable workflows loaded on demand\s*)$/gim,
|
||||
(_, prefix, __, suffix) => `${prefix}${catalog.skills.count}${suffix}`,
|
||||
'README.md project tree (skills)'
|
||||
);
|
||||
nextContent = replaceOrThrow(
|
||||
nextContent,
|
||||
/(\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?)(\d+)(\s+agents\s*\|)/i,
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Carrier runtime-closure validator.
|
||||
*
|
||||
* Carriers are read-only file projections. Nothing here executes a planned
|
||||
* script: literal require/import/export dependencies are parsed statically and checked
|
||||
* against the planned file set of the carrier it would ship in.
|
||||
*
|
||||
* The static require-graph extraction technique is adapted from community
|
||||
* PR #2788 (affaan-m/ECC). The staged load smoke from that work -- actually
|
||||
* loading entry scripts from inside a materialized carrier -- is deliberately
|
||||
* not ported here, and neither is command pruning.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { builtinModules } = require('node:module');
|
||||
const { planContextCarrier } = require('../lib/context-carriers');
|
||||
const { loadContextRegistry } = require('../lib/context-pack-registry');
|
||||
const { DEFAULT_REPO_ROOT } = require('../lib/context-profile-support');
|
||||
|
||||
const PROFILES = Object.freeze(['lean@1', 'full@1']);
|
||||
const BUILTINS = new Set(builtinModules);
|
||||
const SCRIPT_EXTENSIONS = new Set(['.js', '.cjs', '.mjs']);
|
||||
const ts = require('typescript');
|
||||
|
||||
function isAbsoluteSpecifier(value) {
|
||||
return value.startsWith('/') || value.startsWith('\\\\') || /^[A-Za-z]:[\\/]/.test(value) || /^file:/i.test(value);
|
||||
}
|
||||
|
||||
/** Parse without evaluating source, and reject incomplete or invalid syntax. */
|
||||
function scanSource(rawSource) {
|
||||
const source = ts.createSourceFile('carrier.js', String(rawSource || ''),
|
||||
ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
||||
if (source.parseDiagnostics.length) {
|
||||
const diagnostic = source.parseDiagnostics[0];
|
||||
const error = new SyntaxError(ts.flattenDiagnosticMessageText(diagnostic.messageText, ' '));
|
||||
error.position = diagnostic.start;
|
||||
throw error;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/** Extract literal dependencies and report dynamic expressions without execution. */
|
||||
function extractReferences(rawSource) {
|
||||
const source = scanSource(rawSource);
|
||||
const specifiers = new Set(), imports = new Set(), packages = new Set(), dynamic = new Set();
|
||||
const record = (value, viaImport) => {
|
||||
if (value.startsWith('.') || isAbsoluteSpecifier(value)) {
|
||||
specifiers.add(value);
|
||||
if (viaImport) imports.add(value);
|
||||
} else if (!value.startsWith('node:') && !BUILTINS.has(value.split('/')[0])) {
|
||||
packages.add(value.startsWith('@') ? value.split('/').slice(0, 2).join('/') : value.split('/')[0]);
|
||||
}
|
||||
};
|
||||
const identifier = (node, name) => ts.isIdentifier(node) && node.text === name;
|
||||
const member = (node, receiver, name) => ts.isPropertyAccessExpression(node)
|
||||
&& identifier(node.expression, receiver) && node.name.text === name;
|
||||
const literal = node => ts.isStringLiteralLike(node) ? node.text : null;
|
||||
const staticJoin = node => {
|
||||
if (!ts.isCallExpression(node) || !member(node.expression, 'path', 'join')
|
||||
|| !node.arguments.length || !identifier(node.arguments[0], '__dirname')) return null;
|
||||
const parts = node.arguments.slice(1).map(literal);
|
||||
return parts.every(value => value !== null) ? `./${parts.join('/')}` : null;
|
||||
};
|
||||
const visit = node => {
|
||||
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier) {
|
||||
record(node.moduleSpecifier.text, true);
|
||||
} else if (ts.isCallExpression(node)) {
|
||||
const viaImport = node.expression.kind === ts.SyntaxKind.ImportKeyword;
|
||||
if (viaImport || identifier(node.expression, 'require') || member(node.expression, 'module', 'require')) {
|
||||
const argument = node.arguments[0];
|
||||
if (argument) {
|
||||
const value = literal(argument);
|
||||
const joined = viaImport ? null : staticJoin(argument);
|
||||
if (value !== null) record(value, viaImport);
|
||||
else if (joined !== null) record(joined, false);
|
||||
else dynamic.add(argument.getText(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
visit(source);
|
||||
return { specifiers: [...specifiers], imports: [...imports], packages: [...packages], dynamic: [...dynamic] };
|
||||
}
|
||||
|
||||
function candidateDestinations(destinationPath, specifier, exact) {
|
||||
if (exact && !isAbsoluteSpecifier(specifier)) {
|
||||
// ESM resolves file URLs: query/fragment change identity, not the file path.
|
||||
// Node rejects encoded separators; never normalize them into a safe path.
|
||||
if (/%2f|%5c/i.test(specifier)) return { base: specifier, candidates: null, reason: 'invalid-url-separator' };
|
||||
try { specifier = decodeURIComponent(specifier.split(/[?#]/)[0]); }
|
||||
catch (_error) { return { base: specifier, candidates: null, reason: 'invalid-url-encoding' }; }
|
||||
}
|
||||
if (isAbsoluteSpecifier(specifier)) return { base: specifier, candidates: null, reason: 'outside-carrier-tree' };
|
||||
if (specifier.includes('\\')) return { base: specifier, candidates: null, reason: 'non-portable-separator' };
|
||||
const base = path.posix.join(path.posix.dirname(destinationPath), specifier);
|
||||
if (base === '..' || base.startsWith('../')) return { base, candidates: null, reason: 'outside-carrier-tree' };
|
||||
const candidates = exact
|
||||
? [base]
|
||||
: [base, `${base}.js`, `${base}.json`, `${base}/index.js`, `${base}/index.json`];
|
||||
return { base, candidates, reason: null };
|
||||
}
|
||||
|
||||
function sourceReader(repoRoot) {
|
||||
const cache = new Map();
|
||||
return file => {
|
||||
if (file.kind === 'generated') return file.content;
|
||||
if (!cache.has(file.sourcePath)) {
|
||||
cache.set(file.sourcePath, fs.readFileSync(path.join(repoRoot, ...file.sourcePath.split('/')), 'utf8'));
|
||||
}
|
||||
return cache.get(file.sourcePath);
|
||||
};
|
||||
}
|
||||
|
||||
function inspectProjection(carrier, profileId, read, report) {
|
||||
const planned = new Set(carrier.files.map(file => file.destinationPath));
|
||||
const scripts = carrier.files.filter(file => SCRIPT_EXTENSIONS.has(path.posix.extname(file.destinationPath)));
|
||||
for (const file of scripts) {
|
||||
let references;
|
||||
try { references = extractReferences(read(file)); }
|
||||
catch (error) {
|
||||
report.failures.push({ target: carrier.target, layout: carrier.layout.id, profileId,
|
||||
file: file.destinationPath, source: file.sourcePath || null, reason: 'parse-error', diagnostic: error.message });
|
||||
continue;
|
||||
}
|
||||
const { specifiers, imports, packages, dynamic } = references;
|
||||
const exactOnly = new Set(imports);
|
||||
const isModule = file.destinationPath.endsWith('.mjs');
|
||||
const location = {
|
||||
target: carrier.target, layout: carrier.layout.id, profileId,
|
||||
file: file.destinationPath, source: file.sourcePath || null,
|
||||
};
|
||||
for (const specifier of specifiers) {
|
||||
report.specifierCount += 1;
|
||||
const exact = isModule || exactOnly.has(specifier);
|
||||
const { base, candidates, reason } = candidateDestinations(file.destinationPath, specifier, exact);
|
||||
if (!candidates) {
|
||||
report.failures.push({ ...location, specifier, resolved: base, reason });
|
||||
} else if (candidates.some(candidate => planned.has(candidate))) {
|
||||
report.resolvedCount += 1;
|
||||
} else {
|
||||
report.failures.push({ ...location, specifier, resolved: base, reason: 'unplanned-target' });
|
||||
}
|
||||
}
|
||||
for (const expression of dynamic) {
|
||||
report.dynamicCount += 1;
|
||||
report.warnings.push({ ...location, kind: 'dynamic', expression });
|
||||
}
|
||||
for (const name of packages) {
|
||||
report.packageCount += 1;
|
||||
report.warnings.push({ ...location, kind: 'package', package: name });
|
||||
}
|
||||
}
|
||||
report.plannedFileCount += carrier.files.length;
|
||||
report.scriptCount += scripts.length;
|
||||
}
|
||||
|
||||
function validate(repoRoot = DEFAULT_REPO_ROOT) {
|
||||
const registry = loadContextRegistry({ repoRoot });
|
||||
const read = sourceReader(repoRoot);
|
||||
const report = {
|
||||
plannedFileCount: 0, scriptCount: 0, specifierCount: 0, resolvedCount: 0, dynamicCount: 0, packageCount: 0,
|
||||
failures: [], warnings: [],
|
||||
};
|
||||
const layouts = [];
|
||||
const unsupportedTargets = [];
|
||||
|
||||
for (const target of registry.targets) {
|
||||
let supported = null;
|
||||
for (const profileId of PROFILES) {
|
||||
const carrier = planContextCarrier({ repoRoot, profileId, target });
|
||||
if (!carrier.layout) break;
|
||||
supported = { target, layout: carrier.layout.id, skillRoot: carrier.layout.skillRoot };
|
||||
inspectProjection(carrier, profileId, read, report);
|
||||
}
|
||||
if (supported) layouts.push(supported); else unsupportedTargets.push(target);
|
||||
}
|
||||
|
||||
return {
|
||||
schemaVersion: 'ecc.carrier-closure.v1',
|
||||
status: report.failures.length ? 'failure' : 'success',
|
||||
profiles: [...PROFILES], layouts, unsupportedTargets,
|
||||
projectionCount: layouts.length * PROFILES.length,
|
||||
registryDigest: registry.registryDigest,
|
||||
loadSmoke: 'deferred',
|
||||
limitations: ['Dynamic dependency expressions and arbitrary aliases are warnings or outside this literal-dependency gate.',
|
||||
'Parsing does not execute scripts or certify provider-native runtime behavior.'],
|
||||
lineage: 'static require-graph technique adapted from community PR #2788',
|
||||
...report,
|
||||
};
|
||||
}
|
||||
|
||||
function uniqueLines(entries, render) {
|
||||
return [...new Set(entries.map(render))];
|
||||
}
|
||||
|
||||
function main(args = process.argv.slice(2)) {
|
||||
try {
|
||||
for (const arg of args) {
|
||||
if (arg !== '--json') throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
const result = validate();
|
||||
if (args.includes('--json')) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
for (const line of uniqueLines(result.warnings, w => (w.kind === 'package'
|
||||
? `Package import, not shipped in carriers: ${w.file} -> ${w.package}`
|
||||
: `Dynamic require, not resolvable statically: ${w.file} -> ${w.expression}`))) {
|
||||
console.warn(line);
|
||||
}
|
||||
for (const line of uniqueLines(result.failures, f => `${f.layout} ${f.profileId}: ${f.file} -> ${f.specifier} (${f.reason})`)) {
|
||||
console.error(line);
|
||||
}
|
||||
console.log(`Carrier closure ${result.status}: ${result.layouts.length} layouts x ${result.profiles.length} profiles = ${result.projectionCount} projections, ${result.plannedFileCount} planned files, ${result.scriptCount} scripts, ${result.resolvedCount}/${result.specifierCount} relative specifiers resolved, ${result.failures.length} escapes, ${result.dynamicCount} dynamic requires, ${result.packageCount} package imports. Load smoke: deferred.`);
|
||||
}
|
||||
return result.status === 'success' ? 0 : 1;
|
||||
} catch (error) {
|
||||
console.error(`Carrier closure validation failed: ${error.message}`);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) process.exitCode = main();
|
||||
module.exports = { extractReferences, main, scanSource, validate };
|
||||
@@ -15,7 +15,7 @@ const COMPONENT_FAMILY_PREFIXES = {
|
||||
skill: 'skill:',
|
||||
locale: 'locale:',
|
||||
};
|
||||
const SUPPORTED_LOCALES = Object.freeze(['ja', 'zh-CN', 'ko-KR', 'pt-BR', 'ru', 'tr', 'vi-VN', 'zh-TW', 'de-DE', 'uk-UA', 'pl', 'bn']);
|
||||
const SUPPORTED_LOCALES = Object.freeze(['ja', 'zh-CN', 'ko-KR', 'pt-BR', 'ru', 'tr', 'vi-VN', 'zh-TW', 'de-DE', 'uk-UA', 'pl', 'bn', 'ar']);
|
||||
const LOCALE_ALIAS_TO_COMPONENT_ID = Object.freeze({
|
||||
'ja': 'locale:ja',
|
||||
'ja-JP': 'locale:ja',
|
||||
@@ -37,7 +37,9 @@ const LOCALE_ALIAS_TO_COMPONENT_ID = Object.freeze({
|
||||
'pl': 'locale:pl',
|
||||
'pl-PL': 'locale:pl',
|
||||
'bn': 'locale:bn',
|
||||
'bn-BD': 'locale:bn'
|
||||
'bn-BD': 'locale:bn',
|
||||
'ar': 'locale:ar',
|
||||
'ar-SA': 'locale:ar'
|
||||
});
|
||||
|
||||
function listSupportedLocales() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
/**
|
||||
* Language detection rules.
|
||||
@@ -84,6 +85,11 @@ const LANGUAGE_RULES = [
|
||||
type: 'php',
|
||||
markers: ['composer.json', 'composer.lock'],
|
||||
extensions: ['.php']
|
||||
},
|
||||
{
|
||||
type: 'dart',
|
||||
markers: ['pubspec.yaml', 'pubspec.lock'],
|
||||
extensions: ['.dart']
|
||||
}
|
||||
];
|
||||
|
||||
@@ -129,7 +135,10 @@ const FRAMEWORK_RULES = [
|
||||
{ framework: 'symfony', language: 'php', markers: ['symfony.lock'], packageKeys: ['symfony/framework-bundle'] },
|
||||
|
||||
// Elixir frameworks
|
||||
{ framework: 'phoenix', language: 'elixir', markers: [], packageKeys: ['phoenix'] }
|
||||
{ framework: 'phoenix', language: 'elixir', markers: [], packageKeys: ['phoenix'] },
|
||||
|
||||
// Dart frameworks
|
||||
{ framework: 'flutter', language: 'dart', markers: [], packageKeys: [], customCheck: hasFlutterSdkDependency }
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -330,6 +339,27 @@ function getElixirDeps(projectDir) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether pubspec.yaml declares the Flutter SDK as a dependency,
|
||||
* which distinguishes a Flutter app from a plain Dart package.
|
||||
* @param {string} projectDir - Project root directory
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasFlutterSdkDependency(projectDir) {
|
||||
try {
|
||||
const pubspecPath = path.join(projectDir, 'pubspec.yaml');
|
||||
if (!fs.existsSync(pubspecPath)) return false;
|
||||
const content = fs.readFileSync(pubspecPath, 'utf8');
|
||||
const pubspec = yaml.load(content, { schema: yaml.JSON_SCHEMA });
|
||||
// Only a declared Flutter SDK dependency identifies the framework.
|
||||
// Unrelated configuration, comments, and strings must not count.
|
||||
return pubspec?.dependencies?.flutter?.sdk === 'flutter'
|
||||
|| pubspec?.dev_dependencies?.flutter?.sdk === 'flutter';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect project languages and frameworks
|
||||
* @param {string} [projectDir] - Project directory (defaults to cwd)
|
||||
@@ -409,7 +439,9 @@ function detectProjectType(projectDir) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasMarker || hasDep) {
|
||||
const hasCustom = typeof rule.customCheck === 'function' ? rule.customCheck(projectDir) : false;
|
||||
|
||||
if (hasMarker || hasDep || hasCustom) {
|
||||
frameworks.push(rule.framework);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: agent-runtime-gateway-smoke-test
|
||||
description: Verify a local agent API, temporary gateway tunnel, and remote sandbox callback with a tool-free task, then restore the original app connection.
|
||||
metadata:
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Agent Runtime Gateway Smoke Test
|
||||
|
||||
Use this workflow when an agent task runs in a remote sandbox that calls back to a local API through a gateway. It verifies the whole path without relying on connected tools or private production data.
|
||||
|
||||
## When to Activate
|
||||
|
||||
- A local agent API needs an end-to-end runtime check.
|
||||
- A task stalls after dispatch and the gateway or tunnel may be unreachable.
|
||||
- A web app is temporarily pointed at a local API for agent testing.
|
||||
- A previous test left the web app pointing at a stopped API.
|
||||
|
||||
## Required Inputs
|
||||
|
||||
- Repository-relative instructions for starting the API and web app.
|
||||
- The gateway route path and its expected anonymous response.
|
||||
- An isolated test agent, a tool-free prompt, and its exact expected answer.
|
||||
- A way to inspect task status and confirm a callback reached the local API.
|
||||
- Provider credentials, supplied through the project's normal secret store. Name the providers in shared documentation; never copy credential values into the skill or test report.
|
||||
|
||||
If these inputs are unavailable, report the missing input instead of guessing an endpoint or using a real account.
|
||||
|
||||
## Authorization and Cleanup
|
||||
|
||||
Confirm authorization to expose the local test API through the selected tunnel provider, dispatch the test task, and temporarily change the web app route. Use an isolated local web app or test deployment; do not switch a shared production app as part of this smoke test. Keep tunnel access scoped to the required authenticated route.
|
||||
|
||||
Install cleanup before changing the route: on success, error, timeout, interruption, or cancellation, restore the recorded target and verify health before stopping the API and tunnel. Track only the process IDs or resources created by this test. If restoration fails, report it immediately and keep the evidence needed to recover; do not claim success.
|
||||
|
||||
## Smoke Test
|
||||
|
||||
1. **Record and check the original route.** Note the web app's current API target and verify it is healthy. Keep this value locally for restoration. Confirm the test agent and data are isolated from real users.
|
||||
2. **Start the local API and tunnel.** Use a process manager that will keep both alive for the full test. Verify API health. Probe the gateway route anonymously and confirm the documented authentication response, usually `401` or `403`. Confirm the probe appears in the local API log; an edge response alone does not prove the API was reached.
|
||||
3. **Switch the web app only after both checks pass.** Verify a request from the web app reaches the local API. Check the route again after any web server restart or configuration reload.
|
||||
4. **Run one tool-free task.** Use a short prompt with an exact expected answer. Confirm the remote sandbox called the local gateway, the task reached a terminal state, and the returned answer matches. A queued or running task is not a pass.
|
||||
5. **Restore the original route.** Set the web app back to its recorded API target, verify that target and a representative page load, then stop the test API and tunnel. If restoration fails, report the broken route immediately.
|
||||
|
||||
Example checks, with values supplied by the project:
|
||||
|
||||
```sh
|
||||
curl --silent --show-error --output /dev/null --write-out '%{http_code}\n' "$LOCAL_API_HEALTH_URL"
|
||||
curl --silent --show-error --output /dev/null --write-out '%{http_code}\n' "$PUBLIC_GATEWAY_PROBE_URL"
|
||||
```
|
||||
|
||||
The first command should return the project's healthy status. The second should return the documented anonymous authentication status **and** appear in the local API log. Do not put a credential in either URL.
|
||||
|
||||
## Connected Tool Follow-up
|
||||
|
||||
Run a separate test only after the tool-free task passes. Use an explicit test connection and grants. Report connection presence and actions granted as different facts. Do not infer that an empty action search means no app is connected. Do not execute a write action against a real account as a smoke test.
|
||||
|
||||
## Failure Handling
|
||||
|
||||
| Observation | Next check |
|
||||
| --- | --- |
|
||||
| Web page loads but API requests fail | Compare the configured API target with the listening process and restore the original route. |
|
||||
| Gateway probe gets a connection error or `404` | Check tunnel lifetime, route path, and local API health before dispatching a task. |
|
||||
| Gateway probe gets `401` or `403`, but no local log entry | The response may come from the tunnel edge; verify the callback path. |
|
||||
| Task stays queued or running | Check whether another dispatcher claimed it and whether the sandbox reached the local gateway. |
|
||||
| Task finishes with the wrong answer | Capture the task status and redacted error; do not call the smoke test successful. |
|
||||
|
||||
## Output Contract
|
||||
|
||||
Return a short report with:
|
||||
|
||||
- Local API healthy: yes or no
|
||||
- Gateway probe status and local log confirmation
|
||||
- Remote callback observed: yes or no
|
||||
- Tool-free task terminal status and whether the exact answer matched
|
||||
- Original web API route restored and healthy: yes or no
|
||||
- One unresolved blocker or next action, if any
|
||||
|
||||
Keep account details, credential values, personal paths, public tunnel URLs, and raw private logs out of the report. Keep the source operator notes local.
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `hermes-imports` for sanitizing a local workflow before sharing it.
|
||||
- `agent-introspection-debugging` when an agent run fails repeatedly.
|
||||
- `verification-loop` after changing runtime code.
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
name: brownfield-retrospective
|
||||
description: Run the ECC engineering process retroactively against an existing project slice. Audit current state, reconstruct past decisions, produce a four-axis gap register (thinking, coding practice, missing features, technology selection), then execute fixes forward through the plan-TDD-review pipeline. Use when adopting ECC mid-project, taking over an inherited codebase, or asking what was missed and what would have been built differently from day one.
|
||||
metadata:
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Brownfield Retrospective
|
||||
|
||||
Run ECC's engineering process retroactively against a project that was built without it. Pick any slice of an existing codebase, audit what exists, reconstruct how it got there, extract a four-axis gap register, then execute the fixes forward through the standard ECC pipeline.
|
||||
|
||||
The workflow is slice-first: no whole-repo onboarding is required before it is useful, and every phase is re-enterable from a durable artifact on disk. Start in the middle, work backwards, then move forward.
|
||||
|
||||
## When to Activate
|
||||
|
||||
- Adopting ECC in a repository that already exists and is already shaped
|
||||
- User asks to "audit my existing project", "retro this codebase", or "what did we miss?"
|
||||
- Taking over, inheriting, or returning to a codebase after a long gap
|
||||
- User asks "why is it like this?" followed by "should it stay like this?"
|
||||
- A module, feature, or service needs a rethink before the next feature lands on top of it
|
||||
- Deciding whether a legacy area should be fixed forward, rewritten, or left documented as-is
|
||||
|
||||
## When Not to Use
|
||||
|
||||
- Greenfield work with no existing code — use `planner` and `tdd-workflow` directly
|
||||
- Onboarding to spec-driven development without the redesign loop — the `spec-miner` agent does that on its own
|
||||
- A launch go/no-go production-readiness pass — use `production-audit`
|
||||
- Security-only sweeps — use `security-review`
|
||||
- Team process retrospectives (sprint ceremonies) — this audits a codebase, not people
|
||||
|
||||
## The Working Set
|
||||
|
||||
All artifacts are written inside the audited project, under `docs/retro/<slice>/`:
|
||||
|
||||
| File | Phase | Purpose |
|
||||
|------|-------|---------|
|
||||
| `gap-register.md` | 0-6 | The state machine — perimeter, `Current phase:` marker, and rows. The only file required to resume mid-stream. |
|
||||
| `baseline-audit.md` | 1 | Reviewer findings, test/CI snapshot, TODO inventory. |
|
||||
| `reconstructed-spec.md` | 2 | What the code actually does today (Requirements + Invariants). |
|
||||
| `target-design.md` | 4 | Target shape for the slice and the fix sequence. |
|
||||
|
||||
Backfilled decisions go to `docs/adr/` per the `architecture-decision-records` skill. If the project uses OpenSpec, `reconstructed-spec.md` may instead be `openspec/specs/<capability>/spec.md` produced by `spec-miner`.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Phase 0 — Pick the Entry Point
|
||||
|
||||
Start from what the user cares about today. Do not map the whole repo first.
|
||||
|
||||
Any of these is a valid slice:
|
||||
|
||||
| User signal | Slice |
|
||||
|-------------|-------|
|
||||
| Names a directory, module, or service | That path plus its direct callers |
|
||||
| Names a feature or user flow | The files that implement it, traced from the entry point |
|
||||
| Names a bug, incident, or pain point | The code that produced it plus one level of call chain |
|
||||
| Names a dependency or tech choice | Everything that touches it |
|
||||
| Names nothing | The 2-3 hottest paths by churn — propose them and let the user pick |
|
||||
|
||||
Record the perimeter at the top of `gap-register.md` before anything else: paths in scope, entry points, callers, blast radius. Budget the slice at roughly 15 files or 2,000 lines; if the user's pick is bigger, cut it into named sub-slices and register each as its own row of work. A slice too big to audit in one session is a slice that never finishes. Keep a `Current phase:` marker beside the perimeter and advance it each time a phase completes.
|
||||
|
||||
### Phase 1 — Present-State Audit
|
||||
|
||||
Establish what exists now, with evidence. Do not fix anything yet.
|
||||
|
||||
1. Recon the slice: manifest and framework fingerprint, entry points, test layout, CI checks (borrow the recon steps from `codebase-onboarding`, scoped to the perimeter).
|
||||
2. Run the reviewers that match the stack, in parallel where possible. Every finding must carry a `file:line` pointer:
|
||||
- The language reviewer for the stack (`typescript-reviewer`, `python-reviewer`, `go-reviewer`, ...)
|
||||
- `security-reviewer`
|
||||
- `silent-failure-hunter` for swallowed errors
|
||||
- `type-design-analyzer` where invariants are encoded in types
|
||||
- `performance-optimizer` only if the slice is on a hot path
|
||||
3. Snapshot the current state: test count and coverage, lint/CI status, `TODO`/`FIXME`/`HACK` inventory inside the perimeter.
|
||||
|
||||
Write everything to `baseline-audit.md`. Findings that get fixed during this phase corrupt the baseline — hold them for the register.
|
||||
|
||||
### Phase 2 — Backward Reconstruction
|
||||
|
||||
Work backwards from the present state to the decisions that produced it.
|
||||
|
||||
1. Mine the behavioral spec: run `spec-miner` against the slice. Its Requirements (WHEN → THEN) and Invariants become `reconstructed-spec.md` — the baseline truth for Phase 5 tests.
|
||||
2. Git archaeology on the perimeter:
|
||||
- `git log --oneline -- <slice>` for directory history; use `--follow` only with a single file path — age, authors, cadence
|
||||
- Largest and most reverted commits — where the design changed under pressure
|
||||
- Churn hotspots — files changed far more often than neighbors
|
||||
- Dead ends — branches or scaffolding that never shipped
|
||||
3. Decision inventory: list the significant decisions frozen into the code (stack and framework choices, data model, boundaries, error strategy, state management). Check each against `docs/adr/`, README, and comments. Every decision with no written record is itself a pre-existing thinking gap — record it in Phase 3, do not silently backfill it here.
|
||||
4. Where behavior is opaque, use `code-explorer` to trace execution paths before guessing at intent.
|
||||
|
||||
### Phase 3 — Gap Analysis (Four Axes)
|
||||
|
||||
Convert the audit and reconstruction into register rows. One axis per class of gap:
|
||||
|
||||
| Axis | Question | Sources |
|
||||
|------|-----------|---------|
|
||||
| Thinking | What was decided but never recorded? Which invariants live only in code? | Decision inventory, reconstructed spec |
|
||||
| Coding practice | Where does the code diverge from how ECC would have written it? | Phase 1 findings, the `rules/` pack for the language, spec items with no test anchor |
|
||||
| Missing features | What would a day-one build have included that this slice never got? | Boundary input validation, observability, rate limiting, idempotency, migrations plus rollback, a11y, i18n, user docs, CI gates |
|
||||
| Technology selection | Is the chosen tech fighting the codebase? Outdated, duplicated, risky, or missing? | Dependency inventory, git archaeology, recon |
|
||||
|
||||
Register format (the table is the skill's durable output):
|
||||
|
||||
```markdown
|
||||
| ID | Axis | Gap | Evidence | Forward fix | Severity | Effort | Status |
|
||||
|----|------|-----|----------|-------------|----------|--------|--------|
|
||||
| G-001 | practice | Errors swallowed in retry loop | src/api/retry.ts:42 | Propagate + map at boundary | P1 | S | open |
|
||||
```
|
||||
|
||||
- Severity: `P0` security exposure or data loss, `P1` correctness, `P2` practice and observability, `P3` selection and cleanup.
|
||||
- Effort: `S` under a session, `M` a few sessions, `L` needs its own plan.
|
||||
- Status: `open`, `in-progress`, `done`, `wont-fix`.
|
||||
- Every row needs an evidence pointer. A gap you cannot point at is an opinion.
|
||||
|
||||
### Phase 4 — Redesign / Rethink
|
||||
|
||||
Turn the register into a sequenced plan. Follow the project's existing task and approval authority. Keep the register as audit evidence linked to that system; do not create competing canonical task state. Confirm scope and obtain project-required approval before a rewrite, technology replacement, migration, or other material change.
|
||||
|
||||
1. Run `architect` or `code-architect` over the `P0` and `P1` rows to produce `target-design.md`: target shape for the slice, the fix sequence, and an explicit "what does not change" list to protect working behavior.
|
||||
2. Backfill retro-ADRs for every undocumented decision from Phase 2. Mark them as reconstructed (`status: proposed (reconstructed)`, date of the retro, note the original decision predates the record). Separate observed history from inferred intent, and have the project owner confirm a decision before marking it accepted. Never present a backfilled ADR as if it were written at decision time.
|
||||
3. Triage every register row into one of: `fix-forward` (repair in place), `rewrite` (replace the slice), `leave-documented` (wont-fix). A `wont-fix` must record its reason in the register — a wont-fix with a reason closes the thinking gap; a silent one reopens it.
|
||||
4. Sequence the backlog: `P0` first, then interleave quick wins with larger repairs to keep momentum.
|
||||
|
||||
### Phase 5 — Forward Execution
|
||||
|
||||
Work the register through the standard ECC pipeline, one row per session as a baseline pace:
|
||||
|
||||
```text
|
||||
planner (scope the row)
|
||||
→ tdd-guide (failing test first — for a legacy gap the first test
|
||||
usually encodes the missing invariant from reconstructed-spec.md)
|
||||
→ implement
|
||||
→ code-reviewer
|
||||
→ security-reviewer (if the row touches input, auth, or data)
|
||||
→ doc-updater + ADR (if the decision changed)
|
||||
→ conventional commit
|
||||
→ mark the register row done
|
||||
```
|
||||
|
||||
- Update `gap-register.md` statuses as rows close; the register is the durable state, not the conversation.
|
||||
- On session boundaries, hand off through the project's memory mechanism (`unified-memory`, context carriers) so the next session resumes from the register alone.
|
||||
|
||||
### Phase 6 — Closure and Re-Audit
|
||||
|
||||
When every row is `done` or `wont-fix`:
|
||||
|
||||
1. Re-run the Phase 1 audit over the same perimeter.
|
||||
2. Diff against `baseline-audit.md` and verify with `verification-loop` — the gaps claimed closed must stay closed.
|
||||
3. Record the delta (findings then, findings now, rows closed, rows deferred) at the bottom of `gap-register.md`.
|
||||
4. Anything that resurfaced becomes a new row, not a failure — the register is a loop, not a one-shot.
|
||||
|
||||
## Resuming Mid-Stream
|
||||
|
||||
Any session can start in the middle. Read `docs/retro/<slice>/gap-register.md` first:
|
||||
|
||||
- Register exists with a row `in-progress` → Phase 5, finish that row.
|
||||
- Register exists with at least one row, all rows closed → Phase 6.
|
||||
- Register exists, rows open, none in progress → resume at the `Current phase:` marker (Phase 3 register awaiting triage, or Phase 4 backlog awaiting its first row).
|
||||
- No register, but `baseline-audit.md` exists → Phase 2.
|
||||
- No artifacts at all → Phase 0 with the user.
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- Auditing the whole repo before any slice — the workflow is slice-first by design.
|
||||
- Fixing code during Phases 1-3 — audit phases produce the register; edits mid-audit corrupt the baseline.
|
||||
- Register rows without evidence pointers.
|
||||
- `wont-fix` without a recorded reason.
|
||||
- Presenting backfilled ADRs as contemporaneous records.
|
||||
- Skipping the Phase 6 re-audit — closure is verified, not declared.
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `codebase-onboarding` — recon baseline for Phase 1
|
||||
- `architecture-decision-records` — retro-ADR backfill in Phase 4
|
||||
- `tdd-workflow` — Phase 5 execution discipline
|
||||
- `verification-loop` — Phase 6 closure
|
||||
- `production-audit` — launch-readiness lens (complementary, later)
|
||||
- `security-review` — security-only requests
|
||||
@@ -22,6 +22,7 @@ Invoke this skill:
|
||||
|
||||
### Phase 1: Build Verification
|
||||
```bash
|
||||
set -o pipefail # report the build's exit status, not tail's
|
||||
# Check if project builds
|
||||
npm run build 2>&1 | tail -20
|
||||
# OR
|
||||
@@ -34,25 +35,30 @@ If build fails, STOP and fix before continuing.
|
||||
```bash
|
||||
set -o pipefail
|
||||
# TypeScript projects
|
||||
npx --no-install tsc --noEmit 2>&1 | head -30
|
||||
npx --no-install tsc --noEmit 2>&1 | sed -n '1,30p'
|
||||
|
||||
# Python projects
|
||||
pyright . 2>&1 | head -30
|
||||
pyright . 2>&1 | sed -n '1,30p'
|
||||
```
|
||||
|
||||
Report all type errors. Fix critical ones before continuing.
|
||||
|
||||
### Phase 3: Lint Check
|
||||
|
||||
Use `sed` to limit displayed output while consuming the whole stream. This keeps a
|
||||
successful linter from failing on SIGPIPE when `pipefail` is enabled.
|
||||
```bash
|
||||
set -o pipefail
|
||||
# JavaScript/TypeScript
|
||||
npm run lint 2>&1 | head -30
|
||||
npm run lint 2>&1 | sed -n '1,30p'
|
||||
|
||||
# Python
|
||||
ruff check . 2>&1 | head -30
|
||||
ruff check . 2>&1 | sed -n '1,30p'
|
||||
```
|
||||
|
||||
### Phase 4: Test Suite
|
||||
```bash
|
||||
set -o pipefail
|
||||
# Run tests with coverage
|
||||
npm run test -- --coverage 2>&1 | tail -50
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ function writeEnglishReadme(root, counts, options = {}) {
|
||||
fs.writeFileSync(path.join(root, 'README.md'), `Access to ${counts.agents} agents, ${counts.skills} skills, and ${counts.commands} commands.
|
||||
- **Public surface synced to the live repo** - metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: ${counts.agents} agents, ${counts.skills} skills, and ${counts.commands} legacy command shims.
|
||||
|-- agents/ # ${counts.agents} specialized subagents for delegation
|
||||
|-- agents/ # ${counts.agents + 1} specialized subagents for delegation
|
||||
|-- skills/ # ${counts.skills} reusable workflows loaded on demand
|
||||
|-- commands/ # ${counts.commands} maintained slash-command shims
|
||||
${options.extraTreeBlock === undefined ? `|-- agents/ # ${counts.agents + 1} specialized subagents for delegation` : options.extraTreeBlock}
|
||||
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Agents | PASS: ${tableCounts.agents} agents |
|
||||
@@ -171,7 +173,7 @@ function writeCatalogFixture(root, options = {}) {
|
||||
fs.writeFileSync(path.join(root, 'commands', 'notes.txt'), 'not counted\n');
|
||||
fs.mkdirSync(path.join(root, 'skills', 'missing-skill-file'), { recursive: true });
|
||||
|
||||
writeEnglishReadme(root, documentedCounts, { unrelatedSkillsCount });
|
||||
writeEnglishReadme(root, documentedCounts, { unrelatedSkillsCount, extraTreeBlock: options.extraTreeBlock });
|
||||
writeEnglishAgents(root, documentedCounts, { skillsMinimum });
|
||||
writeCrossHarnessIdentityDocs(root, documentedCounts);
|
||||
writeZhRootReadme(root, documentedCounts);
|
||||
@@ -294,6 +296,76 @@ function runTests() {
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('flags a stale count hidden in a second project-tree block', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeCatalogFixture(testDir, {
|
||||
actualCounts: { agents: 2, skills: 3, commands: 4 },
|
||||
documentedCounts: { agents: 2, skills: 3, commands: 4 },
|
||||
extraTreeBlock: '|-- agents/ # 1 specialized subagents for delegation',
|
||||
});
|
||||
|
||||
const result = runCatalogCheck({ root: testDir });
|
||||
const failing = result.checks.filter(check => !check.ok && check.source.includes('project tree'));
|
||||
|
||||
assert.strictEqual(failing.length, 1);
|
||||
assert.match(failing[0].source, /project tree \(agents\) block \d+/);
|
||||
assert.strictEqual(failing[0].expected, 1);
|
||||
assert.strictEqual(failing[0].actual, 2);
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('write mode repairs every project-tree block, not just the first', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeCatalogFixture(testDir, {
|
||||
actualCounts: { agents: 2, skills: 3, commands: 4 },
|
||||
documentedCounts: { agents: 2, skills: 3, commands: 4 },
|
||||
extraTreeBlock: [
|
||||
'|-- agents/ # 1 specialized subagents for delegation',
|
||||
'|-- skills/ # 1 reusable workflows loaded on demand',
|
||||
].join('\n'),
|
||||
});
|
||||
|
||||
const result = runCatalogCheck({ root: testDir, writeMode: true });
|
||||
|
||||
assert.strictEqual(result.checks.filter(check => !check.ok).length, 0);
|
||||
|
||||
const readme = fs.readFileSync(path.join(testDir, 'README.md'), 'utf8');
|
||||
assert.ok(!readme.includes('# 1 specialized subagents for delegation'));
|
||||
assert.ok(!readme.includes('# 1 reusable workflows loaded on demand'));
|
||||
assert.strictEqual(readme.split('# 2 specialized subagents for delegation').length - 1, 2);
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('throws when a project-tree count entry is missing entirely', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeCatalogFixture(testDir, {
|
||||
actualCounts: { agents: 2, skills: 3, commands: 4 },
|
||||
documentedCounts: { agents: 2, skills: 3, commands: 4 },
|
||||
});
|
||||
|
||||
const readmePath = path.join(testDir, 'README.md');
|
||||
const withoutSkills = fs.readFileSync(readmePath, 'utf8')
|
||||
.split('\n')
|
||||
.filter(line => !line.includes('reusable workflows loaded on demand'))
|
||||
.join('\n');
|
||||
fs.writeFileSync(readmePath, withoutSkills);
|
||||
|
||||
assert.throws(
|
||||
() => runCatalogCheck({ root: testDir }),
|
||||
/README\.md project tree \(skills\) is missing the skills count/
|
||||
);
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('throws a clear error for missing tracked documents', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
|
||||
@@ -260,6 +260,9 @@ function writeCatalogFixture(testDir, options = {}) {
|
||||
const {
|
||||
readmeCounts = { agents: 1, skills: 1, commands: 1 },
|
||||
readmeProjectTreeAgents = readmeCounts.agents,
|
||||
readmeProjectTreeSkills = readmeCounts.skills,
|
||||
readmeProjectTreeCommands = readmeCounts.commands,
|
||||
readmeExtraTreeBlock = '',
|
||||
readmeTableCounts = readmeCounts,
|
||||
readmeParityCounts = readmeCounts,
|
||||
readmeUnrelatedSkillsCount = 16,
|
||||
@@ -306,7 +309,10 @@ function writeCatalogFixture(testDir, options = {}) {
|
||||
fs.writeFileSync(path.join(testDir, 'commands', 'plan.md'), '---\ndescription: Plan\n---\n# Plan');
|
||||
fs.writeFileSync(path.join(testDir, 'skills', 'demo-skill', 'SKILL.md'), '---\nname: demo-skill\ndescription: Demo skill\norigin: ECC\n---\n# Demo Skill');
|
||||
|
||||
fs.writeFileSync(readmePath, `Access to ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} commands.\n- **Public surface synced to the live repo** - metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} legacy command shims.\n|-- agents/ # ${readmeProjectTreeAgents} specialized subagents for delegation\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| Agents | PASS: ${readmeTableCounts.agents} agents | Shared | Shared | 1 |\n| Commands | PASS: ${readmeTableCounts.commands} commands | Shared | Shared | 1 |\n| Skills | PASS: ${readmeTableCounts.skills} skills | Shared | Shared | 1 |\n\n| Feature | Count | Format |\n|-----------|-------|---------|\n| Skills | ${readmeUnrelatedSkillsCount} | .agents/skills/ |\n\n## Cross-Tool Feature Parity\n\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| **Agents** | ${readmeParityCounts.agents} | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |\n| **Commands** | ${readmeParityCounts.commands} | Shared | Instruction-based | 31 |\n| **Skills** | ${readmeParityCounts.skills} | Shared | 10 (native format) | 37 |\n`);
|
||||
fs.writeFileSync(readmePath, `Access to ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} commands.\n- **Public surface synced to the live repo** - metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} legacy command shims.\n|-- agents/ # ${readmeProjectTreeAgents} specialized subagents for delegation
|
||||
|-- skills/ # ${readmeProjectTreeSkills} reusable workflows loaded on demand
|
||||
|-- commands/ # ${readmeProjectTreeCommands} maintained slash-command shims
|
||||
${readmeExtraTreeBlock}\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| Agents | PASS: ${readmeTableCounts.agents} agents | Shared | Shared | 1 |\n| Commands | PASS: ${readmeTableCounts.commands} commands | Shared | Shared | 1 |\n| Skills | PASS: ${readmeTableCounts.skills} skills | Shared | Shared | 1 |\n\n| Feature | Count | Format |\n|-----------|-------|---------|\n| Skills | ${readmeUnrelatedSkillsCount} | .agents/skills/ |\n\n## Cross-Tool Feature Parity\n\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| **Agents** | ${readmeParityCounts.agents} | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |\n| **Commands** | ${readmeParityCounts.commands} | Shared | Instruction-based | 31 |\n| **Skills** | ${readmeParityCounts.skills} | Shared | 10 (native format) | 37 |\n`);
|
||||
fs.writeFileSync(agentsPath, `This is a **production-ready AI coding plugin** providing ${summaryCounts.agents} specialized agents, ${summaryCounts.skills} skills, ${summaryCounts.commands} commands, and automated hook workflows for software development.\n\n\`\`\`\n${structureLines.join('\n')}\n\`\`\`\n`);
|
||||
fs.writeFileSync(zhRootReadmePath, `**完成!** 你现在可以使用 ${zhRootReadmeCounts.agents} 个代理、${zhRootReadmeCounts.skills} 个技能和 ${zhRootReadmeCounts.commands} 个命令。\n`);
|
||||
fs.writeFileSync(zhDocsReadmePath, `**搞定!** 你现在可以使用 ${zhDocsReadmeCounts.agents} 个智能体、${zhDocsReadmeCounts.skills} 项技能和 ${zhDocsReadmeCounts.commands} 个命令了。\n| 功能特性 | Claude Code | OpenCode | 状态 |\n|---------|-------------|----------|--------|\n| 智能体 | \u2705 ${zhDocsTableCounts.agents} 个 | \u2705 12 个 | **Claude Code 领先** |\n| 命令 | \u2705 ${zhDocsTableCounts.commands} 个 | \u2705 31 个 | **Claude Code 领先** |\n| 技能 | \u2705 ${zhDocsTableCounts.skills} 项 | \u2705 37 项 | **Claude Code 领先** |\n\n| 功能特性 | 数量 | 格式 |\n|-----------|-------|---------|\n| 技能 | ${zhDocsUnrelatedSkillsCount} | .agents/skills/ |\n\n## 跨工具功能对等\n\n| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| **智能体** | ${zhDocsParityCounts.agents} | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |\n| **命令** | ${zhDocsParityCounts.commands} | 共享 | 基于指令 | 31 |\n| **技能** | ${zhDocsParityCounts.skills} | 共享 | 10 (原生格式) | 37 |\n`);
|
||||
@@ -842,6 +848,77 @@ function runTests() {
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('flags a stale count hidden in a second README project-tree block', () => {
|
||||
const testDir = createTestDir();
|
||||
const {
|
||||
readmePath,
|
||||
agentsPath,
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
zhAgentsPath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir, {
|
||||
readmeExtraTreeBlock: '|-- agents/ # 9 specialized subagents for delegation',
|
||||
});
|
||||
|
||||
const result = runCatalogValidator({
|
||||
ROOT: testDir,
|
||||
README_PATH: readmePath,
|
||||
AGENTS_PATH: agentsPath,
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: zhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
const output = result.stdout + result.stderr;
|
||||
assert.strictEqual(result.code, 1, `Should fail on a stale second block, got ${output}`);
|
||||
assert.ok(
|
||||
output.includes('README.md project tree (agents) block'),
|
||||
`Should name the offending block, got ${output}`
|
||||
);
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('write mode repairs a stale second README project-tree block', () => {
|
||||
const testDir = createTestDir();
|
||||
const {
|
||||
readmePath,
|
||||
agentsPath,
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
zhAgentsPath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir, {
|
||||
readmeExtraTreeBlock: '|-- agents/ # 9 specialized subagents for delegation',
|
||||
});
|
||||
|
||||
const result = runCatalogValidator({
|
||||
argv: ['--write', '--text'],
|
||||
ROOT: testDir,
|
||||
README_PATH: readmePath,
|
||||
AGENTS_PATH: agentsPath,
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: zhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 0, `Should sync every block, got ${result.stderr}`);
|
||||
const readme = fs.readFileSync(readmePath, 'utf8');
|
||||
assert.ok(!readme.includes('# 9 specialized subagents for delegation'), 'Should repair the second block too');
|
||||
assert.strictEqual(
|
||||
readme.split('# 1 specialized subagents for delegation').length - 1,
|
||||
2,
|
||||
'Should correct both blocks'
|
||||
);
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('exits 0 when hooks.json does not exist', () => {
|
||||
const result = runValidatorWithDir('validate-hooks', 'HOOKS_FILE', '/nonexistent/hooks.json');
|
||||
assert.strictEqual(result.code, 0, 'Should skip when no hooks.json');
|
||||
|
||||
@@ -26,6 +26,7 @@ const publicInstallDocs = [
|
||||
'README.zh-CN.md',
|
||||
'docs/pt-BR/README.md',
|
||||
'docs/zh-CN/README.md',
|
||||
'docs/ar/README.md',
|
||||
'docs/ja-JP/skills/configure-ecc/SKILL.md',
|
||||
'docs/zh-CN/skills/configure-ecc/SKILL.md',
|
||||
];
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Regression coverage for Kiro steering file inclusion modes.
|
||||
*
|
||||
* Kiro only loads a steering file in every conversation with `inclusion: always`;
|
||||
* `auto` loads it only when the request matches its description.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const KIRO_DIR = path.join(__dirname, '..', '..', '.kiro');
|
||||
const STEERING_DIR = path.join(KIRO_DIR, 'steering');
|
||||
const README = path.join(KIRO_DIR, 'README.md');
|
||||
|
||||
const ALWAYS_ON = [
|
||||
'coding-style.md',
|
||||
'security.md',
|
||||
'testing.md',
|
||||
'development-workflow.md',
|
||||
'git-workflow.md',
|
||||
'patterns.md',
|
||||
'performance.md',
|
||||
'lessons-learned.md',
|
||||
];
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function readFrontmatter(file) {
|
||||
const content = fs.readFileSync(path.join(STEERING_DIR, file), 'utf8');
|
||||
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
assert.ok(match, `${file} should start with YAML frontmatter`);
|
||||
const fields = {};
|
||||
for (const line of match[1].split(/\r?\n/)) {
|
||||
const field = line.match(/^(\w+):\s*(.*)$/);
|
||||
if (field) fields[field[1]] = field[2].trim();
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
function readReadmeInclusions() {
|
||||
const readme = fs.readFileSync(README, 'utf8');
|
||||
const inclusions = {};
|
||||
for (const row of readme.matchAll(/^\| `([\w-]+\.md)` \| (always|auto|manual|fileMatch)\b/gm)) {
|
||||
inclusions[row[1]] = row[2];
|
||||
}
|
||||
return inclusions;
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing Kiro steering inclusion modes ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const steeringFiles = fs.readdirSync(STEERING_DIR).filter(file => file.endsWith('.md'));
|
||||
|
||||
if (test('core steering files use always inclusion', () => {
|
||||
for (const file of ALWAYS_ON) {
|
||||
assert.strictEqual(
|
||||
readFrontmatter(file).inclusion,
|
||||
'always',
|
||||
`${file} should use inclusion: always so it loads in every conversation`
|
||||
);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('README steering table matches each file frontmatter', () => {
|
||||
const documented = readReadmeInclusions();
|
||||
for (const file of steeringFiles) {
|
||||
assert.ok(documented[file], `${file} should be listed in the .kiro/README.md steering table`);
|
||||
assert.strictEqual(
|
||||
readFrontmatter(file).inclusion,
|
||||
documented[file],
|
||||
`${file} frontmatter inclusion should match the README table`
|
||||
);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('steering files declare the fields their inclusion mode requires', () => {
|
||||
for (const file of steeringFiles) {
|
||||
const fields = readFrontmatter(file);
|
||||
if (fields.inclusion === 'auto') {
|
||||
assert.ok(fields.name && fields.description, `${file} uses auto inclusion and needs name and description`);
|
||||
}
|
||||
if (fields.inclusion === 'fileMatch') {
|
||||
assert.ok(fields.fileMatchPattern, `${file} uses fileMatch inclusion and needs fileMatchPattern`);
|
||||
}
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
@@ -53,9 +53,63 @@ function runTests() {
|
||||
assert.ok(components.some(component => component.id === 'locale:de-de'));
|
||||
assert.ok(components.some(component => component.id === 'locale:uk-ua'));
|
||||
assert.ok(components.some(component => component.id === 'locale:pl'));
|
||||
assert.ok(components.some(component => component.id === 'locale:ar'));
|
||||
assert.ok(components.every(component => component.family === 'locale'));
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('locale:ar resolves to the Arabic translated docs module', () => {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-plan-ar-'));
|
||||
try {
|
||||
const plan = resolveInstallPlan({
|
||||
includeComponentIds: ['locale:ar'],
|
||||
target: 'claude',
|
||||
homeDir,
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(plan.selectedModuleIds, ['docs-ar']);
|
||||
assert.ok(
|
||||
plan.operations.some(operation => (
|
||||
normalizePlanPath(operation.sourceRelativePath) === 'docs/ar'
|
||||
&& normalizePlanPath(operation.destinationPath).endsWith('/.claude/docs/ar')
|
||||
)),
|
||||
'Should map docs/ar to ~/.claude/docs/ar'
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(homeDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('end-to-end: --locale ar-SA dry-run includes docs-ar operations', () => {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-dry-run-ar-'));
|
||||
const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-dry-run-ar-project-'));
|
||||
|
||||
try {
|
||||
const output = runInstallApply([
|
||||
'--locale', 'ar-SA',
|
||||
'--dry-run',
|
||||
'--json',
|
||||
], {
|
||||
cwd: projectDir,
|
||||
env: { HOME: homeDir },
|
||||
});
|
||||
const json = JSON.parse(output);
|
||||
|
||||
assert.strictEqual(json.plan.mode, 'manifest');
|
||||
assert.deepStrictEqual(json.plan.includedComponentIds, ['locale:ar']);
|
||||
assert.deepStrictEqual(json.plan.selectedModuleIds, ['docs-ar']);
|
||||
assert.ok(
|
||||
json.plan.operations.some(operation => (
|
||||
normalizePlanPath(operation.sourceRelativePath) === 'docs/ar/README.md'
|
||||
&& normalizePlanPath(operation.destinationPath).endsWith('/.claude/docs/ar/README.md')
|
||||
)),
|
||||
'Should copy translated README into ~/.claude/docs/ar'
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(homeDir, { recursive: true, force: true });
|
||||
fs.rmSync(projectDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('locale:pl resolves to the Polish translated docs module', () => {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-plan-pl-'));
|
||||
try {
|
||||
@@ -327,6 +381,38 @@ function runTests() {
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('end-to-end: --locale ar installs translated docs cleanly', () => {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-install-ar-'));
|
||||
const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-install-ar-project-'));
|
||||
|
||||
try {
|
||||
runInstallApply([
|
||||
'--locale', 'ar',
|
||||
], {
|
||||
cwd: projectDir,
|
||||
env: { HOME: homeDir },
|
||||
});
|
||||
|
||||
const claudeRoot = path.join(homeDir, '.claude');
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(claudeRoot, 'docs', 'ar', 'README.md')),
|
||||
'Should install Arabic README under docs/ar'
|
||||
);
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(claudeRoot, 'skills', 'configure-ecc', 'SKILL.md')),
|
||||
'Locale-only install should not install English skills'
|
||||
);
|
||||
|
||||
const statePath = path.join(claudeRoot, 'ecc', 'install-state.json');
|
||||
const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
||||
assert.deepStrictEqual(state.request.includeComponents, ['locale:ar']);
|
||||
assert.deepStrictEqual(state.resolution.selectedModules, ['docs-ar']);
|
||||
} finally {
|
||||
fs.rmSync(homeDir, { recursive: true, force: true });
|
||||
fs.rmSync(projectDir, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('end-to-end: --locale uk-UA installs translated docs cleanly', () => {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-install-uk-'));
|
||||
const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'locale-install-uk-project-'));
|
||||
|
||||
@@ -493,6 +493,87 @@ function runTests() {
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// Dart/Flutter detection
|
||||
console.log('\nDart/Flutter Detection:');
|
||||
|
||||
if (test('detects dart from pubspec.yaml and .dart files', () => {
|
||||
const dir = createTempDir();
|
||||
try {
|
||||
writeTestFile(dir, 'pubspec.yaml', 'name: test_pkg\nversion: 1.0.0\n');
|
||||
writeTestFile(dir, 'main.dart', 'void main() {}\n');
|
||||
const result = detectProjectType(dir);
|
||||
assert.ok(result.languages.includes('dart'));
|
||||
} finally {
|
||||
cleanupDir(dir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('detects flutter framework when pubspec.yaml depends on the Flutter SDK', () => {
|
||||
const dir = createTempDir();
|
||||
try {
|
||||
writeTestFile(dir, 'pubspec.yaml', [
|
||||
'name: halal_cosmetics_app',
|
||||
'dependencies:',
|
||||
' flutter:',
|
||||
' sdk: flutter',
|
||||
' cupertino_icons: ^1.0.2'
|
||||
].join('\n'));
|
||||
const result = detectProjectType(dir);
|
||||
assert.ok(result.languages.includes('dart'));
|
||||
assert.ok(result.frameworks.includes('flutter'), `flutter not detected: ${JSON.stringify(result.frameworks)}`);
|
||||
assert.strictEqual(result.primary, 'flutter');
|
||||
} finally {
|
||||
cleanupDir(dir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('does not misclassify a plain Dart package as flutter', () => {
|
||||
const dir = createTempDir();
|
||||
try {
|
||||
writeTestFile(dir, 'pubspec.yaml', [
|
||||
'name: plain_dart_pkg',
|
||||
'dependencies:',
|
||||
' http: ^1.0.0'
|
||||
].join('\n'));
|
||||
const result = detectProjectType(dir);
|
||||
assert.ok(result.languages.includes('dart'));
|
||||
assert.ok(!result.frameworks.includes('flutter'), `plain Dart package wrongly detected as flutter: ${JSON.stringify(result.frameworks)}`);
|
||||
assert.strictEqual(result.primary, 'dart');
|
||||
} finally {
|
||||
cleanupDir(dir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('does not combine unrelated Flutter and SDK configuration', () => {
|
||||
const dir = createTempDir();
|
||||
try {
|
||||
writeTestFile(dir, 'pubspec.yaml', 'name: plain_dart\nflutter:\n assets: []\ndependencies:\n other:\n sdk: flutter\n');
|
||||
assert.ok(!detectProjectType(dir).frameworks.includes('flutter'));
|
||||
} finally {
|
||||
cleanupDir(dir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('detects inline Flutter SDK dependencies', () => {
|
||||
const dir = createTempDir();
|
||||
try {
|
||||
writeTestFile(dir, 'pubspec.yaml', 'name: flutter_pkg\ndependencies: {flutter: {sdk: flutter}}\n');
|
||||
assert.ok(detectProjectType(dir).frameworks.includes('flutter'));
|
||||
} finally {
|
||||
cleanupDir(dir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('malformed pubspec does not claim Flutter detection', () => {
|
||||
const dir = createTempDir();
|
||||
try {
|
||||
writeTestFile(dir, 'pubspec.yaml', 'dependencies: [\nflutter:\n sdk: flutter\n');
|
||||
assert.ok(!detectProjectType(dir).frameworks.includes('flutter'));
|
||||
} finally {
|
||||
cleanupDir(dir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
// Edge cases
|
||||
console.log('\nEdge Cases:');
|
||||
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const childProcess = require('node:child_process');
|
||||
const test = require('node:test');
|
||||
const { withFixture, write } = require('../lib/helpers/context-fixture');
|
||||
|
||||
const REPO_ROOT = path.resolve(__dirname, '../..');
|
||||
const SCRIPT_PATH = path.join(REPO_ROOT, 'scripts/ci/validate-carrier-closure.js');
|
||||
|
||||
function validator() {
|
||||
return require(SCRIPT_PATH);
|
||||
}
|
||||
|
||||
let realRun = null;
|
||||
function realRegistryRun() {
|
||||
if (!realRun) {
|
||||
const result = childProcess.spawnSync(process.execPath, [SCRIPT_PATH, '--json'], {
|
||||
cwd: REPO_ROOT, encoding: 'utf8', timeout: 120_000,
|
||||
});
|
||||
realRun = { status: result.status, stderr: result.stderr, receipt: JSON.parse(result.stdout) };
|
||||
}
|
||||
return realRun;
|
||||
}
|
||||
|
||||
function plantSkillScript(root, source) {
|
||||
write(root, 'skills/ecc-guide/scripts/tool.js', source);
|
||||
}
|
||||
|
||||
function specifiersOf(failures) {
|
||||
return failures.map(failure => failure.specifier);
|
||||
}
|
||||
|
||||
test('a planted require that leaves the carrier tree fails with its specifier', () => withFixture(root => {
|
||||
const escape = '../../../../../scripts/lib/context-profile-support';
|
||||
plantSkillScript(root, `'use strict';\n\nconst support = require('${escape}');\n\nmodule.exports = { support };\n`);
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(specifiersOf(result.failures).includes(escape));
|
||||
assert.ok(result.failures.some(failure => failure.reason === 'outside-carrier-tree'));
|
||||
assert.ok(result.failures.every(failure => failure.file.endsWith('skills/ecc-guide/scripts/tool.js')));
|
||||
}));
|
||||
|
||||
test('a relative require that stays inside the tree but is unplanned fails', () => withFixture(root => {
|
||||
plantSkillScript(root, "'use strict';\n\nrequire('../../shared/helper');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(specifiersOf(result.failures).includes('../../shared/helper'));
|
||||
assert.ok(result.failures.some(failure => failure.reason === 'unplanned-target'));
|
||||
}));
|
||||
|
||||
test('a dynamic require warns without failing', () => withFixture(root => {
|
||||
plantSkillScript(root, "'use strict';\n\nmodule.exports = require(process.env.ECC_PLUGIN_PATH);\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success');
|
||||
assert.deepEqual(result.failures, []);
|
||||
assert.ok(result.warnings.length > 0);
|
||||
assert.ok(result.warnings.every(warning => warning.kind === 'dynamic' && warning.expression === 'process.env.ECC_PLUGIN_PATH'));
|
||||
assert.ok(result.warnings.every(warning => warning.file.endsWith('skills/ecc-guide/scripts/tool.js')));
|
||||
assert.equal(result.dynamicCount, result.warnings.length);
|
||||
}));
|
||||
|
||||
test('an absolute require leaves the carrier tree and fails', () => withFixture(root => {
|
||||
plantSkillScript(root, "'use strict';\n\nrequire('/opt/shared/helper');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(specifiersOf(result.failures).includes('/opt/shared/helper'));
|
||||
assert.ok(result.failures.every(failure => failure.reason === 'outside-carrier-tree'));
|
||||
}));
|
||||
|
||||
for (const specifier of ['C:/shared/helper', 'C:\\shared\\helper', '\\\\server\\share\\helper', 'file:///opt/helper.js']) {
|
||||
test(`an absolute Windows or file URL require fails: ${specifier}`, () => withFixture(root => {
|
||||
plantSkillScript(root, `'use strict';\n\nrequire(${JSON.stringify(specifier)});\n`);
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(result.failures.length > 0);
|
||||
assert.ok(result.failures.every(failure => failure.reason === 'outside-carrier-tree'));
|
||||
assert.deepEqual(result.warnings, []);
|
||||
}));
|
||||
}
|
||||
|
||||
test('a directory require resolves through index.json', () => withFixture(root => {
|
||||
write(root, 'skills/ecc-guide/scripts/data/index.json', '{}\n');
|
||||
plantSkillScript(root, "'use strict';\n\nmodule.exports = require('./data');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success', JSON.stringify(result.failures));
|
||||
}));
|
||||
|
||||
// Node resolves import() and static imports by exact path: no extension
|
||||
// search and no directory index, even when the calling file is CommonJS.
|
||||
for (const [label, planned, source] of [
|
||||
['an extensionless import()', 'helper.js', "module.exports = import('./helper');\n"],
|
||||
['a directory import() through index.json', 'data/index.json', "module.exports = import('./data');\n"],
|
||||
['a directory import() through index.js', 'data/index.js', "module.exports = import('./data');\n"],
|
||||
['an extensionless static import', 'helper.js', "import helper from './helper';\nexport default helper;\n"],
|
||||
]) {
|
||||
test(`${label} fails even when the require form would resolve`, () => withFixture(root => {
|
||||
write(root, `skills/ecc-guide/scripts/${planned}`, planned.endsWith('.json') ? '{}\n' : 'module.exports = 1;\n');
|
||||
plantSkillScript(root, source);
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(result.failures.every(failure => failure.reason === 'unplanned-target'));
|
||||
}));
|
||||
}
|
||||
|
||||
test('an import() naming the planned file exactly resolves', () => withFixture(root => {
|
||||
write(root, 'skills/ecc-guide/scripts/helper.js', 'module.exports = 1;\n');
|
||||
plantSkillScript(root, "module.exports = import('./helper.js');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success', JSON.stringify(result.failures));
|
||||
}));
|
||||
|
||||
test('an extensionless require still resolves through the CommonJS extension search', () => withFixture(root => {
|
||||
write(root, 'skills/ecc-guide/scripts/helper.js', 'module.exports = 1;\n');
|
||||
plantSkillScript(root, "module.exports = require('./helper');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success', JSON.stringify(result.failures));
|
||||
}));
|
||||
|
||||
// An extensionless require searches only .js, .json and .node, then a
|
||||
// directory's index; .cjs and .mjs must be named explicitly.
|
||||
for (const extension of ['cjs', 'mjs']) {
|
||||
test(`an extensionless require does not resolve to a .${extension} file`, () => withFixture(root => {
|
||||
write(root, `skills/ecc-guide/scripts/helper.${extension}`, 'module.exports = 1;\n');
|
||||
plantSkillScript(root, "module.exports = require('./helper');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(result.failures.every(failure => failure.reason === 'unplanned-target'));
|
||||
}));
|
||||
}
|
||||
|
||||
test('an extensionless require resolves to a .json file', () => withFixture(root => {
|
||||
write(root, 'skills/ecc-guide/scripts/helper.json', '{}\n');
|
||||
plantSkillScript(root, "module.exports = require('./helper');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success', JSON.stringify(result.failures));
|
||||
}));
|
||||
|
||||
test('an object method named require is not a module load', () => withFixture(root => {
|
||||
plantSkillScript(root, "const records = { require: name => name };\nmodule.exports = records.require('./label');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success', JSON.stringify(result.failures));
|
||||
assert.deepEqual(result.warnings, []);
|
||||
}));
|
||||
|
||||
test('a tagged template named from is not an import', () => withFixture(root => {
|
||||
plantSkillScript(root, 'const from = parts => parts[0];\nmodule.exports = from`./helper`;\n');
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success', JSON.stringify(result.failures));
|
||||
assert.deepEqual(result.warnings, []);
|
||||
}));
|
||||
|
||||
test('a backslash-separated relative require fails: Linux and macOS read it as one filename', () => withFixture(root => {
|
||||
write(root, 'skills/ecc-guide/scripts/lib/helper.js', 'module.exports = 1;\n');
|
||||
plantSkillScript(root, "module.exports = require('./lib\\\\helper.js');\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(specifiersOf(result.failures).includes('./lib\\helper.js'));
|
||||
assert.ok(result.failures.every(failure => failure.reason === 'non-portable-separator'));
|
||||
}));
|
||||
|
||||
test('a package import warns because carriers ship no node_modules; builtins do not', () => withFixture(root => {
|
||||
plantSkillScript(root, "'use strict';\n\nconst yaml = require('js-yaml');\nconst { x } = require('@scope/pkg/deep');\n"
|
||||
+ "const fs = require('node:fs');\nconst path = require('path');\nmodule.exports = { yaml, x, fs, path };\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success');
|
||||
const packages = result.warnings.filter(warning => warning.kind === 'package').map(warning => warning.package);
|
||||
assert.deepEqual([...new Set(packages)].sort(), ['@scope/pkg', 'js-yaml']);
|
||||
assert.equal(result.packageCount, packages.length);
|
||||
}));
|
||||
|
||||
test('a require shape quoted inside a string literal is not a dependency', () => withFixture(root => {
|
||||
plantSkillScript(root, "'use strict';\n\nconst snippet = \"require('../../../../../etc/passwd')\";\n\nmodule.exports = { snippet };\n");
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'success');
|
||||
assert.deepEqual(result.warnings, []);
|
||||
}));
|
||||
|
||||
test('--json emits a machine-readable receipt and exits zero on a clean registry', () => {
|
||||
const { status, stderr, receipt } = realRegistryRun();
|
||||
assert.equal(status, 0, stderr);
|
||||
assert.equal(receipt.schemaVersion, 'ecc.carrier-closure.v1');
|
||||
assert.equal(receipt.projectionCount, receipt.layouts.length * receipt.profiles.length);
|
||||
assert.ok(Array.isArray(receipt.warnings));
|
||||
assert.ok(Number.isInteger(receipt.scriptCount));
|
||||
assert.equal(receipt.loadSmoke, 'deferred');
|
||||
});
|
||||
|
||||
test('the real registry closes: every planned script resolves inside its carrier', () => {
|
||||
const { receipt } = realRegistryRun();
|
||||
assert.equal(receipt.status, 'success');
|
||||
assert.deepEqual(receipt.failures, []);
|
||||
assert.equal(receipt.layouts.length, 5);
|
||||
assert.deepEqual(receipt.profiles, ['lean@1', 'full@1']);
|
||||
assert.ok(receipt.scriptCount > 0, 'expected planned JavaScript files to inspect');
|
||||
assert.ok(receipt.resolvedCount > 0, 'expected at least one resolved relative specifier');
|
||||
assert.ok(receipt.plannedFileCount > receipt.scriptCount);
|
||||
});
|
||||
|
||||
const SCANNER_CASES = [
|
||||
['division before a require', "const r = a / b; require('./after-division');", ['./after-division']],
|
||||
['regex holding a quote', "const re = /it's/; require('./after-regex');", ['./after-regex']],
|
||||
['regex holding a slash in a class', "const re = /[/]/g; require('./after-class');", ['./after-class']],
|
||||
['regex after return', "function f() { return /x/.test(y); } require('./after-return');", ['./after-return']],
|
||||
['division after a closing paren', "const v = (a) / 2 / 3; require('./after-paren');", ['./after-paren']],
|
||||
['object braces inside an interpolation', "const s = `a ${ {k: 1}.k } b`; require('./after-template');", ['./after-template']],
|
||||
['require text inside a template', "const s = `require('./fake')`;", []],
|
||||
['require inside an interpolation', "const s = `${require('./in-interpolation')}`;", ['./in-interpolation']],
|
||||
['block comment', "/* require('./commented') */ require('./real');", ['./real']],
|
||||
['multi-line import', "import {\n a,\n b\n} from\n './multi';", ['./multi']],
|
||||
['re-export', 'export * from "./reexport.js";', ['./reexport.js']],
|
||||
['path.join from __dirname', "require(path.join(__dirname, '..', 'lib', 'x.js'));", ['./../lib/x.js']],
|
||||
['whitespace before the call paren', "require ('./spaced'); require\n('./newline'); import ('./spaced-import');",
|
||||
['./spaced', './newline', './spaced-import']],
|
||||
['whitespace inside path.join from __dirname', "require (path.join (__dirname, 'lib', 'y.js'));", ['./lib/y.js']],
|
||||
['unicode escape in a specifier', "require('\\u002e./escaped');", ['../escaped']],
|
||||
['hex and braced escapes in a specifier', "require('\\x2e/a'); require(`\\u{2e}/b`);", ['./a', './b']],
|
||||
['escaped quote and backslash', "require('./it\\'s'); require('.\\\\win');", ["./it's", '.\\win']],
|
||||
['regex after an if condition', "if (ok) /['\"]/.test(text); require('./missing');", ['./missing']],
|
||||
['regex after a while condition', "while (more) /\"/.test(s); require('./after-while');", ['./after-while']],
|
||||
['division after a method named if', "const v = a.if(x) / 2, q = \"/\"; require('./after-method');", ['./after-method']],
|
||||
['division after a call inside a condition', "if (f(a) / 2 > 1) { q = \"/\"; } require('./after-nested');", ['./after-nested']],
|
||||
['regex after a block', "if (ok) {} /\"/.test(text); require('./after-block');", ['./after-block']],
|
||||
['division after a method named if across a line break',
|
||||
"const n = obj.\nif(x) / 2, q = \"/\"; require('./after-spaced-method');", ['./after-spaced-method']],
|
||||
['division after an optional-chained method named while',
|
||||
"const n = obj?. while(x) / 2, q = \"/\"; require('./after-optional');", ['./after-optional']],
|
||||
['a tagged template named from', 'const from = p => p[0]; const s = from`./helper`;', []],
|
||||
['a template import() is still a dependency', 'import(`./templated`);', ['./templated']],
|
||||
['member calls named require or import',
|
||||
"r.require('./label'); obj?.import('./x'); obj . require('./y'); obj.\nimport('./z'); require('./real');", ['./real']],
|
||||
];
|
||||
|
||||
test('scanner: computed arguments nested more than one level deep still warn', () => {
|
||||
const { dynamic } = validator().extractReferences(
|
||||
"require(path.join(__dirname, getName())); require(a(b(c()))); obj.require(hidden());");
|
||||
assert.deepEqual(dynamic.sort(), ['a(b(c()))', 'path.join(__dirname, getName())']);
|
||||
});
|
||||
|
||||
for (const [label, source, expected] of SCANNER_CASES) {
|
||||
test(`scanner: ${label}`, () => {
|
||||
assert.deepEqual(validator().extractReferences(source).specifiers.sort(), [...expected].sort());
|
||||
});
|
||||
}
|
||||
|
||||
test('scanner: an identifier named from before a template chunk is not an import', () => {
|
||||
assert.deepEqual(validator().extractReferences('const s = `${from} is shipped as ${to}`;'),
|
||||
{ specifiers: [], imports: [], packages: [], dynamic: [] });
|
||||
});
|
||||
|
||||
test('scanner: import() and static imports are reported apart from require', () => {
|
||||
const { specifiers, imports } = validator().extractReferences(
|
||||
"require('./a'); import('./b'); import c from './c'; import './d'; require(path.join(__dirname, 'e.js'));");
|
||||
assert.deepEqual(specifiers.sort(), ['./a', './b', './c', './d', './e.js']);
|
||||
assert.deepEqual(imports.sort(), ['./b', './c', './d']);
|
||||
});
|
||||
|
||||
test('unknown flags are rejected', () => {
|
||||
const result = childProcess.spawnSync(process.execPath, [SCRIPT_PATH, '--write'], {
|
||||
cwd: REPO_ROOT, encoding: 'utf8', timeout: 120_000,
|
||||
});
|
||||
assert.equal(result.status, 1);
|
||||
assert.match(result.stderr, /Unknown argument/);
|
||||
});
|
||||
|
||||
test('the gate is registered in the normal test workflow', () => {
|
||||
const { scripts } = require('../../package.json');
|
||||
assert.equal(scripts['carrier-closure:check'], 'node scripts/ci/validate-carrier-closure.js');
|
||||
assert.ok(scripts.test.includes('validate-carrier-closure.js'));
|
||||
});
|
||||
|
||||
for (const [label, source] of [
|
||||
['division after an object literal', "const ratio = {} / 2; require('./missing');"],
|
||||
['division after a postfix increment', "let i = 1; i++ / 2; require('./missing');"],
|
||||
['the native module.require entrypoint', "module.require('./missing');"],
|
||||
]) {
|
||||
test(`parser: ${label} retains its literal dependency`, () => withFixture(root => {
|
||||
plantSkillScript(root, source);
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(result.failures.some(failure => failure.specifier === './missing'));
|
||||
}));
|
||||
}
|
||||
|
||||
test('parser: malformed carrier source fails instead of producing an empty success', () => withFixture(root => {
|
||||
plantSkillScript(root, 'const = ;');
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(result.failures.every(failure => failure.reason === 'parse-error'));
|
||||
}));
|
||||
|
||||
test('parser: import options preserve the literal module dependency', () => {
|
||||
assert.deepEqual(validator().extractReferences("import('./x.json', { with: { type: 'json' } });").imports, ['./x.json']);
|
||||
});
|
||||
|
||||
for (const [specifier, file] of [['./helper.js?version=1#tag', 'helper.js'], ['./with%20space.js', 'with space.js']]) {
|
||||
test(`ESM URL path resolves a planned file: ${specifier}`, () => withFixture(root => {
|
||||
write(root, `skills/ecc-guide/scripts/${file}`, 'module.exports = 1;');
|
||||
plantSkillScript(root, `import(${JSON.stringify(specifier)});`);
|
||||
assert.equal(validator().validate(root).status, 'success');
|
||||
}));
|
||||
}
|
||||
|
||||
for (const specifier of ['./lib%2fhelper.js', './lib%5chelper.js', './invalid%zz.js']) {
|
||||
test(`ESM invalid URL encoding fails: ${specifier}`, () => withFixture(root => {
|
||||
plantSkillScript(root, `import(${JSON.stringify(specifier)});`);
|
||||
const result = validator().validate(root);
|
||||
assert.equal(result.status, 'failure');
|
||||
assert.ok(result.failures.every(failure => failure.reason.startsWith('invalid-url-')));
|
||||
}));
|
||||
}
|
||||
Reference in new issue
Block a user