Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 5 additions & 26 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,10 @@

set -euo pipefail

cd "$(git rev-parse --show-toplevel)"

staged_python_files=$(git diff --cached --name-only --diff-filter=ACMR -- "*.py" "*.pyi")

if [ -z "$staged_python_files" ]; then
exit 0
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
script_dir="$(cd "$(dirname "$0")" && pwd)"
repo_root="$(cd "$script_dir/.." && pwd)"
fi

set --
while IFS= read -r file; do
[ -n "$file" ] || continue
[ -f "$file" ] || continue
set -- "$@" "$file"
done <<EOF
$staged_python_files
EOF

if [ "$#" -eq 0 ]; then
exit 0
fi

echo "pre-commit: uv run ruff check --fix"
uv run ruff check --fix "$@"

echo "pre-commit: uv run ruff format"
uv run ruff format "$@"

git add -- "$@"
bash "$repo_root/scripts/hooks/pre-commit.sh"
18 changes: 6 additions & 12 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@

set -euo pipefail

cd "$(git rev-parse --show-toplevel)"
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
script_dir="$(cd "$(dirname "$0")" && pwd)"
repo_root="$(cd "$script_dir/.." && pwd)"
fi

echo "pre-push: uv run ruff check ."
uv run ruff check .

echo "pre-push: uv run ruff format --check ."
uv run ruff format --check .

echo "pre-push: uv run mypy src"
uv run mypy src

echo "pre-push: uv run pytest --ignore=tests/test_mlx_runtime.py --ignore=tests/test_cli_smoke_mlx.py"
uv run pytest --ignore=tests/test_mlx_runtime.py --ignore=tests/test_cli_smoke_mlx.py
bash "$repo_root/scripts/hooks/pre-push.sh"
5 changes: 3 additions & 2 deletions .harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
- `.github/` — Copilot CLI / Chat 진입 규칙과 agent persona
- `.agents/skills/` — repo-local skill 본체
- `AGENTS.md` — 최상위 규칙 / 스키마 정본
- `scripts/hooks/` — canonical hook implementation

- **harness support assets**
- `.harness/reference/` — imported agent-skills 문서 스냅샷, 로컬 적응 규칙, command draft, persona 스냅샷
- `.harness/hooks/` — Claude Code safety hook 스크립트 및 support hook 자산
- `.harness/hooks/` — Claude/Codex hook entrypoint wrapper와 support hook 자산

## 왜 루트 진입점은 그대로 두는가

Expand All @@ -31,4 +32,4 @@ Copilot과 skill discovery는 `.github/`, `.agents/skills/`, `AGENTS.md` 같은

1. 새 support 문서나 스냅샷을 추가할 때는 우선 `.harness/` 아래에 둔다.
2. 외부 툴이 직접 읽는 파일은 `.claude/settings.json`, `.github/`, `.agents/skills/`, `AGENTS.md`에 유지한다.
3. `.harness/hooks/`는 Claude Code active safety hooks의 실제 스크립트 위치로 사용하며, 그 외 hook 참고 자산도 함께 둔다.
3. 실제 hook 구현은 `scripts/hooks/`에 두고, `.harness/hooks/`는 Claude/Codex가 직접 참조하는 wrapper와 support hook 자산을 유지한다.
39 changes: 5 additions & 34 deletions .harness/hooks/check-secrets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,10 @@

set -euo pipefail

if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
exit 0
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
script_dir="$(cd "$(dirname "$0")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
fi

cd "$(git rev-parse --show-toplevel)"

staged_files="$(git diff --cached --name-only)"
if [ -z "$staged_files" ]; then
exit 0
fi

warned=0

while IFS= read -r file; do
[ -n "$file" ] || continue
case "$file" in
*.example)
continue
;;
.env|.env.*|*/.env|*/.env.*|*credentials.json|*secret.json|*secrets.json|*secret.yaml|*secrets.yaml|*secret.yml|*secrets.yml|*secret.toml|*secrets.toml|*.pem|*.key|*.p12|*.pfx)
echo "WARN: staged file looks like secret material: $file" >&2
warned=1
;;
esac
done <<EOF
$staged_files
EOF

if git diff --cached -U0 | grep -Eq '^\+.*(AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]+|sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{20,}|hf_[A-Za-z0-9]{20,}|BEGIN (RSA|OPENSSH|EC|DSA) PRIVATE KEY|anthropic_api_key|youtube_api_key)'; then
echo "WARN: staged diff may contain credentials or private key material." >&2
warned=1
fi

