@olegkoval/agent-skills 1.26.0 → 1.28.0
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/.claude-plugin/plugin.json +2 -1
- package/README.md +7 -3
- package/catalog/skills.json +18 -0
- package/package.json +5 -3
- package/packages/software-development/lekker-review/SKILL.md +519 -0
- package/packages/software-development/lekker-review/adapters/claude/plugin.json +5 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/SKILL.md +520 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/completeness-critic.md +21 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/conventions.md +124 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/fix-verifier.md +84 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/fixer.md +120 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/implementation.md +53 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/prover.md +135 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/quality.md +72 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/simplification.md +45 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/test-quality.md +170 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/triage-logic.md +27 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/triage-quality.md +41 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/agents/verifier.md +295 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/artifact-page.md +143 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/context-gathering.md +162 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/fix-mode.md +329 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/github-post.md +205 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/house-rules.md +76 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/references/output-format.md +232 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/scripts/changed-files.sh +77 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/scripts/setup-worktree.sh +337 -0
- package/packages/software-development/lekker-review/adapters/claude/skills/lekker-review/scripts/verify-fixes.sh +231 -0
- package/packages/software-development/lekker-review/fix-workflow.js +273 -0
- package/packages/software-development/lekker-review/references/agents/completeness-critic.md +21 -0
- package/packages/software-development/lekker-review/references/agents/conventions.md +124 -0
- package/packages/software-development/lekker-review/references/agents/fix-verifier.md +84 -0
- package/packages/software-development/lekker-review/references/agents/fixer.md +120 -0
- package/packages/software-development/lekker-review/references/agents/implementation.md +53 -0
- package/packages/software-development/lekker-review/references/agents/prover.md +135 -0
- package/packages/software-development/lekker-review/references/agents/quality.md +72 -0
- package/packages/software-development/lekker-review/references/agents/simplification.md +45 -0
- package/packages/software-development/lekker-review/references/agents/test-quality.md +170 -0
- package/packages/software-development/lekker-review/references/agents/triage-logic.md +27 -0
- package/packages/software-development/lekker-review/references/agents/triage-quality.md +41 -0
- package/packages/software-development/lekker-review/references/agents/verifier.md +295 -0
- package/packages/software-development/lekker-review/references/artifact-page.md +143 -0
- package/packages/software-development/lekker-review/references/context-gathering.md +162 -0
- package/packages/software-development/lekker-review/references/fix-mode.md +329 -0
- package/packages/software-development/lekker-review/references/github-post.md +205 -0
- package/packages/software-development/lekker-review/references/house-rules.md +76 -0
- package/packages/software-development/lekker-review/references/output-format.md +232 -0
- package/packages/software-development/lekker-review/scripts/changed-files.sh +77 -0
- package/packages/software-development/lekker-review/scripts/setup-worktree.sh +337 -0
- package/packages/software-development/lekker-review/scripts/verify-fixes.sh +231 -0
- package/packages/software-development/lekker-review/workflow.js +602 -0
- package/site/assets/paperbag.css +707 -0
- package/site/assets/paperbag.js +218 -0
- package/site/build.mjs +380 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'lekker-review-fix',
|
|
3
|
+
description: 'Apply verified review findings as real code edits in the review worktree',
|
|
4
|
+
phases: [ { title: 'Fix' }, { title: 'Fix-verify' } ],
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Schemas
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const FIX_RESULT_SCHEMA = {
|
|
12
|
+
type: 'object',
|
|
13
|
+
required: ['file', 'results'],
|
|
14
|
+
properties: {
|
|
15
|
+
file: { type: 'string' },
|
|
16
|
+
filesTouched: { type: 'array', items: { type: 'string' } },
|
|
17
|
+
results: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
items: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
required: ['title', 'status', 'reason'],
|
|
22
|
+
properties: {
|
|
23
|
+
title: { type: 'string' },
|
|
24
|
+
line: { type: 'integer' },
|
|
25
|
+
status: { enum: ['applied', 'skipped', 'failed'] },
|
|
26
|
+
reason: { type: 'string' },
|
|
27
|
+
needsCrossFile: { type: 'boolean' },
|
|
28
|
+
summary: { type: 'string' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const FIX_VERDICT_SCHEMA = {
|
|
36
|
+
type: 'object',
|
|
37
|
+
required: ['verdict', 'reasoning'],
|
|
38
|
+
properties: {
|
|
39
|
+
verdict: { enum: ['good', 'incomplete', 'harmful'] },
|
|
40
|
+
reasoning: { type: 'string' },
|
|
41
|
+
problems: { type: 'array', items: { type: 'string' } },
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Args
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
const input = (typeof args === 'string') ? JSON.parse(args) : (args || {})
|
|
50
|
+
const {
|
|
51
|
+
repoSlug,
|
|
52
|
+
prNumber,
|
|
53
|
+
worktreePath,
|
|
54
|
+
diffFile,
|
|
55
|
+
contextFile,
|
|
56
|
+
promptDir,
|
|
57
|
+
findings,
|
|
58
|
+
} = input
|
|
59
|
+
|
|
60
|
+
if (!repoSlug || !prNumber || !worktreePath || !promptDir || !Array.isArray(findings)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
'lekker-review fix workflow: missing required args (got type ' + typeof args +
|
|
63
|
+
'): ' + JSON.stringify({
|
|
64
|
+
repoSlug, prNumber, worktreePath, promptDir,
|
|
65
|
+
findingCount: Array.isArray(findings) ? findings.length : null,
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const budgetAtStart = budget.spent()
|
|
71
|
+
|
|
72
|
+
if (findings.length === 0) {
|
|
73
|
+
log('no fixable findings passed; nothing to do')
|
|
74
|
+
return {
|
|
75
|
+
groups: [],
|
|
76
|
+
agentCount: 0,
|
|
77
|
+
outputTokens: budget.spent() - budgetAtStart,
|
|
78
|
+
turnTokensTotal: budget.spent(),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let agentCount = 0
|
|
83
|
+
let retryCount = 0
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Group findings by file: one agent per file, so two agents never edit the
|
|
87
|
+
// same file concurrently.
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
const byFile = new Map()
|
|
91
|
+
for (const f of findings) {
|
|
92
|
+
if (!byFile.has(f.file)) {
|
|
93
|
+
byFile.set(f.file, [])
|
|
94
|
+
}
|
|
95
|
+
byFile.get(f.file).push(f)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const groups = Array.from(byFile.entries()).map(function(entry) {
|
|
99
|
+
return { file: entry[0], findings: entry[1] }
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
log(`fixing ${findings.length} finding(s) across ${groups.length} file(s)`)
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Prompts
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
function fixPrompt(group, priorVerdict) {
|
|
109
|
+
const parts = [
|
|
110
|
+
`You are the fix agent for PR #${prNumber} in ${repoSlug}.`,
|
|
111
|
+
`Read and follow the prompt file: ${promptDir}/fixer.md.`,
|
|
112
|
+
`WORKTREE_PATH=${worktreePath}, TARGET_FILE=${group.file},`,
|
|
113
|
+
`DIFF_FILE=${diffFile}, CONTEXT_FILE=${contextFile}.`,
|
|
114
|
+
`FINDINGS (JSON): ${JSON.stringify(group.findings)}.`,
|
|
115
|
+
`Edit ONLY files you list in filesTouched, and never a file outside ${worktreePath}.`,
|
|
116
|
+
`Do not run git commit, git add, git push, or any git write command.`,
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
if (priorVerdict) {
|
|
120
|
+
parts.push(
|
|
121
|
+
`RETRY: your previous attempt was judged "${priorVerdict.verdict}".`,
|
|
122
|
+
`Verifier reasoning: ${priorVerdict.reasoning}.`,
|
|
123
|
+
`Problems: ${JSON.stringify(priorVerdict.problems || [])}.`,
|
|
124
|
+
`Correct the edits in place. This is the final attempt.`
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return parts.join(' ')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function fixVerifyPrompt(group, fixResult) {
|
|
132
|
+
return [
|
|
133
|
+
`You are the fix verifier for PR #${prNumber} in ${repoSlug}.`,
|
|
134
|
+
`Read and follow the prompt file: ${promptDir}/fix-verifier.md.`,
|
|
135
|
+
`WORKTREE_PATH=${worktreePath}, TARGET_FILE=${group.file},`,
|
|
136
|
+
`DIFF_FILE=${diffFile}, CONTEXT_FILE=${contextFile}.`,
|
|
137
|
+
`FINDINGS the fix was meant to resolve (JSON): ${JSON.stringify(group.findings)}.`,
|
|
138
|
+
`FIX AGENT REPORT (JSON): ${JSON.stringify(fixResult)}.`,
|
|
139
|
+
`Inspect the actual uncommitted edits with git diff inside the worktree.`,
|
|
140
|
+
`You are read-only: never edit, stage, or commit anything.`,
|
|
141
|
+
].join(' ')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Stages
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
async function fixStage(group) {
|
|
149
|
+
agentCount++
|
|
150
|
+
const result = await agent(fixPrompt(group, null), {
|
|
151
|
+
label: `fix:${group.file}`,
|
|
152
|
+
phase: 'Fix',
|
|
153
|
+
schema: FIX_RESULT_SCHEMA,
|
|
154
|
+
model: 'sonnet',
|
|
155
|
+
effort: 'high',
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
if (!result) {
|
|
159
|
+
log(`fix:${group.file}: agent returned null`)
|
|
160
|
+
return {
|
|
161
|
+
file: group.file,
|
|
162
|
+
findings: group.findings,
|
|
163
|
+
fixResult: null,
|
|
164
|
+
verdict: null,
|
|
165
|
+
appliedCount: 0,
|
|
166
|
+
note: 'fix agent returned null; no edits trusted',
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { file: group.file, findings: group.findings, fixResult: result }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function verifyStage(state) {
|
|
174
|
+
if (!state.fixResult) {
|
|
175
|
+
return state
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const applied = (state.fixResult.results || []).filter(function(r) {
|
|
179
|
+
return r.status === 'applied'
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
if (applied.length === 0) {
|
|
183
|
+
return Object.assign({}, state, { verdict: null, appliedCount: 0 })
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
agentCount++
|
|
187
|
+
let verdict = await agent(fixVerifyPrompt(state, state.fixResult), {
|
|
188
|
+
label: `fix-verify:${state.file}`,
|
|
189
|
+
phase: 'Fix-verify',
|
|
190
|
+
schema: FIX_VERDICT_SCHEMA,
|
|
191
|
+
model: 'sonnet',
|
|
192
|
+
effort: 'high',
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// One retry only (VERIFICATION.md: surface retries, never loop).
|
|
196
|
+
if (verdict && verdict.verdict !== 'good') {
|
|
197
|
+
retryCount++
|
|
198
|
+
log(`fix:${state.file}: verdict=${verdict.verdict}, retrying once`)
|
|
199
|
+
|
|
200
|
+
agentCount++
|
|
201
|
+
const retryResult = await agent(fixPrompt(state, verdict), {
|
|
202
|
+
label: `fix-retry:${state.file}`,
|
|
203
|
+
phase: 'Fix',
|
|
204
|
+
schema: FIX_RESULT_SCHEMA,
|
|
205
|
+
model: 'sonnet',
|
|
206
|
+
effort: 'high',
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
if (retryResult) {
|
|
210
|
+
state = Object.assign({}, state, { fixResult: retryResult, retried: true })
|
|
211
|
+
agentCount++
|
|
212
|
+
verdict = await agent(fixVerifyPrompt(state, retryResult), {
|
|
213
|
+
label: `fix-reverify:${state.file}`,
|
|
214
|
+
phase: 'Fix-verify',
|
|
215
|
+
schema: FIX_VERDICT_SCHEMA,
|
|
216
|
+
model: 'sonnet',
|
|
217
|
+
effort: 'high',
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const finalApplied = (state.fixResult.results || []).filter(function(r) {
|
|
223
|
+
return r.status === 'applied'
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
return Object.assign({}, state, {
|
|
227
|
+
verdict,
|
|
228
|
+
appliedCount: finalApplied.length,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Pipeline
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
phase('Fix')
|
|
237
|
+
|
|
238
|
+
const results = await pipeline(groups, fixStage, verifyStage)
|
|
239
|
+
|
|
240
|
+
const groupsOut = results.filter(Boolean).map(function(state) {
|
|
241
|
+
const verdict = state.verdict || null
|
|
242
|
+
return {
|
|
243
|
+
file: state.file,
|
|
244
|
+
findings: state.findings.map(function(f) {
|
|
245
|
+
return { title: f.title, line: f.line, severity: f.severity }
|
|
246
|
+
}),
|
|
247
|
+
results: (state.fixResult && state.fixResult.results) || [],
|
|
248
|
+
filesTouched: (state.fixResult && state.fixResult.filesTouched) || [],
|
|
249
|
+
verdict: verdict ? verdict.verdict : null,
|
|
250
|
+
reasoning: verdict ? verdict.reasoning : (state.note || 'not verified'),
|
|
251
|
+
problems: verdict ? (verdict.problems || []) : [],
|
|
252
|
+
retried: Boolean(state.retried),
|
|
253
|
+
// Only a "good" verdict is committable; anything else must be reverted by
|
|
254
|
+
// the caller.
|
|
255
|
+
committable: Boolean(verdict && verdict.verdict === 'good' && state.appliedCount > 0),
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
const failedGroups = results.filter(function(r) { return !r })
|
|
260
|
+
if (failedGroups.length > 0) {
|
|
261
|
+
log(`${failedGroups.length} file group(s) died in the pipeline and were dropped`)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
log(`fix complete: ${groupsOut.filter(function(g) { return g.committable }).length}/${groups.length} file group(s) committable, retries=${retryCount}`)
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
groups: groupsOut,
|
|
268
|
+
droppedGroups: failedGroups.length,
|
|
269
|
+
agentCount,
|
|
270
|
+
retryCount,
|
|
271
|
+
outputTokens: budget.spent() - budgetAtStart,
|
|
272
|
+
turnTokensTotal: budget.spent(),
|
|
273
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# completeness-critic — lekker-review agent prompt
|
|
2
|
+
You will receive in your task message: REPO_SLUG, PR_NUMBER, PR_URL, DIFF_FILE, CONTEXT_FILE (JSON), WORKTREE_PATH (may be null).
|
|
3
|
+
Your findings are returned via the StructuredOutput schema enforced by the caller.
|
|
4
|
+
|
|
5
|
+
You are a completeness critic for a code review of PR #<PR_NUMBER> in <REPO_SLUG>.
|
|
6
|
+
Below are the findings reported by 5 specialist review agents.
|
|
7
|
+
|
|
8
|
+
Your task: identify up to 3 review angles that were NOT adequately covered or
|
|
9
|
+
were declared "no findings" too quickly. For each angle:
|
|
10
|
+
1. Name the specific axis (e.g. "concurrency safety", "rollback on partial write")
|
|
11
|
+
2. Give the specific file:line from the diff that warrants another look
|
|
12
|
+
3. Write one sentence on why it deserves re-examination
|
|
13
|
+
|
|
14
|
+
Be concrete — cite diff lines, not vibes. If you genuinely cannot find a missed
|
|
15
|
+
angle, return "No gaps found."
|
|
16
|
+
|
|
17
|
+
Agent findings:
|
|
18
|
+
<AGENT_FINDINGS_SUMMARY>
|
|
19
|
+
|
|
20
|
+
Diff: read DIFF_FILE.
|
|
21
|
+
Worktree: WORKTREE_PATH is given in your task message (null in scan mode — diff only).
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# conventions — lekker-review agent prompt
|
|
2
|
+
You will receive in your task message: REPO_SLUG, PR_NUMBER, PR_URL, DIFF_FILE, CONTEXT_FILE (JSON), WORKTREE_PATH (may be null).
|
|
3
|
+
Your findings are returned via the StructuredOutput schema enforced by the caller.
|
|
4
|
+
|
|
5
|
+
Review the PR diff for deviations from this codebase's own established
|
|
6
|
+
conventions and idioms. This is the "strong teammate" lens: the suggestions a
|
|
7
|
+
senior engineer on this team would leave — non-blocking, but they make the
|
|
8
|
+
code match how the rest of the codebase is written. You are the ONLY agent
|
|
9
|
+
allowed to look beyond the diff for evidence; the other agents are
|
|
10
|
+
diff-scoped, you are not.
|
|
11
|
+
|
|
12
|
+
Axes to cover (the examples below are illustrative — swap in the idioms that
|
|
13
|
+
actually matter for your stack, e.g. via `house-rules.md`'s Stack context
|
|
14
|
+
section):
|
|
15
|
+
- Type-system idioms:
|
|
16
|
+
* A hand-written interface/type that duplicates an existing Zod schema —
|
|
17
|
+
should be `z.infer<typeof zSchema>` so the schema stays the single source
|
|
18
|
+
of truth. (Grep for a matching z-schema in the same feature folder.)
|
|
19
|
+
* Raw `string` used for a Shopify GID or an entity id where a branded
|
|
20
|
+
`ID<'Customer'>` (or similar) type exists and is used elsewhere.
|
|
21
|
+
* A union typed as `as readonly string[]` / a hand-rolled `is...` guard where
|
|
22
|
+
a `z.enum([...])` + `z.infer` would give validation, narrowing, and the
|
|
23
|
+
options array in one declaration.
|
|
24
|
+
* An unnecessary `satisfies` / redundant type annotation the compiler already
|
|
25
|
+
infers.
|
|
26
|
+
* A GID validated/parsed inline where a shared helper exists (e.g.
|
|
27
|
+
`zNamespacedGid`). Grep the shared libs and the repo before asserting.
|
|
28
|
+
- Reuse (search the worktree AND, if you keep sibling repos checked out
|
|
29
|
+
locally, those too, before flagging):
|
|
30
|
+
* Inline fetch/client logic that should reuse — or be promoted into — a
|
|
31
|
+
shared client (e.g. a company-switcher client) that already exists or that
|
|
32
|
+
the codebase clearly wants.
|
|
33
|
+
* A util/helper that already exists elsewhere being re-implemented inline.
|
|
34
|
+
* A symbol defined locally that is (or should be) exported from a shared
|
|
35
|
+
module — "are we not exporting this somewhere?"
|
|
36
|
+
- Consistency:
|
|
37
|
+
* Cache-key / composite-key separators that disagree with the repo's
|
|
38
|
+
prevailing choice (e.g. `:` vs `::`). Grep existing key-building code to
|
|
39
|
+
find the prevailing pattern, then flag the deviation.
|
|
40
|
+
* Ad-hoc error throwing where the repo has an idiom (e.g. `throw new
|
|
41
|
+
HttpError('...', 403)` instead of a bare string / generic Error).
|
|
42
|
+
* Naming/casing that breaks the convention used by sibling files.
|
|
43
|
+
|
|
44
|
+
MANDATORY SWEEP — do this FIRST, before forming any opinion:
|
|
45
|
+
|
|
46
|
+
The axes above are symptom-driven: they only fire once you already suspect a
|
|
47
|
+
duplication. That is how a re-implemented helper slips through — nobody thinks to
|
|
48
|
+
look. So run these enumerations mechanically, whether or not anything looks wrong.
|
|
49
|
+
|
|
50
|
+
1. **Sibling sweep for every file the diff ADDS.** For each added file, list its
|
|
51
|
+
directory and read the exports of its neighbours. A helper that solves the same
|
|
52
|
+
problem is usually sitting in the same folder.
|
|
53
|
+
```bash
|
|
54
|
+
git -C <WORKTREE_PATH> diff --name-status <base>...HEAD | awk '$1=="A"{print $2}'
|
|
55
|
+
ls <dir of each added file> # what already lives beside it
|
|
56
|
+
grep -rn "^export " <dir>/*.ts <dir>/*.tsx 2>/dev/null | grep -v "<the added file>"
|
|
57
|
+
```
|
|
58
|
+
A new `foo/bar-thing.ts` next to an existing `foo/thing.ts` is a finding waiting
|
|
59
|
+
to happen. Read the neighbour, do not just note its name.
|
|
60
|
+
|
|
61
|
+
2. **New-symbol sweep.** For every function/const the diff exports, search the repo
|
|
62
|
+
for something that already does that job, by BEHAVIOUR not just by name. Names
|
|
63
|
+
rarely match; behaviour does.
|
|
64
|
+
```bash
|
|
65
|
+
grep -rn "export \(function\|const\) " <diff added lines> # collect new symbols
|
|
66
|
+
# then for each, search by what it does, e.g. a locale normaliser:
|
|
67
|
+
grep -rln "toLowerCase()\|normalize\|isoCode\|split('-')" <WORKTREE_PATH> --include=*.ts --include=*.tsx
|
|
68
|
+
```
|
|
69
|
+
Pick 2 or 3 behavioural keywords from the new function's body and grep those.
|
|
70
|
+
Reviewing the diff alone cannot catch this; you are the only agent who can.
|
|
71
|
+
|
|
72
|
+
3. **State what you swept.** In your output, name the directories you listed and the
|
|
73
|
+
behavioural greps you ran, even when they found nothing. A sweep that is not
|
|
74
|
+
reported did not happen, and the next reviewer cannot tell "no duplication exists"
|
|
75
|
+
from "nobody looked".
|
|
76
|
+
|
|
77
|
+
HARD RULES:
|
|
78
|
+
- Only raise a finding when the better pattern PROVABLY ALREADY EXISTS. Cite it:
|
|
79
|
+
the file:line where the helper/type/convention lives, or the sibling file that
|
|
80
|
+
does it the idiomatic way. If you cannot find a concrete precedent, DROP the
|
|
81
|
+
finding — "this would be nicer as X" on taste alone is not allowed.
|
|
82
|
+
- Every finding must still trace to a `+` line in the diff (the deviation must
|
|
83
|
+
be code this PR added/changed). The supporting precedent may live outside the
|
|
84
|
+
diff; the deviation may not.
|
|
85
|
+
- These are suggestions, not blockers. Do not inflate severity. Report each as
|
|
86
|
+
`file:line — <deviation> (precedent: <file:line of the existing pattern>)`.
|
|
87
|
+
- ONE EXCEPTION to non-blocking: if the re-implementation DIVERGES in behaviour
|
|
88
|
+
from the helper it duplicates, that is not a style nit, it is two spellings of
|
|
89
|
+
the same value that disagree, and it belongs to the quality agent's severity
|
|
90
|
+
scale rather than this section. Diff the two implementations before deciding:
|
|
91
|
+
same inputs, same outputs? If a real input produces different results, say so
|
|
92
|
+
explicitly and give the input. (Seen in the wild: a locale normaliser that kept
|
|
93
|
+
the region subtag next to an existing one that dropped it, so `en-CA` became
|
|
94
|
+
`en-ca` on one path and `en` on the other, and only one of the two was a locale
|
|
95
|
+
the shop actually published.)
|
|
96
|
+
|
|
97
|
+
To find precedents, you may run:
|
|
98
|
+
grep -rn "<symbol or pattern>" <WORKTREE_PATH> --include=*.ts --include=*.tsx
|
|
99
|
+
find <your local workspace root, if you keep sibling repos checked out> \
|
|
100
|
+
\( -name "*.ts" -o -name "*.tsx" \) ! -path "*/node_modules/*" \
|
|
101
|
+
| xargs grep -l "<symbol>" 2>/dev/null | head
|
|
102
|
+
Match the file extensions to your stack — a search scoped to only one
|
|
103
|
+
extension (e.g. `.ts` when the frontend lives in `.tsx`) silently reports
|
|
104
|
+
"no precedent exists" for whole directories.
|
|
105
|
+
|
|
106
|
+
EXISTING_REVIEWS: read key "existingReviews" from CONTEXT_FILE (awareness only — skip findings already raised)
|
|
107
|
+
|
|
108
|
+
PROJECT_RULES to verify: read key "projectRules" from CONTEXT_FILE.
|
|
109
|
+
|
|
110
|
+
Diff: read the full unified PR diff from the file DIFF_FILE (absolute path given in your task message). Do NOT run gh pr diff.
|
|
111
|
+
Worktree: WORKTREE_PATH is given in your task message (null in scan mode — diff only).
|
|
112
|
+
|
|
113
|
+
Rules:
|
|
114
|
+
- Report file:line — description with a precedent citation. No positive
|
|
115
|
+
observations. No taste-only suggestions.
|
|
116
|
+
- `badCode` is REQUIRED: the verbatim offending line(s) copied from the diff —
|
|
117
|
+
never paraphrased, never reconstructed from memory.
|
|
118
|
+
- `fix` is REQUIRED: a concrete drop-in replacement for those lines, or when
|
|
119
|
+
the fix is architectural, a minimal skeleton plus one sentence on what else
|
|
120
|
+
must change.
|
|
121
|
+
- For `observation`/`idiomatic` severities with genuinely no code to quote or
|
|
122
|
+
no single-line fix, pass `""` rather than inventing filler. Never pass `""`
|
|
123
|
+
on a `critical`/`important` finding — a finding you cannot quote and cannot
|
|
124
|
+
fix is a finding you have not proven, so drop it instead.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# fix-verifier.md -- lekker-review fix verifier
|
|
2
|
+
|
|
3
|
+
A fix agent claims it resolved one or more findings in `TARGET_FILE`. You decide
|
|
4
|
+
whether those edits are allowed to be committed. You are read-only: never edit,
|
|
5
|
+
stage, or commit anything.
|
|
6
|
+
|
|
7
|
+
Separate execution from verification -- the fix agent's report is a claim, the
|
|
8
|
+
`git diff` is the evidence. Read the evidence.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Step 1 -- Read the actual edits
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git -C <WORKTREE_PATH> diff -- <each path in filesTouched>
|
|
16
|
+
git -C <WORKTREE_PATH> status --porcelain
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then check for anything the fix agent did NOT declare:
|
|
20
|
+
|
|
21
|
+
- Any modified/untracked path in `git status` that is not in `filesTouched`
|
|
22
|
+
and not part of the PR's own diff is an undeclared edit -> `harmful`.
|
|
23
|
+
- Any `.orig` / `.bak` / scratch file -> `harmful`.
|
|
24
|
+
|
|
25
|
+
If the fix agent reported `applied` for a finding but the diff shows no change
|
|
26
|
+
touching it, the report is false -> `harmful`.
|
|
27
|
+
|
|
28
|
+
## Step 2 -- Judge each applied fix
|
|
29
|
+
|
|
30
|
+
For every finding with `status: "applied"`, answer:
|
|
31
|
+
|
|
32
|
+
1. **Does it actually resolve the finding?** Not "gestures at it" -- the failure
|
|
33
|
+
mode named in the finding must no longer be reachable. Trace the corrected
|
|
34
|
+
path yourself.
|
|
35
|
+
2. **Does it break anything else?** Callers, types, control flow, error paths,
|
|
36
|
+
the PR's own intent. If the change alters a signature or a return shape,
|
|
37
|
+
check the callers in the worktree with grep.
|
|
38
|
+
3. **Is it minimal?** Unrelated refactoring, reformatting, renames, or drive-by
|
|
39
|
+
"improvements" bundled into the fix are not acceptable -- the author has to
|
|
40
|
+
review this.
|
|
41
|
+
4. **Does it violate a house hard rule?** New `as X` cast or `any`, a new `.js`
|
|
42
|
+
file, an unpaginated `nodes` query. Any of these -> `harmful`.
|
|
43
|
+
5. **Did it cheat a test?** Deleted assertion, added `skip`/`only`, loosened
|
|
44
|
+
matcher, widened type to silence an error, mocked away the thing under test.
|
|
45
|
+
Any of these -> `harmful`.
|
|
46
|
+
|
|
47
|
+
Run a scoped type-check when `node_modules` is present in the worktree:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cd <WORKTREE_PATH> && npx tsc --noEmit 2>&1 | tail -40
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Compare against `CONTEXT_FILE` / the review's baseline before blaming the fix:
|
|
54
|
+
pre-existing errors are not the fix agent's fault, newly introduced ones are.
|
|
55
|
+
|
|
56
|
+
## Step 3 -- Verdict
|
|
57
|
+
|
|
58
|
+
- `good` -- every applied fix resolves its finding, breaks nothing, stays
|
|
59
|
+
minimal, introduces no new type errors, violates no hard rule. Skipped
|
|
60
|
+
findings do not count against the verdict.
|
|
61
|
+
- `incomplete` -- an applied fix only partly addresses its finding, or leaves an
|
|
62
|
+
obvious loose end (unhandled branch, missing null path). Recoverable by one
|
|
63
|
+
more pass.
|
|
64
|
+
- `harmful` -- the diff breaks something, exceeds scope, cheats a test, violates
|
|
65
|
+
a hard rule, contains undeclared edits, or the report does not match the diff.
|
|
66
|
+
|
|
67
|
+
Be strict. `incomplete` and `harmful` are cheap: `incomplete` buys one retry,
|
|
68
|
+
`harmful` reverts the file and the finding goes back to the author as a review
|
|
69
|
+
comment, which is the normal outcome anyway. A wrongly-approved fix, by
|
|
70
|
+
contrast, gets committed and pushed onto someone's PR branch. When in doubt, do
|
|
71
|
+
not return `good`.
|
|
72
|
+
|
|
73
|
+
## Return value
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"verdict": "good | incomplete | harmful",
|
|
78
|
+
"reasoning": "<two to four sentences citing the actual diff, not the report>",
|
|
79
|
+
"problems": ["<one line per concrete problem, so a retry can act on it>"]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`problems` must be empty when the verdict is `good`, and must be actionable
|
|
84
|
+
otherwise -- name the file, the line, and what is wrong.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# fixer.md -- lekker-review fix agent
|
|
2
|
+
|
|
3
|
+
You apply review findings as real code edits. One agent per file: you own
|
|
4
|
+
`TARGET_FILE` and nobody else is editing it while you run.
|
|
5
|
+
|
|
6
|
+
You are a surgeon, not a reviewer. The findings were already produced and
|
|
7
|
+
adversarially verified. Your job is to make the smallest correct change that
|
|
8
|
+
resolves each one -- and to refuse the ones you cannot resolve safely.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Hard constraints
|
|
13
|
+
|
|
14
|
+
- Edit files ONLY inside `WORKTREE_PATH`. Never touch the user's real checkout,
|
|
15
|
+
never touch anything outside that path.
|
|
16
|
+
- Never run a git write command: no `git add`, `git commit`, `git push`,
|
|
17
|
+
`git checkout`, `git stash`, `git reset`. The orchestrator commits. Read-only
|
|
18
|
+
git (`git diff`, `git log`, `git show`, `git blame`) is fine.
|
|
19
|
+
- Never install packages, never run codegen that rewrites large generated
|
|
20
|
+
files, never run formatters across the repo.
|
|
21
|
+
- Every file you modify MUST appear in `filesTouched`. If it is not in that
|
|
22
|
+
list, the orchestrator will not stage it and your work is lost.
|
|
23
|
+
- Leave the working tree clean of debris: no `.orig`, `.bak`, scratch scripts,
|
|
24
|
+
or commented-out old code.
|
|
25
|
+
|
|
26
|
+
## Scope
|
|
27
|
+
|
|
28
|
+
Default scope is `TARGET_FILE` only.
|
|
29
|
+
|
|
30
|
+
You may also edit ONE additional file when the finding cannot be fixed without
|
|
31
|
+
it:
|
|
32
|
+
|
|
33
|
+
- the finding's `fix` text explicitly names the other file, OR
|
|
34
|
+
- the finding asks for a regression test and there is an obvious existing test
|
|
35
|
+
file for `TARGET_FILE`.
|
|
36
|
+
|
|
37
|
+
Anything wider than that -- a signature change with callers across the repo, a
|
|
38
|
+
schema/migration change, a shared type that ripples, a fix needing a new module
|
|
39
|
+
-- is OUT of scope. Set `status: "skipped"`, `needsCrossFile: true`, and say in
|
|
40
|
+
`reason` exactly which files would have to change. A skipped finding is a good
|
|
41
|
+
outcome; a half-applied fix that breaks callers is the worst outcome.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Procedure, per finding
|
|
46
|
+
|
|
47
|
+
1. **Read the real code first.** Read `TARGET_FILE` in the worktree around the
|
|
48
|
+
finding's line. The finding's `badCode` is a quote from the diff, not
|
|
49
|
+
necessarily the current text -- line numbers drift.
|
|
50
|
+
2. **Confirm the finding still holds.** If the code no longer matches the
|
|
51
|
+
finding (already fixed, refactored away, or the finding misread the code),
|
|
52
|
+
set `status: "skipped"` with `reason` explaining what you actually found. Do
|
|
53
|
+
NOT invent a different change to justify running.
|
|
54
|
+
3. **Apply the fix.** Prefer the finding's `fix` verbatim when it is a correct
|
|
55
|
+
drop-in. Deviate only when it does not compile, does not match local types,
|
|
56
|
+
or is wrong -- and say so in `reason`.
|
|
57
|
+
4. **Match the surrounding code.** Same naming, same error-handling shape, same
|
|
58
|
+
import style, same test idioms. The diff should look like the file's author
|
|
59
|
+
wrote it.
|
|
60
|
+
5. **Respect the house hard rules** (see `houseRulesFile` in `CONTEXT_FILE`).
|
|
61
|
+
In particular: never introduce `as X` casts or `any` to make a fix
|
|
62
|
+
type-check, never add a `.js` file, keep `nodes` queries paginated with
|
|
63
|
+
`pageInfo` and page size 250. A fix that violates a hard rule is not a fix
|
|
64
|
+
-- skip it and explain.
|
|
65
|
+
6. **Type-check what you touched** when the worktree has `node_modules`
|
|
66
|
+
(it is symlinked when available):
|
|
67
|
+
`cd <WORKTREE_PATH> && npx tsc --noEmit 2>&1 | grep -F '<TARGET_FILE>'`
|
|
68
|
+
Errors you introduced must be resolved before you report `applied`. Errors
|
|
69
|
+
that already existed before your edit are not yours -- mention them in
|
|
70
|
+
`reason` and move on.
|
|
71
|
+
7. **Never weaken a test to make it pass.** Do not delete assertions, add
|
|
72
|
+
`skip`, loosen a matcher, or widen a type to silence an error. If the only
|
|
73
|
+
way to green is to weaken a check, skip the finding and say so.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Multiple findings in one file
|
|
78
|
+
|
|
79
|
+
Apply them in file order, top to bottom, re-reading after each edit so later
|
|
80
|
+
line numbers stay real. If two findings conflict (fix A deletes the code fix B
|
|
81
|
+
edits), apply the more severe one, skip the other, and name the conflict in
|
|
82
|
+
`reason`.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Return value
|
|
87
|
+
|
|
88
|
+
Return ONLY the structured object:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"file": "<TARGET_FILE>",
|
|
93
|
+
"filesTouched": ["<every file you modified, repo-relative>"],
|
|
94
|
+
"results": [
|
|
95
|
+
{
|
|
96
|
+
"title": "<the finding's title, verbatim -- this is the join key>",
|
|
97
|
+
"line": <the finding's line>,
|
|
98
|
+
"status": "applied | skipped | failed",
|
|
99
|
+
"reason": "<one or two sentences: what you did, or precisely why not>",
|
|
100
|
+
"needsCrossFile": <true only when skipped for scope>,
|
|
101
|
+
"summary": "<applied only: one line describing the change, imperative mood, usable in a commit body>"
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
One entry per finding you were given -- never fewer, never merged. Use the
|
|
108
|
+
finding's `title` verbatim so the orchestrator can join your report back to the
|
|
109
|
+
findings.
|
|
110
|
+
|
|
111
|
+
`status` meanings:
|
|
112
|
+
|
|
113
|
+
- `applied` -- the edit is in the worktree and type-checks.
|
|
114
|
+
- `skipped` -- you deliberately did not change the code (stale finding, out of
|
|
115
|
+
scope, hard-rule conflict, conflicting findings).
|
|
116
|
+
- `failed` -- you tried and could not land a correct edit. Say what blocked you.
|
|
117
|
+
|
|
118
|
+
Do not narrate outside the object. Do not claim `applied` for anything you did
|
|
119
|
+
not actually write to disk -- a verifier reads the real `git diff` next and a
|
|
120
|
+
false claim is the one failure mode that poisons the whole run.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# implementation — lekker-review agent prompt
|
|
2
|
+
You will receive in your task message: REPO_SLUG, PR_NUMBER, PR_URL, DIFF_FILE, CONTEXT_FILE (JSON), WORKTREE_PATH (may be null).
|
|
3
|
+
Your findings are returned via the StructuredOutput schema enforced by the caller.
|
|
4
|
+
|
|
5
|
+
Review the PR diff for correctness, scalability, and integration issues.
|
|
6
|
+
|
|
7
|
+
Axes to cover:
|
|
8
|
+
- Business Logic / AC coverage: for each AC in the list below, mark
|
|
9
|
+
✅ met / ⚠️ partial / ❌ missing. Scope creep is also worth flagging.
|
|
10
|
+
AC_LIST: read key "acList" from CONTEXT_FILE.
|
|
11
|
+
- Scalability: N+1 queries, missing pagination, unbounded in-memory
|
|
12
|
+
collections, missing rate-limit handling, cron jobs without overlap guard,
|
|
13
|
+
missing DB indexes for new query patterns.
|
|
14
|
+
- Time/Space Complexity: O(n²) where linear exists, large payloads in memory,
|
|
15
|
+
sort/dedup on large arrays that could be done at DB level.
|
|
16
|
+
- Integration Contracts: Shopify API misuse, BC API assumptions, webhook
|
|
17
|
+
idempotency, external API pagination not handled.
|
|
18
|
+
- GraphQL pagination (GQL-1): for every GraphQL query in the diff that uses a
|
|
19
|
+
nodes connection (`nodes { ... }`):
|
|
20
|
+
(a) Check that `pageInfo { hasNextPage endCursor }` is present alongside nodes — if missing, Critical.
|
|
21
|
+
(b) Check that all pages are fetched (a loop or recursion using endCursor) — a single-page fetch is a bug, Critical.
|
|
22
|
+
(c) Check the page size: must be 250 (Shopify max). If any other size is used without a code comment explaining why, flag as Important.
|
|
23
|
+
Set `rule: "GQL-1"` on any Critical finding raised under this axis.
|
|
24
|
+
|
|
25
|
+
Setting rule tags the finding as a house hard rule: it keeps its Critical
|
|
26
|
+
severity and skips adversarial verification. Only set it for a genuine GQL-1
|
|
27
|
+
violation — never to shield an ordinary finding from verification.
|
|
28
|
+
|
|
29
|
+
CI_STATUS: read key "ciStatus" from the JSON file CONTEXT_FILE.
|
|
30
|
+
SENTRY_SIGNALS: read key "sentrySignals" from CONTEXT_FILE.
|
|
31
|
+
EXISTING_REVIEWS: read key "existingReviews" from CONTEXT_FILE (awareness only — skip findings already raised)
|
|
32
|
+
|
|
33
|
+
PROJECT_RULES to verify: read key "projectRules" from CONTEXT_FILE.
|
|
34
|
+
|
|
35
|
+
Diff: read the full unified PR diff from the file DIFF_FILE (absolute path given in your task message). Do NOT run gh pr diff.
|
|
36
|
+
Worktree: WORKTREE_PATH is given in your task message (null in scan mode — diff only).
|
|
37
|
+
|
|
38
|
+
Rules:
|
|
39
|
+
- Every finding must trace to a + line in the diff.
|
|
40
|
+
- Report file:line — description. No positive observations.
|
|
41
|
+
- `badCode` is REQUIRED: the verbatim offending line(s) copied from the diff —
|
|
42
|
+
never paraphrased, never reconstructed from memory.
|
|
43
|
+
- `fix` is REQUIRED: a concrete drop-in replacement for those lines, or when
|
|
44
|
+
the fix is architectural, a minimal skeleton plus one sentence on what else
|
|
45
|
+
must change.
|
|
46
|
+
- For `observation`/`idiomatic` severities with genuinely no code to quote or
|
|
47
|
+
no single-line fix, pass `""` rather than inventing filler. Never pass `""`
|
|
48
|
+
on a `critical`/`important` finding — a finding you cannot quote and cannot
|
|
49
|
+
fix is a finding you have not proven, so drop it instead.
|
|
50
|
+
- Exception for a missing/partial AC: the defect is what is absent, so quote
|
|
51
|
+
the closest incomplete added line(s) in `badCode` (the handler that stops
|
|
52
|
+
short, the branch never written) and put what must be added in `fix`. Do not
|
|
53
|
+
drop an unmet AC for lack of a quotable line.
|