health

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Claude Code Configuration Health Audit

Claude Code 配置健康审计

Audit the current project's Claude Code setup with the six-layer framework:
CLAUDE.md → rules → skills → hooks → subagents → verifiers
The goal is to find violations and identify the misaligned layer, calibrated to project complexity.
Output language: Check in order: (1) CLAUDE.md
## Communication
rule (global takes precedence over local); (2) language of the user's recent conversation messages; (3) default English. Apply the detected language to all output including progress lines, the report, and the stop-condition question.
IMPORTANT: Before the first tool call, output a progress block in the output language:
Step 1/3: Collecting configuration data [1/10]
  · CLAUDE.md (global + local) · rules/ · settings.local.json · hooks
  · MCP servers · skills inventory + security scan
  · conversation history (up to 2 recent sessions)
使用六层框架审计当前项目的Claude Code配置:
CLAUDE.md → rules → skills → hooks → subagents → verifiers
目标是找出违规项,识别配置不一致的层级,校准适配项目复杂度。
**输出语言:**按顺序检查:(1) CLAUDE.md的
## Communication
规则(全局配置优先级高于本地);(2) 用户最近对话消息的语言;(3) 默认英语。将检测到的语言应用于所有输出,包括进度行、报告和停止条件问题。
**重要提示:**在第一次工具调用前,用检测到的输出语言输出进度块:
Step 1/3: Collecting configuration data [1/10]
  · CLAUDE.md (global + local) · rules/ · settings.local.json · hooks
  · MCP servers · skills inventory + security scan
  · conversation history (up to 2 recent sessions)

Step 0: Assess project tier

步骤0:评估项目层级

Pick tier:
TierSignalWhat's expected
Simple<500 project files, 1 contributor, no CICLAUDE.md only; 0–1 skills; no rules/; hooks optional
Standard500–5K project files, small team or CI presentCLAUDE.md + 1–2 rules files; 2–4 skills; basic hooks
Complex>5K project files, multi-contributor, multi-language, active CIFull six-layer setup required
Apply only the detected tier's requirements.
选择对应层级:
层级判断信号预期配置
简单项目文件<500个、1名贡献者、无CI仅需CLAUDE.md;0-1个skill;无rules/目录;hooks可选
标准项目文件500-5000个、小型团队或已接入CICLAUDE.md + 1-2个规则文件;2-4个skill;基础hooks
复杂项目文件>5000个、多贡献者、多语言、活跃CI需要完整的六层配置
仅应用检测到的层级对应的要求。

Step 1: Collect all data (single bash block)

步骤1:收集所有数据(单个bash代码块)

Run one block to collect data.
bash
P=$(pwd)
SETTINGS="$P/.claude/settings.local.json"