if [ "$warned" -eq 1 ]; then
echo "WARN: review staged changes for accidental secret exposure before pushing." >&2
fi
bash "$repo_root/scripts/hooks/check-secrets.sh"
18 changes: 6 additions & 12 deletions .harness/hooks/claude-stop-checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@

set -euo pipefail

cd "$(git rev-parse --show-toplevel)"
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
script_dir="$(cd "$(dirname "$0")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
fi

echo "claude stop: uv run ruff check ."
uv run ruff check .

echo "claude stop: uv run ruff format --check ."
uv run ruff format --check .

echo "claude stop: uv run mypy src"
uv run mypy src

echo "claude stop: uv run pytest --ignore=tests/test_mlx_runtime.py --ignore=tests/test_cli_smoke_mlx.py"
uv run pytest --ignore=tests/test_mlx_runtime.py --ignore=tests/test_cli_smoke_mlx.py
bash "$repo_root/scripts/hooks/claude-stop-checks.sh"
180 changes: 1 addition & 179 deletions .harness/hooks/graphify-auto-refresh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,182 +8,4 @@ if [ -z "$repo_root" ]; then
repo_root="$(cd "$script_dir/../.." && pwd)"
fi

cd "$repo_root"

mkdir -p .graphify-work

STATE_FILE=".graphify-work/auto_refresh_state.json"

python3 - <<'PY'
from __future__ import annotations

import hashlib
import json
import subprocess
from pathlib import Path

ROOT = Path(".").resolve()
STATE_FILE = ROOT / ".graphify-work" / "auto_refresh_state.json"
BUILD_INFO_FILE = ROOT / "graphify-out" / "BUILD_INFO.json"


def _iter_files(paths: list[Path]) -> list[Path]:
files: set[Path] = set()
for path in paths:
if path.is_file():
files.add(path)
elif path.is_dir():
files.update(candidate for candidate in path.rglob("*") if candidate.is_file())
return sorted(files)


def _digest(paths: list[Path]) -> str:
digest = hashlib.sha256()
for path in _iter_files(paths):
digest.update(path.relative_to(ROOT).as_posix().encode("utf-8"))
digest.update(b"\0")
digest.update(path.read_bytes())
digest.update(b"\0")
return digest.hexdigest()


def _load_state() -> dict[str, str]:
if not STATE_FILE.exists():
return {}
return json.loads(STATE_FILE.read_text(encoding="utf-8"))


