Merge remote-tracking branch 'origin/main' into feat/optimize-biome-hooks

# Conflicts:
#	tests/hooks/hooks.test.js
#	tests/run-all.js
This commit is contained in:
Affaan Mustafa
2026-03-10 20:25:22 -07:00
29 changed files with 2705 additions and 167 deletions

View File

@@ -0,0 +1,162 @@
# Curated instincts for affaan-m/everything-claude-code
# Import with: /instinct-import .claude/homunculus/instincts/inherited/everything-claude-code-instincts.yaml
---
id: everything-claude-code-conventional-commits
trigger: "when making a commit in everything-claude-code"
confidence: 0.9
domain: git
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Conventional Commits
## Action
Use conventional commit prefixes such as `feat:`, `fix:`, `docs:`, `test:`, `chore:`, and `refactor:`.
## Evidence
- Mainline history consistently uses conventional commit subjects.
- Release and changelog automation expect readable commit categorization.
---
id: everything-claude-code-commit-length
trigger: "when writing a commit subject in everything-claude-code"
confidence: 0.8
domain: git
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Commit Length
## Action
Keep commit subjects concise and close to the repository norm of about 70 characters.
## Evidence
- Recent history clusters around ~70 characters, not ~50.
- Short, descriptive subjects read well in release notes and PR summaries.
---
id: everything-claude-code-js-file-naming
trigger: "when creating a new JavaScript or TypeScript module in everything-claude-code"
confidence: 0.85
domain: code-style
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code JS File Naming
## Action
Prefer camelCase for JavaScript and TypeScript module filenames, and keep skill or command directories in kebab-case.
## Evidence
- `scripts/` and test helpers mostly use camelCase module names.
- `skills/` and `commands/` directories use kebab-case consistently.
---
id: everything-claude-code-test-runner
trigger: "when adding or updating tests in everything-claude-code"
confidence: 0.9
domain: testing
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Test Runner
## Action
Use the repository's existing Node-based test flow: targeted `*.test.js` files first, then `node tests/run-all.js` or `npm test` for broader verification.
## Evidence
- The repo uses `tests/run-all.js` as the central test orchestrator.
- Test files follow the `*.test.js` naming pattern across hook, CI, and integration coverage.
---
id: everything-claude-code-hooks-change-set
trigger: "when modifying hooks or hook-adjacent behavior in everything-claude-code"
confidence: 0.88
domain: workflow
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Hooks Change Set
## Action
Update the hook script, its configuration, its tests, and its user-facing documentation together.
## Evidence
- Hook fixes routinely span `hooks/hooks.json`, `scripts/hooks/`, `tests/hooks/`, `tests/integration/`, and `hooks/README.md`.
- Partial hook changes are a common source of regressions and stale docs.
---
id: everything-claude-code-cross-platform-sync
trigger: "when shipping a user-visible feature across ECC surfaces"
confidence: 0.9
domain: workflow
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Cross Platform Sync
## Action
Treat the root repo as the source of truth, then mirror shipped changes to `.cursor/`, `.codex/`, `.opencode/`, and `.agents/` only where the feature actually exists.
## Evidence
- ECC maintains multiple harness-specific surfaces with overlapping but not identical files.
- The safest workflow is root-first followed by explicit parity updates.
---
id: everything-claude-code-release-sync
trigger: "when preparing a release for everything-claude-code"
confidence: 0.86
domain: workflow
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Release Sync
## Action
Keep package versions, plugin manifests, and release-facing docs synchronized before publishing.
## Evidence
- Release work spans `package.json`, `.claude-plugin/*`, `.opencode/package.json`, and release-note content.
- Version drift causes broken update paths and confusing install surfaces.
---
id: everything-claude-code-learning-curation
trigger: "when importing or evolving instincts for everything-claude-code"
confidence: 0.84
domain: workflow
source: repo-curation
source_repo: affaan-m/everything-claude-code
---
# Everything Claude Code Learning Curation
## Action
Prefer a small set of accurate instincts over bulk-generated, duplicated, or contradictory instincts.
## Evidence
- Auto-generated instinct dumps can duplicate rules, widen triggers too far, or preserve placeholder detector output.
- Curated instincts are easier to import, audit, and trust during continuous-learning workflows.

View File

@@ -0,0 +1,97 @@
# Everything Claude Code
Use this skill when working inside the `everything-claude-code` repository and you need repo-specific guidance instead of generic coding advice.
Optional companion instincts live at `.claude/homunculus/instincts/inherited/everything-claude-code-instincts.yaml` for teams using `continuous-learning-v2`.
## When to Use
Activate this skill when the task touches one or more of these areas:
- cross-platform parity across Claude Code, Cursor, Codex, and OpenCode
- hook scripts, hook docs, or hook tests
- skills, commands, agents, or rules that must stay synchronized across surfaces
- release work such as version bumps, changelog updates, or plugin metadata updates
- continuous-learning or instinct workflows inside this repository
## How It Works
### 1. Follow the repo's development contract
- Use conventional commits such as `feat:`, `fix:`, `docs:`, `test:`, `chore:`.
- Keep commit subjects concise and close to the repo norm of about 70 characters.
- Prefer camelCase for JavaScript and TypeScript module filenames.
- Use kebab-case for skill directories and command filenames.
- Keep test files on the existing `*.test.js` pattern.
### 2. Treat the root repo as the source of truth
Start from the root implementation, then mirror changes where they are intentionally shipped.
Typical mirror targets:
- `.cursor/`
- `.codex/`
- `.opencode/`
- `.agents/`
Do not assume every `.claude/` artifact needs a cross-platform copy. Only mirror files that are part of the shipped multi-platform surface.
### 3. Update hooks with tests and docs together
When changing hook behavior:
1. update `hooks/hooks.json` or the relevant script in `scripts/hooks/`
2. update matching tests in `tests/hooks/` or `tests/integration/`
3. update `hooks/README.md` if behavior or configuration changed
4. verify parity for `.cursor/hooks/` and `.opencode/plugins/` when applicable
### 4. Keep release metadata in sync
When preparing a release, verify the same version is reflected anywhere it is surfaced:
- `package.json`
- `.claude-plugin/plugin.json`
- `.claude-plugin/marketplace.json`
- `.opencode/package.json`
- release notes or changelog entries when the release process expects them
### 5. Be explicit about continuous-learning changes
If the task touches `skills/continuous-learning-v2/` or imported instincts:
- prefer accurate, low-noise instincts over auto-generated bulk output
- keep instinct files importable by `instinct-cli.py`
- remove duplicated or contradictory instincts instead of layering more guidance on top
## Examples
### Naming examples
```text
skills/continuous-learning-v2/SKILL.md
commands/update-docs.md
scripts/hooks/session-start.js
tests/hooks/hooks.test.js
```
### Commit examples
```text
fix: harden session summary extraction on Stop hook
docs: align Codex config examples with current schema
test: cover Windows formatter fallback behavior
```
### Skill update checklist
```text
1. Update the root skill or command.
2. Mirror it only where that surface is shipped.
3. Run targeted tests first, then the broader suite if behavior changed.
4. Review docs and release notes for user-visible changes.
```
### Release checklist
```text
1. Bump package and plugin versions.
2. Run npm test.
3. Verify platform-specific manifests.
4. Publish the release notes with a human-readable summary.
```

View File

