Files
wehub-resource-sync 0ef5fcb1c5
Security / Dependency audit (pip-audit) (push) Has been cancelled
Security / CodeQL (javascript-typescript) (push) Has been cancelled
Security / CodeQL (python) (push) Has been cancelled
Security / Secret scan (gitleaks) (push) Has been cancelled
rust / test (ubuntu) (push) Has been cancelled
rust / simulator e2e (macos-latest) (push) Has been cancelled
rust / simulator e2e (ubuntu-latest) (push) Has been cancelled
rust / simulator e2e (windows-latest) (push) Has been cancelled
rust / wheels (aarch64-apple-darwin) (push) Has been cancelled
rust / wheels (x86_64-unknown-linux-gnu) (push) Has been cancelled
rust / wheels (x86_64-apple-darwin) (push) Has been cancelled
rust / audit (push) Has been cancelled
rust / parity (nightly, allowed to fail during Phase 0) (push) Has been cancelled
CI / commitlint (push) Has been skipped
Dev Containers / validate (.devcontainer/devcontainer.json, default) (push) Failing after 0s
Dev Containers / validate (.devcontainer/memory-stack/devcontainer.json, memory-stack) (push) Failing after 0s
Dev Containers / validate-worktree (push) Failing after 0s
CI / changes (push) Failing after 4s
Deploy Documentation / validate (push) Has been skipped
Deploy Documentation / deploy (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, claude) (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, codex) (push) Failing after 1s
Install Native E2E / install-native (ubuntu-latest) (push) Failing after 1s
OpenCode Plugin / typecheck + build + test (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, copilot) (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
Wrap E2E / docker-wrap-e2e (push) Failing after 1s
Wrap Native E2E / wrap-native (ubuntu-latest) (push) Failing after 1s
Init E2E / docker-init-e2e (push) Failing after 4s
Merge Conflicts / merge-conflicts (push) Failing after 4s
CI / lint (push) Has been cancelled
CI / build-wheel (push) Has been cancelled
CI / build-wheel-windows (push) Has been cancelled
CI / prefetch-model (push) Has been cancelled
CI / test-dashboard-ui (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test-extras (push) Has been cancelled
CI / test-agno (push) Has been cancelled
CI / build (push) Has been cancelled
CI / workflow-validation (push) Has been cancelled
CI / docker-native-e2e (push) Has been cancelled
CI / windows-native-wrapper (push) Has been cancelled
CI / macos-native-wrapper (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / promote-latest (push) Has been cancelled
Init Native E2E / init-native (macos-latest, claude) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, codex) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, copilot) (push) Has been cancelled
Install Native E2E / install-native (macos-latest) (push) Has been cancelled
Wrap Native E2E / wrap-native (macos-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:03:20 +08:00

163 lines
5.3 KiB
Python

"""Shared utilities for headroom learn plugins.
Error classification, tool name normalization, and other helpers
used across all scanner plugins.
"""
from __future__ import annotations
import os
import re
from pathlib import Path
from .models import ErrorCategory
def claude_config_dir() -> Path:
"""Resolve the Claude Code config directory, honoring ``CLAUDE_CONFIG_DIR``.
Claude Code relocates its config (including ``projects/`` logs and the
global ``CLAUDE.md``) when ``CLAUDE_CONFIG_DIR`` is set. ``headroom learn``
must read and write the same directory, mirroring the override already
honored in ``subscription`` and ``mcp_registry``. Falls back to
``~/.claude``.
"""
base = os.environ.get("CLAUDE_CONFIG_DIR")
return Path(base) if base else Path.home() / ".claude"
# =============================================================================
# Error Classification
# =============================================================================
# Patterns checked in order — first match wins
_ERROR_PATTERNS: list[tuple[re.Pattern[str], ErrorCategory]] = [
(
re.compile(r"No such file or directory|ENOENT|FileNotFoundError|does not exist", re.I),
ErrorCategory.FILE_NOT_FOUND,
),
(
re.compile(r"ModuleNotFoundError|ImportError|No module named", re.I),
ErrorCategory.MODULE_NOT_FOUND,
),
(re.compile(r"command not found", re.I), ErrorCategory.COMMAND_NOT_FOUND),
(
re.compile(r"Permission denied|EACCES|EPERM|auto-denied", re.I),
ErrorCategory.PERMISSION_DENIED,
),
(
re.compile(r"file is too large|too many lines|exceeds.*limit", re.I),
ErrorCategory.FILE_TOO_LARGE,
),
(re.compile(r"EISDIR|Is a directory", re.I), ErrorCategory.IS_DIRECTORY),
(re.compile(r"SyntaxError|IndentationError", re.I), ErrorCategory.SYNTAX_ERROR),
(re.compile(r"Traceback \(most recent|Exception:|Error:", re.I), ErrorCategory.RUNTIME_ERROR),
(re.compile(r"timed? ?out|TimeoutError|deadline exceeded", re.I), ErrorCategory.TIMEOUT),
(re.compile(r"No (?:matches|files|results) found|0 matches", re.I), ErrorCategory.NO_MATCHES),
(
re.compile(r"user.*reject|user.*denied|declined|didn't want to proceed", re.I),
ErrorCategory.USER_REJECTED,
),
(re.compile(r"[Ss]ibling tool call errored", re.I), ErrorCategory.SIBLING_ERROR),
(re.compile(r"exit code|non-zero|exited with", re.I), ErrorCategory.EXIT_CODE),
(
re.compile(r"ConnectionError|ConnectionRefused|ECONNREFUSED|network", re.I),
ErrorCategory.CONNECTION_ERROR,
),
(
re.compile(r"BUILD FAILED|compilation error|compile error", re.I),
ErrorCategory.BUILD_FAILURE,
),
]
def classify_error(content: str) -> ErrorCategory:
"""Classify an error message into a category."""
for pattern, category in _ERROR_PATTERNS:
if pattern.search(content[:2000]): # Only check first 2KB
return category
return ErrorCategory.UNKNOWN
def is_error_content(content: str) -> bool:
"""Heuristic: does this tool result look like an error?"""
if not content or len(content) < 10:
return False
# Check for common error indicators in first 1KB
snippet = content[:1000]
indicators = [
"Error:",
"error:",
"ENOENT",
"No such file",
"command not found",
"Permission denied",
"ModuleNotFoundError",
"Traceback (most recent",
"FAILED",
"EISDIR",
"auto-denied",
"Sibling tool call errored",
"timed out",
"exit code",
"FileNotFoundError",
]
return any(ind in snippet for ind in indicators)
# =============================================================================
# Tool Name Normalization
# =============================================================================
# Consolidated mapping from all agent-specific tool names to the cross-agent schema.
# Plugins can use normalize_tool_name() or extend this map for custom tools.
_TOOL_NAME_MAP: dict[str, str] = {
# Shell / command execution
"shell": "Bash",
"run_shell_command": "Bash",
"execute_command": "Bash",
"exec_command": "Bash",
"terminal": "Bash",
"run_command": "Bash",
"run_terminal_command": "Bash",
# File reading
"read_file": "Read",
"read_many_files": "Read",
"readfile": "Read",
"view_file": "Read",
"cat": "Read",
# File writing
"write_file": "Write",
"write_new_file": "Write",
"create_file": "Write",
"writefile": "Write",
# File editing
"edit_file": "Edit",
"replace_in_file": "Edit",
"editfile": "Edit",
"apply_diff": "Edit",
# File search / glob
"search_files": "Glob",
"find_files": "Glob",
"glob": "Glob",
"list_directory": "Glob",
"list_dir": "Glob",
# Text search / grep
"grep": "Grep",
"search_text": "Grep",
"search_code": "Grep",
"codebase_search": "Grep",
# Web
"browser": "WebFetch",
"web_search": "WebSearch",
}
def normalize_tool_name(name: str) -> str:
"""Map agent-specific tool names to the cross-agent schema.
Looks up the name (case-insensitive) in the shared tool name map.
Returns the original name if no mapping exists.
"""
return _TOOL_NAME_MAP.get(name.lower(), _TOOL_NAME_MAP.get(name, name))