def _write_state(payload: dict[str, str]) -> None:
payload = {"schema_version": "raw-source-v1", **payload}
STATE_FILE.write_text(
json.dumps(payload, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)


def _run(command: list[str], *, label: str) -> bool:
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode == 0:
print(f"graphify auto-refresh: {label} completed")
return True

print(f"graphify auto-refresh: {label} failed", flush=True)
if result.stdout.strip():
print(result.stdout.strip(), flush=True)
if result.stderr.strip():
print(result.stderr.strip(), flush=True)
return False


def _detect_bootstrap_changes() -> tuple[bool, bool]:
result = subprocess.run(
[
"git",
"status",
"--short",
"--untracked-files=all",
"--",
"src",
"tests",
"raw",
"scripts/graphify_code_refresh.sh",
"scripts/graphify_prepare_corpus.sh",
"scripts/graphify_full_refresh.py",
"scripts/graphify_semantic_adapter.py",
"scripts/graphify_verify_full_refresh.py",
"scripts/graphify_sync_staged.sh",
"scripts/graphify_ci_candidate.sh",
],
capture_output=True,
text=True,
check=True,
)
changed_paths = [line[3:] for line in result.stdout.splitlines() if len(line) >= 4]
has_full_refresh_changes = any(
path.startswith("raw/")
or path
in {
"scripts/graphify_prepare_corpus.sh",
"scripts/graphify_full_refresh.py",
"scripts/graphify_semantic_adapter.py",
"scripts/graphify_verify_full_refresh.py",
"scripts/graphify_sync_staged.sh",
"scripts/graphify_ci_candidate.sh",
}
for path in changed_paths
)
has_code_changes = any(
path.startswith("src/")
or path.startswith("tests/")
or path == "scripts/graphify_code_refresh.sh"
for path in changed_paths
)
return has_code_changes, has_full_refresh_changes


code_inputs = _digest([ROOT / "src", ROOT / "tests"])
full_inputs = _digest(
[
ROOT / "raw",
ROOT / "scripts" / "graphify_full_refresh.py",
ROOT / "scripts" / "graphify_semantic_adapter.py",
ROOT / "scripts" / "graphify_verify_full_refresh.py",
]
)

state = _load_state()
if state.get("schema_version") != "raw-source-v1":
state = {}
last_code_inputs = state.get("code_inputs")
last_full_inputs = state.get("full_inputs")

if not state and BUILD_INFO_FILE.exists():
existing_build_info = json.loads(BUILD_INFO_FILE.read_text(encoding="utf-8"))
existing_mode = existing_build_info.get("mode")
has_code_changes, has_full_refresh_changes = _detect_bootstrap_changes()
bootstrap_code_inputs = code_inputs
bootstrap_full_inputs = full_inputs
if has_full_refresh_changes:
bootstrap_full_inputs = ""
elif has_code_changes or existing_mode != "full_refresh":
bootstrap_code_inputs = ""
_write_state({"code_inputs": bootstrap_code_inputs, "full_inputs": bootstrap_full_inputs})
last_code_inputs = bootstrap_code_inputs
last_full_inputs = bootstrap_full_inputs
print("graphify auto-refresh: initialized state from existing graph")

if last_full_inputs != full_inputs:
ok = _run(["bash", "scripts/graphify_prepare_corpus.sh"], label="prepare corpus")
ok = ok and _run(
[
"uv",
"run",
"--with",
"graphifyy==0.4.23",
"python",
"scripts/graphify_full_refresh.py",
".graphify-work/corpus",
],
label="full refresh producer",
)
ok = ok and _run(
[
"python3",
"scripts/graphify_verify_full_refresh.py",
".graphify-work/corpus/graphify-out",
],
label="verify full refresh",
)
ok = ok and _run(["bash", "scripts/graphify_sync_staged.sh"], label="sync full refresh")
if ok:
_write_state({"code_inputs": code_inputs, "full_inputs": full_inputs})
raise SystemExit(0)

if last_code_inputs != code_inputs:
ok = _run(["bash", "scripts/graphify_code_refresh.sh"], label="code refresh")
if ok:
_write_state({"code_inputs": code_inputs, "full_inputs": full_inputs})
raise SystemExit(0)

print("graphify auto-refresh: inputs unchanged")
PY
bash "$repo_root/scripts/hooks/graphify-auto-refresh.sh"
41 changes: 5 additions & 36 deletions .harness/hooks/graphify-mode-note.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,10 @@

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
REPORT="$REPO_ROOT/graphify-out/GRAPH_REPORT.md"
GRAPH_JSON="$REPO_ROOT/graphify-out/graph.json"
BUILD_INFO="$REPO_ROOT/graphify-out/BUILD_INFO.json"

if [ ! -f "$REPORT" ] || [ ! -f "$GRAPH_JSON" ]; then
exit 0
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
script_dir="$(cd "$(dirname "$0")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
fi

python3 - "$BUILD_INFO" <<'PY'
import json
import sys
from pathlib import Path

build_info = Path(sys.argv[1])
if not build_info.exists():
print(
"graphify: Primary graph exists at graphify-out/. Read GRAPH_REPORT.md first, then graph.json. "
"BUILD_INFO is missing, so inspect raw/ directly if the graph lacks needed design or external source context."
)
raise SystemExit

data = json.loads(build_info.read_text(encoding="utf-8"))
mode = data.get("mode")
verified = bool(data.get("verified"))

if mode == "full_refresh" and verified:
print(
"graphify: Verified full_refresh graph with raw source coverage exists at graphify-out/. Read GRAPH_REPORT.md first, then graph.json and BUILD_INFO.json. "
"Inspect raw/ only if the graph still lacks the needed design or external source context."
)
else:
print(
"graphify: Code-only or unverified graph exists at graphify-out/. Read GRAPH_REPORT.md first, then graph.json and BUILD_INFO.json, "
"and inspect raw/ directly for design or external source context when needed."
)
PY
bash "$repo_root/scripts/hooks/graphify-mode-note.sh"
13 changes: 5 additions & 8 deletions .harness/hooks/graphify-pretool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
NOTE="$("$SCRIPT_DIR/graphify-mode-note.sh" || true)"

if [ -z "${NOTE:-}" ]; then
exit 0
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
script_dir="$(cd "$(dirname "$0")" && pwd)"
repo_root="$(cd "$script_dir/../.." && pwd)"
fi

cat <<EOF
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"$NOTE"}}
EOF
bash "$repo_root/scripts/hooks/graphify-pretool.sh"
Loading
Loading