@moon791017/neo-skills 1.1.23 → 1.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/package.json +1 -1
- package/skills/neo-code-review/SKILL.md +51 -62
- package/skills/neo-code-review/evals/eval_queries.json +22 -20
- package/skills/neo-code-review/evals/evals.json +26 -15
- package/skills/neo-code-review/scripts/git-diff-reviewer.py +151 -87
- package/skills/neo-harness/SKILL.md +18 -13
- package/skills/neo-harness/references/execution-protocol.md +17 -0
- package/skills/neo-harness/scripts/generate-agents-md.py +74 -1
package/AGENTS.md
CHANGED
|
@@ -46,8 +46,8 @@ graph TD
|
|
|
46
46
|
4. **Plan Edits**: Decompose the task into small, atomic edit plans to avoid large single-turn changes.
|
|
47
47
|
5. **Minimal Implementation**: Apply minimal code changes while preserving existing comments and public API contracts.
|
|
48
48
|
6. **Incremental Verification**: Run local test, lint, and build commands immediately after each edit.
|
|
49
|
-
7. **Self-Review**: Inspect diffs and execution logs; if checks fail, read full un-truncated error logs to find the root cause.
|
|
50
|
-
8. **Remediate on Failure**: If verification or
|
|
49
|
+
7. **Self-Review**: After an AI Agent finishes modifying code, tests, configuration, or scripts, activate `neo-code-review` and review the current uncommitted working tree with `--working-tree`. Inspect diffs and execution logs; if checks fail, read full un-truncated error logs to find the root cause.
|
|
50
|
+
8. **Remediate on Failure**: If verification or review finds a critical issue, the review scope is unclear, or the skill cannot be loaded, do not declare completion or hand off. Return to "Plan Edits / Minimal Implementation", then rerun verification and `neo-code-review` after each remediation.
|
|
51
51
|
9. **Handoff upon Pass**: Once all verification sensors pass, present a concise report with empirical proof for handoff.
|
|
52
52
|
|
|
53
53
|
---
|
package/package.json
CHANGED
|
@@ -1,81 +1,70 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: neo-code-review
|
|
3
3
|
description: >
|
|
4
|
-
Use this skill when the user asks
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Use this skill when the user asks to review or audit source code, a PR, diff, commit,
|
|
5
|
+
or recent changes for bugs, security, performance, tests, compatibility, or
|
|
6
|
+
maintainability. Also use it after an AI agent finishes modifying code to inspect the
|
|
7
|
+
current uncommitted working tree before handoff and report actionable findings with
|
|
8
|
+
file and line evidence.
|
|
9
|
+
compatibility: Requires Python 3.11+ and uv for the Git change helper.
|
|
7
10
|
---
|
|
8
11
|
|
|
9
|
-
# Code Review
|
|
12
|
+
# Code Review
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
Use the Reviewer workflow. Keep the review read-only and report only evidence-backed, actionable findings.
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## 1. Perceive Phase
|
|
16
|
+
## Trigger On
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- If the user provides specific file paths or directly attaches code snippets in the conversation, use them as the primary targets for review.
|
|
20
|
-
- **Scenario B: No files are specified but inside a Git repository**
|
|
21
|
-
- If the user asks to "review changes", "review PR", or asks for a review without providing explicit code but the project is a Git repository, **proactively run the helper script** to fetch the changes:
|
|
22
|
-
```bash
|
|
23
|
-
uv run skills/neo-code-review/scripts/git-diff-reviewer.py
|
|
24
|
-
```
|
|
25
|
-
- To review only the staging area, append the `--staged` argument.
|
|
26
|
-
- To review a specific commit range, append the `--commit <range>` argument.
|
|
27
|
-
- **Scenario C: No changes detected and not a Git repository**
|
|
28
|
-
- If no code input is detected and the environment is not a Git repository, gracefully prompt the user: "No code changes detected. Please provide a specific code snippet or file path." and terminate the review.
|
|
18
|
+
- The user requests a code, PR, diff, commit, security, performance, test, or maintainability review.
|
|
19
|
+
- An AI Agent finishes modifying code, tests, configuration, or scripts before handoff.
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
- Identify the programming language (e.g., TypeScript, C#, Python) and relevant frameworks in the code changes to apply language-specific style standards in subsequent phases.
|
|
21
|
+
## Workflow
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
1. **Collect the review scope**
|
|
24
|
+
- Use the files or code snippets supplied by the user as the primary scope.
|
|
25
|
+
- If no scope is supplied inside a Git repository, run:
|
|
26
|
+
```bash
|
|
27
|
+
uv run skills/neo-code-review/scripts/git-diff-reviewer.py --working-tree
|
|
28
|
+
```
|
|
29
|
+
- Use `--staged` for staged-only changes and `--commit <range>` for a commit range.
|
|
30
|
+
- Read surrounding code, related tests, and existing contracts. If no reviewable input exists, report it and stop.
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
2. **Load the rules**
|
|
33
|
+
- Read [review-checklist.md](references/review-checklist.md) before evaluating code.
|
|
34
|
+
- Identify the actual language, framework, and affected behavior from the change. Do not infer unverified rules from filenames.
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- Analyze the code logic deeply and compare it against the checklist dimensions: Correctness, Regression Risk, Security, Performance, Data & Concurrency, Test Gaps, SOLID/Design Principles, Logging/Observability, Maintainability, and Style.
|
|
43
|
-
- Prioritize evidence-backed findings that create concrete behavioral, security, operational, compatibility, or maintainability risk.
|
|
44
|
-
- Filter out **🔴 Critical Issues (Must-fix)** for defects such as requirement breakage, security vulnerabilities, data corruption/loss, broken authorization, severe regressions, or production-impacting reliability failures.
|
|
45
|
-
- Classify lower-risk performance, design, logging, test coverage, readability, and maintainability issues under **🟡 Suggestions** when they have a clear reason and actionable remediation.
|
|
46
|
-
|
|
47
|
-
---
|
|
36
|
+
3. **Analyze risk**
|
|
37
|
+
- Prioritize correctness, regressions, security, data consistency, resource lifetime, concurrency, and high-risk test gaps.
|
|
38
|
+
- `🔴 Critical Issues` contain only security vulnerabilities, data loss, incorrect behavior, authorization bypasses, or major regressions.
|
|
39
|
+
- `🟡 Suggestions` contain only actionable, non-blocking issues with a clear impact and remediation direction.
|
|
40
|
+
- Do not report preferences, unsupported speculation, or checklist items that were not triggered by evidence.
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
4. **Produce a concise report**
|
|
43
|
+
- Write in English and preserve file paths and line numbers, such as `src/user.ts:42`.
|
|
44
|
+
- Use this structure. Write `None` when a section has no findings; do not omit the severity sections:
|
|
50
45
|
|
|
51
|
-
|
|
46
|
+
```markdown
|
|
47
|
+
## 🔍 Review Summary
|
|
48
|
+
- **Status**: `🟢 Ready to deliver`, `🟡 Fix recommended`, `🔴 Delivery blocked`, or `⚪ No reviewable changes`
|
|
49
|
+
- **Scope**: Files or changes that were inspected.
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
51
|
+
## 🔴 Critical Issues
|
|
52
|
+
- **Location**: `path:line`
|
|
53
|
+
- **Problem**: Specific behavioral defect.
|
|
54
|
+
- **Impact**: Verifiable consequence.
|
|
55
|
+
- **Fix**: Concrete remediation direction; include code only when it clarifies the fix.
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- **
|
|
60
|
-
- **問題描述 (Problem Description)**: Clearly state what the problem is.
|
|
61
|
-
- **嚴重原因 (Severity Reason)**: Explain *why* this is a critical issue and its potential consequences.
|
|
62
|
-
- **修復方案 (Remediation)**: Provide a **concrete corrected code snippet (Code Snippet)** comparing it with the original or showing the fixed version.
|
|
57
|
+
## 🟡 Suggestions
|
|
58
|
+
- **Location**: `path:line`
|
|
59
|
+
- **Recommendation**: Specific, non-blocking improvement.
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- **[檔案路徑 / 程式碼區段]** (File Path / Code Snippet)
|
|
67
|
-
- **建議事項 (Recommendation)**: How to optimize the code for performance, readability, or maintainability.
|
|
68
|
-
- **推薦做法 (Proposed Fix)**: Provide an optimized code example.
|
|
69
|
-
|
|
70
|
-
### 🟢 優秀之處 (Praise) (Optional)
|
|
71
|
-
- Highlight exceptionally well-written, elegant, highly readable, or well-tested code sections to encourage best practices.
|
|
72
|
-
|
|
73
|
-
### ❓ 疑問與釐清 (Questions) (Optional)
|
|
74
|
-
- Ask objective questions regarding business logic or unexpected implementations to seek clarification from the user.
|
|
75
|
-
|
|
76
|
-
---
|
|
61
|
+
## 🟢 Strengths
|
|
62
|
+
- List only strengths directly supported by the change.
|
|
77
63
|
|
|
78
|
-
##
|
|
64
|
+
## Validation
|
|
65
|
+
- List executed tests, builds, or checks that were not run.
|
|
66
|
+
```
|
|
79
67
|
|
|
80
|
-
|
|
81
|
-
-
|
|
68
|
+
5. **Apply the delivery gate**
|
|
69
|
+
- If an automatic review finds `🔴 Critical Issues`, cannot establish the review scope, or cannot load the skill, do not declare completion or hand off.
|
|
70
|
+
- After fixing findings, rerun the required checks and review the changes again.
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"should_trigger": [
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
3
|
+
"Please review this pull request for correctness and regressions",
|
|
4
|
+
"Does this code contain potential bugs or memory leaks?",
|
|
5
|
+
"Review the security of this JavaScript file",
|
|
6
|
+
"Audit this Python script for potential SQL injection or security flaws",
|
|
7
|
+
"Review today's Git changes",
|
|
8
|
+
"Review the changes in my latest Git commit",
|
|
9
|
+
"Refactor and review this C# code for SOLID violations",
|
|
10
|
+
"Is this TypeScript code well designed? What should be improved?",
|
|
11
|
+
"Check this SQL query for performance problems, slow queries, or missing indexes",
|
|
12
|
+
"Run static code analysis and identify problems in this Go project",
|
|
13
|
+
"An AI Agent just finished modifying code. Review the uncommitted changes before handoff"
|
|
13
14
|
],
|
|
14
15
|
"should_not_trigger": [
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
16
|
+
"How do I install Git?",
|
|
17
|
+
"Write a FastAPI endpoint that uploads files",
|
|
18
|
+
"Explain the N+1 query problem and how to solve it",
|
|
19
|
+
"Create a new React project",
|
|
20
|
+
"Recommend a Python linter",
|
|
21
|
+
"How do I configure tsconfig.json for ESM?",
|
|
22
|
+
"How do I deploy a Docker container on macOS?",
|
|
23
|
+
"Translate this Python code into Rust",
|
|
24
|
+
"What is the principle behind SQL injection?",
|
|
25
|
+
"Generate today's development progress report",
|
|
26
|
+
"Run the current project's tests only; do not perform a code review"
|
|
25
27
|
]
|
|
26
28
|
}
|
|
@@ -3,27 +3,38 @@
|
|
|
3
3
|
"evals": [
|
|
4
4
|
{
|
|
5
5
|
"id": 1,
|
|
6
|
-
"prompt": "
|
|
7
|
-
"expected_output": "
|
|
6
|
+
"prompt": "Review this Python code:\n```python\nimport sqlite3\n\ndef get_user_data(user_id):\n conn = sqlite3.connect('users.db')\n cursor = conn.cursor()\n query = f\"SELECT * FROM users WHERE id = '{user_id}'\"\n cursor.execute(query)\n return cursor.fetchall()\n```",
|
|
7
|
+
"expected_output": "A concise English review report. It must identify SQL injection caused by user_id and the unreleased database resources, classify SQL injection as a 🔴 Critical Issue, and provide concrete remediation using a parameterized query and resource management.",
|
|
8
8
|
"assertions": [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"提供使用參數化查詢 (parameterized query) 或 with 語法的修復代碼範例"
|
|
9
|
+
"The output contains '🔍 Review Summary', '🔴 Critical Issues', '🟡 Suggestions', and '## Validation'",
|
|
10
|
+
"The report identifies the SQL injection vulnerability and its verifiable impact",
|
|
11
|
+
"The report identifies the database connection or cursor resource leak",
|
|
12
|
+
"The report classifies SQL injection as a delivery-blocking issue",
|
|
13
|
+
"The report provides remediation using a parameterized query and resource management",
|
|
14
|
+
"The report is written in English and contains no unsupported additional findings"
|
|
16
15
|
]
|
|
17
16
|
},
|
|
18
17
|
{
|
|
19
18
|
"id": 2,
|
|
20
|
-
"prompt": "
|
|
21
|
-
"expected_output": "
|
|
19
|
+
"prompt": "Review this TypeScript code with no obvious problems and provide recommendations:\n```typescript\nexport interface User {\n id: string;\n name: string;\n email: string;\n}\n\nexport function formatUserGreeting(user: User): string {\n return `Hello, ${user.name}! Welcome back.`;\n}\n```",
|
|
20
|
+
"expected_output": "A concise English review report. 🔴 Critical Issues must be None, the status must not block delivery, and the 🟢 Strengths section should recognize the clear types and simple function design.",
|
|
22
21
|
"assertions": [
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
22
|
+
"The output contains '🔍 Review Summary', '🔴 Critical Issues', '🟢 Strengths', and '## Validation'",
|
|
23
|
+
"The 🔴 Critical Issues section explicitly says None",
|
|
24
|
+
"The report does not invent security, performance, or test defects",
|
|
25
|
+
"The report recognizes clear type definitions, simple logic, and good readability"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": 3,
|
|
30
|
+
"prompt": "An AI Agent just finished modifying code, tests, and configuration. Before handoff, run neo-code-review against the current uncommitted working tree, including staged, unstaged, and untracked files. Stop delivery if critical issues are found.",
|
|
31
|
+
"expected_output": "A concise English report scoped to the current uncommitted working tree. It must cite actual file and line evidence and use 🔴 Critical Issues to decide whether delivery is blocked. If no readable changes exist, it must state that no reviewable changes were found without inventing findings.",
|
|
32
|
+
"assertions": [
|
|
33
|
+
"The report confirms that the current uncommitted working tree was reviewed",
|
|
34
|
+
"The report distinguishes the staged, unstaged, and untracked file inputs",
|
|
35
|
+
"The report cites file paths and line numbers or clearly explains why they are unavailable",
|
|
36
|
+
"If 🔴 Critical Issues are found, the status blocks delivery",
|
|
37
|
+
"The report invents no findings when no changes exist"
|
|
27
38
|
]
|
|
28
39
|
}
|
|
29
40
|
]
|
|
@@ -4,168 +4,232 @@
|
|
|
4
4
|
# dependencies = []
|
|
5
5
|
# ///
|
|
6
6
|
|
|
7
|
-
"""
|
|
8
|
-
AI Agentic Git Diff & Code Reader Tool (Non-interactive)
|
|
9
|
-
專為 AI Agent 設計的非互動式程式碼變更與內容擷取腳本。
|
|
7
|
+
"""Non-interactive Git change and file reader for AI Agents.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
1.
|
|
13
|
-
2.
|
|
14
|
-
3.
|
|
9
|
+
Output rules:
|
|
10
|
+
1. Never request interactive input.
|
|
11
|
+
2. Write diagnostics to stderr.
|
|
12
|
+
3. Write JSON results to stdout or the file supplied with --output.
|
|
15
13
|
"""
|
|
16
14
|
|
|
17
|
-
import sys
|
|
18
15
|
import argparse
|
|
19
|
-
import subprocess
|
|
20
16
|
import json
|
|
21
17
|
import os
|
|
22
|
-
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
from typing import Any, Dict, List
|
|
21
|
+
|
|
23
22
|
|
|
24
23
|
def log_diagnostic(message: str) -> None:
|
|
25
|
-
"""
|
|
24
|
+
"""Write a diagnostic message to stderr."""
|
|
26
25
|
print(f"[LOG] {message}", file=sys.stderr)
|
|
27
26
|
|
|
27
|
+
|
|
28
28
|
def run_command(cmd: List[str]) -> str:
|
|
29
|
-
"""
|
|
29
|
+
"""Run a command and return stdout while preserving stderr on failure."""
|
|
30
30
|
try:
|
|
31
31
|
result = subprocess.run(
|
|
32
32
|
cmd,
|
|
33
33
|
capture_output=True,
|
|
34
34
|
text=True,
|
|
35
|
-
check=True
|
|
35
|
+
check=True,
|
|
36
36
|
)
|
|
37
37
|
return result.stdout
|
|
38
|
-
except subprocess.CalledProcessError as
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
except subprocess.CalledProcessError as error:
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
f"Command failed: {' '.join(cmd)}\nStderr: {error.stderr}"
|
|
41
|
+
) from error
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def is_git_repository() -> bool:
|
|
45
|
+
"""Return whether the current path is inside a Git worktree."""
|
|
46
|
+
try:
|
|
47
|
+
run_command(["git", "rev-parse", "--is-inside-work-tree"])
|
|
48
|
+
return True
|
|
49
|
+
except RuntimeError:
|
|
50
|
+
return False
|
|
41
51
|
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
|
|
53
|
+
def has_git_head() -> bool:
|
|
54
|
+
"""Return whether the repository has at least one commit."""
|
|
55
|
+
try:
|
|
56
|
+
run_command(["git", "rev-parse", "--verify", "HEAD"])
|
|
57
|
+
return True
|
|
58
|
+
except RuntimeError:
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_git_diff(staged_only: bool = False, commit_range: str | None = None) -> str:
|
|
63
|
+
"""Get staged changes or a requested commit range."""
|
|
44
64
|
cmd = ["git", "diff"]
|
|
45
65
|
if commit_range:
|
|
46
66
|
cmd.append(commit_range)
|
|
47
67
|
elif staged_only:
|
|
48
68
|
cmd.append("--cached")
|
|
49
|
-
|
|
50
|
-
log_diagnostic(f"
|
|
69
|
+
|
|
70
|
+
log_diagnostic(f"Running Git command: {' '.join(cmd)}")
|
|
51
71
|
return run_command(cmd)
|
|
52
72
|
|
|
73
|
+
|
|
74
|
+
def get_working_tree_diff() -> str:
|
|
75
|
+
"""Get tracked changes after HEAD, including staged and unstaged changes."""
|
|
76
|
+
if has_git_head():
|
|
77
|
+
cmd = ["git", "diff", "HEAD", "--"]
|
|
78
|
+
log_diagnostic(f"Running Git command: {' '.join(cmd)}")
|
|
79
|
+
return run_command(cmd)
|
|
80
|
+
|
|
81
|
+
log_diagnostic("The repository has no HEAD; collecting staged and unstaged changes separately.")
|
|
82
|
+
parts = [
|
|
83
|
+
run_command(["git", "diff", "--cached", "--"]),
|
|
84
|
+
run_command(["git", "diff", "--"]),
|
|
85
|
+
]
|
|
86
|
+
return "\n".join(part for part in parts if part.strip())
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_untracked_files() -> List[str]:
|
|
90
|
+
"""Get non-ignored untracked files while preserving spaces in paths."""
|
|
91
|
+
output = run_command(["git", "ls-files", "--others", "--exclude-standard", "-z"])
|
|
92
|
+
return [path for path in output.split("\0") if path]
|
|
93
|
+
|
|
94
|
+
|
|
53
95
|
def read_files_content(paths: List[str]) -> List[Dict[str, str]]:
|
|
54
|
-
"""
|
|
55
|
-
contents = []
|
|
96
|
+
"""Read the contents of the supplied files."""
|
|
97
|
+
contents: List[Dict[str, str]] = []
|
|
56
98
|
for path in paths:
|
|
57
99
|
if not os.path.exists(path):
|
|
58
|
-
log_diagnostic(f"[
|
|
100
|
+
log_diagnostic(f"[WARN] File does not exist; skipping: {path}")
|
|
59
101
|
continue
|
|
60
102
|
if os.path.isdir(path):
|
|
61
|
-
log_diagnostic(f"[
|
|
103
|
+
log_diagnostic(f"[WARN] Path is a directory; skipping: {path}")
|
|
62
104
|
continue
|
|
63
|
-
|
|
105
|
+
|
|
64
106
|
try:
|
|
65
|
-
log_diagnostic(f"
|
|
66
|
-
with open(path, "r", encoding="utf-8", errors="ignore") as
|
|
67
|
-
contents.append({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
except Exception as e:
|
|
72
|
-
log_diagnostic(f"[錯誤] 無法讀取檔案 {path}: {str(e)}")
|
|
73
|
-
|
|
107
|
+
log_diagnostic(f"Reading file: {path}")
|
|
108
|
+
with open(path, "r", encoding="utf-8", errors="ignore") as file:
|
|
109
|
+
contents.append({"path": path, "content": file.read()})
|
|
110
|
+
except OSError as error:
|
|
111
|
+
log_diagnostic(f"[ERROR] Could not read {path}: {error}")
|
|
112
|
+
|
|
74
113
|
return contents
|
|
75
114
|
|
|
115
|
+
|
|
76
116
|
def parse_args() -> argparse.Namespace:
|
|
77
117
|
parser = argparse.ArgumentParser(
|
|
78
|
-
description="
|
|
79
|
-
epilog="
|
|
118
|
+
description="Non-interactive Git change and file reader for AI Agents.",
|
|
119
|
+
epilog="Example: uv run git-diff-reviewer.py --working-tree",
|
|
80
120
|
)
|
|
81
121
|
parser.add_argument(
|
|
82
|
-
"-i",
|
|
122
|
+
"-i",
|
|
123
|
+
"--input",
|
|
83
124
|
nargs="*",
|
|
84
|
-
help="
|
|
125
|
+
help="File paths to review; use Git change mode when omitted.",
|
|
85
126
|
)
|
|
86
127
|
parser.add_argument(
|
|
87
128
|
"--staged",
|
|
88
129
|
action="store_true",
|
|
89
|
-
help="
|
|
130
|
+
help="Read staged Git changes only.",
|
|
131
|
+
)
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--working-tree",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Read tracked changes after HEAD and non-ignored untracked files.",
|
|
90
136
|
)
|
|
91
137
|
parser.add_argument(
|
|
92
138
|
"--commit",
|
|
93
|
-
help="
|
|
139
|
+
help="Commit hash or range, for example HEAD~1..HEAD.",
|
|
94
140
|
)
|
|
95
141
|
parser.add_argument(
|
|
96
|
-
"-o",
|
|
97
|
-
|
|
142
|
+
"-o",
|
|
143
|
+
"--output",
|
|
144
|
+
help="Write JSON to this file instead of stdout.",
|
|
98
145
|
)
|
|
99
146
|
parser.add_argument(
|
|
100
147
|
"--dry-run",
|
|
101
148
|
action="store_true",
|
|
102
|
-
help="
|
|
149
|
+
help="Check the selected mode and Git state without reading change content.",
|
|
103
150
|
)
|
|
104
|
-
|
|
151
|
+
args = parser.parse_args()
|
|
152
|
+
|
|
153
|
+
selectors = [
|
|
154
|
+
bool(args.input),
|
|
155
|
+
args.staged,
|
|
156
|
+
args.working_tree,
|
|
157
|
+
bool(args.commit),
|
|
158
|
+
]
|
|
159
|
+
if sum(selectors) > 1:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
"The --input, --staged, --working-tree, and --commit options are mutually exclusive."
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return args
|
|
165
|
+
|
|
105
166
|
|
|
106
167
|
def main() -> None:
|
|
107
168
|
try:
|
|
108
169
|
args = parse_args()
|
|
109
|
-
|
|
110
|
-
# 準備回傳的結構化資料
|
|
111
170
|
result: Dict[str, Any] = {
|
|
112
171
|
"mode": "git-diff",
|
|
113
172
|
"diff": "",
|
|
114
173
|
"files": [],
|
|
115
|
-
"status": "success"
|
|
174
|
+
"status": "success",
|
|
116
175
|
}
|
|
117
|
-
|
|
118
|
-
# 1. 如果指定了具體檔案
|
|
176
|
+
|
|
119
177
|
if args.input:
|
|
120
178
|
result["mode"] = "specific-files"
|
|
121
|
-
log_diagnostic(f"
|
|
179
|
+
log_diagnostic(f"Specific-file review mode; file count: {len(args.input)}")
|
|
122
180
|
if not args.dry_run:
|
|
123
181
|
result["files"] = read_files_content(args.input)
|
|
124
|
-
|
|
125
|
-
# 2. 否則,預設使用 Git 變更模式
|
|
126
182
|
else:
|
|
127
|
-
|
|
128
|
-
log_diagnostic("未指定特定檔案,進入 Git 變更審查模式。")
|
|
129
|
-
|
|
130
|
-
# 先檢查是否為 git 專案
|
|
131
|
-
try:
|
|
132
|
-
run_command(["git", "rev-parse", "--is-inside-work-tree"])
|
|
133
|
-
except Exception:
|
|
183
|
+
if not is_git_repository():
|
|
134
184
|
result["status"] = "error"
|
|
135
|
-
result["error_message"] = "
|
|
136
|
-
print(json.dumps(result, indent=2))
|
|
137
|
-
log_diagnostic("[
|
|
185
|
+
result["error_message"] = "The current path is not a Git repository and no input files were supplied."
|
|
186
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
187
|
+
log_diagnostic("[ERROR] The current path is not a Git repository.")
|
|
138
188
|
sys.exit(1)
|
|
139
|
-
|
|
140
|
-
if
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
result["
|
|
145
|
-
|
|
146
|
-
result["
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
189
|
+
|
|
190
|
+
if args.working_tree:
|
|
191
|
+
result["mode"] = "working-tree"
|
|
192
|
+
log_diagnostic("Entering working-tree review mode.")
|
|
193
|
+
if not args.dry_run:
|
|
194
|
+
result["diff"] = get_working_tree_diff()
|
|
195
|
+
untracked_files = get_untracked_files()
|
|
196
|
+
result["files"] = read_files_content(untracked_files)
|
|
197
|
+
if not result["diff"].strip() and not untracked_files:
|
|
198
|
+
result["status"] = "empty"
|
|
199
|
+
log_diagnostic("No tracked or untracked changes were detected.")
|
|
200
|
+
else:
|
|
201
|
+
log_diagnostic("Entering Git change review mode.")
|
|
202
|
+
if not args.dry_run:
|
|
203
|
+
diff_content = get_git_diff(
|
|
204
|
+
staged_only=args.staged,
|
|
205
|
+
commit_range=args.commit,
|
|
206
|
+
)
|
|
207
|
+
if not diff_content.strip():
|
|
208
|
+
result["status"] = "empty"
|
|
209
|
+
log_diagnostic("No Git changes were detected.")
|
|
210
|
+
else:
|
|
211
|
+
result["diff"] = diff_content
|
|
212
|
+
|
|
213
|
+
output_str = json.dumps(result, indent=2, ensure_ascii=False)
|
|
150
214
|
if args.output:
|
|
151
|
-
with open(args.output, "w", encoding="utf-8") as
|
|
152
|
-
|
|
153
|
-
log_diagnostic(f"
|
|
215
|
+
with open(args.output, "w", encoding="utf-8") as file:
|
|
216
|
+
file.write(output_str)
|
|
217
|
+
log_diagnostic(f"Wrote JSON result to: {args.output}")
|
|
154
218
|
else:
|
|
155
|
-
# 乾淨地將結果印在 stdout
|
|
156
219
|
print(output_str)
|
|
157
|
-
|
|
220
|
+
|
|
158
221
|
sys.exit(0)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
222
|
+
except Exception as error:
|
|
223
|
+
log_diagnostic(f"[FATAL] Execution failed: {error}")
|
|
224
|
+
print(
|
|
225
|
+
json.dumps(
|
|
226
|
+
{"status": "error", "error_message": str(error)},
|
|
227
|
+
indent=2,
|
|
228
|
+
ensure_ascii=False,
|
|
229
|
+
)
|
|
230
|
+
)
|
|
168
231
|
sys.exit(1)
|
|
169
232
|
|
|
233
|
+
|
|
170
234
|
if __name__ == "__main__":
|
|
171
235
|
main()
|
|
@@ -25,7 +25,8 @@ Agent = Model (Reasoning & Generation) + Harness (Guides + Sensors + Gates + Top
|
|
|
25
25
|
The generated `AGENTS.md` acts as the single source of truth governing all AI Agents operating in the project.
|
|
26
26
|
|
|
27
27
|
> [!IMPORTANT]
|
|
28
|
-
> **Size Constraint**:
|
|
28
|
+
> **Template Compliance & Size Constraint**:
|
|
29
|
+
> Any created or modified `AGENTS.md` MUST strictly adhere to the design specification of [`assets/AGENTS.md.template`](assets/AGENTS.md.template) (containing 4 mandatory sections: Project Overview, Commands Table, 9-stage Workflow Mermaid loop, and Forbidden Antipattern Redlines Table) and MUST NOT exceed 32 KiB (32,768 bytes).
|
|
29
30
|
|
|
30
31
|
---
|
|
31
32
|
|
|
@@ -39,6 +40,9 @@ python3 skills/neo-harness/scripts/generate-agents-md.py --target-dir /path/to/r
|
|
|
39
40
|
|
|
40
41
|
# Generate AGENTS.md directly in target repository
|
|
41
42
|
python3 skills/neo-harness/scripts/generate-agents-md.py --target-dir /path/to/repo --force
|
|
43
|
+
|
|
44
|
+
# Validate existing AGENTS.md against assets/AGENTS.md.template design specification
|
|
45
|
+
python3 skills/neo-harness/scripts/generate-agents-md.py --target-dir /path/to/repo --validate
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
---
|
|
@@ -59,17 +63,18 @@ Load detailed reference knowledge as needed:
|
|
|
59
63
|
* **Execution Protocol & SOP**: Read [references/execution-protocol.md](references/execution-protocol.md) to enforce the 5 Harness Laws, dual-loop SOP, zero-fake pass, and antipattern rules.
|
|
60
64
|
* **Orchestration Topologies**: Read [references/orchestration-patterns.md](references/orchestration-patterns.md) to select appropriate routing, chaining, or human-in-the-loop (HITL) gates.
|
|
61
65
|
|
|
62
|
-
### Step 3 — Generate `AGENTS.md`
|
|
63
|
-
Use `scripts/generate-agents-md.py`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
### Step 3 — Generate & Format `AGENTS.md`
|
|
67
|
+
Use `scripts/generate-agents-md.py` with [assets/AGENTS.md.template](assets/AGENTS.md.template) as the strict baseline template.
|
|
68
|
+
All created or modified `AGENTS.md` files MUST strictly contain the following 4 structural sections in order:
|
|
69
|
+
1. **`## 1. Project Overview`**: Primary tech stack, modular architecture, and coding conventions.
|
|
70
|
+
2. **`## 2. Commands`**: Standard markdown table (`Command Type`, `Exact Command Line`, `Purpose / Scope`) listing test, typecheck, lint, build, or syntax check commands.
|
|
71
|
+
3. **`## 3. Workflow`**: The mandatory 9-stage closed loop Mermaid diagram (`graph TD` from Step1 to Step9) followed by the 9 numbered step definitions.
|
|
72
|
+
4. **`## 4. Forbidden Antipattern Redlines`**: Standard markdown table (`Forbidden Action`, `Why It Is Prohibited`, `Required Behavior`) specifying non-negotiable redlines.
|
|
73
|
+
|
|
74
|
+
### Step 4 — Verify Template Compliance & Quality Gate
|
|
75
|
+
1. Validate structural compliance against `assets/AGENTS.md.template`:
|
|
76
|
+
- Run `python3 skills/neo-harness/scripts/generate-agents-md.py --target-dir /path/to/repo --validate`.
|
|
77
|
+
2. Check the file size of the generated `AGENTS.md`:
|
|
73
78
|
- Must be `<= 32,768 bytes`.
|
|
74
79
|
- If over 32 KiB, prune redundant descriptions or move extended reference docs to separate files under `.agents/` or `docs/`.
|
|
75
|
-
|
|
80
|
+
3. Validate syntax and markdown formatting.
|
|
@@ -45,3 +45,20 @@ Immediately pause and request human intervention when encountering:
|
|
|
45
45
|
2. Identify the exact file line number and root cause exception message.
|
|
46
46
|
3. Formulate a targeted fix addressing the root cause, not just masking the symptom.
|
|
47
47
|
4. Re-run verification commands to confirm the fix works cleanly.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## AGENTS.md Template Specification Gate
|
|
52
|
+
|
|
53
|
+
All created or updated `AGENTS.md` files MUST comply with the design specification of `assets/AGENTS.md.template`:
|
|
54
|
+
|
|
55
|
+
1. **Mandatory Sections**:
|
|
56
|
+
- Section 1: `## 1. Project Overview` (Tech stack, architecture, modularity)
|
|
57
|
+
- Section 2: `## 2. Commands` (3-column markdown table: `Command Type`, `Exact Command Line`, `Purpose / Scope`)
|
|
58
|
+
- Section 3: `## 3. Workflow` (9-stage closed loop Mermaid diagram `graph TD` and 9 numbered steps)
|
|
59
|
+
- Section 4: `## 4. Forbidden Antipattern Redlines` (3-column markdown table: `Forbidden Action`, `Why It Is Prohibited`, `Required Behavior`)
|
|
60
|
+
2. **File Size Limit**:
|
|
61
|
+
- Strictly `<= 32,768 bytes` (32 KiB).
|
|
62
|
+
3. **Automated Validation**:
|
|
63
|
+
- Run `python3 skills/neo-harness/scripts/generate-agents-md.py --target-dir <repo-path> --validate`.
|
|
64
|
+
|
|
@@ -202,9 +202,51 @@ def enforce_size_limit(content: str) -> str:
|
|
|
202
202
|
return pruned_content
|
|
203
203
|
|
|
204
204
|
|
|
205
|
+
def validate_agents_md_structure(content: str) -> list[str]:
|
|
206
|
+
"""
|
|
207
|
+
Validate content against assets/AGENTS.md.template structural requirements.
|
|
208
|
+
Returns a list of error validation messages (empty list if compliant).
|
|
209
|
+
"""
|
|
210
|
+
errors = []
|
|
211
|
+
|
|
212
|
+
# 1. Size limit check
|
|
213
|
+
byte_size = len(content.encode("utf-8"))
|
|
214
|
+
if byte_size > MAX_BYTES:
|
|
215
|
+
errors.append(f"File size ({byte_size} bytes) exceeds maximum limit of 32 KiB ({MAX_BYTES} bytes).")
|
|
216
|
+
|
|
217
|
+
# 2. Section 1 check
|
|
218
|
+
if "## 1. Project Overview" not in content:
|
|
219
|
+
errors.append("Missing required section: '## 1. Project Overview'")
|
|
220
|
+
|
|
221
|
+
# 3. Section 2 check
|
|
222
|
+
if "## 2. Commands" not in content:
|
|
223
|
+
errors.append("Missing required section: '## 2. Commands'")
|
|
224
|
+
else:
|
|
225
|
+
if "| Command Type | Exact Command Line | Purpose / Scope |" not in content:
|
|
226
|
+
errors.append("Section 2 missing required table header: '| Command Type | Exact Command Line | Purpose / Scope |'")
|
|
227
|
+
|
|
228
|
+
# 4. Section 3 check
|
|
229
|
+
if "## 3. Workflow" not in content:
|
|
230
|
+
errors.append("Missing required section: '## 3. Workflow'")
|
|
231
|
+
else:
|
|
232
|
+
if "```mermaid" not in content or "graph TD" not in content:
|
|
233
|
+
errors.append("Section 3 missing required Mermaid diagram ('```mermaid' with 'graph TD').")
|
|
234
|
+
if "1. **Perceive Task**" not in content or "9. **Handoff upon Pass**" not in content:
|
|
235
|
+
errors.append("Section 3 missing standard 9-stage closed loop workflow steps.")
|
|
236
|
+
|
|
237
|
+
# 5. Section 4 check
|
|
238
|
+
if "## 4. Forbidden Antipattern Redlines" not in content:
|
|
239
|
+
errors.append("Missing required section: '## 4. Forbidden Antipattern Redlines'")
|
|
240
|
+
else:
|
|
241
|
+
if "| Forbidden Action | Why It Is Prohibited | Required Behavior |" not in content:
|
|
242
|
+
errors.append("Section 4 missing required table header: '| Forbidden Action | Why It Is Prohibited | Required Behavior |'")
|
|
243
|
+
|
|
244
|
+
return errors
|
|
245
|
+
|
|
246
|
+
|
|
205
247
|
def main():
|
|
206
248
|
parser = argparse.ArgumentParser(
|
|
207
|
-
description="Non-interactive AGENTS.md generator for neo-harness."
|
|
249
|
+
description="Non-interactive AGENTS.md generator and validator for neo-harness."
|
|
208
250
|
)
|
|
209
251
|
parser.add_argument(
|
|
210
252
|
"--target-dir",
|
|
@@ -228,6 +270,11 @@ def main():
|
|
|
228
270
|
action="store_true",
|
|
229
271
|
help="Overwrite output file if it already exists",
|
|
230
272
|
)
|
|
273
|
+
parser.add_argument(
|
|
274
|
+
"--validate",
|
|
275
|
+
action="store_true",
|
|
276
|
+
help="Validate target AGENTS.md against AGENTS.md.template design specification",
|
|
277
|
+
)
|
|
231
278
|
|
|
232
279
|
args = parser.parse_args()
|
|
233
280
|
|
|
@@ -238,6 +285,25 @@ def main():
|
|
|
238
285
|
|
|
239
286
|
output_path = Path(args.output) if args.output else target_dir / "AGENTS.md"
|
|
240
287
|
|
|
288
|
+
if args.validate:
|
|
289
|
+
if not output_path.exists():
|
|
290
|
+
log_diag(f"Validation Error: Target file '{output_path}' does not exist.")
|
|
291
|
+
sys.exit(1)
|
|
292
|
+
|
|
293
|
+
log_diag(f"Validating AGENTS.md file: {output_path}")
|
|
294
|
+
with open(output_path, "r", encoding="utf-8") as f:
|
|
295
|
+
target_content = f.read()
|
|
296
|
+
|
|
297
|
+
val_errors = validate_agents_md_structure(target_content)
|
|
298
|
+
if val_errors:
|
|
299
|
+
log_diag("AGENTS.md Template Validation Failed:")
|
|
300
|
+
for err in val_errors:
|
|
301
|
+
log_diag(f" - {err}")
|
|
302
|
+
sys.exit(1)
|
|
303
|
+
else:
|
|
304
|
+
log_diag(f"AGENTS.md Template Validation Passed for '{output_path}'.")
|
|
305
|
+
sys.exit(0)
|
|
306
|
+
|
|
241
307
|
log_diag(f"Scanning target directory: {target_dir}")
|
|
242
308
|
facts = detect_project_facts(target_dir)
|
|
243
309
|
log_diag(f"Detected project name: {facts['project_name']}, tech stack: {facts['tech_stack']}")
|
|
@@ -245,6 +311,12 @@ def main():
|
|
|
245
311
|
content = generate_agents_md_content(facts)
|
|
246
312
|
content = enforce_size_limit(content)
|
|
247
313
|
|
|
314
|
+
val_errors = validate_agents_md_structure(content)
|
|
315
|
+
if val_errors:
|
|
316
|
+
log_diag("Warning: Generated AGENTS.md failed template validation:")
|
|
317
|
+
for err in val_errors:
|
|
318
|
+
log_diag(f" - {err}")
|
|
319
|
+
|
|
248
320
|
byte_size = len(content.encode("utf-8"))
|
|
249
321
|
log_diag(f"Generated AGENTS.md size: {byte_size} bytes ({byte_size/1024:.2f} KiB)")
|
|
250
322
|
|
|
@@ -264,3 +336,4 @@ def main():
|
|
|
264
336
|
|
|
265
337
|
if __name__ == "__main__":
|
|
266
338
|
main()
|
|
339
|
+
|