modify skill and plugin

This commit is contained in:
NINI
2026-05-13 22:27:48 +09:00
parent d414338780
commit 2d59191df2
24 changed files with 2051 additions and 1 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
#:schema https://developers.openai.com/codex/config-schema.json #:schema https://developers.openai.com/codex/config-schema.json
[features] [features]
codex_hooks = true hooks = true
@@ -0,0 +1,42 @@
---
name: harness-review
description: Use when reviewing this Harness repository: local changes, generated phase files, step outputs, implementation diffs, missing tests, build readiness, or compliance with AGENTS.md, docs/ARCHITECTURE.md, docs/ADR.md, and Harness acceptance criteria.
---
# Harness Review
## Overview
Use this skill to review Harness work against the repository's persistent rules, architecture docs, and executable verification requirements. Prioritize bugs, regressions, missing tests, and rule violations.
## Review Process
1. Read `/AGENTS.md`, `/docs/ARCHITECTURE.md`, and `/docs/ADR.md`.
2. Inspect the changed files with `git status --short` and `git diff`.
3. Check architecture, stack choices, tests, critical rules, and build readiness.
4. Run relevant verification commands when feasible. If a command cannot be run, report that as residual risk.
5. Lead with actionable findings. Keep summaries secondary.
## Checklist
| Item | Question |
| --- | --- |
| Architecture | Does the change follow `docs/ARCHITECTURE.md` directory and module boundaries? |
| Stack | Does the change stay within choices documented in `docs/ADR.md`? |
| Tests | Are new or changed behaviors covered by tests? |
| Critical Rules | Does the change violate any `AGENTS.md` CRITICAL rule? |
| Build | Do relevant build/test/lint commands pass? |
## Output Format
If there are findings, list them first in severity order with file and line references when possible. Then include this table:
| 항목 | 결과 | 비고 |
| --- | --- | --- |
| 아키텍처 준수 | PASS/FAIL | {상세} |
| 기술 스택 준수 | PASS/FAIL | {상세} |
| 테스트 존재 | PASS/FAIL | {상세} |
| CRITICAL 규칙 | PASS/FAIL | {상세} |
| 빌드 가능 | PASS/FAIL | {상세} |
When there are no findings, say that clearly, then mention any commands not run or remaining risk.
@@ -0,0 +1,4 @@
interface:
display_name: "Harness Review"
short_description: "Review Harness changes safely"
default_prompt: "Use $harness-review to review Harness repository changes."
@@ -0,0 +1,124 @@
---
name: harness-workflow
description: Use when planning or running this Harness framework: reading AGENTS.md and docs/*.md, discussing implementation scope, creating or updating phases/index.json, phases/{task}/index.json, phases/{task}/stepN.md, or invoking scripts/execute.py for staged Codex execution.
---
# Harness Workflow
## Overview
Use this skill to turn a user-approved task into small, self-contained Harness steps that another Codex session can execute reliably. Keep the workflow grounded in repository docs and executable acceptance criteria.
## Workflow
1. Read `AGENTS.md` and relevant files under `docs/`, especially `docs/PRD.md`, `docs/ARCHITECTURE.md`, and `docs/ADR.md`.
2. Discuss unresolved product or technical decisions with the user before writing phase files.
3. When the user asks for an implementation plan, draft steps and get approval before creating files.
4. Create or update `phases/index.json`, `phases/{task-name}/index.json`, and one `phases/{task-name}/stepN.md` per step.
5. Run the phase with `python scripts/execute.py {task-name}` when asked to execute it. Use `--push` only when the user asks to push.
## Step Design Rules
- Scope each step to one layer or module. Split steps when multiple modules would otherwise change together.
- Make every step self-contained. Do not rely on prior conversation; include all required context and file paths.
- Force context gathering. Each step must tell Codex which docs and previous outputs to read before editing.
- Specify interfaces and signatures, not full implementations, unless exact code is required for a constraint.
- Put core invariants directly in the step: idempotency, security, data integrity, API contracts, or other non-negotiables.
- Use executable acceptance criteria such as `npm run build && npm test`, not abstract statements.
- Write cautions concretely: "Do not do X. Reason: Y."
- Name steps with kebab-case slugs such as `project-setup`, `api-layer`, or `auth-flow`.
## Phase Files
Create or update `phases/index.json`:
```json
{
"phases": [
{
"dir": "0-mvp",
"status": "pending"
}
]
}
```
Create `phases/{task-name}/index.json`:
```json
{
"project": "<project-name>",
"phase": "<task-name>",
"steps": [
{ "step": 0, "name": "project-setup", "status": "pending" },
{ "step": 1, "name": "core-types", "status": "pending" },
{ "step": 2, "name": "api-layer", "status": "pending" }
]
}
```
Rules:
- `project` comes from `AGENTS.md`.
- `phase` matches the task directory name.
- `steps[].step` starts at `0`.
- Initial status is always `pending`.
- Do not add timestamps when creating files. `scripts/execute.py` records `created_at`, `started_at`, `completed_at`, `failed_at`, and `blocked_at`.
## Step Template
```markdown
# Step {N}: {name}
## 읽어야 할 파일
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 설계 의도를 파악하라:
- `/AGENTS.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- {previously created or modified files}
이전 step에서 만들어진 코드를 꼼꼼히 읽고, 설계 의도를 이해한 뒤 작업하라.
## 작업
{Concrete instructions with file paths, interfaces, signatures, and rules.}
## Acceptance Criteria
```bash
npm run build
npm test
```
## 검증 절차
1. 위 AC 커맨드를 실행한다.
2. 아키텍처 체크리스트를 확인한다:
- ARCHITECTURE.md 디렉토리 구조를 따르는가?
- ADR 기술 스택을 벗어나지 않았는가?
- AGENTS.md CRITICAL 규칙을 위반하지 않았는가?
3. 결과에 따라 `phases/{task-name}/index.json`의 해당 step을 업데이트한다:
- 성공: `"status": "completed"`, `"summary": "산출물 한 줄 요약"`
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
## 금지사항
- {Do not do X. Reason: Y.}
- 기존 테스트를 깨뜨리지 마라.
```
## Execution And Recovery
Run:
```bash
python scripts/execute.py {task-name}
python scripts/execute.py {task-name} --push
```
`scripts/execute.py` creates or checks out `feat-{task-name}`, injects `AGENTS.md` and `docs/*.md` into each prompt, carries completed step summaries forward, retries failed steps up to three times, separates code and metadata commits, and records timestamps.
If a step is `error`, set it back to `pending` and remove `error_message` after fixing the cause. If a step is `blocked`, resolve `blocked_reason`, set it back to `pending`, remove `blocked_reason`, and rerun.
@@ -0,0 +1,4 @@
interface:
display_name: "Harness Workflow"
short_description: "Plan staged Harness workflow steps"
default_prompt: "Use $harness-workflow to plan Harness phases and step files."
+4
View File
@@ -0,0 +1,4 @@
#:schema https://developers.openai.com/codex/config-schema.json
[features]
hooks = true
+28
View File
@@ -0,0 +1,28 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{
"type": "command",
"command": "python -c \"import pathlib, runpy, subprocess; root = pathlib.Path(subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip()); runpy.run_path(str(root / '.codex' / 'hooks' / 'pre_commit_checks.py'), run_name='__main__')\"",
"timeout": 600,
"statusMessage": "Running pre-commit checks"
}
]
},
{
"matcher": "^(apply_patch|Edit|Write)$",
"hooks": [
{
"type": "command",
"command": "python -c \"import pathlib, runpy, subprocess; root = pathlib.Path(subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip()); runpy.run_path(str(root / '.codex' / 'hooks' / 'tdd-guard.py'), run_name='__main__')\"",
"timeout": 30,
"statusMessage": "Checking TDD guard"
}
]
}
]
}
}
@@ -0,0 +1,111 @@
import json
import re
import shutil
import subprocess
import sys
from pathlib import Path
CHECKS = ("lint", "build", "test")
def _repo_root(cwd: Path) -> Path:
try:
root = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"],
cwd=cwd,
text=True,
stderr=subprocess.DEVNULL,
).strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return cwd
return Path(root)
def _load_scripts(root: Path) -> dict[str, str]:
package_json = root / "package.json"
if not package_json.exists():
return {}
try:
package = json.loads(package_json.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
_deny(f"Invalid package.json: {exc}")
raise SystemExit(0) from exc
scripts = package.get("scripts", {})
if not isinstance(scripts, dict):
return {}
return {str(name): str(command) for name, command in scripts.items()}
def _is_git_commit(command: str) -> bool:
return re.search(r"\bgit(?:\s+(?:-[A-Za-z]\s+\S+|--[A-Za-z0-9-]+(?:=\S+)?))*\s+commit\b", command) is not None
def _deny(reason: str) -> None:
print(
json.dumps(
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": reason,
}
}
)
)
def _tail(text: str, limit: int = 1200) -> str:
text = text.strip()
if len(text) <= limit:
return text
return text[-limit:]
def _run_checks(root: Path, scripts: dict[str, str]) -> str | None:
npm = shutil.which("npm") or shutil.which("npm.cmd")
if npm is None:
return "npm was not found, so pre-commit checks could not run."
for check in CHECKS:
if check not in scripts:
continue
result = subprocess.run(
[npm, "run", check],
cwd=root,
capture_output=True,
text=True,
)
if result.returncode != 0:
details = _tail(result.stdout + "\n" + result.stderr)
if details:
return f"npm run {check} failed:\n{details}"
return f"npm run {check} failed with exit code {result.returncode}."
return None
def main() -> int:
try:
payload = json.load(sys.stdin)
except json.JSONDecodeError:
return 0
command = payload.get("tool_input", {}).get("command", "")
if not isinstance(command, str) or not _is_git_commit(command):
return 0
cwd = Path(payload.get("cwd") or Path.cwd())
root = _repo_root(cwd)
failure = _run_checks(root, _load_scripts(root))
if failure:
_deny(f"PRE-COMMIT CHECKS: {failure}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+189
View File
@@ -0,0 +1,189 @@
import json
import subprocess
import sys
from pathlib import Path
SOURCE_SUFFIXES = {".ts", ".tsx", ".js", ".jsx"}
TEST_SUFFIXES = ("ts", "tsx", "js", "jsx")
CONFIG_SUFFIXES = {".json", ".css", ".scss", ".md", ".yml", ".yaml"}
NEXT_SPECIAL_FILES = {
"layout.ts",
"layout.tsx",
"page.ts",
"page.tsx",
"loading.tsx",
"error.tsx",
"not-found.tsx",
"globals.css",
}
def _repo_root(cwd: Path) -> Path:
try:
root = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"],
cwd=cwd,
text=True,
stderr=subprocess.DEVNULL,
).strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return cwd
return Path(root)
def _extract_patch_paths(command: str) -> list[str]:
prefixes = (
"*** Add File: ",
"*** Update File: ",
"*** Delete File: ",
"*** Move to: ",
)
paths: list[str] = []
for raw_line in command.splitlines():
line = raw_line.strip()
for prefix in prefixes:
if line.startswith(prefix):
paths.append(line[len(prefix) :].strip())
break
return paths
def _touched_paths(payload: dict) -> list[str]:
tool_input = payload.get("tool_input", {})
if not isinstance(tool_input, dict):
return []
file_path = tool_input.get("file_path")
if isinstance(file_path, str) and file_path:
return [file_path]
command = tool_input.get("command")
if isinstance(command, str):
return _extract_patch_paths(command)
return []
def _normalize(path_text: str) -> str:
return path_text.replace("\\", "/").lower()
def _is_test_path(path_text: str) -> bool:
normalized = _normalize(path_text)
name = normalized.rsplit("/", 1)[-1]
return (
"__tests__/" in normalized
or ".test." in name
or ".spec." in name
or "test" in name
or "spec" in name
)
def _is_exempt(path_text: str) -> bool:
normalized = _normalize(path_text)
path = Path(path_text)
name = path.name.lower()
if _is_test_path(path_text):
return True
if name in NEXT_SPECIAL_FILES:
return True
if path.suffix.lower() in CONFIG_SUFFIXES:
return True
if ".env" in name or ".config." in name:
return True
if any(token in name for token in ("tailwind", "postcss", "next.config", "tsconfig")):
return True
if "/types/" in normalized or name in {"types.ts", "types.d.ts"}:
return True
return False
def _resolve_path(path_text: str, cwd: Path) -> Path:
path = Path(path_text)
if path.is_absolute():
return path
return (cwd / path).resolve()
def _base_name(path: Path) -> str:
for suffix in (".tsx", ".ts", ".jsx", ".js"):
if path.name.endswith(suffix):
return path.name[: -len(suffix)]
return path.stem
def _has_existing_test(path: Path, root: Path) -> bool:
directory = path.parent
parent = directory.parent
base = _base_name(path)
for ext in TEST_SUFFIXES:
if (directory / f"{base}.test.{ext}").exists():
return True
if (directory / f"{base}.spec.{ext}").exists():
return True
for ext in TEST_SUFFIXES:
if (parent / "__tests__" / f"{base}.test.{ext}").exists():
return True
if (directory / "__tests__" / f"{base}.test.{ext}").exists():
return True
for ext in TEST_SUFFIXES:
if (root / "src" / "__tests__" / f"{base}.test.{ext}").exists():
return True
return False
def _guarded_paths(paths: list[str], cwd: Path, root: Path) -> list[str]:
missing_tests: list[str] = []
for path_text in paths:
if _is_exempt(path_text):
continue
path = _resolve_path(path_text, cwd)
if path.suffix.lower() not in SOURCE_SUFFIXES:
continue
if not _has_existing_test(path, root):
missing_tests.append(_base_name(path))
return missing_tests
def main() -> int:
try:
payload = json.load(sys.stdin)
except json.JSONDecodeError:
return 0
cwd = Path(payload.get("cwd") or Path.cwd())
root = _repo_root(cwd)
missing_tests = _guarded_paths(_touched_paths(payload), cwd, root)
if not missing_tests:
return 0
names = ", ".join(sorted(set(missing_tests)))
print(
json.dumps(
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": (
"TDD GUARD: missing test file for "
f"{names}. Write or add the test first."
),
}
}
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+9
View File
@@ -0,0 +1,9 @@
node_modules/
.next/
out/
next-env.d.ts
tsconfig.tsbuildinfo
# phase execution outputs
phases/**/phase*-output.json
phases/**/step*-output.json
+21
View File
@@ -0,0 +1,21 @@
# 프로젝트: {프로젝트명}
## 기술 스택
- {프레임워크 (예: Next.js 15)}
- {언어 (예: TypeScript strict mode)}
- {스타일링 (예: Tailwind CSS)}
## 아키텍처 규칙
- CRITICAL: {절대 지켜야 할 규칙 1 (예: 모든 API 로직은 app/api/ 라우트 핸들러에서만 처리)}
- CRITICAL: {절대 지켜야 할 규칙 2 (예: 클라이언트 컴포넌트에서 직접 외부 API를 호출하지 말 것)}
- {일반 규칙 (예: 컴포넌트는 components/ 폴더에, 타입은 types/ 폴더에 분리)}
## 개발 프로세스
- CRITICAL: 새 기능 구현 시 반드시 테스트를 먼저 작성하고, 테스트가 통과하는 구현을 작성할 것 (TDD)
- 커밋 메시지는 conventional commits 형식을 따를 것 (feat:, fix:, docs:, refactor:)
## 명령어
npm run dev # 개발 서버
npm run build # 프로덕션 빌드
npm run lint # ESLint
npm run test # 테스트
+21
View File
@@ -0,0 +1,21 @@
# Architecture Decision Records
## 철학
{프로젝트의 핵심 가치관 (예: MVP 속도 최우선. 외부 의존성 최소화. 작동하는 최소 구현을 선택.)}
---
### ADR-001: {결정 사항 (예: Next.js App Router 선택)}
**결정**: {뭘 선택했는지}
**이유**: {왜 선택했는지}
**트레이드오프**: {뭘 포기했는지}
### ADR-002: {결정 사항}
**결정**: {뭘 선택했는지}
**이유**: {왜 선택했는지}
**트레이드오프**: {뭘 포기했는지}
### ADR-003: {결정 사항}
**결정**: {뭘 선택했는지}
**이유**: {왜 선택했는지}
**트레이드오프**: {뭘 포기했는지}
+24
View File
@@ -0,0 +1,24 @@
# 아키텍처
## 디렉토리 구조
```
src/
├── app/ # 페이지 + API 라우트
├── components/ # UI 컴포넌트
├── types/ # TypeScript 타입 정의
├── lib/ # 유틸리티 + 헬퍼
└── services/ # 외부 API 래퍼
```
## 패턴
{사용하는 디자인 패턴 (예: Server Components 기본, 인터랙션이 필요한 곳만 Client Component)}
## 데이터 흐름
```
{데이터가 어떻게 흐르는지 (예:
사용자 입력 → Client Component → API Route → 외부 API → 응답 → UI 업데이트
)}
```
## 상태 관리
{상태 관리 방식 (예: 서버 상태는 Server Components, 클라이언트 상태는 useState/useReducer)}
+21
View File
@@ -0,0 +1,21 @@
# PRD: {프로젝트명}
## 목표
{이 프로젝트가 해결하려는 문제를 한 줄로 요약}
## 사용자
{누가 이 제품을 쓰는지}
## 핵심 기능
1. {기능 1}
2. {기능 2}
3. {기능 3}
## MVP 제외 사항
- {안 만들 것 1}
- {안 만들 것 2}
- {안 만들 것 3}
## 디자인
- {디자인 방향 (예: 다크모드 고정, 미니멀)}
- {색상 (예: 무채색 + 포인트 1가지)}
+76
View File
@@ -0,0 +1,76 @@
# UI 디자인 가이드
## 디자인 원칙
1. {원칙 1 — 예: "도구처럼 보여야 한다. 마케팅 페이지가 아니라 매일 쓰는 대시보드."}
2. {원칙 2}
3. {원칙 3}
## AI 슬롭 안티패턴 — 하지 마라
| 금지 사항 | 이유 |
|-----------|------|
| backdrop-filter: blur() | glass morphism은 AI 템플릿의 가장 흔한 징후 |
| gradient-text (배경 그라데이션 텍스트) | AI가 만든 SaaS 랜딩의 1번 특징 |
| "Powered by AI" 배지 | 기능이 아니라 장식. 사용자에게 가치 없음 |
| box-shadow 글로우 애니메이션 | 네온 글로우 = AI 슬롭 |
| 보라/인디고 브랜드 색상 | "AI = 보라색" 클리셰 |
| 모든 카드에 동일한 rounded-2xl | 균일한 둥근 모서리는 템플릿 느낌 |
| 배경 gradient orb (blur-3xl 원형) | 모든 AI 랜딩 페이지에 있는 장식 |
## 색상
### 배경
| 용도 | 값 |
|------|------|
| 페이지 | {예: #0a0a0a} |
| 카드 | {예: #141414} |
### 텍스트
| 용도 | 값 |
|------|------|
| 주 텍스트 | {예: text-white} |
| 본문 | {예: text-neutral-300} |
| 보조 | {예: text-neutral-400} |
| 비활성 | {예: text-neutral-500} |
### 데이터/시맨틱 색상
| 용도 | 값 |
|------|------|
| {긍정/성공} | {예: #22c55e} |
| {부정/에러} | {예: #ef4444} |
| {중립/기본} | {예: #525252} |
## 컴포넌트
### 카드
```
{예: rounded-lg bg-[#141414] border border-neutral-800 p-6}
```
### 버튼
```
Primary: {예: rounded-lg bg-white text-black hover:bg-neutral-200}
Text: {예: text-neutral-500 hover:text-neutral-300}
```
### 입력 필드
```
{예: rounded-lg bg-neutral-900 border border-neutral-800 px-4 py-3}
```
## 레이아웃
- 전체 너비: {예: max-w-5xl}
- 정렬: {예: 좌측 정렬 기본. 중앙 정렬 금지}
- 간격: {예: gap-3~4, 섹션 간 space-y-8}
## 타이포그래피
| 용도 | 스타일 |
|------|--------|
| 페이지 제목 | {예: text-4xl font-semibold text-white} |
| 카드 제목 | {예: text-sm font-medium text-neutral-400} |
| 본문 | {예: text-sm text-neutral-300 leading-relaxed} |
## 애니메이션
- {허용할 애니메이션만 나열. 예: fade-in (0.4s), slide-up (0.5s)}
- {그 외 모든 애니메이션 금지}
## 아이콘
- {예: SVG 인라인, strokeWidth 1.5}
- {예: 아이콘 컨테이너(둥근 배경 박스)로 감싸지 않는다}
+417
View File
@@ -0,0 +1,417 @@
#!/usr/bin/env python3
"""
Harness Step Executor — phase 내 step을 순차 실행하고 자가 교정한다.
Usage:
python scripts/execute.py <phase-dir> [--push]
"""
import argparse
import contextlib
import json
import os
import subprocess
import sys
import threading
import time
import types
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Optional
ROOT = Path(__file__).resolve().parent.parent
@contextlib.contextmanager
def progress_indicator(label: str):
"""터미널 진행 표시기. with 문으로 사용하며 .elapsed 로 경과 시간을 읽는다."""
frames = "◐◓◑◒"
stop = threading.Event()
t0 = time.monotonic()
def _animate():
idx = 0
while not stop.wait(0.12):
sec = int(time.monotonic() - t0)
sys.stderr.write(f"\r{frames[idx % len(frames)]} {label} [{sec}s]")
sys.stderr.flush()
idx += 1
sys.stderr.write("\r" + " " * (len(label) + 20) + "\r")
sys.stderr.flush()
th = threading.Thread(target=_animate, daemon=True)
th.start()
info = types.SimpleNamespace(elapsed=0.0)
try:
yield info
finally:
stop.set()
th.join()
info.elapsed = time.monotonic() - t0
class StepExecutor:
"""Phase 디렉토리 안의 step들을 순차 실행하는 하네스."""
MAX_RETRIES = 3
FEAT_MSG = "feat({phase}): step {num}{name}"
CHORE_MSG = "chore({phase}): step {num} output"
TZ = timezone(timedelta(hours=9))
def __init__(self, phase_dir_name: str, *, auto_push: bool = False):
self._root = str(ROOT)
self._phases_dir = ROOT / "phases"
self._phase_dir = self._phases_dir / phase_dir_name
self._phase_dir_name = phase_dir_name
self._top_index_file = self._phases_dir / "index.json"
self._auto_push = auto_push
if not self._phase_dir.is_dir():
print(f"ERROR: {self._phase_dir} not found")
sys.exit(1)
self._index_file = self._phase_dir / "index.json"
if not self._index_file.exists():
print(f"ERROR: {self._index_file} not found")
sys.exit(1)
idx = self._read_json(self._index_file)
self._project = idx.get("project", "project")
self._phase_name = idx.get("phase", phase_dir_name)
self._total = len(idx["steps"])
def run(self):
self._print_header()
self._check_blockers()
self._checkout_branch()
guardrails = self._load_guardrails()
self._ensure_created_at()
self._execute_all_steps(guardrails)
self._finalize()
# --- timestamps ---
def _stamp(self) -> str:
return datetime.now(self.TZ).strftime("%Y-%m-%dT%H:%M:%S%z")
# --- JSON I/O ---
@staticmethod
def _read_json(p: Path) -> dict:
return json.loads(p.read_text(encoding="utf-8"))
@staticmethod
def _write_json(p: Path, data: dict):
p.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
# --- git ---
def _run_git(self, *args) -> subprocess.CompletedProcess:
cmd = ["git"] + list(args)
return subprocess.run(cmd, cwd=self._root, capture_output=True, text=True)
def _checkout_branch(self):
branch = f"feat-{self._phase_name}"
r = self._run_git("rev-parse", "--abbrev-ref", "HEAD")
if r.returncode != 0:
print(f" ERROR: git을 사용할 수 없거나 git repo가 아닙니다.")
print(f" {r.stderr.strip()}")
sys.exit(1)
if r.stdout.strip() == branch:
return
r = self._run_git("rev-parse", "--verify", branch)
r = self._run_git("checkout", branch) if r.returncode == 0 else self._run_git("checkout", "-b", branch)
if r.returncode != 0:
print(f" ERROR: 브랜치 '{branch}' checkout 실패.")
print(f" {r.stderr.strip()}")
print(f" Hint: 변경사항을 stash하거나 commit한 후 다시 시도하세요.")
sys.exit(1)
print(f" Branch: {branch}")
def _commit_step(self, step_num: int, step_name: str):
output_rel = f"phases/{self._phase_dir_name}/step{step_num}-output.json"
index_rel = f"phases/{self._phase_dir_name}/index.json"
self._run_git("add", "-A")
self._run_git("reset", "HEAD", "--", output_rel)
self._run_git("reset", "HEAD", "--", index_rel)
if self._run_git("diff", "--cached", "--quiet").returncode != 0:
msg = self.FEAT_MSG.format(phase=self._phase_name, num=step_num, name=step_name)
r = self._run_git("commit", "-m", msg)
if r.returncode == 0:
print(f" Commit: {msg}")
else:
print(f" WARN: 코드 커밋 실패: {r.stderr.strip()}")
self._run_git("add", "-A")
if self._run_git("diff", "--cached", "--quiet").returncode != 0:
msg = self.CHORE_MSG.format(phase=self._phase_name, num=step_num)
r = self._run_git("commit", "-m", msg)
if r.returncode != 0:
print(f" WARN: housekeeping 커밋 실패: {r.stderr.strip()}")
# --- top-level index ---
def _update_top_index(self, status: str):
if not self._top_index_file.exists():
return
top = self._read_json(self._top_index_file)
ts = self._stamp()
for phase in top.get("phases", []):
if phase.get("dir") == self._phase_dir_name:
phase["status"] = status
ts_key = {"completed": "completed_at", "error": "failed_at", "blocked": "blocked_at"}.get(status)
if ts_key:
phase[ts_key] = ts
break
self._write_json(self._top_index_file, top)
# --- guardrails & context ---
def _load_guardrails(self) -> str:
sections = []
agents_md = ROOT / "AGENTS.md"
if agents_md.exists():
sections.append(f"## 프로젝트 규칙 (AGENTS.md)\n\n{agents_md.read_text(encoding='utf-8')}")
docs_dir = ROOT / "docs"
if docs_dir.is_dir():
for doc in sorted(docs_dir.glob("*.md")):
sections.append(f"## {doc.stem}\n\n{doc.read_text(encoding='utf-8')}")
return "\n\n---\n\n".join(sections) if sections else ""
@staticmethod
def _build_step_context(index: dict) -> str:
lines = [
f"- Step {s['step']} ({s['name']}): {s['summary']}"
for s in index["steps"]
if s["status"] == "completed" and s.get("summary")
]
if not lines:
return ""
return "## 이전 Step 산출물\n\n" + "\n".join(lines) + "\n\n"
def _build_preamble(self, guardrails: str, step_context: str,
prev_error: Optional[str] = None) -> str:
commit_example = self.FEAT_MSG.format(
phase=self._phase_name, num="N", name="<step-name>"
)
retry_section = ""
if prev_error:
retry_section = (
f"\n## ⚠ 이전 시도 실패 — 아래 에러를 반드시 참고하여 수정하라\n\n"
f"{prev_error}\n\n---\n\n"
)
return (
f"당신은 {self._project} 프로젝트의 개발자입니다. 아래 step을 수행하세요.\n\n"
f"{guardrails}\n\n---\n\n"
f"{step_context}{retry_section}"
f"## 작업 규칙\n\n"
f"1. 이전 step에서 작성된 코드를 확인하고 일관성을 유지하라.\n"
f"2. 이 step에 명시된 작업만 수행하라. 추가 기능이나 파일을 만들지 마라.\n"
f"3. 기존 테스트를 깨뜨리지 마라.\n"
f"4. AC(Acceptance Criteria) 검증을 직접 실행하라.\n"
f"5. /phases/{self._phase_dir_name}/index.json의 해당 step status를 업데이트하라:\n"
f" - AC 통과 → \"completed\" + \"summary\" 필드에 이 step의 산출물을 한 줄로 요약\n"
f" - {self.MAX_RETRIES}회 수정 시도 후에도 실패 → \"error\" + \"error_message\" 기록\n"
f" - 사용자 개입이 필요한 경우 (API 키, 인증, 수동 설정 등) → \"blocked\" + \"blocked_reason\" 기록 후 즉시 중단\n"
f"6. 모든 변경사항을 커밋하라:\n"
f" {commit_example}\n\n---\n\n"
)
# --- Codex 호출 ---
def _invoke_codex(self, step: dict, preamble: str) -> dict:
step_num, step_name = step["step"], step["name"]
step_file = self._phase_dir / f"step{step_num}.md"
if not step_file.exists():
print(f" ERROR: {step_file} not found")
sys.exit(1)
prompt = preamble + step_file.read_text(encoding="utf-8")
result = subprocess.run(
["codex", "exec", "--dangerously-bypass-approvals-and-sandbox", "--json", prompt],
cwd=self._root, capture_output=True, text=True, timeout=1800,
)
if result.returncode != 0:
print(f"\n WARN: Codex가 비정상 종료됨 (code {result.returncode})")
if result.stderr:
print(f" stderr: {result.stderr[:500]}")
output = {
"step": step_num, "name": step_name,
"exitCode": result.returncode,
"stdout": result.stdout, "stderr": result.stderr,
}
out_path = self._phase_dir / f"step{step_num}-output.json"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(output, f, indent=2, ensure_ascii=False)
return output
# --- 헤더 & 검증 ---
def _print_header(self):
print(f"\n{'='*60}")
print(f" Harness Step Executor")
print(f" Phase: {self._phase_name} | Steps: {self._total}")
if self._auto_push:
print(f" Auto-push: enabled")
print(f"{'='*60}")
def _check_blockers(self):
index = self._read_json(self._index_file)
for s in reversed(index["steps"]):
if s["status"] == "error":
print(f"\n ✗ Step {s['step']} ({s['name']}) failed.")
print(f" Error: {s.get('error_message', 'unknown')}")
print(f" Fix and reset status to 'pending' to retry.")
sys.exit(1)
if s["status"] == "blocked":
print(f"\n ⏸ Step {s['step']} ({s['name']}) blocked.")
print(f" Reason: {s.get('blocked_reason', 'unknown')}")
print(f" Resolve and reset status to 'pending' to retry.")
sys.exit(2)
if s["status"] != "pending":
break
def _ensure_created_at(self):
index = self._read_json(self._index_file)
if "created_at" not in index:
index["created_at"] = self._stamp()
self._write_json(self._index_file, index)
# --- 실행 루프 ---
def _execute_single_step(self, step: dict, guardrails: str) -> bool:
"""단일 step 실행 (재시도 포함). 완료되면 True, 실패/차단이면 False."""
step_num, step_name = step["step"], step["name"]
done = sum(1 for s in self._read_json(self._index_file)["steps"] if s["status"] == "completed")
prev_error = None
for attempt in range(1, self.MAX_RETRIES + 1):
index = self._read_json(self._index_file)
step_context = self._build_step_context(index)
preamble = self._build_preamble(guardrails, step_context, prev_error)
tag = f"Step {step_num}/{self._total - 1} ({done} done): {step_name}"
if attempt > 1:
tag += f" [retry {attempt}/{self.MAX_RETRIES}]"
with progress_indicator(tag) as pi:
self._invoke_codex(step, preamble)
elapsed = int(pi.elapsed)
index = self._read_json(self._index_file)
status = next((s.get("status", "pending") for s in index["steps"] if s["step"] == step_num), "pending")
ts = self._stamp()
if status == "completed":
for s in index["steps"]:
if s["step"] == step_num:
s["completed_at"] = ts
self._write_json(self._index_file, index)
self._commit_step(step_num, step_name)
print(f" ✓ Step {step_num}: {step_name} [{elapsed}s]")
return True
if status == "blocked":
for s in index["steps"]:
if s["step"] == step_num:
s["blocked_at"] = ts
self._write_json(self._index_file, index)
reason = next((s.get("blocked_reason", "") for s in index["steps"] if s["step"] == step_num), "")
print(f" ⏸ Step {step_num}: {step_name} blocked [{elapsed}s]")
print(f" Reason: {reason}")
self._update_top_index("blocked")
sys.exit(2)
err_msg = next(
(s.get("error_message", "Step did not update status") for s in index["steps"] if s["step"] == step_num),
"Step did not update status",
)
if attempt < self.MAX_RETRIES:
for s in index["steps"]:
if s["step"] == step_num:
s["status"] = "pending"
s.pop("error_message", None)
self._write_json(self._index_file, index)
prev_error = err_msg
print(f" ↻ Step {step_num}: retry {attempt}/{self.MAX_RETRIES}{err_msg}")
else:
for s in index["steps"]:
if s["step"] == step_num:
s["status"] = "error"
s["error_message"] = f"[{self.MAX_RETRIES}회 시도 후 실패] {err_msg}"
s["failed_at"] = ts
self._write_json(self._index_file, index)
self._commit_step(step_num, step_name)
print(f" ✗ Step {step_num}: {step_name} failed after {self.MAX_RETRIES} attempts [{elapsed}s]")
print(f" Error: {err_msg}")
self._update_top_index("error")
sys.exit(1)
return False # unreachable
def _execute_all_steps(self, guardrails: str):
while True:
index = self._read_json(self._index_file)
pending = next((s for s in index["steps"] if s["status"] == "pending"), None)
if pending is None:
print("\n All steps completed!")
return
step_num = pending["step"]
for s in index["steps"]:
if s["step"] == step_num and "started_at" not in s:
s["started_at"] = self._stamp()
self._write_json(self._index_file, index)
break
self._execute_single_step(pending, guardrails)
def _finalize(self):
index = self._read_json(self._index_file)
index["completed_at"] = self._stamp()
self._write_json(self._index_file, index)
self._update_top_index("completed")
self._run_git("add", "-A")
if self._run_git("diff", "--cached", "--quiet").returncode != 0:
msg = f"chore({self._phase_name}): mark phase completed"
r = self._run_git("commit", "-m", msg)
if r.returncode == 0:
print(f"{msg}")
if self._auto_push:
branch = f"feat-{self._phase_name}"
r = self._run_git("push", "-u", "origin", branch)
if r.returncode != 0:
print(f"\n ERROR: git push 실패: {r.stderr.strip()}")
sys.exit(1)
print(f" ✓ Pushed to origin/{branch}")
print(f"\n{'='*60}")
print(f" Phase '{self._phase_name}' completed!")
print(f"{'='*60}")
def main():
parser = argparse.ArgumentParser(description="Harness Step Executor")
parser.add_argument("phase_dir", help="Phase directory name (e.g. 0-mvp)")
parser.add_argument("--push", action="store_true", help="Push branch after completion")
args = parser.parse_args()
StepExecutor(args.phase_dir, auto_push=args.push).run()
if __name__ == "__main__":
main()
@@ -0,0 +1,42 @@
{
"name": "agents-md-management",
"version": "1.0.0-codex.1",
"description": "Tools to maintain Codex AGENTS.md guidance: audit quality, capture session learnings, and keep project instructions current.",
"author": {
"name": "Anthropic, converted for Codex locally",
"email": "support@anthropic.com",
"url": "https://github.com/anthropics/claude-plugins-official"
},
"homepage": "https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-md-management",
"repository": "https://github.com/anthropics/claude-plugins-official",
"license": "Apache-2.0",
"keywords": [
"agents-md",
"codex",
"instructions",
"project-memory",
"skills"
],
"skills": "./skills/",
"interface": {
"displayName": "AGENTS.md Management",
"shortDescription": "Audit and update Codex AGENTS.md guidance",
"longDescription": "A Codex conversion of Anthropic's CLAUDE.md management plugin. It provides skills for auditing AGENTS.md quality and capturing useful session learnings into Codex project instructions.",
"developerName": "Anthropic / local Codex conversion",
"category": "Productivity",
"capabilities": [
"Interactive",
"Read",
"Write"
],
"websiteURL": "https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-md-management",
"privacyPolicyURL": "https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement",
"termsOfServiceURL": "https://docs.github.com/en/site-policy/github-terms/github-terms-of-service",
"defaultPrompt": [
"Audit my AGENTS.md files",
"Revise AGENTS.md with this session's learnings"
],
"brandColor": "#10A37F",
"screenshots": []
}
}
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+36
View File
@@ -0,0 +1,36 @@
# AGENTS.md Management Plugin
Codex conversion of Anthropic's CLAUDE.md management plugin.
## What It Does
Two complementary skills help keep Codex instruction files useful and current:
| Skill | Purpose | Use when |
| --- | --- | --- |
| `agents-md-improver` | Audit AGENTS.md quality against the current codebase | You want to check, improve, or repair project guidance |
| `revise-agents-md` | Capture durable learnings from the current session | The session revealed commands, gotchas, setup quirks, or conventions future Codex runs should know |
## Codex Mapping
- `CLAUDE.md` became `AGENTS.md`.
- `.claude.local.md` became Codex's supported override pattern: `AGENTS.override.md`.
- The original `/revise-claude-md` slash command became the `revise-agents-md` skill.
Codex reads global guidance from `~/.codex/AGENTS.md` or `~/.codex/AGENTS.override.md`, then walks from the project root toward the current directory and includes one `AGENTS.override.md` or `AGENTS.md` per directory.
## Usage
Ask Codex things like:
```text
Audit my AGENTS.md files.
Check whether this repo's AGENTS.md is current.
Revise AGENTS.md with the learnings from this session.
```
## Source
Based on `claude-md-management` from:
https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-md-management
@@ -0,0 +1,151 @@
---
name: agents-md-improver
description: Audit and improve Codex AGENTS.md and AGENTS.override.md files in repositories. Use when the user asks to check, audit, update, improve, fix, optimize, or maintain AGENTS.md files, Codex project instructions, or agent guidance.
---
# AGENTS.md Improver
Audit, evaluate, and improve Codex instruction files so future Codex sessions get concise, current, actionable project context.
This skill can write to AGENTS.md files. Always present a quality report and proposed diffs before editing. Only apply changes after the user approves the exact target files or has explicitly asked you to apply a specific diff.
## Codex Instruction Model
Codex discovers guidance in this order:
1. Global scope: `~/.codex/AGENTS.override.md` if present, otherwise `~/.codex/AGENTS.md`.
2. Project scope: from the project root toward the current working directory. In each directory, Codex checks `AGENTS.override.md`, then `AGENTS.md`, then configured fallback names.
3. Merge order: root guidance appears first; files closer to the current directory appear later and can override earlier guidance.
Use `AGENTS.override.md` for stronger or more specific overrides. Use `AGENTS.md` for normal shared guidance.
## Workflow
### Phase 1: Discovery
Find repository instruction files:
```powershell
rg --files -g 'AGENTS.md' -g 'AGENTS.override.md'
```
When the user asks about global defaults too, inspect:
```powershell
Test-Path "$env:USERPROFILE/.codex/AGENTS.md"
Test-Path "$env:USERPROFILE/.codex/AGENTS.override.md"
```
Also inspect relevant project files that prove current reality:
- Package or build manifests: `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `Makefile`, `justfile`, workflow files.
- Repo structure: top-level directories and any nested package boundaries.
- Test and lint configuration.
- Existing `.codex/config.toml`, hooks, and project-specific scripts.
Prefer `rg` and `rg --files` for search. Use the repo's existing conventions before proposing new sections.
### Phase 2: Quality Assessment
For each file, evaluate against `references/quality-criteria.md`.
Score out of 100:
| Criterion | Points |
| --- | ---: |
| Commands and workflows | 20 |
| Architecture clarity | 20 |
| Non-obvious patterns | 15 |
| Conciseness | 15 |
| Currency | 15 |
| Actionability | 15 |
Grades:
- A: 90-100, comprehensive, current, actionable.
- B: 70-89, good coverage with minor gaps.
- C: 50-69, basic info with important gaps.
- D: 30-49, sparse or outdated.
- F: 0-29, missing or severely outdated.
### Phase 3: Quality Report
Always output the report before edits:
```markdown
## AGENTS.md Quality Report
### Summary
- Files found: X
- Average score: X/100
- Files needing update: X
### File-by-File Assessment
#### 1. ./AGENTS.md
**Score: XX/100 (Grade: X)**
| Criterion | Score | Notes |
| --- | ---: | --- |
| Commands/workflows | X/20 | ... |
| Architecture clarity | X/20 | ... |
| Non-obvious patterns | X/15 | ... |
| Conciseness | X/15 | ... |
| Currency | X/15 | ... |
| Actionability | X/15 | ... |
**Issues:**
- ...
**Recommended additions:**
- ...
```
### Phase 4: Targeted Updates
Propose only changes that help future Codex sessions:
- Real commands or workflows discovered during analysis.
- Current architecture facts that are not obvious from filenames.
- Gotchas, environment quirks, or testing approaches that save rediscovery.
- Repository-specific rules that affect how Codex should edit or verify work.
Avoid:
- Generic best practices.
- Obvious descriptions of self-named classes or directories.
- Verbose explanations where one line is enough.
- One-off fixes unlikely to recur.
- Instructions that contradict higher-priority user or project guidance.
Show every proposed change as a focused diff:
```markdown
### Update: ./AGENTS.md
**Why:** The repo has a lint command but the current instructions do not mention verification.
```diff
+## Commands
+
+`npm run lint` - Run ESLint before handing off frontend changes.
```
```
### Phase 5: Apply Approved Updates
After approval, edit only approved files. Preserve existing structure when it is clear. If a file is mostly placeholder content, replace it with a concise project-specific structure using `references/templates.md`.
Before finalizing, validate:
- Commands exist and are named correctly.
- Paths are real.
- Each addition is project-specific.
- The result stays concise enough to be prompt-friendly.
- Nested `AGENTS.override.md` files do not accidentally hide useful `AGENTS.md` guidance in the same directory.
## References
- `references/quality-criteria.md` - scoring rubric.
- `references/templates.md` - concise AGENTS.md templates.
- `references/update-guidelines.md` - what to add and what to avoid.
@@ -0,0 +1,115 @@
# AGENTS.md Quality Criteria
## Scoring Rubric
### 1. Commands and Workflows - 20 points
20 points: all essential commands documented with context.
- Build, test, lint, format, dev, and deploy commands are present when relevant.
- Development workflow is clear.
- Common operations and verification expectations are documented.
15 points: most commands present, some missing context.
10 points: basic commands only, little workflow.
5 points: few commands, many missing.
0 points: no commands documented.
### 2. Architecture Clarity - 20 points
20 points: clear codebase map.
- Key directories explained.
- Module relationships documented.
- Entry points identified.
- Data flow described where relevant.
15 points: good structure overview, minor gaps.
10 points: basic directory listing only.
5 points: vague or incomplete.
0 points: no architecture info.
### 3. Non-Obvious Patterns - 15 points
15 points: gotchas and quirks captured.
- Known issues documented.
- Workarounds explained.
- Edge cases noted.
- Unusual "why this way" rules explained briefly.
10 points: some patterns documented.
5 points: minimal pattern documentation.
0 points: no patterns or gotchas.
### 4. Conciseness - 15 points
15 points: dense, valuable content.
- No filler or obvious info.
- Each line adds value.
- No redundancy with code comments or README basics unless Codex specifically needs it.
10 points: mostly concise, some padding.
5 points: verbose in places.
0 points: mostly filler or restates obvious code.
### 5. Currency - 15 points
15 points: reflects current codebase.
- Commands work as documented.
- File references are accurate.
- Tech stack and architecture are current.
10 points: mostly current, minor staleness.
5 points: several outdated references.
0 points: severely outdated.
### 6. Actionability - 15 points
15 points: instructions are executable.
- Commands can be copied and run.
- Steps are concrete.
- Paths are real.
- Verification expectations are clear.
10 points: mostly actionable.
5 points: some vague instructions.
0 points: vague or theoretical.
## Assessment Process
1. Read each instruction file completely.
2. Cross-reference with the actual codebase.
3. Check documented commands against package manifests, scripts, and config.
4. Verify file and directory references.
5. Score each criterion.
6. Calculate total and assign grade.
7. List specific issues.
8. Propose concrete, concise improvements.
## Red Flags
- Commands that would fail.
- References to deleted files or folders.
- Outdated tech versions.
- Placeholder template content.
- Generic advice not specific to the project.
- Duplicate or contradictory guidance across nested instruction files.
- An `AGENTS.override.md` that accidentally hides a sibling `AGENTS.md`.
@@ -0,0 +1,180 @@
# AGENTS.md Templates
## Key Principles
- Concise: dense, human-readable content; one line per concept when possible.
- Actionable: commands should be copy-paste ready.
- Project-specific: document patterns unique to this repo.
- Current: reflect the actual codebase and active tooling.
- Scoped: put broad rules at the repo root; put narrow rules in nested `AGENTS.md` or `AGENTS.override.md`.
## Recommended Sections
Use only sections that matter for the project.
### Commands
```markdown
## Commands
| Command | Purpose |
| --- | --- |
| `<install command>` | Install dependencies |
| `<dev command>` | Start development server |
| `<build command>` | Production build |
| `<test command>` | Run tests |
| `<lint command>` | Lint or format |
```
### Architecture
```markdown
## Architecture
```
<root>/
<dir>/ # <purpose>
<dir>/ # <purpose>
<dir>/ # <purpose>
```
```
### Key Files
```markdown
## Key Files
- `<path>` - <purpose>
- `<path>` - <purpose>
```
### Code Style
```markdown
## Code Style
- <project-specific convention>
- <preference over alternative>
```
### Environment
```markdown
## Environment
Required:
- `<VAR_NAME>` - <purpose>
Setup:
- <setup step>
```
### Testing
```markdown
## Testing
- `<test command>` - <what it tests>
- <testing convention or pattern>
```
### Gotchas
```markdown
## Gotchas
- <non-obvious issue, prerequisite, or ordering dependency>
```
### Workflow
```markdown
## Workflow
- <when to do X>
- <preferred approach for Y>
```
## Minimal Project Root Template
```markdown
# <Project Name>
<One-line description>
## Commands
| Command | Purpose |
| --- | --- |
| `<command>` | <description> |
## Architecture
```
<structure>
```
## Gotchas
- <gotcha>
```
## Comprehensive Project Root Template
```markdown
# <Project Name>
<One-line description>
## Commands
| Command | Purpose |
| --- | --- |
| `<command>` | <description> |
## Architecture
```
<structure with descriptions>
```
## Key Files
- `<path>` - <purpose>
## Code Style
- <convention>
## Environment
- `<VAR>` - <purpose>
## Testing
- `<command>` - <scope>
## Gotchas
- <gotcha>
```
## Nested Override Template
Use this for a subdirectory whose rules differ from the repo root.
```markdown
# <Directory> Instructions
## Scope
- Applies to `<path>/`.
## Local Rules
- <specific rule that differs from root guidance>
## Verification
- `<command>` - <focused check for this directory>
```
@@ -0,0 +1,131 @@
# AGENTS.md Update Guidelines
## Core Principle
Only add information that will genuinely help future Codex sessions. The context window is precious; every line must earn its place.
## What To Add
### Commands and Workflows Discovered
```markdown
## Commands
`npm run build` - Production build.
`npm run lint` - ESLint check required before handoff.
```
Why: saves future sessions from rediscovering commands.
### Gotchas and Non-Obvious Patterns
```markdown
## Gotchas
- Tests must run sequentially because they share a local database.
- Regenerate API clients with `npm run codegen` after editing OpenAPI files.
```
Why: prevents repeated debugging.
### Package Relationships
```markdown
## Architecture
- `packages/api` owns route handlers; `packages/ui` must call it through the shared client.
```
Why: captures relationships that are not obvious from filenames.
### Testing Approaches That Worked
```markdown
## Testing
- API endpoint tests use `tests/helpers/request.ts`; avoid ad hoc server setup.
```
Why: establishes known-good patterns.
### Configuration Quirks
```markdown
## Environment
- `NEXT_PUBLIC_*` variables must be set at build time.
```
Why: environment details often waste time when omitted.
## What Not To Add
### Obvious Code Info
Bad:
```markdown
The `UserService` class handles user operations.
```
The class name already says that.
### Generic Best Practices
Bad:
```markdown
Always write tests for new features.
Use meaningful variable names.
```
Add only project-specific rules.
### One-Off Fixes
Bad:
```markdown
We fixed a bug where the login button did not work.
```
Unless the lesson is durable, leave it out.
### Verbose Explanations
Bad:
```markdown
The authentication system uses JWT tokens. JWT means JSON Web Token...
```
Good:
```markdown
Auth: JWT HS256 tokens in `Authorization: Bearer <token>`.
```
## Diff Format
For each suggested change:
```markdown
### Update: ./AGENTS.md
**Why:** The build command exists but was missing from project instructions.
```diff
+## Commands
+
+`npm run build` - Production build.
```
```
## Validation Checklist
- Each addition is project-specific.
- No generic advice or obvious info.
- Commands are verified against the repo.
- File paths are accurate.
- The wording is concise.
- A new Codex session would benefit from the line.
@@ -0,0 +1,97 @@
---
name: revise-agents-md
description: Capture durable learnings from the current Codex session into AGENTS.md or AGENTS.override.md. Use when the user asks to revise AGENTS.md, update Codex project instructions from this session, capture session learnings, or keep AGENTS.md current at the end of work.
---
# Revise AGENTS.md
Review this session for durable learnings about working with Codex in this codebase. Propose concise updates to `AGENTS.md` or `AGENTS.override.md` that would help future Codex sessions be more effective.
Always show proposed changes before editing. Only apply changes after user approval unless the user provided an exact patch to apply.
## Step 1: Reflect
Identify context that was missing or newly confirmed:
- Commands that were used or discovered.
- Code style patterns followed.
- Testing approaches that worked.
- Environment or configuration quirks.
- Warnings, gotchas, or failure modes encountered.
- Codex-specific workflow notes, such as hooks, plugins, skill locations, or project instruction precedence.
Do not add transient details, personal commentary, or one-off fixes.
## Step 2: Find Instruction Files
Find project instruction files:
```powershell
rg --files -g 'AGENTS.md' -g 'AGENTS.override.md'
```
Choose the right target:
- `AGENTS.md` - Normal shared guidance for the repo or a directory.
- `AGENTS.override.md` - Stronger guidance that should override sibling `AGENTS.md` in the same directory.
- `~/.codex/AGENTS.md` - User-wide Codex defaults, only when the learning is not repo-specific.
- `~/.codex/AGENTS.override.md` - User-wide hard override, use sparingly.
If no project `AGENTS.md` exists and the user wants persistent repo guidance, propose creating one at the project root.
## Step 3: Draft Additions
Keep additions concise. One line per concept is usually enough.
Preferred format:
```markdown
`<command or path>` - <brief durable note>
```
or, for rules:
```markdown
- <specific recurring rule or gotcha>
```
Avoid:
- Verbose explanations.
- Obvious information.
- Generic best practices.
- One-off fixes unlikely to recur.
- Duplicating content already present in a clearer form.
## Step 4: Show Proposed Changes
For each addition:
```markdown
### Update: ./AGENTS.md
**Why:** <one-line reason>
```diff
+<the addition - keep it brief>
```
```
If multiple files could receive the update, explain why the selected target has the right scope.
## Step 5: Apply with Approval
Ask whether the user wants to apply the changes. If approved:
- Edit only the approved files.
- Preserve existing section order where possible.
- Add a new section only when the content does not fit an existing one.
- Keep wording terse and actionable.
## Final Check
Before final response:
- Confirm the final file still reads cleanly.
- Confirm no stale or contradictory guidance was introduced.
- Mention which file changed and what category of learning was captured.