@@ -39,6 +39,20 @@ Available skills:
Configure in `~/.codex/config.toml` under `[mcp_servers]`. See `.codex/config.toml` for reference configuration with GitHub, Context7, Memory, and Sequential Thinking servers.
## Multi-Agent Support
Codex now supports multi-agent workflows behind the experimental `features.multi_agent` flag.
- Enable it in `.codex/config.toml` with `[features] multi_agent = true`
- Define project-local roles under `[agents.<name>]`
- Point each role at a TOML layer under `.codex/agents/`
- Use `/agent` inside Codex CLI to inspect and steer child agents
Sample role configs in this repo:
- `.codex/agents/explorer.toml` — read-only evidence gathering
- `.codex/agents/reviewer.toml` — correctness/security review
- `.codex/agents/docs-researcher.toml` — API and release-note verification
## Key Differences from Claude Code
| Feature | Claude Code | Codex CLI |
@@ -47,7 +61,7 @@ Configure in `~/.codex/config.toml` under `[mcp_servers]`. See `.codex/config.to
| Context file | CLAUDE.md + AGENTS.md | AGENTS.md only |
| Skills | Skills loaded via plugin | `.agents/skills/` directory |
| Commands | `/slash` commands | Instruction-based |
| Agents | Subagent Task tool | Single agent model |
| Agents | Subagent Task tool | Multi-agent via `/agent` and `[agents.<name>]` roles |
| Security | Hook-based enforcement | Instruction + sandbox |
| MCP | Full support | Command-based only |

View File

@@ -0,0 +1,9 @@
model = "gpt-5.4"
model_reasoning_effort = "medium"
sandbox_mode = "read-only"
developer_instructions = """
Verify APIs, framework behavior, and release-note claims against primary documentation before changes land.
Cite the exact docs or file paths that support each claim.
Do not invent undocumented behavior.
"""

View File

@@ -0,0 +1,9 @@
model = "o4-mini"
model_reasoning_effort = "medium"
sandbox_mode = "read-only"
developer_instructions = """
Stay in exploration mode.
Trace the real execution path, cite files and symbols, and avoid proposing fixes unless the parent agent asks for them.
Prefer targeted search and file reads over broad scans.
"""

View File

@@ -0,0 +1,9 @@
model = "gpt-5.4"
model_reasoning_effort = "high"
sandbox_mode = "read-only"
developer_instructions = """
Review like an owner.
Prioritize correctness, security, behavioral regressions, and missing tests.
Lead with concrete findings and avoid style-only feedback unless it hides a real bug.
"""

View File

@@ -1,42 +1,37 @@
# Everything Claude Code (ECC) — Codex CLI Reference Configuration
#:schema https://developers.openai.com/codex/config-schema.json
# Everything Claude Code (ECC) — Codex Reference Configuration
#
# Copy this file to ~/.codex/config.toml to apply globally.
# Or keep it in your project root for project-level config.
# Copy this file to ~/.codex/config.toml for global defaults, or keep it in
# the project root as .codex/config.toml for project-local settings.
#
# Docs: https://github.com/openai/codex
# Official docs:
# - https://developers.openai.com/codex/config-reference
# - https://developers.openai.com/codex/multi-agent
# Model selection
model = "o4-mini"
model_provider = "openai"
# Permissions
[permissions]
# "untrusted" = no writes, "on-request" = ask per action, "never" = full auto
# Top-level runtime settings (current Codex schema)
approval_policy = "on-request"
# "off", "workspace-read", "workspace-write", "danger-full-access"
sandbox_mode = "workspace-write"
web_search = "live"
# Notifications (macOS)
[notify]
command = "terminal-notifier -title 'Codex ECC' -message 'Task completed!' -sound default"
on_complete = true
# External notifications receive a JSON payload on stdin.
notify = [
"terminal-notifier",
"-title", "Codex ECC",
"-message", "Task completed!",
"-sound", "default",
]
# History - persistent instructions applied to every session
[history]
# These are prepended to every conversation
persistent_instructions = """
Follow ECC principles:
1. Test-Driven Development (TDD) - write tests first, 80%+ coverage required
2. Immutability - always create new objects, never mutate
3. Security-First - validate all inputs, no hardcoded secrets
4. Conventional commits: feat|fix|refactor|docs|test|chore|perf|ci: description
5. File organization: many small files (200-400 lines, 800 max)
6. Error handling: handle at every level, never swallow errors
7. Input validation: schema-based validation at system boundaries
"""
# Prefer AGENTS.md and project-local .codex/AGENTS.md for instructions.
# model_instructions_file replaces built-in instructions instead of AGENTS.md,
# so leave it unset unless you intentionally want a single override file.
# model_instructions_file = "/absolute/path/to/instructions.md"
# MCP Servers
# Codex supports command-based MCP servers
# MCP servers
[mcp_servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
@@ -62,19 +57,41 @@ args = ["-y", "@modelcontextprotocol/server-sequential-thinking"]
# command = "npx"
# args = ["-y", "firecrawl-mcp"]
#
# [mcp_servers.railway]
# [mcp_servers.cloudflare]
# command = "npx"
# args = ["-y", "@anthropic/railway-mcp"]
# args = ["-y", "@cloudflare/mcp-server-cloudflare"]
# Features
[features]
web_search_request = true
# Codex multi-agent support is experimental as of March 2026.
multi_agent = true
# Profiles — switch with CODEX_PROFILE=<name>
# Profiles — switch with `codex -p <name>`
[profiles.strict]
approval_policy = "on-request"
sandbox_mode = "workspace-read"
sandbox_mode = "read-only"
web_search = "cached"
[profiles.yolo]
approval_policy = "never"
sandbox_mode = "workspace-write"
web_search = "live"
# Optional project-local multi-agent roles.
# Keep these commented in global config, because config_file paths are resolved
# relative to the config.toml file and must exist at load time.
#
# [agents]
# max_threads = 6
# max_depth = 1
#
# [agents.explorer]
# description = "Read-only codebase explorer for gathering evidence before changes are proposed."
# config_file = "agents/explorer.toml"
#
# [agents.reviewer]
# description = "PR reviewer focused on correctness, security, and missing tests."
# config_file = "agents/reviewer.toml"
#
# [agents.docs_researcher]
# description = "Documentation specialist that verifies APIs, framework behavior, and release notes."
# config_file = "agents/docs-researcher.toml"

View File

@@ -156,6 +156,9 @@ jobs:
with:
node-version: '20.x'
- name: Install validation dependencies
run: npm ci --ignore-scripts
- name: Validate agents
run: node scripts/ci/validate-agents.js
continue-on-error: false

View File

@@ -24,6 +24,9 @@ jobs:
with:
node-version: ${{ inputs.node-version }}
- name: Install validation dependencies
run: npm ci --ignore-scripts
- name: Validate agents
run: node scripts/ci/validate-agents.js

1
.gitignore vendored
View File

@@ -23,6 +23,7 @@ node_modules/
# Build output
dist/
coverage/
# Python
__pycache__/

View File

@@ -1,6 +1,6 @@
# Everything Claude Code (ECC) — Agent Instructions
This is a **production-ready AI coding plugin** providing 13 specialized agents, 50+ skills, 33 commands, and automated hook workflows for software development.
This is a **production-ready AI coding plugin** providing 16 specialized agents, 65+ skills, 40 commands, and automated hook workflows for software development.
## Core Principles
@@ -27,6 +27,9 @@ This is a **production-ready AI coding plugin** providing 13 specialized agents,
| go-build-resolver | Go build errors | Go build failures |
| database-reviewer | PostgreSQL/Supabase specialist | Schema design, query optimization |
| python-reviewer | Python code review | Python projects |
| chief-of-staff | Communication triage and drafts | Multi-channel email, Slack, LINE, Messenger |
| loop-operator | Autonomous loop execution | Run loops safely, monitor stalls, intervene |
| harness-optimizer | Harness config tuning | Reliability, cost, throughput |
## Agent Orchestration
@@ -36,6 +39,9 @@ Use agents proactively without user prompt:
- Bug fix or new feature → **tdd-guide**
- Architectural decision → **architect**
- Security-sensitive code → **security-reviewer**
- Multi-channel communication triage → **chief-of-staff**
- Autonomous loops / loop monitoring → **loop-operator**
- Harness config reliability and cost → **harness-optimizer**
Use parallel execution for independent operations — launch multiple agents simultaneously.
@@ -123,8 +129,8 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
```
agents/ — 13 specialized subagents
skills/ — 50+ workflow skills and domain knowledge
commands/ — 33 slash commands
skills/ — 65+ workflow skills and domain knowledge
commands/ — 40 slash commands
hooks/ — Trigger-based automations
rules/ — Always-follow guidelines (common + per-language)
scripts/ — Cross-platform Node.js utilities

View File

