modify skill and plugin
This commit is contained in:
@@ -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."
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#:schema https://developers.openai.com/codex/config-schema.json
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hooks = true
|
||||||
@@ -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())
|
||||||
@@ -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())
|
||||||
@@ -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
|
||||||
@@ -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 # 테스트
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Architecture Decision Records
|
||||||
|
|
||||||
|
## 철학
|
||||||
|
{프로젝트의 핵심 가치관 (예: MVP 속도 최우선. 외부 의존성 최소화. 작동하는 최소 구현을 선택.)}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ADR-001: {결정 사항 (예: Next.js App Router 선택)}
|
||||||
|
**결정**: {뭘 선택했는지}
|
||||||
|
**이유**: {왜 선택했는지}
|
||||||
|
**트레이드오프**: {뭘 포기했는지}
|
||||||
|
|
||||||
|
### ADR-002: {결정 사항}
|
||||||
|
**결정**: {뭘 선택했는지}
|
||||||
|
**이유**: {왜 선택했는지}
|
||||||
|
**트레이드오프**: {뭘 포기했는지}
|
||||||
|
|
||||||
|
### ADR-003: {결정 사항}
|
||||||
|
**결정**: {뭘 선택했는지}
|
||||||
|
**이유**: {왜 선택했는지}
|
||||||
|
**트레이드오프**: {뭘 포기했는지}
|
||||||
@@ -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)}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# PRD: {프로젝트명}
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
{이 프로젝트가 해결하려는 문제를 한 줄로 요약}
|
||||||
|
|
||||||
|
## 사용자
|
||||||
|
{누가 이 제품을 쓰는지}
|
||||||
|
|
||||||
|
## 핵심 기능
|
||||||
|
1. {기능 1}
|
||||||
|
2. {기능 2}
|
||||||
|
3. {기능 3}
|
||||||
|
|
||||||
|
## MVP 제외 사항
|
||||||
|
- {안 만들 것 1}
|
||||||
|
- {안 만들 것 2}
|
||||||
|
- {안 만들 것 3}
|
||||||
|
|
||||||
|
## 디자인
|
||||||
|
- {디자인 방향 (예: 다크모드 고정, 미니멀)}
|
||||||
|
- {색상 (예: 무채색 + 포인트 1가지)}
|
||||||
@@ -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}
|
||||||
|
- {예: 아이콘 컨테이너(둥근 배경 박스)로 감싸지 않는다}
|
||||||
@@ -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": []
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
+115
@@ -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>
|
||||||
|
```
|
||||||
+131
@@ -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.
|
||||||
Reference in New Issue
Block a user