Skip to content

Commit 2d70a44

Browse files
authored
feat: add a skill (#223)
* feat: add a skill * feat: move structure of skill to a claude plugin offering subagent * fix: remake skill
1 parent 0d618bd commit 2d70a44

9 files changed

Lines changed: 511 additions & 48 deletions

File tree

.github/workflows/lint.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ jobs:
4646
run: |
4747
just lint
4848
49+
- name: Check plugin prompts are up to date
50+
run: |
51+
just check-plugin-prompts
52+
4953
lint-commit:
5054
runs-on: ubuntu-latest
5155
name: "Lint commit message"

.gitignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,5 @@ GitHub.sublime-settings
106106

107107
# Visual Studio Code #
108108
.vscode/*
109-
!.vscode/settings.json
110-
!.vscode/tasks.json
111-
!.vscode/launch.json
112-
!.vscode/extensions.json
113109
.history
114110
/.ruff_cache/

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ repos:
1919
entry: "poetry check --lock"
2020
language: system
2121
pass_filenames: false
22+
- id: build-plugin-prompts
23+
name: Keep plugin prompts up to date
24+
entry: "just build-plugin-prompts"
25+
language: system
26+
pass_filenames: false
27+
stages:
28+
- "pre-commit"
2229
- id: format-code
2330
name: Format Code
2431
entry: "just format"

justfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ lint-commit: venv
6767
build:
6868
docker build -t lgtm-ai .
6969

70+
# Injects the lgtm-ai prompts inline into the Claude plugin agent file.
71+
build-plugin-prompts: venv
72+
{{ run }} python scripts/build_skill_prompts.py
73+
74+
# Checks that the inlined prompts in the Claude plugin agent file are up to date. Fails if they differ.
75+
check-plugin-prompts: venv
76+
{{ run }} python scripts/build_skill_prompts.py --check
77+
7078
# Pushes the docker image to the registry
7179
push version: build
7280
docker image tag lgtm-ai {{ image }}:{{ version }}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "lgtm-review",
3+
"description": "A plugin that integrates an LGTM AI code reviewer agent into Claude, providing consistent code reviews of code changes.",
4+
"version": "1.5.6",
5+
"author": {
6+
"name": "Sergio Castillo",
7+
"email": "s.cast.lara@gmail.com"
8+
}
9+
}

plugins/lgtm-review/skills/lgtm-review/SKILL.md

Lines changed: 287 additions & 0 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,10 @@ exclude_lines = [
169169

170170

171171
[tool.commitizen]
172-
version_files = ["pyproject.toml:^version"]
172+
version_files = [
173+
"pyproject.toml:^version",
174+
"plugins/lgtm-review/.claude-plugin/plugin.json:version",
175+
]
173176
name = "cz_customize"
174177
version = "1.5.6"
175178
tag_format = "v$version"

scripts/build_skill_prompts.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env python3
2+
"""Inject lgtm-ai prompts inline into the Claude plugin agent file.
3+
4+
Ensures the inlined prompts stay in sync with lgtm-cli as the Python prompts evolve.
5+
6+
Usage:
7+
poetry run python scripts/build_skill_prompts.py # update in place
8+
poetry run python scripts/build_skill_prompts.py --check # fail if out of date
9+
# or via just:
10+
just build-plugin-prompts
11+
"""
12+
13+
import re
14+
import sys
15+
from pathlib import Path
16+
17+
from lgtm_ai.ai.prompts import REVIEWER_SYSTEM_PROMPT, SUMMARIZING_SYSTEM_PROMPT
18+
19+
PLUGIN_AGENT_FILE = Path(__file__).parent.parent / "plugins" / "lgtm-review" / "skills" / "lgtm-review" / "SKILL.md"
20+
21+
# Replaces <diff-format> in the reviewer prompt.
22+
# The skill uses raw `git diff` output; lgtm-cli converts it to JSON first.
23+
_UNIFIED_DIFF_DESCRIPTION = (
24+
"- A git diff in standard unified diff format (output of `git diff`)."
25+
" Lines starting with `+` are additions, lines with `-` are removals,"
26+
" context lines have no prefix."
27+
" `@@ -old_start,count +new_start,count @@` headers indicate where in the file changes occur."
28+
)
29+
30+
# Replaces <hunk-boundary> in the summarizer prompt.
31+
# Drops the JSON field names (hunk_start_new / hunk_start_old) irrelevant to unified diff.
32+
_SKILL_HUNK_BOUNDARY = (
33+
"Ensure that suggestions don't span outside git hunk boundaries."
34+
" If they do, adjust the suggestion to fit within the hunk."
35+
)
36+
37+
_REVIEWER_OUTPUT_SCHEMA = """
38+
Return ONLY the following JSON (no markdown wrapping, no extra text):
39+
40+
{
41+
"summary": "<overall review summary>",
42+
"raw_score": <integer 1-5>,
43+
"comments": [
44+
{
45+
"file": "<file path>",
46+
"line_number": <integer>,
47+
"comment": "<review comment in markdown>",
48+
"category": "<Correctness|Quality|Testing|Security>",
49+
"severity": "<LOW|MEDIUM|HIGH>",
50+
"quote_snippet": "<relevant code snippet, no diff artifacts>"
51+
}
52+
]
53+
}
54+
"""
55+
56+
_SUMMARIZER_OUTPUT_SCHEMA = """
57+
Return ONLY the following JSON (no markdown wrapping, no extra text):
58+
59+
{
60+
"summary": "<improved summary>",
61+
"raw_score": <integer 1-5>,
62+
"comments": [
63+
{
64+
"file": "<file path>",
65+
"line_number": <integer>,
66+
"comment": "<refined comment>",
67+
"category": "<Correctness|Quality|Testing|Security>",
68+
"severity": "<LOW|MEDIUM|HIGH>",
69+
"quote_snippet": "<code snippet>",
70+
"suggestion": {
71+
"snippet": "<suggested replacement code>",
72+
"lines_above": <integer>,
73+
"lines_below": <integer>,
74+
"ready_for_replacement": <true|false>
75+
}
76+
}
77+
]
78+
}
79+
"""
80+
81+
82+
def _replace_xml_block(text: str, tag: str, replacement: str) -> str:
83+
"""Replace <tag>...</tag> (including the tags themselves) with replacement."""
84+
return re.sub(rf"<{tag}>.*?</{tag}>", replacement, text, flags=re.DOTALL)
85+
86+
87+
def _inject_into_sentinel(text: str, sentinel: str, content: str) -> str:
88+
"""Replace content between <!-- BEGIN:sentinel --> and <!-- END:sentinel --> markers."""
89+
pattern = rf"(<!-- BEGIN:{re.escape(sentinel)} -->).*?(<!-- END:{re.escape(sentinel)} -->)"
90+
replacement = rf"\1\n{content}\n\2"
91+
return re.sub(pattern, replacement, text, flags=re.DOTALL)
92+
93+
94+
def build_reviewer_prompt() -> str:
95+
prompt = REVIEWER_SYSTEM_PROMPT.strip()
96+
prompt = _replace_xml_block(prompt, "diff-format", _UNIFIED_DIFF_DESCRIPTION)
97+
return prompt + "\n" + _REVIEWER_OUTPUT_SCHEMA
98+
99+
100+
def build_summarizer_prompt() -> str:
101+
prompt = SUMMARIZING_SYSTEM_PROMPT.strip()
102+
prompt = _replace_xml_block(prompt, "hunk-boundary", _SKILL_HUNK_BOUNDARY)
103+
return prompt + "\n" + _SUMMARIZER_OUTPUT_SCHEMA
104+
105+
106+
def main() -> None:
107+
check_only = "--check" in sys.argv
108+
109+
reviewer_prompt = build_reviewer_prompt()
110+
summarizer_prompt = build_summarizer_prompt()
111+
112+
original = PLUGIN_AGENT_FILE.read_text()
113+
updated = _inject_into_sentinel(original, "reviewer-prompt", reviewer_prompt)
114+
updated = _inject_into_sentinel(updated, "summarizer-prompt", summarizer_prompt)
115+
116+
if check_only:
117+
if original != updated:
118+
print(
119+
f"ERROR: {PLUGIN_AGENT_FILE} is out of date. Run 'just build-plugin-prompts' to fix.", file=sys.stderr
120+
)
121+
sys.exit(1)
122+
print(f"OK: {PLUGIN_AGENT_FILE}")
123+
else:
124+
PLUGIN_AGENT_FILE.write_text(updated)
125+
print(f"Updated: {PLUGIN_AGENT_FILE}")
126+
127+
128+
if __name__ == "__main__":
129+
main()

src/lgtm_ai/ai/prompts.py

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@ def _get_full_category_explanation() -> str:
2929

3030

3131
REVIEWER_SYSTEM_PROMPT = f"""
32+
<role>
3233
You are a senior software developer making code reviews for your colleagues.
34+
</role>
3335
36+
<inputs>
3437
You will receive:
3538
- The metadata of the PR, including the title and description.
39+
<diff-format>
3640
- A git diff which corresponds to a PR made by one of these colleagues, and you must make a full review of the code.
3741
- The git diff format will be a list of changes in JSON format, with the following structure:
3842
```json
@@ -54,75 +58,91 @@ def _get_full_category_explanation() -> str:
5458
],
5559
}}
5660
```
61+
</diff-format>
5762
- `Context`, which consists on the contents of each of the changed files in the source (PR) branch or the target branch. This should help you to understand the context of the PR.
5863
- Optionally, `User Story` that the PR is implementing, which will consist of a title and a description. You must evaluate whether the PR is correctly implementing the user story (in its totality or partially).
5964
- Optionally, `Additional context` that the author of the PR has provided, which may contain a prompt (to give you a hint on what to use it for), and some content.
65+
</inputs>
6066
67+
<instructions>
6168
You should make two types of comments:
6269
- A summary comment, explaining what the overall quality of the code is, if there are any major issues, and a summary of the changes you require the author to make.
6370
- Line comments:
6471
- Identify possible bugs, errors, and code quality issues; and answer to the PR pointing them out using GitHub style PR comments (markdown).
6572
- Specify the line number where the comment should be placed in the PR, together with the file name. Be mindful of whether the comment is on the old file or the new file.
6673
- Always quote the relevant code snippet the comment refers to (it can be multiple lines). Do not add artifacts from the git diff into the snippet.
6774
- Comments have a severity, which can be:
68-
{_SEVERITY_EXPLANATION}
75+
<severity>{_SEVERITY_EXPLANATION} </severity>
6976
- The comments should be grouped by category, and the categories are:
70-
{_get_full_category_explanation()}
77+
<categories>
78+
{_get_full_category_explanation()}
79+
</categories>
7180
- Assume there are other steps in the CI/CD pipeline: type checking, linting, testing. Do not add comments asking the author to ensure stuff that will be picked up by those steps.
7281
- Do not feel like you need to say something for the sake of saying it. Filter out noise.
7382
- Do not ask the author to "check this", "validate this", "make sure this is correct", "ensure this does not break something", etc. Focus on issues you really see.
7483
7584
If everything is correct and of good quality, you should answer with ONLY "LGTM". If there are issues or changes required, there MUST be at least some comments.
85+
</instructions>
7686
87+
<scoring>
7788
Score the quality of the PR between 1 and 5, where:
7889
- 5 is a perfect PR, with almost no issues.
7990
- 1 is a PR that is completely wrong, and the author needs to rethink the approach.
91+
</scoring>
8092
8193
"""
8294

8395

8496
SUMMARIZING_SYSTEM_PROMPT = f"""
85-
You are working within a team of AI agents that are reviewing code Pull Requests in a development team.
86-
You are an agent that will edit a Pull Request review, created by another AI agent.
87-
88-
Your job is to improve it in several ways.
89-
90-
The review contains a summary and a list of comments. The summary is a general overview of the PR, and the comments are specific issues that need to be addressed.
91-
The comments are categorized, and each comment has a severity level.
92-
The categories are:
93-
{_get_full_category_explanation()}
94-
The comment severity levels are:
95-
{_SEVERITY_EXPLANATION}
96-
97-
Follow these instructions:
98-
- Filter out noise. The reviewer agent has a tendency to include useless comments ("check that this is correct", "talk to your colleagues about this", etc.). Remove those.
99-
- Remove comments that are just praising or commenting on the code. These are useless.
100-
- Remove comments that are not part of the modified lines of the PR. Do not include comments for lines that the author did not touch.
101-
- Remove comments that are not in the provided categories below.
102-
- Evaluate whether some comments are more likely to simply be incorrect. If they are likely to be incorrect, remove them.
103-
- Merge duplicate comments. If there are two comments that refer to the same issue, merge them into one.
104-
- Comments have a code snippet that they refer to. Consider whether the snippet needs a bit more code context, and if so, expand the snippet. Otherwise don't touch them.
105-
- Check that categories of each comment are correct. Re-categorize them if needed.
106-
- Check the summary. Feel free to rephrase it, add more information, or generally improve it. The summary comment must be a general comment informing the PR author about the overall quality of the PR, the weakpoints it has, and which general issues need to be addressed.
107-
- If you can add a suggestion code snippet to the comment text, do it. Do it only when you are very sure about the suggestion with the context you have.
108-
- Suggestions must be passed separately (not as part of the comment content), and they must include how many lines above and below the comment to include in the suggestion.
109-
- The offsets of suggestions must encompass all the code that needs to be changed. e.g., if you intend to change a whole function, the suggestion must include the full function. If you intend to change a single line, then the offsets will be 0.
110-
- If a suggestion is given, a flag indicating whether the suggestion is ready to be applied directly by the author must be given. That is, if the suggestion includes comments to be filled by the author, or skips parts and is intended for clarification, the flag `ready_for_replacement` must be set to `false`.
111-
- Be mindful of indentation in suggestions, ensure they are correctly indented.
112-
- Ensure that suggestions don't span outside git hunk boundaries (`hunk_start_new` and `hunk_start_old` in the modified lines; new for comments on new path, old for comments on old path). If they do, adjust the suggestion to fit within the hunk.
113-
114-
The review will have a score for the PR (1-5, with 5 being the best). It is your job to evaluate whether this score holds after removing the comments.
115-
You must evaluate the score, and change it if necessary. Here is some guidance:
116-
- 5: All issues are `LOW` and the PR is generally ready to be merged.
117-
- 4: There are some minor issues, but the PR is almost ready to be merged. Most of those issues should have severity `LOW`, and the quality of the PR is still high.
118-
- 3: There are some issues (not many, but some) with the PR (some `LOW`, some `MEDIUM`, maybe one or two `HIGH`), and it is not ready to be merged. The approach is generally good, the fundamental structure is there, but there are some issues that need to be fixed. If there are only `LOW` severity issues, you cannot score it as `Needs Some Work`.
119-
- 2: Issues are major, overarching, and/or numerous. However, the approach taken is not necessarily wrong: the author just needs to address the issues. The PR is definitely not ready to be merged as is.
120-
- 1: The approach taken is wrong, and the author needs to start from scratch. The PR is not ready to be merged as is at all. Provide a summary in the main section of which alternative approach should be taken, and why.
121-
122-
Be more lenient than the reviewer: it tends to be too strict and nitpicky with the score. Have a more human approach to the review when it comes to scoring.
123-
You are not allowed to decrease the score, only increase it or keep it the same.
124-
125-
You will receive both the Review and the PR diff. The PR diff is the same as the one the reviewer agent received, and it is there to help you understand the context of the PR.
97+
<role>
98+
You are working within a team of AI agents that are reviewing code Pull Requests in a development team.
99+
You are an agent that will edit a Pull Request review, created by another AI agent.
100+
</role>
101+
102+
<context>
103+
The review contains a summary and a list of comments. The summary is a general overview of the PR, and the comments are specific issues that need to be addressed.
104+
The comments are categorized, and each comment has a severity level.
105+
The categories are:
106+
<categories>
107+
{_get_full_category_explanation()}
108+
</categories>
109+
The comment severity levels are:
110+
<severity>{_SEVERITY_EXPLANATION} </severity>
111+
</context>
112+
113+
<instructions>
114+
Your job is to improve the review in several ways. Follow these instructions:
115+
- Filter out noise. The reviewer agent has a tendency to include useless comments ("check that this is correct", "talk to your colleagues about this", etc.). Remove those.
116+
- Remove comments that are just praising or commenting on the code. These are useless.
117+
- Remove comments that are not part of the modified lines of the PR. Do not include comments for lines that the author did not touch.
118+
- Remove comments that are not in the provided categories below.
119+
- Evaluate whether some comments are more likely to simply be incorrect. If they are likely to be incorrect, remove them.
120+
- Merge duplicate comments. If there are two comments that refer to the same issue, merge them into one.
121+
- Comments have a code snippet that they refer to. Consider whether the snippet needs a bit more code context, and if so, expand the snippet. Otherwise don't touch them.
122+
- Check that categories of each comment are correct. Re-categorize them if needed.
123+
- Check the summary. Feel free to rephrase it, add more information, or generally improve it. The summary comment must be a general comment informing the PR author about the overall quality of the PR, the weakpoints it has, and which general issues need to be addressed.
124+
- If you can add a suggestion code snippet to the comment text, do it. Do it only when you are very sure about the suggestion with the context you have.
125+
- Suggestions must be passed separately (not as part of the comment content), and they must include how many lines above and below the comment to include in the suggestion.
126+
- The offsets of suggestions must encompass all the code that needs to be changed. e.g., if you intend to change a whole function, the suggestion must include the full function. If you intend to change a single line, then the offsets will be 0.
127+
- If a suggestion is given, a flag indicating whether the suggestion is ready to be applied directly by the author must be given. That is, if the suggestion includes comments to be filled by the author, or skips parts and is intended for clarification, the flag `ready_for_replacement` must be set to `false`.
128+
- Be mindful of indentation in suggestions, ensure they are correctly indented.
129+
- <hunk-boundary>Ensure that suggestions don't span outside git hunk boundaries (`hunk_start_new` and `hunk_start_old` in the modified lines; new for comments on new path, old for comments on old path). If they do, adjust the suggestion to fit within the hunk.</hunk-boundary>
130+
</instructions>
131+
132+
<scoring>
133+
The review will have a score for the PR (1-5, with 5 being the best). It is your job to evaluate whether this score holds after removing the comments.
134+
You must evaluate the score, and change it if necessary. Here is some guidance:
135+
- 5: All issues are `LOW` and the PR is generally ready to be merged.
136+
- 4: There are some minor issues, but the PR is almost ready to be merged. Most of those issues should have severity `LOW`, and the quality of the PR is still high.
137+
- 3: There are some issues (not many, but some) with the PR (some `LOW`, some `MEDIUM`, maybe one or two `HIGH`), and it is not ready to be merged. The approach is generally good, the fundamental structure is there, but there are some issues that need to be fixed. If there are only `LOW` severity issues, you cannot score it as `Needs Some Work`.
138+
- 2: Issues are major, overarching, and/or numerous. However, the approach taken is not necessarily wrong: the author just needs to address the issues. The PR is definitely not ready to be merged as is.
139+
- 1: The approach taken is wrong, and the author needs to start from scratch. The PR is not ready to be merged as is at all. Provide a summary in the main section of which alternative approach should be taken, and why.
140+
141+
Be more lenient than the reviewer: it tends to be too strict and nitpicky with the score. Have a more human approach to the review when it comes to scoring.
142+
You are not allowed to decrease the score, only increase it or keep it the same.
143+
</scoring>
144+
145+
You will receive both the Review and the PR diff. The PR diff is the same as the one the reviewer agent received, and it is there to help you understand the context of the PR.
126146
"""
127147

128148

0 commit comments

Comments
 (0)