@@ -10,6 +10,7 @@ Thanks for wanting to contribute! This repo is a community resource for Claude C
- [Contributing Agents](#contributing-agents)
- [Contributing Hooks](#contributing-hooks)
- [Contributing Commands](#contributing-commands)
- [Cross-Harness and Translations](#cross-harness-and-translations)
- [Pull Request Process](#pull-request-process)
---
@@ -348,6 +349,29 @@ What the user receives.
---
## Cross-Harness and Translations
### Skill subsets (Codex and Cursor)
ECC ships skill subsets for other harnesses:
- **Codex:** `.agents/skills/` — skills listed in `agents/openai.yaml` are loaded by Codex.
- **Cursor:** `.cursor/skills/` — a subset of skills is bundled for Cursor.
When you **add a new skill** that should be available on Codex or Cursor:
1. Add the skill under `skills/your-skill-name/` as usual.
2. If it should be available on **Codex**, add it to `.agents/skills/` (copy the skill directory or add a reference) and ensure it is referenced in `agents/openai.yaml` if required.
3. If it should be available on **Cursor**, add it under `.cursor/skills/` per Cursor's layout.
Check existing skills in those directories for the expected structure. Keeping these subsets in sync is manual; mention in your PR if you updated them.
### Translations
Translations live under `docs/` (e.g. `docs/zh-CN`, `docs/zh-TW`, `docs/ja-JP`). If you change agents, commands, or skills that are translated, consider updating the corresponding translation files or opening an issue so maintainers or translators can update them.
---
## Pull Request Process
### 1. PR Title Format

View File

@@ -893,27 +893,29 @@ ECC provides **first-class Codex support** for both the macOS app and CLI, with
### Quick Start (Codex App + CLI)
```bash
# Copy the reference config to your home directory
cp .codex/config.toml ~/.codex/config.toml
# Run Codex CLI in the repo — AGENTS.md is auto-detected
# Run Codex CLI in the repo — AGENTS.md and .codex/ are auto-detected
codex
# Optional: copy the global-safe defaults to your home directory
cp .codex/config.toml ~/.codex/config.toml
```
Codex macOS app:
- Open this repository as your workspace.
- The root `AGENTS.md` is auto-detected.
- Optional: copy `.codex/config.toml` to `~/.codex/config.toml` for CLI/app behavior consistency.
- `.codex/config.toml` and `.codex/agents/*.toml` work best when kept project-local.
- Optional: copy `.codex/config.toml` to `~/.codex/config.toml` for global defaults; keep the multi-agent role files project-local unless you also copy `.codex/agents/`.
### What's Included
| Component | Count | Details |
|-----------|-------|---------|
| Config | 1 | `.codex/config.toml`model, permissions, MCP servers, persistent instructions |
| Config | 1 | `.codex/config.toml`top-level approvals/sandbox/web_search, MCP servers, notifications, profiles |
| AGENTS.md | 2 | Root (universal) + `.codex/AGENTS.md` (Codex-specific supplement) |
| Skills | 16 | `.agents/skills/` — SKILL.md + agents/openai.yaml per skill |
| MCP Servers | 4 | GitHub, Context7, Memory, Sequential Thinking (command-based) |
| Profiles | 2 | `strict` (read-only sandbox) and `yolo` (full auto-approve) |
| Agent Roles | 3 | `.codex/agents/` — explorer, reviewer, docs-researcher |
### Skills
@@ -940,7 +942,24 @@ Skills at `.agents/skills/` are auto-loaded by Codex:
### Key Limitation
Codex does **not yet provide Claude-style hook execution parity**. ECC enforcement there is instruction-based via `AGENTS.md` and `persistent_instructions`, plus sandbox permissions.
Codex does **not yet provide Claude-style hook execution parity**. ECC enforcement there is instruction-based via `AGENTS.md`, optional `model_instructions_file` overrides, and sandbox/approval settings.
### Multi-Agent Support
Current Codex builds support experimental multi-agent workflows.
- Enable `features.multi_agent = true` in `.codex/config.toml`
- Define roles under `[agents.<name>]`
- Point each role at a file under `.codex/agents/`
- Use `/agent` in the CLI to inspect or steer child agents
ECC ships three sample role configs:
| Role | Purpose |
|------|---------|
| `explorer` | Read-only codebase evidence gathering before edits |
| `reviewer` | Correctness, security, and missing-test review |
| `docs_researcher` | Documentation and API verification before release/docs changes |
---
@@ -1090,7 +1109,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
- **AGENTS.md** at root is the universal cross-tool file (read by all 4 tools)
- **DRY adapter pattern** lets Cursor reuse Claude Code's hook scripts without duplication
- **Skills format** (SKILL.md with YAML frontmatter) works across Claude Code, Codex, and OpenCode
- Codex's lack of hooks is compensated by `persistent_instructions` and sandbox permissions
- Codex's lack of hooks is compensated by `AGENTS.md`, optional `model_instructions_file` overrides, and sandbox permissions
---

422
TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,422 @@
# Troubleshooting Guide
Common issues and solutions for Everything Claude Code (ECC) plugin.
## Table of Contents
- [Memory & Context Issues](#memory--context-issues)
- [Agent Harness Failures](#agent-harness-failures)
- [Hook & Workflow Errors](#hook--workflow-errors)
- [Installation & Setup](#installation--setup)
- [Performance Issues](#performance-issues)
- [Common Error Messages](#common-error-messages)
- [Getting Help](#getting-help)
---
## Memory & Context Issues
### Context Window Overflow
**Symptom:** "Context too long" errors or incomplete responses
**Causes:**
- Large file uploads exceeding token limits
- Accumulated conversation history
- Multiple large tool outputs in single session
**Solutions:**
```bash
# 1. Clear conversation history and start fresh
# Use Claude Code: "New Chat" or Cmd/Ctrl+Shift+N
# 2. Reduce file size before analysis
head -n 100 large-file.log > sample.log
# 3. Use streaming for large outputs
head -n 50 large-file.txt
# 4. Split tasks into smaller chunks
# Instead of: "Analyze all 50 files"
# Use: "Analyze files in src/components/ directory"
```
### Memory Persistence Failures
**Symptom:** Agent doesn't remember previous context or observations
**Causes:**
- Disabled continuous-learning hooks
- Corrupted observation files
- Project detection failures
**Solutions:**
```bash
# Check if observations are being recorded
ls ~/.claude/homunculus/projects/*/observations.jsonl
# Find the current project's hash id
python3 - <<'PY'
import json, os
registry_path = os.path.expanduser("~/.claude/homunculus/projects.json")
with open(registry_path) as f:
registry = json.load(f)
for project_id, meta in registry.items():
if meta.get("root") == os.getcwd():
print(project_id)
break
else:
raise SystemExit("Project hash not found in ~/.claude/homunculus/projects.json")
PY
# View recent observations for that project
tail -20 ~/.claude/homunculus/projects/<project-hash>/observations.jsonl
# Back up a corrupted observations file before recreating it
mv ~/.claude/homunculus/projects/<project-hash>/observations.jsonl \
~/.claude/homunculus/projects/<project-hash>/observations.jsonl.bak.$(date +%Y%m%d-%H%M%S)
# Verify hooks are enabled
grep -r "observe" ~/.claude/settings.json
```
---
## Agent Harness Failures
### Agent Not Found
**Symptom:** "Agent not loaded" or "Unknown agent" errors
**Causes:**
- Plugin not installed correctly
- Agent path misconfiguration
- Marketplace vs manual install mismatch
**Solutions:**
```bash
# Check plugin installation
ls ~/.claude/plugins/cache/
# Verify agent exists (marketplace install)
ls ~/.claude/plugins/cache/*/agents/
# For manual install, agents should be in:
ls ~/.claude/agents/ # Custom agents only
# Reload plugin
# Claude Code → Settings → Extensions → Reload
```
### Workflow Execution Hangs
**Symptom:** Agent starts but never completes
**Causes:**
- Infinite loops in agent logic
- Blocked on user input
- Network timeout waiting for API
**Solutions:**
```bash
# 1. Check for stuck processes
ps aux | grep claude
# 2. Enable debug mode
export CLAUDE_DEBUG=1
# 3. Set shorter timeouts
export CLAUDE_TIMEOUT=30
# 4. Check network connectivity
curl -I https://api.anthropic.com
```
### Tool Use Errors
**Symptom:** "Tool execution failed" or permission denied
**Causes:**
- Missing dependencies (npm, python, etc.)
- Insufficient file permissions
- Path not found
**Solutions:**
```bash
# Verify required tools are installed
which node python3 npm git
# Fix permissions on hook scripts
chmod +x ~/.claude/plugins/cache/*/hooks/*.sh
chmod +x ~/.claude/plugins/cache/*/skills/*/hooks/*.sh
# Check PATH includes necessary binaries
echo $PATH
```
---
## Hook & Workflow Errors
### Hooks Not Firing
**Symptom:** Pre/post hooks don't execute
**Causes:**
- Hooks not registered in settings.json
- Invalid hook syntax
- Hook script not executable
**Solutions:**
```bash
# Check hooks are registered
grep -A 10 '"hooks"' ~/.claude/settings.json
# Verify hook files exist and are executable
ls -la ~/.claude/plugins/cache/*/hooks/
# Test hook manually
bash ~/.claude/plugins/cache/*/hooks/pre-bash.sh <<< '{"command":"echo test"}'
# Re-register hooks (if using plugin)
# Disable and re-enable plugin in Claude Code settings
```
### Python/Node Version Mismatches
**Symptom:** "python3 not found" or "node: command not found"
**Causes:**
- Missing Python/Node installation
- PATH not configured
- Wrong Python version (Windows)
**Solutions:**
```bash
# Install Python 3 (if missing)
# macOS: brew install python3
# Ubuntu: sudo apt install python3
# Windows: Download from python.org
# Install Node.js (if missing)
# macOS: brew install node
# Ubuntu: sudo apt install nodejs npm
# Windows: Download from nodejs.org
# Verify installations
python3 --version
node --version
npm --version
# Windows: Ensure python (not python3) works
python --version
```
### Dev Server Blocker False Positives
**Symptom:** Hook blocks legitimate commands mentioning "dev"
**Causes:**
- Heredoc content triggering pattern match
- Non-dev commands with "dev" in arguments
**Solutions:**
```bash
# This is fixed in v1.8.0+ (PR #371)
# Upgrade plugin to latest version
# Workaround: Wrap dev servers in tmux
tmux new-session -d -s dev "npm run dev"
tmux attach -t dev
# Disable hook temporarily if needed
# Edit ~/.claude/settings.json and remove pre-bash hook
```
---
## Installation & Setup
### Plugin Not Loading
**Symptom:** Plugin features unavailable after install
**Causes:**
- Marketplace cache not updated
- Claude Code version incompatibility
- Corrupted plugin files
**Solutions:**
```bash
# Inspect the plugin cache before changing it
ls -la ~/.claude/plugins/cache/
# Back up the plugin cache instead of deleting it in place
mv ~/.claude/plugins/cache ~/.claude/plugins/cache.backup.$(date +%Y%m%d-%H%M%S)
mkdir -p ~/.claude/plugins/cache
# Reinstall from marketplace
# Claude Code → Extensions → Everything Claude Code → Uninstall
# Then reinstall from marketplace
# Check Claude Code version
claude --version
# Requires Claude Code 2.0+
# Manual install (if marketplace fails)
git clone https://github.com/affaan-m/everything-claude-code.git
cp -r everything-claude-code ~/.claude/plugins/ecc
```
### Package Manager Detection Fails
**Symptom:** Wrong package manager used (npm instead of pnpm)
**Causes:**
- No lock file present
- CLAUDE_PACKAGE_MANAGER not set
- Multiple lock files confusing detection
**Solutions:**
```bash
# Set preferred package manager globally
export CLAUDE_PACKAGE_MANAGER=pnpm
# Add to ~/.bashrc or ~/.zshrc
# Or set per-project
echo '{"packageManager": "pnpm"}' > .claude/package-manager.json
# Or use package.json field
npm pkg set packageManager="pnpm@8.15.0"
# Warning: removing lock files can change installed dependency versions.
# Commit or back up the lock file first, then run a fresh install and re-run CI.
# Only do this when intentionally switching package managers.
rm package-lock.json # If using pnpm/yarn/bun
```
---
## Performance Issues
### Slow Response Times
**Symptom:** Agent takes 30+ seconds to respond
**Causes:**
- Large observation files
- Too many active hooks
- Network latency to API
**Solutions:**
```bash
# Archive large observations instead of deleting them
archive_dir="$HOME/.claude/homunculus/archive/$(date +%Y%m%d)"
mkdir -p "$archive_dir"
find ~/.claude/homunculus/projects -name "observations.jsonl" -size +10M -exec sh -c '
for file do
base=$(basename "$(dirname "$file")")
gzip -c "$file" > "'"$archive_dir"'/${base}-observations.jsonl.gz"
: > "$file"
done
' sh {} +
# Disable unused hooks temporarily
# Edit ~/.claude/settings.json
# Keep active observation files small
# Large archives should live under ~/.claude/homunculus/archive/
```
### High CPU Usage
**Symptom:** Claude Code consuming 100% CPU
**Causes:**
- Infinite observation loops
- File watching on large directories
- Memory leaks in hooks
**Solutions:**
```bash
# Check for runaway processes
top -o cpu | grep claude
# Disable continuous learning temporarily
touch ~/.claude/homunculus/disabled
# Restart Claude Code
# Cmd/Ctrl+Q then reopen
# Check observation file size
du -sh ~/.claude/homunculus/*/
```
---
## Common Error Messages
### "EACCES: permission denied"
```bash
# Fix hook permissions
find ~/.claude/plugins -name "*.sh" -exec chmod +x {} \;
# Fix observation directory permissions
chmod -R u+rwX,go+rX ~/.claude/homunculus
```
### "MODULE_NOT_FOUND"
```bash
# Install plugin dependencies
cd ~/.claude/plugins/cache/everything-claude-code
npm install
# Or for manual install
cd ~/.claude/plugins/ecc
npm install
```
### "spawn UNKNOWN"
```bash
# Windows-specific: Ensure scripts use correct line endings
# Convert CRLF to LF
find ~/.claude/plugins -name "*.sh" -exec dos2unix {} \;
# Or install dos2unix
# macOS: brew install dos2unix
# Ubuntu: sudo apt install dos2unix
```
---
## Getting Help
If you're still experiencing issues:
1. **Check GitHub Issues**: [github.com/affaan-m/everything-claude-code/issues](https://github.com/affaan-m/everything-claude-code/issues)
2. **Enable Debug Logging**:
```bash
export CLAUDE_DEBUG=1
export CLAUDE_LOG_LEVEL=debug
```
3. **Collect Diagnostic Info**:
```bash
claude --version
node --version
python3 --version
echo $CLAUDE_PACKAGE_MANAGER
ls -la ~/.claude/plugins/cache/
```
4. **Open an Issue**: Include debug logs, error messages, and diagnostic info
---
## Related Documentation
- [README.md](./README.md) - Installation and features
- [CONTRIBUTING.md](./CONTRIBUTING.md) - Development guidelines
- [docs/](./docs/) - Detailed documentation
- [examples/](./examples/) - Usage examples

View File

@@ -1,10 +1,10 @@
---
description: Extract reusable patterns from the session, self-evaluate quality before saving, and determine the right save location (Global vs Project).
description: "Extract reusable patterns from the session, self-evaluate quality before saving, and determine the right save location (Global vs Project)."
---
# /learn-eval - Extract, Evaluate, then Save
Extends `/learn` with a quality gate and save-location decision before writing any skill file.
Extends `/learn` with a quality gate, save-location decision, and knowledge-placement awareness before writing any skill file.
## What to Extract
@@ -51,36 +51,61 @@ origin: auto-extracted
[Trigger conditions]
```
5. **Self-evaluate before saving** using this rubric:
5. **Quality gate — Checklist + Holistic verdict**
| Dimension | 1 | 3 | 5 |
|-----------|---|---|---|
| Specificity | Abstract principles only, no code examples | Representative code example present | Rich examples covering all usage patterns |
| Actionability | Unclear what to do | Main steps are understandable | Immediately actionable, edge cases covered |
| Scope Fit | Too broad or too narrow | Mostly appropriate, some boundary ambiguity | Name, trigger, and content perfectly aligned |
| Non-redundancy | Nearly identical to another skill | Some overlap but unique perspective exists | Completely unique value |
| Coverage | Covers only a fraction of the target task | Main cases covered, common variants missing | Main cases, edge cases, and pitfalls covered |
### 5a. Required checklist (verify by actually reading files)
- Score each dimension 15
- If any dimension scores 12, improve the draft and re-score until all dimensions are ≥ 3
- Show the user the scores table and the final draft
Execute **all** of the following before evaluating the draft:
6. Ask user to confirm:
- Show: proposed save path + scores table + final draft
- Wait for explicit confirmation before writing
- [ ] Grep `~/.claude/skills/` and relevant project `.claude/skills/` files by keyword to check for content overlap
- [ ] Check MEMORY.md (both project and global) for overlap
- [ ] Consider whether appending to an existing skill would suffice
- [ ] Confirm this is a reusable pattern, not a one-off fix
7. Save to the determined location
### 5b. Holistic verdict
## Output Format for Step 5 (scores table)
Synthesize the checklist results and draft quality, then choose **one** of the following:
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Specificity | N/5 | ... |
| Actionability | N/5 | ... |
| Scope Fit | N/5 | ... |
| Non-redundancy | N/5 | ... |
| Coverage | N/5 | ... |
| **Total** | **N/25** | |
| Verdict | Meaning | Next Action |
|---------|---------|-------------|
| **Save** | Unique, specific, well-scoped | Proceed to Step 6 |
| **Improve then Save** | Valuable but needs refinement | List improvements → revise → re-evaluate (once) |
| **Absorb into [X]** | Should be appended to an existing skill | Show target skill and additions → Step 6 |
| **Drop** | Trivial, redundant, or too abstract | Explain reasoning and stop |
**Guideline dimensions** (informing the verdict, not scored):
- **Specificity & Actionability**: Contains code examples or commands that are immediately usable
- **Scope Fit**: Name, trigger conditions, and content are aligned and focused on a single pattern
- **Uniqueness**: Provides value not covered by existing skills (informed by checklist results)
- **Reusability**: Realistic trigger scenarios exist in future sessions
6. **Verdict-specific confirmation flow**
- **Improve then Save**: Present the required improvements + revised draft + updated checklist/verdict after one re-evaluation; if the revised verdict is **Save**, save after user confirmation, otherwise follow the new verdict
- **Save**: Present save path + checklist results + 1-line verdict rationale + full draft → save after user confirmation
- **Absorb into [X]**: Present target path + additions (diff format) + checklist results + verdict rationale → append after user confirmation
- **Drop**: Show checklist results + reasoning only (no confirmation needed)
7. Save / Absorb to the determined location
## Output Format for Step 5
```
### Checklist
- [x] skills/ grep: no overlap (or: overlap found → details)
- [x] MEMORY.md: no overlap (or: overlap found → details)
- [x] Existing skill append: new file appropriate (or: should append to [X])
- [x] Reusability: confirmed (or: one-off → Drop)
### Verdict: Save / Improve then Save / Absorb into [X] / Drop
**Rationale:** (1-2 sentences explaining the verdict)
```
## Design Rationale
This version replaces the previous 5-dimension numeric scoring rubric (Specificity, Actionability, Scope Fit, Non-redundancy, Coverage scored 1-5) with a checklist-based holistic verdict system. Modern frontier models (Opus 4.6+) have strong contextual judgment — forcing rich qualitative signals into numeric scores loses nuance and can produce misleading totals. The holistic approach lets the model weigh all factors naturally, producing more accurate save/drop decisions while the explicit checklist ensures no critical check is skipped.
## Notes
@@ -88,4 +113,4 @@ origin: auto-extracted
- Don't extract one-time issues (specific API outages, etc.)
- Focus on patterns that will save time in future sessions
- Keep skills focused — one pattern per skill
- If Coverage score is low, add related variants before saving
- When the verdict is Absorb, append to the existing skill rather than creating a new file

View File

@@ -0,0 +1,146 @@
# Architecture Improvement Recommendations
This document captures architect-level improvements for the Everything Claude Code (ECC) project. It is written from the perspective of a Claude Code coding architect aiming to improve maintainability, consistency, and long-term quality.
---
## 1. Documentation and Single Source of Truth
### 1.1 Agent / Command / Skill Count Sync
**Issue:** AGENTS.md states "13 specialized agents, 50+ skills, 33 commands" while the repo has **16 agents**, **65+ skills**, and **40 commands**. README and other docs also vary. This causes confusion for contributors and users.
**Recommendation:**
- **Single source of truth:** Derive counts (and optionally tables) from the filesystem or a small manifest. Options:
- **Option A:** Add a script (e.g. `scripts/ci/catalog.js`) that scans `agents/*.md`, `commands/*.md`, and `skills/*/SKILL.md` and outputs JSON/Markdown. CI and docs can consume this.
- **Option B:** Maintain one `docs/catalog.json` (or YAML) that lists agents, commands, and skills with metadata; scripts and docs read from it. Requires discipline to update on add/remove.
- **Short-term:** Manually sync AGENTS.md, README.md, and CLAUDE.md with actual counts and list any new agents (e.g. chief-of-staff, loop-operator, harness-optimizer) in the agent table.
**Impact:** High — affects first impression and contributor trust.
---
### 1.2 Command → Agent / Skill Map
**Issue:** There is no single machine- or human-readable map of "which command uses which agent(s) or skill(s)." This lives in README tables and individual command `.md` files, which can drift.
**Recommendation:**
- Add a **command registry** (e.g. in `docs/` or as frontmatter in command files) that lists for each command: name, description, primary agent(s), skills referenced. Can be generated from command file content or maintained by hand.
- Expose a "map" in docs (e.g. `docs/COMMAND-AGENT-MAP.md`) or in the generated catalog for discoverability and for tooling (e.g. "which commands use tdd-guide?").
**Impact:** Medium — improves discoverability and refactoring safety.
---
## 2. Testing and Quality
### 2.1 Test Discovery vs Hardcoded List
**Issue:** `tests/run-all.js` uses a **hardcoded list** of test files. New test files are not run unless someone updates `run-all.js`, so coverage can be incomplete by omission.
**Recommendation:**
- **Glob-based discovery:** Discover test files by pattern (e.g. `**/*.test.js` under `tests/`) and run them, with an optional allowlist/denylist for special cases. This makes new tests automatically part of the suite.
- Keep a single entry point (`tests/run-all.js`) that runs discovered tests and aggregates results.
**Impact:** High — prevents regression where new tests exist but are never executed.
---
### 2.2 Test Coverage Metrics
**Issue:** There is no coverage tool (e.g. nyc/c8/istanbul). The project cannot assert "80%+ coverage" for its own scripts; coverage is implicit.
**Recommendation:**
- Introduce a coverage tool for Node scripts (e.g. `c8` or `nyc`) and run it in CI. Start with a baseline (e.g. 60%) and raise over time; or at least report coverage in CI without failing so the team can see trends.
- Focus on `scripts/` (lib + hooks + ci) as the primary target; exclude one-off scripts if needed.
**Impact:** Medium — aligns the project with its own AGENTS.md guidance (80%+ coverage) and surfaces untested paths.
---
## 3. Schema and Validation
### 3.1 Use Hooks JSON Schema in CI
**Issue:** `schemas/hooks.schema.json` exists and defines the hook configuration shape, but `scripts/ci/validate-hooks.js` does **not** use it. Validation is duplicated (VALID_EVENTS, structure) and can drift from the schema.
**Recommendation:**
- Use a JSON Schema validator (e.g. `ajv`) in `validate-hooks.js` to validate `hooks/hooks.json` against `schemas/hooks.schema.json`. Keep the validator as the single source of truth for structure; retain only hook-specific checks (e.g. inline JS syntax) in the script.
- Ensures schema and validator stay in sync and allows IDE/editor validation via `$schema` in hooks.json.
**Impact:** Medium — reduces drift and improves contributor experience when editing hooks.
---
## 4. Cross-Harness and i18n
### 4.1 Skill/Agent Subset Sync (.agents/skills, .cursor/skills)
**Issue:** `.agents/skills/` (Codex) and `.cursor/skills/` are subsets of `skills/`. Adding or removing a skill in the main repo requires manually updating these subsets, which can be forgotten.
**Recommendation:**
- Document in CONTRIBUTING.md that adding a skill may require updating `.agents/skills` and `.cursor/skills` (and how to do it).
- Optionally: a CI check or script that compares `skills/` to the subsets and fails or warns if a skill is in one set but not the other when it should be (e.g. by convention or by a small manifest).
**Impact:** LowMedium — reduces cross-harness drift.
---
### 4.2 Translation Drift (docs/ zh-CN, zh-TW, ja-JP)
**Issue:** Translations in `docs/` duplicate agents, commands, skills. As the English source evolves, translations can become outdated without clear process or tooling.
**Recommendation:**
- Document a **translation process:** when to update (e.g. on release), who owns each locale, and how to detect stale content (e.g. diff file lists or key sections).
- Consider: translation status file (e.g. `docs/i18n-status.md`) or CI that checks translation file existence/timestamps and warns if English was updated more recently than a translation.
- Long-term: consider extraction/placeholder format (e.g. i18n keys) so translations reference the same structure as the English source.
**Impact:** Medium — improves experience for non-English users and reduces confusion from outdated translations.
---
## 5. Hooks and Scripts
### 5.1 Hook Runtime Consistency
**Issue:** Most hooks invoke Node scripts via `run-with-flags.js`; one path uses `run-with-flags-shell.sh` + `observe.sh`. The mixed runtime is documented but could be simplified over time.
**Recommendation:**
- Prefer Node for new hooks when possible (cross-platform, single runtime). If shell is required, document why and keep the surface small.
- Ensure `ECC_HOOK_PROFILE` and `ECC_DISABLED_HOOKS` are respected in all code paths (including shell) so behavior is consistent.
**Impact:** Low — maintains current design; improves if more hooks migrate to Node.
---
## 6. Summary Table
| Area | Improvement | Priority | Effort |
|-------------------|--------------------------------------|----------|---------|
| Doc sync | Sync AGENTS.md/README counts & table | High | Low |
| Single source | Catalog script or manifest | High | Medium |
| Test discovery | Glob-based test runner | High | Low |
| Coverage | Add c8/nyc and CI coverage | Medium | Medium |
| Hook schema in CI | Validate hooks.json via schema | Medium | Low |
| Command map | Command → agent/skill registry | Medium | Medium |
| Subset sync | Document/CI for .agents/.cursor | LowMed | LowMed |
| Translations | Process + stale detection | Medium | Medium |
| Hook runtime | Prefer Node; document shell use | Low | Low |
---
## 7. Quick Wins (Immediate)
1. **Update AGENTS.md:** Set agent count to 16; add chief-of-staff, loop-operator, harness-optimizer to the agent table; align skill/command counts with repo.
2. **Test discovery:** Change `run-all.js` to discover `**/*.test.js` under `tests/` (with optional allowlist) so new tests are always run.
3. **Wire hooks schema:** In `validate-hooks.js`, validate `hooks/hooks.json` against `schemas/hooks.schema.json` using ajv (or similar) and keep only hook-specific checks in the script.
These three can be done in one or two sessions and materially improve consistency and reliability.

61
docs/COMMAND-AGENT-MAP.md Normal file
View File

@@ -0,0 +1,61 @@
# Command → Agent / Skill Map
This document lists each slash command and the primary agent(s) or skills it invokes. Use it to discover which commands use which agents and to keep refactoring consistent.
| Command | Primary agent(s) | Notes |
|---------|------------------|--------|
| `/plan` | planner | Implementation planning before code |
| `/tdd` | tdd-guide | Test-driven development |
| `/code-review` | code-reviewer | Quality and security review |
| `/build-fix` | build-error-resolver | Fix build/type errors |
| `/e2e` | e2e-runner | Playwright E2E tests |
| `/refactor-clean` | refactor-cleaner | Dead code removal |
| `/update-docs` | doc-updater | Documentation sync |
| `/update-codemaps` | doc-updater | Codemaps / architecture docs |
| `/go-review` | go-reviewer | Go code review |
| `/go-test` | tdd-guide | Go TDD workflow |
| `/go-build` | go-build-resolver | Fix Go build errors |
| `/python-review` | python-reviewer | Python code review |
| `/harness-audit` | — | Harness scorecard (no single agent) |
| `/loop-start` | loop-operator | Start autonomous loop |
| `/loop-status` | loop-operator | Inspect loop status |
| `/quality-gate` | — | Quality pipeline (hook-like) |
| `/model-route` | — | Model recommendation (no agent) |
| `/orchestrate` | planner, tdd-guide, code-reviewer, security-reviewer, architect | Multi-agent handoff |
| `/multi-plan` | architect (Codex/Gemini prompts) | Multi-model planning |
| `/multi-execute` | architect / frontend prompts | Multi-model execution |
| `/multi-backend` | architect | Backend multi-service |
| `/multi-frontend` | architect | Frontend multi-service |
| `/multi-workflow` | architect | General multi-service |
| `/learn` | — | continuous-learning skill, instincts |
| `/learn-eval` | — | continuous-learning-v2, evaluate then save |
| `/instinct-status` | — | continuous-learning-v2 |
| `/instinct-import` | — | continuous-learning-v2 |
| `/instinct-export` | — | continuous-learning-v2 |
| `/evolve` | — | continuous-learning-v2, cluster instincts |
| `/promote` | — | continuous-learning-v2 |
| `/projects` | — | continuous-learning-v2 |
| `/skill-create` | — | skill-create-output script, git history |
| `/checkpoint` | — | verification-loop skill |
| `/verify` | — | verification-loop skill |
| `/eval` | — | eval-harness skill |
| `/test-coverage` | — | Coverage analysis |
| `/sessions` | — | Session history |
| `/setup-pm` | — | Package manager setup script |
| `/claw` | — | NanoClaw CLI (scripts/claw.js) |
| `/pm2` | — | PM2 service lifecycle |
| `/security-scan` | security-reviewer (skill) | AgentShield via security-scan skill |
## Skills referenced by commands
- **continuous-learning**, **continuous-learning-v2**: `/learn`, `/learn-eval`, `/instinct-*`, `/evolve`, `/promote`, `/projects`
- **verification-loop**: `/checkpoint`, `/verify`
- **eval-harness**: `/eval`
- **security-scan**: `/security-scan` (runs AgentShield)
- **strategic-compact**: suggested at compaction points (hooks)
## How to use this map
- **Discoverability:** Find which command triggers which agent (e.g. “use `/code-review` for code-reviewer”).
- **Refactoring:** When renaming or removing an agent, search this doc and the command files for references.
- **CI/docs:** The catalog script (`node scripts/ci/catalog.js`) outputs agent/command/skill counts; this map complements it with commandagent relationships.

907
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -83,10 +83,13 @@
"postinstall": "echo '\\n ecc-universal installed!\\n Run: npx ecc-install typescript\\n Docs: https://github.com/affaan-m/everything-claude-code\\n'",
"lint": "eslint . && markdownlint '**/*.md' --ignore node_modules",
"claw": "node scripts/claw.js",
"test": "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/validate-no-personal-paths.js && node tests/run-all.js"
"test": "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/validate-no-personal-paths.js && node tests/run-all.js",
"coverage": "c8 --all --include=\"scripts/**/*.js\" --check-coverage --lines 80 --functions 80 --branches 80 --statements 80 --reporter=text --reporter=lcov node tests/run-all.js"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"ajv": "^8.18.0",
"c8": "^10.1.2",
"eslint": "^9.39.2",
"globals": "^17.1.0",
"markdownlint-cli": "^0.47.0"

View File

@@ -9,19 +9,128 @@ paths:
> This file extends [common/coding-style.md](../common/coding-style.md) with TypeScript/JavaScript specific content.
## Types and Interfaces
Use types to make public APIs, shared models, and component props explicit, readable, and reusable.
### Public APIs
- Add parameter and return types to exported functions, shared utilities, and public class methods
- Let TypeScript infer obvious local variable types
- Extract repeated inline object shapes into named types or interfaces
```typescript
// WRONG: Exported function without explicit types
export function formatUser(user) {
return `${user.firstName} ${user.lastName}`
}
// CORRECT: Explicit types on public APIs
interface User {
firstName: string
lastName: string
}
export function formatUser(user: User): string {
return `${user.firstName} ${user.lastName}`
}
```
### Interfaces vs. Type Aliases
- Use `interface` for object shapes that may be extended or implemented
- Use `type` for unions, intersections, tuples, mapped types, and utility types
- Prefer string literal unions over `enum` unless an `enum` is required for interoperability
```typescript
interface User {
id: string
email: string
}
type UserRole = 'admin' | 'member'
type UserWithRole = User & {
role: UserRole
}
```
### Avoid `any`
- Avoid `any` in application code
- Use `unknown` for external or untrusted input, then narrow it safely
- Use generics when a value's type depends on the caller
```typescript
// WRONG: any removes type safety
function getErrorMessage(error: any) {
return error.message
}
// CORRECT: unknown forces safe narrowing
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'Unexpected error'
}
```
### React Props
- Define component props with a named `interface` or `type`
- Type callback props explicitly
- Do not use `React.FC` unless there is a specific reason to do so
```typescript
interface User {
id: string
email: string
}
interface UserCardProps {
user: User
onSelect: (id: string) => void
}
function UserCard({ user, onSelect }: UserCardProps) {
return <button onClick={() => onSelect(user.id)}>{user.email}</button>
}
```
### JavaScript Files
- In `.js` and `.jsx` files, use JSDoc when types improve clarity and a TypeScript migration is not practical
- Keep JSDoc aligned with runtime behavior
```javascript
/**
* @param {{ firstName: string, lastName: string }} user
* @returns {string}
*/
export function formatUser(user) {
return `${user.firstName} ${user.lastName}`
}
```
## Immutability
Use spread operator for immutable updates:
```typescript
interface User {
id: string
name: string
}
// WRONG: Mutation
function updateUser(user, name) {
user.name = name // MUTATION!
function updateUser(user: User, name: string): User {
user.name = name // MUTATION!
return user
}
// CORRECT: Immutability
function updateUser(user, name) {
function updateUser(user: Readonly<User>, name: string): User {
return {
...user,
name
@@ -31,31 +140,56 @@ function updateUser(user, name) {
## Error Handling
Use async/await with try-catch:
Use async/await with try-catch and narrow unknown errors safely:
```typescript
try {
const result = await riskyOperation()
return result
} catch (error) {
console.error('Operation failed:', error)
throw new Error('Detailed user-friendly message')
interface User {
id: string
email: string
}
declare function riskyOperation(userId: string): Promise<User>
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'Unexpected error'
}
const logger = {
error: (message: string, error: unknown) => {
// Replace with your production logger (for example, pino or winston).
}
}
async function loadUser(userId: string): Promise<User> {
try {
const result = await riskyOperation(userId)
return result
} catch (error: unknown) {
logger.error('Operation failed', error)
throw new Error(getErrorMessage(error))
}
}
```
## Input Validation
Use Zod for schema-based validation:
Use Zod for schema-based validation and infer types from the schema:
```typescript
import { z } from 'zod'
const schema = z.object({
const userSchema = z.object({
email: z.string().email(),
age: z.number().int().min(0).max(150)
})
const validated = schema.parse(input)
type UserInput = z.infer<typeof userSchema>
const validated: UserInput = userSchema.parse(input)
```
## Console.log

View File

@@ -1,9 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Claude Code Hooks Configuration",
"description": "Configuration for Claude Code hooks. Event types are validated at runtime and must be one of: PreToolUse, PostToolUse, PreCompact, SessionStart, SessionEnd, Stop, Notification, SubagentStop",
"description": "Configuration for Claude Code hooks. Supports current Claude Code hook events and hook action types.",
"$defs": {
"hookItem": {
"stringArray": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1
},
"commandHookItem": {
"type": "object",
"required": [
"type",
@@ -12,28 +20,17 @@
"properties": {
"type": {
"type": "string",
"enum": [
"PreToolUse",
"PostToolUse",
"PreCompact",
"SessionStart",
"SessionEnd",
"Stop",
"Notification",
"SubagentStop"
],
"description": "Hook event type that triggers this hook"
"const": "command",
"description": "Run a local command"
},
"command": {
"oneOf": [
{
"type": "string"
"type": "string",
"minLength": 1
},
{
"type": "array",
"items": {
"type": "string"
}
"$ref": "#/$defs/stringArray"
}
]
},
@@ -46,17 +43,94 @@
"minimum": 0,
"description": "Timeout in seconds for async hooks"
}
}
},
"additionalProperties": true
},
"httpHookItem": {
"type": "object",
"required": [
"type",
"url"
],
"properties": {
"type": {
"type": "string",
"const": "http"
},
"url": {
"type": "string",
"minLength": 1
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"allowedEnvVars": {
"$ref": "#/$defs/stringArray"
},
"timeout": {
"type": "number",
"minimum": 0
}
},
"additionalProperties": true
},
"promptHookItem": {
"type": "object",
"required": [
"type",
"prompt"
],
"properties": {
"type": {
"type": "string",
"enum": ["prompt", "agent"]
},
"prompt": {
"type": "string",
"minLength": 1
},
"model": {
"type": "string",
"minLength": 1
},
"timeout": {
"type": "number",
"minimum": 0
}
},
"additionalProperties": true
},
"hookItem": {
"oneOf": [
{
"$ref": "#/$defs/commandHookItem"
},
{
"$ref": "#/$defs/httpHookItem"
},
{
"$ref": "#/$defs/promptHookItem"
}
]
},
"matcherEntry": {
"type": "object",
"required": [
"matcher",
"hooks"
],
"properties": {
"matcher": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "object"
}
]
},
"hooks": {
"type": "array",
@@ -79,6 +153,28 @@
},
"hooks": {
"type": "object",
"propertyNames": {
"enum": [
"SessionStart",
"UserPromptSubmit",
"PreToolUse",
"PermissionRequest",
"PostToolUse",
"PostToolUseFailure",
"Notification",
"SubagentStart",
"Stop",
"SubagentStop",
"PreCompact",
"InstructionsLoaded",
"TeammateIdle",
"TaskCompleted",
"ConfigChange",
"WorktreeCreate",
"WorktreeRemove",
"SessionEnd"
]
},
"additionalProperties": {
"type": "array",
"items": {
@@ -98,4 +194,4 @@
}
}
]
}
}

83
scripts/ci/catalog.js Normal file
View File

@@ -0,0 +1,83 @@
#!/usr/bin/env node
/**
* Catalog agents, commands, and skills from the repo.
* Outputs JSON with counts and lists for CI/docs sync.
*
* Usage: node scripts/ci/catalog.js [--json|--md]
* Default: --json to stdout
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.join(__dirname, '../..');
const AGENTS_DIR = path.join(ROOT, 'agents');
const COMMANDS_DIR = path.join(ROOT, 'commands');
const SKILLS_DIR = path.join(ROOT, 'skills');
function listAgents() {
if (!fs.existsSync(AGENTS_DIR)) return [];
try {
return fs.readdirSync(AGENTS_DIR)
.filter(f => f.endsWith('.md'))
.map(f => f.slice(0, -3))
.sort();
} catch (error) {
throw new Error(`Failed to read agents directory (${AGENTS_DIR}): ${error.message}`);
}
}
function listCommands() {
if (!fs.existsSync(COMMANDS_DIR)) return [];
try {
return fs.readdirSync(COMMANDS_DIR)
.filter(f => f.endsWith('.md'))
.map(f => f.slice(0, -3))
.sort();
} catch (error) {
throw new Error(`Failed to read commands directory (${COMMANDS_DIR}): ${error.message}`);
}
}
function listSkills() {
if (!fs.existsSync(SKILLS_DIR)) return [];
try {
const entries = fs.readdirSync(SKILLS_DIR, { withFileTypes: true });
return entries
.filter(e => e.isDirectory() && fs.existsSync(path.join(SKILLS_DIR, e.name, 'SKILL.md')))
.map(e => e.name)
.sort();
} catch (error) {
throw new Error(`Failed to read skills directory (${SKILLS_DIR}): ${error.message}`);
}
}
function run() {
const agents = listAgents();
const commands = listCommands();
const skills = listSkills();
const catalog = {
agents: { count: agents.length, list: agents },
commands: { count: commands.length, list: commands },
skills: { count: skills.length, list: skills }
};
const format = process.argv[2] === '--md' ? 'md' : 'json';
if (format === 'md') {
console.log('# ECC Catalog (generated)\n');
console.log(`- **Agents:** ${catalog.agents.count}`);
console.log(`- **Commands:** ${catalog.commands.count}`);
console.log(`- **Skills:** ${catalog.skills.count}\n`);
console.log('## Agents\n');
catalog.agents.list.forEach(a => { console.log(`- ${a}`); });
console.log('\n## Commands\n');
catalog.commands.list.forEach(c => { console.log(`- ${c}`); });
console.log('\n## Skills\n');
catalog.skills.list.forEach(s => { console.log(`- ${s}`); });
} else {
console.log(JSON.stringify(catalog, null, 2));
}
}
run();

View File

@@ -1,14 +1,45 @@
#!/usr/bin/env node
/**
* Validate hooks.json schema
* Validate hooks.json schema and hook entry rules.
*/
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const Ajv = require('ajv');
const HOOKS_FILE = path.join(__dirname, '../../hooks/hooks.json');
const VALID_EVENTS = ['PreToolUse', 'PostToolUse', 'PreCompact', 'SessionStart', 'SessionEnd', 'Stop', 'Notification', 'SubagentStop'];
const HOOKS_SCHEMA_PATH = path.join(__dirname, '../../schemas/hooks.schema.json');
const VALID_EVENTS = [
'SessionStart',
'UserPromptSubmit',
'PreToolUse',
'PermissionRequest',
'PostToolUse',
'PostToolUseFailure',
'Notification',
'SubagentStart',
'Stop',
'SubagentStop',
'PreCompact',
'InstructionsLoaded',
'TeammateIdle',
'TaskCompleted',
'ConfigChange',
'WorktreeCreate',
'WorktreeRemove',
'SessionEnd',
];
const VALID_HOOK_TYPES = ['command', 'http', 'prompt', 'agent'];
const EVENTS_WITHOUT_MATCHER = new Set(['UserPromptSubmit', 'Notification', 'Stop', 'SubagentStop']);
function isNonEmptyString(value) {
return typeof value === 'string' && value.trim().length > 0;
}
function isNonEmptyStringArray(value) {
return Array.isArray(value) && value.length > 0 && value.every(item => isNonEmptyString(item));
}
/**
* Validate a single hook entry has required fields and valid inline JS
@@ -22,32 +53,72 @@ function validateHookEntry(hook, label) {
if (!hook.type || typeof hook.type !== 'string') {
console.error(`ERROR: ${label} missing or invalid 'type' field`);
hasErrors = true;
}
// Validate optional async and timeout fields
if ('async' in hook && typeof hook.async !== 'boolean') {
console.error(`ERROR: ${label} 'async' must be a boolean`);
} else if (!VALID_HOOK_TYPES.includes(hook.type)) {
console.error(`ERROR: ${label} has unsupported hook type '${hook.type}'`);
hasErrors = true;
}
if ('timeout' in hook && (typeof hook.timeout !== 'number' || hook.timeout < 0)) {
console.error(`ERROR: ${label} 'timeout' must be a non-negative number`);
hasErrors = true;
}
if (!hook.command || (typeof hook.command !== 'string' && !Array.isArray(hook.command)) || (typeof hook.command === 'string' && !hook.command.trim()) || (Array.isArray(hook.command) && (hook.command.length === 0 || !hook.command.every(s => typeof s === 'string' && s.length > 0)))) {
console.error(`ERROR: ${label} missing or invalid 'command' field`);
hasErrors = true;
} else if (typeof hook.command === 'string') {
// Validate inline JS syntax in node -e commands
const nodeEMatch = hook.command.match(/^node -e "(.*)"$/s);
if (nodeEMatch) {
try {
new vm.Script(nodeEMatch[1].replace(/\\\\/g, '\\').replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\t/g, '\t'));
} catch (syntaxErr) {
console.error(`ERROR: ${label} has invalid inline JS: ${syntaxErr.message}`);
hasErrors = true;
if (hook.type === 'command') {
if ('async' in hook && typeof hook.async !== 'boolean') {
console.error(`ERROR: ${label} 'async' must be a boolean`);
hasErrors = true;
}
if (!isNonEmptyString(hook.command) && !isNonEmptyStringArray(hook.command)) {
console.error(`ERROR: ${label} missing or invalid 'command' field`);
hasErrors = true;
} else if (typeof hook.command === 'string') {
const nodeEMatch = hook.command.match(/^node -e "(.*)"$/s);
if (nodeEMatch) {
try {
new vm.Script(nodeEMatch[1].replace(/\\\\/g, '\\').replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\t/g, '\t'));
} catch (syntaxErr) {
console.error(`ERROR: ${label} has invalid inline JS: ${syntaxErr.message}`);
hasErrors = true;
}
}
}
return hasErrors;
}
if ('async' in hook) {
console.error(`ERROR: ${label} 'async' is only supported for command hooks`);
hasErrors = true;
}
if (hook.type === 'http') {
if (!isNonEmptyString(hook.url)) {
console.error(`ERROR: ${label} missing or invalid 'url' field`);
hasErrors = true;
}
if ('headers' in hook && (typeof hook.headers !== 'object' || hook.headers === null || Array.isArray(hook.headers) || !Object.values(hook.headers).every(value => typeof value === 'string'))) {
console.error(`ERROR: ${label} 'headers' must be an object with string values`);
hasErrors = true;
}
if ('allowedEnvVars' in hook && (!Array.isArray(hook.allowedEnvVars) || !hook.allowedEnvVars.every(value => isNonEmptyString(value)))) {
console.error(`ERROR: ${label} 'allowedEnvVars' must be an array of strings`);
hasErrors = true;
}
return hasErrors;
}
if (!isNonEmptyString(hook.prompt)) {
console.error(`ERROR: ${label} missing or invalid 'prompt' field`);
hasErrors = true;
}
if ('model' in hook && !isNonEmptyString(hook.model)) {
console.error(`ERROR: ${label} 'model' must be a non-empty string`);
hasErrors = true;
}
return hasErrors;
@@ -67,6 +138,20 @@ function validateHooks() {
process.exit(1);
}
// Validate against JSON schema
if (fs.existsSync(HOOKS_SCHEMA_PATH)) {
const schema = JSON.parse(fs.readFileSync(HOOKS_SCHEMA_PATH, 'utf-8'));
const ajv = new Ajv({ allErrors: true });
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) {
for (const err of validate.errors) {
console.error(`ERROR: hooks.json schema: ${err.instancePath || '/'} ${err.message}`);
}
process.exit(1);
}
}
// Support both object format { hooks: {...} } and array format
const hooks = data.hooks || data;
let hasErrors = false;
@@ -94,9 +179,12 @@ function validateHooks() {
hasErrors = true;
continue;
}
if (!matcher.matcher) {
if (!('matcher' in matcher) && !EVENTS_WITHOUT_MATCHER.has(eventType)) {
console.error(`ERROR: ${eventType}[${i}] missing 'matcher' field`);
hasErrors = true;
} else if ('matcher' in matcher && typeof matcher.matcher !== 'string' && (typeof matcher.matcher !== 'object' || matcher.matcher === null)) {
console.error(`ERROR: ${eventType}[${i}] has invalid 'matcher' field`);
hasErrors = true;
}
if (!matcher.hooks || !Array.isArray(matcher.hooks)) {
console.error(`ERROR: ${eventType}[${i}] missing 'hooks' array`);
@@ -116,9 +204,12 @@ function validateHooks() {
// Array format (legacy)
for (let i = 0; i < hooks.length; i++) {
const hook = hooks[i];
if (!hook.matcher) {
if (!('matcher' in hook)) {
console.error(`ERROR: Hook ${i} missing 'matcher' field`);
hasErrors = true;
} else if (typeof hook.matcher !== 'string' && (typeof hook.matcher !== 'object' || hook.matcher === null)) {
console.error(`ERROR: Hook ${i} has invalid 'matcher' field`);
hasErrors = true;
}
if (!hook.hooks || !Array.isArray(hook.hooks)) {
console.error(`ERROR: Hook ${i} missing 'hooks' array`);

105
skills/blueprint/SKILL.md Normal file
View File

@@ -0,0 +1,105 @@
---
name: blueprint
description: >-
Turn a one-line objective into a step-by-step construction plan for
multi-session, multi-agent engineering projects. Each step has a
self-contained context brief so a fresh agent can execute it cold.
Includes adversarial review gate, dependency graph, parallel step
detection, anti-pattern catalog, and plan mutation protocol.
TRIGGER when: user requests a plan, blueprint, or roadmap for a
complex multi-PR task, or describes work that needs multiple sessions.
DO NOT TRIGGER when: task is completable in a single PR or fewer
than 3 tool calls, or user says "just do it".
origin: community
---
# Blueprint — Construction Plan Generator
Turn a one-line objective into a step-by-step construction plan that any coding agent can execute cold.
## When to Use
- Breaking a large feature into multiple PRs with clear dependency order
- Planning a refactor or migration that spans multiple sessions
- Coordinating parallel workstreams across sub-agents
- Any task where context loss between sessions would cause rework
**Do not use** for tasks completable in a single PR, fewer than 3 tool calls, or when the user says "just do it."
## How It Works
Blueprint runs a 5-phase pipeline:
1. **Research** — Pre-flight checks (git, gh auth, remote, default branch), then reads project structure, existing plans, and memory files to gather context.
2. **Design** — Breaks the objective into one-PR-sized steps (312 typical). Assigns dependency edges, parallel/serial ordering, model tier (strongest vs default), and rollback strategy per step.
3. **Draft** — Writes a self-contained Markdown plan file to `plans/`. Every step includes a context brief, task list, verification commands, and exit criteria — so a fresh agent can execute any step without reading prior steps.
4. **Review** — Delegates adversarial review to a strongest-model sub-agent (e.g., Opus) against a checklist and anti-pattern catalog. Fixes all critical findings before finalizing.
5. **Register** — Saves the plan, updates memory index, and presents the step count and parallelism summary to the user.
Blueprint detects git/gh availability automatically. With git + GitHub CLI, it generates full branch/PR/CI workflow plans. Without them, it switches to direct mode (edit-in-place, no branches).
## Examples
### Basic usage
```
/blueprint myapp "migrate database to PostgreSQL"
```
Produces `plans/myapp-migrate-database-to-postgresql.md` with steps like:
- Step 1: Add PostgreSQL driver and connection config
- Step 2: Create migration scripts for each table
- Step 3: Update repository layer to use new driver
- Step 4: Add integration tests against PostgreSQL
- Step 5: Remove old database code and config
### Multi-agent project
```
/blueprint chatbot "extract LLM providers into a plugin system"
```
Produces a plan with parallel steps where possible (e.g., "implement Anthropic plugin" and "implement OpenAI plugin" run in parallel after the plugin interface step is done), model tier assignments (strongest for the interface design step, default for implementation), and invariants verified after every step (e.g., "all existing tests pass", "no provider imports in core").
## Key Features
- **Cold-start execution** — Every step includes a self-contained context brief. No prior context needed.
- **Adversarial review gate** — Every plan is reviewed by a strongest-model sub-agent against a checklist covering completeness, dependency correctness, and anti-pattern detection.
- **Branch/PR/CI workflow** — Built into every step. Degrades gracefully to direct mode when git/gh is absent.
- **Parallel step detection** — Dependency graph identifies steps with no shared files or output dependencies.
- **Plan mutation protocol** — Steps can be split, inserted, skipped, reordered, or abandoned with formal protocols and audit trail.
- **Zero runtime risk** — Pure Markdown skill. The entire repository contains only `.md` files — no hooks, no shell scripts, no executable code, no `package.json`, no build step. Nothing runs on install or invocation beyond Claude Code's native Markdown skill loader.
## Installation
This skill ships with Everything Claude Code. No separate installation is needed when ECC is installed.
### Full ECC install
If you are working from the ECC repository checkout, verify the skill is present with:
```bash
test -f skills/blueprint/SKILL.md
```
To update later, review the ECC diff before updating:
```bash
cd /path/to/everything-claude-code
git fetch origin main
git log --oneline HEAD..origin/main # review new commits before updating
git checkout <reviewed-full-sha> # pin to a specific reviewed commit
```
### Vendored standalone install
If you are vendoring only this skill outside the full ECC install, copy the reviewed file from the ECC repository into `~/.claude/skills/blueprint/SKILL.md`. Vendored copies do not have a git remote, so update them by re-copying the file from a reviewed ECC commit rather than running `git pull`.
## Requirements
- Claude Code (for `/blueprint` slash command)
- Git + GitHub CLI (optional — enables full branch/PR/CI workflow; Blueprint detects absence and auto-switches to direct mode)
## Source
Inspired by antbotlab/blueprint — upstream project and reference design.

View File

@@ -124,7 +124,9 @@ try:
# Extract fields - Claude Code hook format
tool_name = data.get("tool_name", data.get("tool", "unknown"))
tool_input = data.get("tool_input", data.get("input", {}))
tool_output = data.get("tool_output", data.get("output", ""))
tool_output = data.get("tool_response")
if tool_output is None:
tool_output = data.get("tool_output", data.get("output", ""))
session_id = data.get("session_id", "unknown")
tool_use_id = data.get("tool_use_id", "")
cwd = data.get("cwd", "")

View File

@@ -74,7 +74,24 @@ Scanning:
### Phase 2 — Quality Evaluation
Launch a Task tool subagent (**Explore agent, model: opus**) with the full inventory and checklist.
Launch an Agent tool subagent (**general-purpose agent**) with the full inventory and checklist:
```text
Agent(
subagent_type="general-purpose",
prompt="
Evaluate the following skill inventory against the checklist.
[INVENTORY]
[CHECKLIST]
Return JSON for each skill:
{ \"verdict\": \"Keep\"|\"Improve\"|\"Update\"|\"Retire\"|\"Merge into [X]\", \"reason\": \"...\" }
"
)
```
The subagent reads each skill, applies the checklist, and returns per-skill JSON:
`{ "verdict": "Keep"|"Improve"|"Update"|"Retire"|"Merge into [X]", "reason": "..." }`

View File

@@ -1927,7 +1927,7 @@ function runTests() {
PreToolUse: [{
matcher: 'Write',
hooks: [{
type: 'intercept',
type: 'command',
command: 'echo test',
async: 'yes' // Should be boolean, not string
}]
@@ -1947,7 +1947,7 @@ function runTests() {
PostToolUse: [{
matcher: 'Edit',
hooks: [{
type: 'intercept',
type: 'command',
command: 'echo test',
timeout: -5 // Must be non-negative
}]
@@ -2105,6 +2105,31 @@ function runTests() {
cleanupTestDir(testDir);
})) passed++; else failed++;
console.log('\nRound 82b: validate-hooks (current official events and hook types):');
if (test('accepts UserPromptSubmit with omitted matcher and prompt/http/agent hooks', () => {
const testDir = createTestDir();
const hooksJson = JSON.stringify({
hooks: {
UserPromptSubmit: [
{
hooks: [
{ type: 'prompt', prompt: 'Summarize the request.' },
{ type: 'agent', prompt: 'Review for security issues.', model: 'gpt-5.4' },
{ type: 'http', url: 'https://example.com/hooks', headers: { Authorization: 'Bearer token' } }
]
}
]
}
});
const hooksFile = path.join(testDir, 'hooks.json');
fs.writeFileSync(hooksFile, hooksJson);
const result = runValidatorWithDir('validate-hooks', 'HOOKS_FILE', hooksFile);
assert.strictEqual(result.code, 0, 'Should accept current official hook event/type combinations');
cleanupTestDir(testDir);
})) passed++; else failed++;
// ── Round 83: validate-agents whitespace-only field, validate-skills empty SKILL.md ──
console.log('\nRound 83: validate-agents (whitespace-only frontmatter field value):');

View File

@@ -63,6 +63,29 @@ function runScript(scriptPath, input = '', env = {}) {
});
}
function runShellScript(scriptPath, args = [], input = '', env = {}, cwd = process.cwd()) {
return new Promise((resolve, reject) => {
const proc = spawn('bash', [scriptPath, ...args], {
cwd,
env: { ...process.env, ...env },
stdio: ['pipe', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
if (input) {
proc.stdin.write(input);
}
proc.stdin.end();
proc.stdout.on('data', data => stdout += data);
proc.stderr.on('data', data => stderr += data);
proc.on('close', code => resolve({ code, stdout, stderr }));
proc.on('error', reject);
});
}
// Create a temporary test directory
function createTestDir() {
const testDir = path.join(os.tmpdir(), `hooks-test-${Date.now()}`);
@@ -2070,11 +2093,47 @@ async function runTests() {
passed++;
else failed++;
if (
await asyncTest('matches .tsx extension for type checking', async () => {
const testDir = createTestDir();
const testFile = path.join(testDir, 'component.tsx');
fs.writeFileSync(testFile, 'const x: number = 1;');
if (await asyncTest('observe.sh falls back to legacy output fields when tool_response is null', async () => {
const homeDir = createTestDir();
const projectDir = createTestDir();
const observePath = path.join(__dirname, '..', '..', 'skills', 'continuous-learning-v2', 'hooks', 'observe.sh');
const payload = JSON.stringify({
tool_name: 'Bash',
tool_input: { command: 'echo hello' },
tool_response: null,
tool_output: 'legacy output',
session_id: 'session-123',
cwd: projectDir
});
try {
const result = await runShellScript(observePath, ['post'], payload, {
HOME: homeDir,
CLAUDE_PROJECT_DIR: projectDir
}, projectDir);
assert.strictEqual(result.code, 0, `observe.sh should exit successfully, stderr: ${result.stderr}`);
const projectsDir = path.join(homeDir, '.claude', 'homunculus', 'projects');
const projectIds = fs.readdirSync(projectsDir);
assert.strictEqual(projectIds.length, 1, 'observe.sh should create one project-scoped observation directory');
const observationsPath = path.join(projectsDir, projectIds[0], 'observations.jsonl');
const observations = fs.readFileSync(observationsPath, 'utf8').trim().split('\n').filter(Boolean);
assert.ok(observations.length > 0, 'observe.sh should append at least one observation');
const observation = JSON.parse(observations[0]);
assert.strictEqual(observation.output, 'legacy output', 'observe.sh should fall back to legacy tool_output when tool_response is null');
} finally {
cleanupTestDir(homeDir);
cleanupTestDir(projectDir);
}
})) passed++; else failed++;
if (await asyncTest('matches .tsx extension for type checking', async () => {
const testDir = createTestDir();
const testFile = path.join(testDir, 'component.tsx');
fs.writeFileSync(testFile, 'const x: number = 1;');
const stdinJson = JSON.stringify({ tool_input: { file_path: testFile } });
const result = await runScript(path.join(scriptsDir, 'post-edit-typecheck.js'), stdinJson);
@@ -2986,6 +3045,7 @@ async function runTests() {
assert.ok(!runAllSource.includes('execSync'), 'Should not use execSync');
// Verify it shows stderr
assert.ok(runAllSource.includes('stderr'), 'Should handle stderr output');
assert.ok(runAllSource.includes('result.status !== 0'), 'Should treat non-zero child exits as failures');
})
)
passed++;

View File

@@ -10,22 +10,25 @@ const path = require('path');
const fs = require('fs');
const testsDir = __dirname;
const testFiles = [
'lib/utils.test.js',
'lib/package-manager.test.js',
'lib/session-manager.test.js',
'lib/session-aliases.test.js',
'lib/project-detect.test.js',
'lib/resolve-formatter.test.js',
'hooks/hooks.test.js',
'hooks/evaluate-session.test.js',
'hooks/suggest-compact.test.js',
'integration/hooks.test.js',
'ci/validators.test.js',
'scripts/claw.test.js',
'scripts/setup-package-manager.test.js',
'scripts/skill-create-output.test.js'
];
/**
* Discover all *.test.js files under testsDir (relative paths for stable output order).
*/
function discoverTestFiles(dir, baseDir = dir, acc = []) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const e of entries) {
const full = path.join(dir, e.name);
const rel = path.relative(baseDir, full);
if (e.isDirectory()) {
discoverTestFiles(full, baseDir, acc);
} else if (e.isFile() && e.name.endsWith('.test.js')) {
acc.push(rel);
}
}
return acc.sort();
}
const testFiles = discoverTestFiles(testsDir);
const BOX_W = 58; // inner width between ║ delimiters
const boxLine = s => `${s.padEnd(BOX_W)}`;
@@ -68,6 +71,17 @@ for (const testFile of testFiles) {
if (passedMatch) totalPassed += parseInt(passedMatch[1], 10);
if (failedMatch) totalFailed += parseInt(failedMatch[1], 10);
if (result.error) {
console.log(`${testFile} failed to start: ${result.error.message}`);
totalFailed += failedMatch ? 0 : 1;
continue;
}
if (result.status !== 0) {
console.log(`${testFile} exited with status ${result.status}`);
totalFailed += failedMatch ? 0 : 1;
}
}
totalTests = totalPassed + totalFailed;