echo "[1/10] Tier metrics..."
echo "=== TIER METRICS ==="
echo "project_files: $(git -C "$P" ls-files 2>/dev/null | wc -l || find "$P" -type f -not -path "*/.git/*" -not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/build/*" | wc -l)"
echo "contributors: $(git -C "$P" log -n 500 --format='%ae' 2>/dev/null | sort -u | wc -l)"
echo "ci_workflows:  $(ls "$P/.github/workflows/"*.yml "$P/.github/workflows/"*.yaml 2>/dev/null | wc -l)"
echo "skills:        $(find "$P/.claude/skills" -name "SKILL.md" 2>/dev/null | grep -v '/health/SKILL.md' | wc -l)"
echo "claude_md_lines: $(wc -l < "$P/CLAUDE.md" 2>/dev/null)"

echo "[2/10] CLAUDE.md (global + local)..."
echo "=== CLAUDE.md (global) ===" ; cat ~/.claude/CLAUDE.md 2>/dev/null || echo "(none)"
echo "=== CLAUDE.md (local) ===" ; cat "$P/CLAUDE.md" 2>/dev/null || echo "(none)"

echo "[3/10] Settings, hooks, MCP..."
echo "=== settings.local.json ===" ; cat "$SETTINGS" 2>/dev/null || echo "(none)"

echo "[4/10] Rules + skill descriptions..."
echo "=== rules/ ===" ; find "$P/.claude/rules" -name "*.md" 2>/dev/null | while IFS= read -r f; do echo "--- $f ---"; cat "$f"; done
echo "=== skill descriptions ===" ; { [ -d "$P/.claude/skills" ] && grep -r "^description:" "$P/.claude/skills" 2>/dev/null; grep -r "^description:" ~/.claude/skills 2>/dev/null; } | sort -u

echo "[5/10] Context budget estimate..."
echo "=== STARTUP CONTEXT ESTIMATE ==="
echo "global_claude_words: $(wc -w < ~/.claude/CLAUDE.md 2>/dev/null | tr -d ' ' || echo 0)"
echo "local_claude_words: $(wc -w < "$P/CLAUDE.md" 2>/dev/null | tr -d ' ' || echo 0)"
echo "rules_words: $(find "$P/.claude/rules" -name "*.md" 2>/dev/null | while IFS= read -r f; do cat "$f"; done | wc -w | tr -d ' ')"
echo "skill_desc_words: $({ [ -d "$P/.claude/skills" ] && grep -r "^description:" "$P/.claude/skills" 2>/dev/null; grep -r "^description:" ~/.claude/skills 2>/dev/null; } | wc -w | tr -d ' ')"
python3 -c "
import json, sys
try:
    d = json.load(open('$SETTINGS'))
except Exception as e:
    msg = '(unavailable: settings.local.json missing or malformed)'
    print('=== hooks ==='); print(msg)
    print('=== MCP ==='); print(msg)
    print('=== MCP FILESYSTEM ==='); print(msg)
    print('=== allowedTools count ==='); print(msg)
    sys.exit(0)

print('=== hooks ===')
print(json.dumps(d.get('hooks', {}), indent=2))

print('=== MCP ===')
s = d.get('mcpServers', d.get('enabledMcpjsonServers', {}))
names = list(s.keys()) if isinstance(s, dict) else list(s)
n = len(names)
print(f'servers({n}):', ', '.join(names))
est = n * 25 * 200
print(f'est_tokens: ~{est} ({round(est/2000)}% of 200K)')

print('=== MCP FILESYSTEM ===')
if isinstance(s, list):
    print('filesystem_present: (array format -- check .mcp.json)')
    print('allowedDirectories: (not detectable)')
else:
    fs = s.get('filesystem') if isinstance(s, dict) else None; a = []
    if isinstance(fs, dict):
        a = fs.get('allowedDirectories') or (fs.get('config', {}).get('allowedDirectories') if isinstance(fs.get('config'), dict) else [])
        if not a and isinstance(fs.get('args'), list):
            args = fs['args']
            for i, v in enumerate(args):
                if v in ('--allowed-directories', '--allowedDirectories') and i+1 < len(args): a = [args[i+1]]; break
            if not a: a = [v for v in args if v.startswith('/') or (v.startswith('~') and len(v) > 1)]
    print('filesystem_present:', 'yes' if fs else 'no')
    print('allowedDirectories:', a or '(missing or not detected)')

print('=== allowedTools count ===')
print(len(d.get('permissions', {}).get('allow', [])))
" 2>/dev/null || echo "(unavailable)"
echo "[6/10] Nested CLAUDE.md + gitignore..."
echo "=== NESTED CLAUDE.md ===" ; find "$P" -maxdepth 4 -name "CLAUDE.md" -not -path "$P/CLAUDE.md" -not -path "*/.git/*" -not -path "*/node_modules/*" 2>/dev/null || echo "(none)"
echo "=== GITIGNORE ==="
_GITIGNORE_HIT=$(git -C "$P" check-ignore -v .claude/settings.local.json 2>/dev/null || true)
if [ -n "$_GITIGNORE_HIT" ]; then
  _GITIGNORE_SOURCE=${_GITIGNORE_HIT%%:*}
  case "$_GITIGNORE_SOURCE" in
    .gitignore|.claude/.gitignore)
      echo "settings.local.json: gitignored"
      ;;
    *)
      echo "settings.local.json: ignored only by non-project rule ($_GITIGNORE_SOURCE) -- add a repo-local ignore rule"
      ;;
  esac
else
  echo "settings.local.json: NOT gitignored -- risk of committing tokens/credentials"
fi
echo "[7/10] HANDOFF.md + MEMORY.md..."
echo "=== HANDOFF.md ===" ; cat "$P/HANDOFF.md" 2>/dev/null || echo "(none)"
echo "=== MEMORY.md ===" ; cat "$HOME/.claude/projects/-$(pwd | sed 's|[/_]|-|g; s|^-||')/memory/MEMORY.md" 2>/dev/null | head -50 || echo "(none)"

echo "[8/10] Conversation extract (up to 2 recent sessions)..."
echo "=== CONVERSATION FILES ==="
PROJECT_PATH=$(pwd | sed 's|[/_]|-|g; s|^-||')
CONVO_DIR=~/.claude/projects/-${PROJECT_PATH}
ls -lhS "$CONVO_DIR"/*.jsonl 2>/dev/null | head -10

echo "=== CONVERSATION EXTRACT (up to 2 most recent, confidence improves with more files) ==="
运行以下代码块收集数据。
bash
P=$(pwd)
SETTINGS="$P/.claude/settings.local.json"

echo "[1/10] Tier metrics..."
echo "=== TIER METRICS ==="
echo "project_files: $(git -C "$P" ls-files 2>/dev/null | wc -l || find "$P" -type f -not -path "*/.git/*" -not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/build/*" | wc -l)"
echo "contributors: $(git -C "$P" log -n 500 --format='%ae' 2>/dev/null | sort -u | wc -l)"
echo "ci_workflows:  $(ls "$P/.github/workflows/"*.yml "$P/.github/workflows/"*.yaml 2>/dev/null | wc -l)"
echo "skills:        $(find "$P/.claude/skills" -name "SKILL.md" 2>/dev/null | grep -v '/health/SKILL.md' | wc -l)"
echo "claude_md_lines: $(wc -l < "$P/CLAUDE.md" 2>/dev/null)"

echo "[2/10] CLAUDE.md (global + local)..."
echo "=== CLAUDE.md (global) ===" ; cat ~/.claude/CLAUDE.md 2>/dev/null || echo "(none)"
echo "=== CLAUDE.md (local) ===" ; cat "$P/CLAUDE.md" 2>/dev/null || echo "(none)"

echo "[3/10] Settings, hooks, MCP..."
echo "=== settings.local.json ===" ; cat "$SETTINGS" 2>/dev/null || echo "(none)"

echo "[4/10] Rules + skill descriptions..."
echo "=== rules/ ===" ; find "$P/.claude/rules" -name "*.md" 2>/dev/null | while IFS= read -r f; do echo "--- $f ---"; cat "$f"; done
echo "=== skill descriptions ===" ; { [ -d "$P/.claude/skills" ] && grep -r "^description:" "$P/.claude/skills" 2>/dev/null; grep -r "^description:" ~/.claude/skills 2>/dev/null; } | sort -u

echo "[5/10] Context budget estimate..."
echo "=== STARTUP CONTEXT ESTIMATE ==="
echo "global_claude_words: $(wc -w < ~/.claude/CLAUDE.md 2>/dev/null | tr -d ' ' || echo 0)"
echo "local_claude_words: $(wc -w < "$P/CLAUDE.md" 2>/dev/null | tr -d ' ' || echo 0)"
echo "rules_words: $(find "$P/.claude/rules" -name "*.md" 2>/dev/null | while IFS= read -r f; do cat "$f"; done | wc -w | tr -d ' ')"
echo "skill_desc_words: $({ [ -d "$P/.claude/skills" ] && grep -r "^description:" "$P/.claude/skills" 2>/dev/null; grep -r "^description:" ~/.claude/skills 2>/dev/null; } | wc -w | tr -d ' ')"
python3 -c "
import json, sys
try:
    d = json.load(open('$SETTINGS'))
except Exception as e:
    msg = '(unavailable: settings.local.json missing or malformed)'
    print('=== hooks ==='); print(msg)
    print('=== MCP ==='); print(msg)
    print('=== MCP FILESYSTEM ==='); print(msg)
    print('=== allowedTools count ==='); print(msg)
    sys.exit(0)

print('=== hooks ===')
print(json.dumps(d.get('hooks', {}), indent=2))

print('=== MCP ===')
s = d.get('mcpServers', d.get('enabledMcpjsonServers', {}))
names = list(s.keys()) if isinstance(s, dict) else list(s)
n = len(names)
print(f'servers({n}):', ', '.join(names))
est = n * 25 * 200
print(f'est_tokens: ~{est} ({round(est/2000)}% of 200K)')

print('=== MCP FILESYSTEM ===')
if isinstance(s, list):
    print('filesystem_present: (array format -- check .mcp.json)')
    print('allowedDirectories: (not detectable)')
else:
    fs = s.get('filesystem') if isinstance(s, dict) else None; a = []
    if isinstance(fs, dict):
        a = fs.get('allowedDirectories') or (fs.get('config', {}).get('allowedDirectories') if isinstance(fs.get('config'), dict) else [])
        if not a and isinstance(fs.get('args'), list):
            args = fs['args']
            for i, v in enumerate(args):
                if v in ('--allowed-directories', '--allowedDirectories') and i+1 < len(args): a = [args[i+1]]; break
            if not a: a = [v for v in args if v.startswith('/') or (v.startswith('~') and len(v) > 1)]
    print('filesystem_present:', 'yes' if fs else 'no')
    print('allowedDirectories:', a or '(missing or not detected)')

print('=== allowedTools count ===')
print(len(d.get('permissions', {}).get('allow', [])))
" 2>/dev/null || echo "(unavailable)"
echo "[6/10] Nested CLAUDE.md + gitignore..."
echo "=== NESTED CLAUDE.md ===" ; find "$P" -maxdepth 4 -name "CLAUDE.md" -not -path "$P/CLAUDE.md" -not -path "*/.git/*" -not -path "*/node_modules/*" 2>/dev/null || echo "(none)"
echo "=== GITIGNORE ==="
_GITIGNORE_HIT=$(git -C "$P" check-ignore -v .claude/settings.local.json 2>/dev/null || true)
if [ -n "$_GITIGNORE_HIT" ]; then
  _GITIGNORE_SOURCE=${_GITIGNORE_HIT%%:*}
  case "$_GITIGNORE_SOURCE" in
    .gitignore|.claude/.gitignore)
      echo "settings.local.json: gitignored"
      ;;
    *)
      echo "settings.local.json: ignored only by non-project rule ($_GITIGNORE_SOURCE) -- add a repo-local ignore rule"
      ;;
  esac
else
  echo "settings.local.json: NOT gitignored -- risk of committing tokens/credentials"
fi
echo "[7/10] HANDOFF.md + MEMORY.md..."
echo "=== HANDOFF.md ===" ; cat "$P/HANDOFF.md" 2>/dev/null || echo "(none)"
echo "=== MEMORY.md ===" ; cat "$HOME/.claude/projects/-$(pwd | sed 's|[/_]|-|g; s|^-||')/memory/MEMORY.md" 2>/dev/null | head -50 || echo "(none)"

echo "[8/10] Conversation extract (up to 2 recent sessions)..."
echo "=== CONVERSATION FILES ==="
PROJECT_PATH=$(pwd | sed 's|[/_]|-|g; s|^-||')
CONVO_DIR=~/.claude/projects/-${PROJECT_PATH}
ls -lhS "$CONVO_DIR"/*.jsonl 2>/dev/null | head -10

echo "=== CONVERSATION EXTRACT (up to 2 most recent, confidence improves with more files) ==="

Skip the active session, it may still be incomplete.

Skip the active session, it may still be incomplete.

_PREV_FILES=$(ls -t "$CONVO_DIR"/*.jsonl 2>/dev/null | tail -n +2 | head -2) if [ -n "$_PREV_FILES" ]; then echo "$_PREV_FILES" | while IFS= read -r F; do [ -f "$F" ] || continue echo "--- file: $F ---" head -c 500K "$F" | jq -r ' if .type == "user" then "USER: " + ((.message.content // "") | if type == "array" then map(select(.type == "text") | .text) | join(" ") else . end) elif .type == "assistant" then "ASSISTANT: " + ((.message.content // []) | map(select(.type == "text") | .text) | join("\n")) else empty end ' 2>/dev/null | grep -v "^ASSISTANT: $" | head -150 || echo "(unavailable: jq not installed or parse error)" done else echo "(no conversation files)" fi
echo "=== MCP ACCESS DENIALS ===" ls -t "$CONVO_DIR"/*.jsonl 2>/dev/null | head -5 | while IFS= read -r F; do head -c 1M "$F" | grep -Em 2 'Access denied - path outside allowed directories|tool-results/.+ not in ' 2>/dev/null done | head -20
_PREV_FILES=$(ls -t "$CONVO_DIR"/*.jsonl 2>/dev/null | tail -n +2 | head -2) if [ -n "$_PREV_FILES" ]; then echo "$_PREV_FILES" | while IFS= read -r F; do [ -f "$F" ] || continue echo "--- file: $F ---" head -c 500K "$F" | jq -r ' if .type == "user" then "USER: " + ((.message.content // "") | if type == "array" then map(select(.type == "text") | .text) | join(" ") else . end) elif .type == "assistant" then "ASSISTANT: " + ((.message.content // []) | map(select(.type == "text") | .text) | join("\n")) else empty end ' 2>/dev/null | grep -v "^ASSISTANT: $" | head -150 || echo "(unavailable: jq not installed or parse error)" done else echo "(no conversation files)" fi
echo "=== MCP ACCESS DENIALS ===" ls -t "$CONVO_DIR"/*.jsonl 2>/dev/null | head -5 | while IFS= read -r F; do head -c 1M "$F" | grep -Em 2 'Access denied - path outside allowed directories|tool-results/.+ not in ' 2>/dev/null done | head -20

--- Skill scan ---

--- Skill scan ---

Exclude self by frontmatter name, stable across install paths.

Exclude self by frontmatter name, stable across install paths.

SELF_SKILL=$( (grep -rl '^name: health$' "$P/.claude/skills" "$HOME/.claude/skills" 2>/dev/null || true) | grep 'SKILL.md' | head -1) [ -z "$SELF_SKILL" ] && SELF_SKILL="health/SKILL.md"
echo "[9/10] Skill inventory + frontmatter + provenance..." echo "=== SKILL INVENTORY ===" for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find -L "$DIR" -maxdepth 4 -name "SKILL.md" 2>/dev/null | grep -v "$SELF_SKILL" | while IFS= read -r f; do WORDS=$(wc -w < "$f" | tr -d ' ') IS_LINK="no"; LINK_TARGET="" SKILL_DIR=$(dirname "$f") if [ -L "$SKILL_DIR" ]; then IS_LINK="yes"; LINK_TARGET=$(readlink -f "$SKILL_DIR") fi echo "path=$f words=$WORDS symlink=$IS_LINK target=$LINK_TARGET" done done
echo "=== SKILL FRONTMATTER ===" for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find -L "$DIR" -maxdepth 4 -name "SKILL.md" 2>/dev/null | grep -v "$SELF_SKILL" | while IFS= read -r f; do if head -1 "$f" | grep -q '^---'; then echo "frontmatter=yes path=$f" sed -n '2,/^---$/p' "$f" | head -10 else echo "frontmatter=MISSING path=$f" fi done done
echo "=== SKILL SYMLINK PROVENANCE ===" for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find "$DIR" -maxdepth 1 -type l 2>/dev/null | while IFS= read -r link; do TARGET=$(readlink -f "$link") echo "link=$(basename "$link") target=$TARGET" if [ -d "$TARGET/.git" ]; then REMOTE=$(git -C "$TARGET" remote get-url origin 2>/dev/null || echo "unknown") COMMIT=$(git -C "$TARGET" rev-parse --short HEAD 2>/dev/null || echo "unknown") echo " git_remote=$REMOTE commit=$COMMIT" fi done done
echo "[10/10] Skill content sample + security scan..." echo "=== SKILL FULL CONTENT (sample: up to 3 skills, 60 lines each) ===" { for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find -L "$DIR" -maxdepth 4 -name "SKILL.md" 2>/dev/null | grep -v "$SELF_SKILL" done } | head -3 | while IFS= read -r f; do echo "--- FULL: $f ---" head -60 "$f" done
undefined
SELF_SKILL=$( (grep -rl '^name: health$' "$P/.claude/skills" "$HOME/.claude/skills" 2>/dev/null || true) | grep 'SKILL.md' | head -1) [ -z "$SELF_SKILL" ] && SELF_SKILL="health/SKILL.md"
echo "[9/10] Skill inventory + frontmatter + provenance..." echo "=== SKILL INVENTORY ===" for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find -L "$DIR" -maxdepth 4 -name "SKILL.md" 2>/dev/null | grep -v "$SELF_SKILL" | while IFS= read -r f; do WORDS=$(wc -w < "$f" | tr -d ' ') IS_LINK="no"; LINK_TARGET="" SKILL_DIR=$(dirname "$f") if [ -L "$SKILL_DIR" ]; then IS_LINK="yes"; LINK_TARGET=$(readlink -f "$SKILL_DIR") fi echo "path=$f words=$WORDS symlink=$IS_LINK target=$LINK_TARGET" done done
echo "=== SKILL FRONTMATTER ===" for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find -L "$DIR" -maxdepth 4 -name "SKILL.md" 2>/dev/null | grep -v "$SELF_SKILL" | while IFS= read -r f; do if head -1 "$f" | grep -q '^---'; then echo "frontmatter=yes path=$f" sed -n '2,/^---$/p' "$f" | head -10 else echo "frontmatter=MISSING path=$f" fi done done
echo "=== SKILL SYMLINK PROVENANCE ===" for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find "$DIR" -maxdepth 1 -type l 2>/dev/null | while IFS= read -r link; do TARGET=$(readlink -f "$link") echo "link=$(basename "$link") target=$TARGET" if [ -d "$TARGET/.git" ]; then REMOTE=$(git -C "$TARGET" remote get-url origin 2>/dev/null || echo "unknown") COMMIT=$(git -C "$TARGET" rev-parse --short HEAD 2>/dev/null || echo "unknown") echo " git_remote=$REMOTE commit=$COMMIT" fi done done
echo "[10/10] Skill content sample + security scan..." echo "=== SKILL FULL CONTENT (sample: up to 3 skills, 60 lines each) ===" { for DIR in "$P/.claude/skills" "$HOME/.claude/skills"; do [ -d "$DIR" ] || continue find -L "$DIR" -maxdepth 4 -name "SKILL.md" 2>/dev/null | grep -v "$SELF_SKILL" done } | head -3 | while IFS= read -r f; do echo "--- FULL: $f ---" head -60 "$f" done
undefined

Gotchas

注意事项

Before interpreting Step 1 output, check these known failure modes.
Data collection silent failures
  • jq
    not installed: conversation extraction prints
    (unavailable: jq not installed or parse error)
    . BEHAVIOR section will be empty -- treat as [INSUFFICIENT DATA], not a finding.
  • python3
    not on PATH: all MCP/hooks/allowedTools sections print
    (unavailable)
    . Do not flag those areas when the data source itself failed.
  • settings.local.json
    absent: hooks, MCP, and allowedTools all show
    (unavailable)
    . Normal for projects using global settings only -- not a misconfiguration.
MEMORY.md path construction
  • Path built with
    sed 's|[/_]|-|g'
    on
    pwd
    . Unusual characters produce the wrong project key. If MEMORY.md shows
    (none)
    but the user mentions prior sessions, verify the path manually before flagging as [!].
Conversation extract scope
  • Only the 2 most recent
    .jsonl
    files are sampled, skipping the active session. Findings from fewer than 2 files carry low signal, always tag [LOW CONFIDENCE].
MCP token estimate
  • Assumes ~25 tools/server and ~200 tokens/tool. Servers with many or few tools cause large over/under-estimates. Treat as directional, not precise.
Tier misclassification edge cases
  • The bash block excludes
    node_modules/
    ,
    dist/
    , and
    build/
    , but not all generators. Monorepos with
    .next/
    ,
    __pycache__/
    , or
    .turbo/
    output can inflate the file count and trigger COMPLEX tier falsely. Recheck manually if the tier feels wrong.
在解读步骤1的输出前,检查以下已知故障模式。
数据收集静默故障
  • 未安装
    jq
    :对话提取会输出
    (unavailable: jq not installed or parse error)
    。行为部分会为空,判定为[数据不足],不视为问题项。
  • 环境变量PATH中没有
    python3
    :所有MCP/hooks/allowedTools部分会输出
    (unavailable)
    。数据源本身故障时,不要标记这些区域存在问题。
  • 不存在
    settings.local.json
    :hooks、MCP和allowedTools都会显示
    (unavailable)
    。这对于仅使用全局配置的项目是正常的,不属于配置错误。
MEMORY.md路径构造问题
  • 路径是对
    pwd
    执行
    sed 's|[/_]|-|g'
    构建的。特殊字符会导致生成错误的项目密钥。如果MEMORY.md显示
    (none)
    但用户提到有之前的会话,在标记为问题前请手动验证路径。
对话提取范围限制
  • 仅采样最近2个
    .jsonl
    文件,跳过活跃会话。少于2个文件的问题结果信号强度低,始终标记为[低置信度]。
MCP token估算偏差
  • 假设每个服务器约有25个工具,每个工具约消耗200个token。工具数量过多或过少的服务器会导致大幅高估/低估。仅作为参考值,不视为精确数据。
层级误判边缘情况
  • bash代码块排除了
    node_modules/
    dist/
    build/
    目录,但没有覆盖所有生成目录。包含
    .next/
    __pycache__/
    .turbo/
    输出的 monorepo 可能会虚高文件计数,错误触发复杂层级。如果层级判定不符合预期,请手动复核。

Step 2: Analyze with tier-adjusted depth

步骤2:根据层级调整深度进行分析

After Step 1 completes, output a data summary line, then the tier line, then the step indicator:
Data collected: {X} CLAUDE.md words · {Y} rules words · {Z} skills found · {N} conversation files sampled
Tier: {SIMPLE/STANDARD/COMPLEX} -- {file_count} files · {contributor_count} contributors · CI: {present/absent}
Step 2/3: {SIMPLE: "Analyzing locally" | STANDARD/COMPLEX: "Launching parallel analysis agents"}
SIMPLE: output "Analyzing locally" above. Do not launch subagents. Analyze from Step 1, prioritize core config checks, skip conversation-heavy cross-validation unless evidence is obvious.
STANDARD/COMPLEX: output "Launching parallel analysis agents" above, then list coverage with check counts:
  · Agent 1: CLAUDE.md (6 checks) · rules (2) · skills (5) · MCP (3) · security (6)
  · Agent 2: hooks (5 checks) · allowedTools (2) · behavior (6) · three-layer defense (3)
Launch two subagents in parallel. Paste all data inline -- do not pass file paths. Before pasting, replace any credential values (API keys, tokens, passwords) with
[REDACTED]
; paste the structural data only.
Fallback: If either subagent fails (API error, timeout, or empty result), do not abort. Analyze that layer locally from Step 1 data instead and note "(analyzed locally -- subagent unavailable)" in the affected section of the report.
步骤1完成后,输出数据汇总行,然后输出层级行,再输出步骤指示器:
Data collected: {X} CLAUDE.md words · {Y} rules words · {Z} skills found · {N} conversation files sampled
Tier: {SIMPLE/STANDARD/COMPLEX} -- {file_count} files · {contributor_count} contributors · CI: {present/absent}
Step 2/3: {SIMPLE: "Analyzing locally" | STANDARD/COMPLEX: "Launching parallel analysis agents"}
简单层级:在上述位置输出"Analyzing locally"。不要启动subagent。基于步骤1的数据进行分析,优先检查核心配置,除非有明显证据,否则跳过依赖对话数据的交叉验证。
标准/复杂层级:在上述位置输出"Launching parallel analysis agents",然后列出覆盖范围和检查项数量:
  · Agent 1: CLAUDE.md (6 checks) · rules (2) · skills (5) · MCP (3) · security (6)
  · Agent 2: hooks (5 checks) · allowedTools (2) · behavior (6) · three-layer defense (3)
并行启动两个subagent。将所有数据内联粘贴,不要传递文件路径。粘贴前,将所有凭证值(API密钥、token、密码)替换为
[REDACTED]
,仅粘贴结构化数据。
**降级方案:**如果任意subagent失败(API错误、超时或结果为空),不要中止。改用步骤1的数据本地分析对应层级,并在报告的受影响部分注明"(本地分析 -- subagent不可用)"。

Agent 1 -- Context + Security Audit (no conversation needed)

Agent 1 -- 上下文 + 安全审计(无需对话数据)

Read
agents/agent1-context.md
from this skill's directory. It specifies which Step 1 sections to paste and the full audit checklist.
读取本skill目录下的
agents/agent1-context.md
文件,其中指定了需要粘贴的步骤1区块和完整审计检查清单。

Agent 2 -- Control + Behavior Audit (uses conversation evidence)

Agent 2 -- 控制 + 行为审计(使用对话证据)

Read
agents/agent2-control.md
from this skill's directory. It specifies which Step 1 sections to paste and the full audit checklist.
读取本skill目录下的
agents/agent2-control.md
文件,其中指定了需要粘贴的步骤1区块和完整审计检查清单。

Step 3: Synthesize and present

步骤3:汇总并展示结果

Before writing the report, output a progress line in the output language:
Step 3/3: Synthesizing report
Aggregate the local analysis and any agent outputs into one report:

Health Report: {project} ({tier} tier, {file_count} files)
编写报告前,用检测到的输出语言输出进度行:
Step 3/3: Synthesizing report
将本地分析结果和所有agent的输出汇总为一份报告:

健康报告:{项目名}({层级} 层级,{文件数} 个文件)

[PASS] Passing

[PASS] 已通过检查

Render a compact table of checks that passed. Include only checks relevant to the detected tier. Limit to 5 rows. Omit rows for checks that have findings.
CheckDetail
settings.local.json gitignoredok
No nested CLAUDE.mdok
Skill security scanno flags
以紧凑表格展示通过的检查项,仅包含与检测到的层级相关的检查,最多5行。跳过存在问题的检查项行。
检查项详情
settings.local.json已加入gitignore正常
无嵌套CLAUDE.md正常
Skill安全扫描无风险标记

[!] Critical -- fix now

[!] 严重问题 -- 立即修复

Rules violated, missing verification definitions, dangerous allowedTools, MCP overhead >12.5%, required-path
Access denied
, active cache-breakers, and security findings.
规则违规、缺失验证定义、危险的allowedTools配置、MCP开销>12.5%、必填路径
访问被拒绝
、活跃缓存中断器、安全问题。

[~] Structural -- fix soon

[~] 结构性问题 -- 尽快修复

CLAUDE.md content that belongs elsewhere, missing hooks, oversized skill descriptions, single-layer critical rules, model switching, verifier gaps, subagent permission gaps, and skill structural issues.
CLAUDE.md中存放了不属于此处的内容、缺失hooks、skill描述过大、单层关键规则、模型切换、验证器缺口、subagent权限缺口、skill结构性问题。

[-] Incremental -- nice to have

[-] 优化项 -- 建议完善

New patterns to add, outdated items to remove, global vs local placement, context hygiene, HANDOFF.md adoption, skill invoke tuning, and provenance issues.

If all three issue sections are empty, output one short line in the output language like:
All relevant checks passed. Nothing to fix.
可新增的模式、待移除的过时内容、全局/本地配置放置不合理、上下文卫生、HANDOFF.md使用率、skill调用调优、来源溯源问题。

如果三个问题板块都为空,用检测到的输出语言输出一行简短内容,例如:
所有相关检查已通过,无需修复。

Non-goals

非适用场景

  • Never auto-apply fixes without confirmation.
  • Never apply complex-tier checks to simple projects.
  • Flag issues, do not replace architectural judgment.
Stop condition: After the report, ask in the output language:
"Should I draft the changes? I can handle each layer separately: global CLAUDE.md / local CLAUDE.md / hooks / skills."
Do not make any edits without explicit confirmation.
  • 未获确认前绝不自动应用修复。
  • 绝不将复杂层级的检查应用于简单项目。
  • 仅标记问题,不替代架构决策判断。
**停止条件:**报告生成后,用检测到的输出语言询问:
"需要我起草修改方案吗?我可以分别处理每个层级:全局CLAUDE.md / 本地CLAUDE.md / hooks / skills。"
未获得明确确认前不要进行任何编辑。