@olegkoval/agent-skills 1.43.1 → 1.44.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/adapters/claude/olko-github-pr/skills/lekker-review/SKILL.md +85 -2
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/pricing.json +9 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-conventions.md +158 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-implementation.md +76 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-quality.md +87 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-simplification.md +63 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-test-quality.md +172 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/phase4-comparison.md +42 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/profile.md +347 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-deep.md +82 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-medium.md +81 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/context.json +3 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/house-rules.md +11 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/revmux-report.json +179 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/install-revmux-prompts.sh +66 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/revmux-adapter.mjs +261 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/revmux-engine.sh +156 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/selftest.mjs +76 -0
- package/package.json +1 -1
- package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/skills/lekker-review/SKILL.md +85 -2
- package/plugins/olko-github-pr/skills/lekker-review/references/pricing.json +9 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-conventions.md +158 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-implementation.md +76 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-quality.md +87 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-simplification.md +63 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-test-quality.md +172 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/phase4-comparison.md +42 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/profile.md +347 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-deep.md +82 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-medium.md +81 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/context.json +3 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/house-rules.md +11 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/revmux-report.json +179 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/install-revmux-prompts.sh +66 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/revmux-adapter.mjs +261 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/revmux-engine.sh +156 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/selftest.mjs +76 -0
- package/plugins/olko-github-pr/skills/lekker-review/workflow.js +43 -6
- package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# revmux-engine.sh -- lekker-review's revmux caller (Phase 2 of the revmux spike).
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# revmux-engine.sh --task SLUG --run NAME --depth medium|deep --workdir DIR \
|
|
6
|
+
# --diff-file FILE --context-file FILE --profile-file FILE --out FILE \
|
|
7
|
+
# [--tasks-dir DIR] [--config-dir DIR]
|
|
8
|
+
#
|
|
9
|
+
# Builds one revmux round (scope.md, goal.md, input/context/, input/profile.md),
|
|
10
|
+
# runs revmux --no-tui, writes its stdout JSON to --out, and exits with revmux's
|
|
11
|
+
# own code (0 = no findings, 1 = findings, 2 = tool error). A round name that
|
|
12
|
+
# already exists is revmux's own error -- this script never deletes anything to
|
|
13
|
+
# work around it.
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
TASK=""
|
|
17
|
+
RUN=""
|
|
18
|
+
DEPTH=""
|
|
19
|
+
WORKDIR=""
|
|
20
|
+
DIFF_FILE=""
|
|
21
|
+
CONTEXT_FILE=""
|
|
22
|
+
PROFILE_FILE=""
|
|
23
|
+
OUT=""
|
|
24
|
+
TASKS_DIR="${LEKKER_REVMUX_TASKS_DIR:-$HOME/code-reviews/revmux-tasks}"
|
|
25
|
+
CONFIG_DIR="${REVMUX_CONFIG_DIR:-$HOME/.config/revmux}"
|
|
26
|
+
|
|
27
|
+
while [[ $# -gt 0 ]]; do
|
|
28
|
+
case "$1" in
|
|
29
|
+
--task) TASK="$2"; shift 2 ;;
|
|
30
|
+
--run) RUN="$2"; shift 2 ;;
|
|
31
|
+
--depth) DEPTH="$2"; shift 2 ;;
|
|
32
|
+
--workdir) WORKDIR="$2"; shift 2 ;;
|
|
33
|
+
--diff-file) DIFF_FILE="$2"; shift 2 ;;
|
|
34
|
+
--context-file) CONTEXT_FILE="$2"; shift 2 ;;
|
|
35
|
+
--profile-file) PROFILE_FILE="$2"; shift 2 ;;
|
|
36
|
+
--out) OUT="$2"; shift 2 ;;
|
|
37
|
+
--tasks-dir) TASKS_DIR="$2"; shift 2 ;;
|
|
38
|
+
--config-dir) CONFIG_DIR="$2"; shift 2 ;;
|
|
39
|
+
*) printf 'revmux-engine: unknown arg %s\n' "$1" >&2; exit 2 ;;
|
|
40
|
+
esac
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
for req in TASK RUN DEPTH WORKDIR DIFF_FILE CONTEXT_FILE PROFILE_FILE OUT; do
|
|
44
|
+
if [[ -z "${!req}" ]]; then
|
|
45
|
+
printf 'revmux-engine: missing required --%s\n' "$(printf '%s' "$req" | tr 'A-Z' 'a-z' | tr '_' '-')" >&2
|
|
46
|
+
exit 2
|
|
47
|
+
fi
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
for input in DIFF_FILE CONTEXT_FILE PROFILE_FILE; do
|
|
51
|
+
if [[ ! -r "${!input}" ]]; then
|
|
52
|
+
printf 'revmux-engine: --%s must be a readable file: %s\n' \
|
|
53
|
+
"$(printf '%s' "$input" | tr 'A-Z' 'a-z' | tr '_' '-')" "${!input}" >&2
|
|
54
|
+
exit 2
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
WORKTREE_JSON="$(dirname "$CONTEXT_FILE")/worktree.json"
|
|
59
|
+
if [[ ! -r "$WORKTREE_JSON" ]]; then
|
|
60
|
+
printf 'revmux-engine: worktree configuration must be readable: %s\n' "$WORKTREE_JSON" >&2
|
|
61
|
+
exit 2
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
case "$DEPTH" in
|
|
65
|
+
medium|deep) ;;
|
|
66
|
+
*)
|
|
67
|
+
printf 'revmux-engine: depth "%s" is not supported by this engine (only medium|deep -- scan stays on the workflow engine, it has no worktree)\n' "$DEPTH" >&2
|
|
68
|
+
exit 2
|
|
69
|
+
;;
|
|
70
|
+
esac
|
|
71
|
+
|
|
72
|
+
PROFILE_NAME="lekker-${DEPTH}"
|
|
73
|
+
|
|
74
|
+
command -v revmux >/dev/null 2>&1 || { printf 'revmux-engine: revmux binary not found on PATH\n' >&2; exit 2; }
|
|
75
|
+
command -v jq >/dev/null 2>&1 || { printf 'revmux-engine: jq not found on PATH\n' >&2; exit 2; }
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Codex guard: refuse to run a profile whose resolved text names a codex/
|
|
79
|
+
# runner. Company does not support the codex CLI (revmux is claude-only here).
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
PROFILE_TEXT_FILE="$CONFIG_DIR/prompts/profiles/${PROFILE_NAME}.md"
|
|
82
|
+
if [[ ! -f "$PROFILE_TEXT_FILE" ]]; then
|
|
83
|
+
printf 'revmux-engine: cannot resolve profile text at %s to run the codex guard -- refusing to proceed\n' "$PROFILE_TEXT_FILE" >&2
|
|
84
|
+
exit 2
|
|
85
|
+
fi
|
|
86
|
+
if grep -q 'codex/' "$PROFILE_TEXT_FILE"; then
|
|
87
|
+
printf 'revmux-engine: profile %s names a codex/ runner -- codex is not supported here, refusing to run\n' "$PROFILE_TEXT_FILE" >&2
|
|
88
|
+
exit 2
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
mkdir -p "$TASKS_DIR"
|
|
92
|
+
|
|
93
|
+
NEW_JSON="$(revmux --tasks-dir "$TASKS_DIR" --config-dir "$CONFIG_DIR" new --task "$TASK" --run "$RUN")"
|
|
94
|
+
|
|
95
|
+
resolve_new_path() {
|
|
96
|
+
local key="$1"
|
|
97
|
+
local value
|
|
98
|
+
if ! value="$(printf '%s' "$NEW_JSON" | jq -er --arg key "$key" \
|
|
99
|
+
'.[$key] | select(type == "string" and length > 0)')" \
|
|
100
|
+
|| [[ -z "$value" || "$value" == "null" ]]; then
|
|
101
|
+
printf 'revmux-engine: revmux new returned an invalid %s path\n' "$key" >&2
|
|
102
|
+
return 2
|
|
103
|
+
fi
|
|
104
|
+
printf '%s' "$value"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
SCOPE_FILE="$(resolve_new_path scope)"
|
|
108
|
+
GOAL_FILE="$(resolve_new_path goal)"
|
|
109
|
+
PROFILE_INPUT_FILE="$(resolve_new_path profile)"
|
|
110
|
+
CONTEXT_DIR="$(resolve_new_path context)"
|
|
111
|
+
|
|
112
|
+
mkdir -p "$CONTEXT_DIR"
|
|
113
|
+
|
|
114
|
+
TARGET_LABEL="$(jq -r '.reviewTarget // "unknown target"' "$CONTEXT_FILE" 2>/dev/null || printf 'unknown target')"
|
|
115
|
+
|
|
116
|
+
BASE_REF=""
|
|
117
|
+
BASE_REF="$(jq -r '.mergeBase // .baseRef // empty' "$WORKTREE_JSON" 2>/dev/null || true)"
|
|
118
|
+
|
|
119
|
+
{
|
|
120
|
+
printf '# Scope\n\n'
|
|
121
|
+
printf 'Under review: %s\n\n' "$TARGET_LABEL"
|
|
122
|
+
printf 'Diff file (authoritative): %s\n\n' "$DIFF_FILE"
|
|
123
|
+
if [[ -n "$BASE_REF" ]]; then
|
|
124
|
+
printf 'Reproduce inside WORKDIR with:\n\n git -C %s diff %s...HEAD\n\n' "$WORKDIR" "$BASE_REF"
|
|
125
|
+
else
|
|
126
|
+
printf 'No base ref was recorded for this worktree; the diff file above is authoritative, do not attempt to reconstruct it from git.\n\n'
|
|
127
|
+
fi
|
|
128
|
+
} > "$SCOPE_FILE"
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
printf '# Goal\n\n'
|
|
132
|
+
printf '## PR title\n\n%s\n\n' "$(jq -r '.reviewTarget // "none provided"' "$CONTEXT_FILE" 2>/dev/null || printf 'none provided')"
|
|
133
|
+
printf '## Ticket / acceptance criteria\n\n%s\n\n' "$(jq -r '.acList // "none provided"' "$CONTEXT_FILE" 2>/dev/null || printf 'none provided')"
|
|
134
|
+
printf '## Scoping decisions\n\n%s\n\n' "$(jq -r '.scopingDecisions // "none provided"' "$CONTEXT_FILE" 2>/dev/null || printf 'none provided')"
|
|
135
|
+
} > "$GOAL_FILE"
|
|
136
|
+
|
|
137
|
+
cp "$CONTEXT_FILE" "$CONTEXT_DIR/context.json"
|
|
138
|
+
cp "$DIFF_FILE" "$CONTEXT_DIR/pr.diff"
|
|
139
|
+
cp "$WORKTREE_JSON" "$CONTEXT_DIR/worktree.json"
|
|
140
|
+
cp "$PROFILE_FILE" "$PROFILE_INPUT_FILE"
|
|
141
|
+
|
|
142
|
+
REVMUX_OUT="$OUT"
|
|
143
|
+
set +e
|
|
144
|
+
revmux --task "$TASK" --run "$RUN" --profile "$PROFILE_NAME" \
|
|
145
|
+
--workdir "$WORKDIR" --tasks-dir "$TASKS_DIR" --config-dir "$CONFIG_DIR" \
|
|
146
|
+
--no-tui > "$REVMUX_OUT"
|
|
147
|
+
CODE=$?
|
|
148
|
+
set -e
|
|
149
|
+
|
|
150
|
+
case "$CODE" in
|
|
151
|
+
0) printf 'revmux exit 0 (no findings)\n' >&2 ;;
|
|
152
|
+
1) printf 'revmux exit 1 (findings reported)\n' >&2 ;;
|
|
153
|
+
*) printf 'revmux exit %s (tool error)\n' "$CODE" >&2 ;;
|
|
154
|
+
esac
|
|
155
|
+
|
|
156
|
+
exit "$CODE"
|
|
@@ -183,5 +183,81 @@ if (!hasRouting) {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
console.log('\nrevmux adapter: fixture mapping')
|
|
187
|
+
{
|
|
188
|
+
const { execFileSync, spawnSync } = await import('node:child_process')
|
|
189
|
+
const fixturePath = join(SKILL, 'scripts', 'fixtures', 'revmux-report.json')
|
|
190
|
+
const contextPath = join(SKILL, 'scripts', 'fixtures', 'context.json')
|
|
191
|
+
const pricingPath = join(SKILL, 'references', 'pricing.json')
|
|
192
|
+
const adapterPath = join(SKILL, 'scripts', 'revmux-adapter.mjs')
|
|
193
|
+
const adapterArgs = [adapterPath, fixturePath, '--pricing', pricingPath, '--context', contextPath]
|
|
194
|
+
const out = execFileSync('node', adapterArgs, { encoding: 'utf8' })
|
|
195
|
+
const adapted = JSON.parse(out)
|
|
196
|
+
|
|
197
|
+
check('engine tag', adapted.engine, 'revmux')
|
|
198
|
+
check('critical confirmed kept critical',
|
|
199
|
+
adapted.findings.some(f => f.title.includes('Race between webhook retry') && f.severity === 'critical'), true)
|
|
200
|
+
check('major refined -> important, agreedBy from 2 sources',
|
|
201
|
+
(() => {
|
|
202
|
+
const f = adapted.findings.find(f => f.title.includes('Discount stacking'))
|
|
203
|
+
return f && f.severity === 'important' && Array.isArray(f.agreedBy) && f.agreedBy.length === 2
|
|
204
|
+
})(), true)
|
|
205
|
+
check('minor + lekker-conventions lens -> idiomatic',
|
|
206
|
+
adapted.findings.find(f => f.title.includes('naming matrix'))?.severity, 'idiomatic')
|
|
207
|
+
check('immaterial verdict dropped, not re-promoted',
|
|
208
|
+
adapted.findings.some(f => f.title.includes('Unused import')), false)
|
|
209
|
+
check('rejected non-hard-rule dropped',
|
|
210
|
+
adapted.findings.some(f => f.title.includes('double-count a tip')), false)
|
|
211
|
+
check('TS-1 rejected but corroborated by quoted code IS re-promoted to critical',
|
|
212
|
+
(() => {
|
|
213
|
+
const f = adapted.findings.find(f => f.title.includes('Unsafe cast on inventory adjustment'))
|
|
214
|
+
return f && f.severity === 'critical' && f.rule === 'TS-1'
|
|
215
|
+
})(), true)
|
|
216
|
+
check('TS-1 rejected with no corroborating code stays dropped',
|
|
217
|
+
adapted.findings.some(f => f.title.includes('Type safety concern in inventory handler')), false)
|
|
218
|
+
check('prose without an extracted snippet leaves badCode empty',
|
|
219
|
+
adapted.findings.find(f => f.title.includes('no retry backoff'))?.badCode, '')
|
|
220
|
+
check('configured custom hard rule with quoted code is re-promoted',
|
|
221
|
+
adapted.findings.find(f => f.title.includes('Session token is logged'))?.severity, 'critical')
|
|
222
|
+
check('configured custom hard rule without quoted code stays dropped',
|
|
223
|
+
adapted.findings.some(f => f.title.includes('Possible session concern')), false)
|
|
224
|
+
check('pre_existing becomes observation with Pre-existing: prefix',
|
|
225
|
+
(() => {
|
|
226
|
+
const f = adapted.findings.find(f => f.title.includes('no retry backoff'))
|
|
227
|
+
return f && f.severity === 'observation' && f.description.startsWith('Pre-existing:')
|
|
228
|
+
})(), true)
|
|
229
|
+
check('open_questions pass through as questions',
|
|
230
|
+
adapted.questions.length, 1)
|
|
231
|
+
check('degraded agent surfaced by name',
|
|
232
|
+
adapted.degraded, ['adversarial'])
|
|
233
|
+
check('droppedCount counts four rejected or immaterial findings', adapted.droppedCount, 4)
|
|
234
|
+
check('hardRuleCount counts built-in and custom re-promotions', adapted.hardRuleCount, 2)
|
|
235
|
+
check('totalUsd computed from known pricing', typeof adapted.totalUsd === 'number' && adapted.totalUsd > 0, true)
|
|
236
|
+
check('malformed token rows do not make totalUsd NaN', Number.isFinite(adapted.totalUsd), true)
|
|
237
|
+
check('malformed token rows keep their own usd unknown',
|
|
238
|
+
adapted.agents.find(a => a.name === 'malformed-cost')?.usd, null)
|
|
239
|
+
check('no pricingMissing when all models are priced', adapted.pricingMissing, undefined)
|
|
240
|
+
check('test-quality coverage verdict is populated from attributed findings',
|
|
241
|
+
adapted.coverageVerdict.includes('Operator-flip mutation'), true)
|
|
242
|
+
check('mutation-slip summary is populated from attributed findings',
|
|
243
|
+
adapted.mutationSlip.includes('retry guard'), true)
|
|
244
|
+
check('mock smells are structured from attributed findings', adapted.mockSmells.length, 1)
|
|
245
|
+
|
|
246
|
+
const noPricingOut = execFileSync('node', [adapterPath, fixturePath, '--context', contextPath], { encoding: 'utf8' })
|
|
247
|
+
const noPricingAdapted = JSON.parse(noPricingOut)
|
|
248
|
+
check('without a pricing file every model is pricingMissing',
|
|
249
|
+
noPricingAdapted.pricingMissing?.includes('claude-sonnet-5'), true)
|
|
250
|
+
check('without a pricing file totalUsd is null', noPricingAdapted.totalUsd, null)
|
|
251
|
+
|
|
252
|
+
for (const [label, badArgs] of [
|
|
253
|
+
['report', [adapterPath, join(SKILL, 'SKILL.md')]],
|
|
254
|
+
['pricing', [adapterPath, fixturePath, '--pricing', join(SKILL, 'SKILL.md'), '--context', contextPath]],
|
|
255
|
+
]) {
|
|
256
|
+
const failedRun = spawnSync('node', badArgs, { encoding: 'utf8' })
|
|
257
|
+
check(`invalid ${label} JSON exits 2`, failedRun.status, 2)
|
|
258
|
+
check(`invalid ${label} JSON has a clear error`, failedRun.stderr.includes(`${label} JSON`), true)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
186
262
|
console.log(failed === 0 ? '\nall checks passed\n' : `\n${failed} check(s) FAILED\n`)
|
|
187
263
|
process.exit(failed === 0 ? 0 : 1)
|
package/package.json
CHANGED
|
@@ -55,6 +55,10 @@ FAANG-grade code review. Isolated worktree checkout, full context gathering
|
|
|
55
55
|
you have MCP tools configured for), then 5 parallel specialized review
|
|
56
56
|
agents, a finding-verification pass, and one unified markdown output.
|
|
57
57
|
|
|
58
|
+
An optional `--engine revmux` flag can replace the Step 2 Workflow-tool pipeline
|
|
59
|
+
with revmux (profiles and lenses under `references/revmux/`); default stays
|
|
60
|
+
`workflow`, and both engines feed the same Step 3 synthesis.
|
|
61
|
+
|
|
58
62
|
Two modes, same review engine:
|
|
59
63
|
|
|
60
64
|
| Mode | Target | Entry |
|
|
@@ -232,6 +236,12 @@ in Step 4. Use it for a read-only pre-push look.
|
|
|
232
236
|
**`--no-artifact` flag:** parse and store as `ARTIFACT=false` (default true).
|
|
233
237
|
Skips Step 3.5 (living review artifact) silently.
|
|
234
238
|
|
|
239
|
+
**`--engine revmux|workflow` flag:** parse and store as `ENGINE`, default
|
|
240
|
+
`workflow`. `revmux` is allowed only at depth `medium` or `deep` - revmux's
|
|
241
|
+
scripts reject `scan` (see Depth gate). If depth resolves to `scan` (explicit
|
|
242
|
+
or auto), force `ENGINE=workflow` regardless of the flag and note `engine
|
|
243
|
+
forced to workflow - revmux needs medium/deep` in the review header.
|
|
244
|
+
|
|
235
245
|
**Re-review detection:** run
|
|
236
246
|
`ls ~/code-reviews/*-<TARGET_SLUG>-<repo-short-name>.md 2>/dev/null | sort | tail -1`
|
|
237
247
|
(`TARGET_SLUG` = `pr-<PR_NUMBER>` in pr mode, `branch-<LOCAL_BRANCH sanitized to
|
|
@@ -420,7 +430,9 @@ their prompts wholesale - the workflow script delivers it by path.
|
|
|
420
430
|
|
|
421
431
|
---
|
|
422
432
|
|
|
423
|
-
## Step 2 -
|
|
433
|
+
## Step 2 - Review engine (review + verify + critic)
|
|
434
|
+
|
|
435
|
+
### ENGINE=workflow (default)
|
|
424
436
|
|
|
425
437
|
Invoke the Workflow tool:
|
|
426
438
|
|
|
@@ -499,12 +511,61 @@ provers on `sonnet`, housekeeping on `haiku`. Only the synthesis in Step 3 runs
|
|
|
499
511
|
on the session model.
|
|
500
512
|
|
|
501
513
|
Return value from the workflow:
|
|
502
|
-
`{findings, droppedCount, downgradedCount, hardRuleCount, proveAttemptCount, provenCount, acCoverage, coverageVerdict, mutationSlip, mockSmells, agentCount, outputTokens, turnTokensTotal}`
|
|
514
|
+
`{engine, findings, droppedCount, downgradedCount, hardRuleCount, proveAttemptCount, provenCount, acCoverage, coverageVerdict, mutationSlip, mockSmells, agentCount, outputTokens, turnTokensTotal}`
|
|
503
515
|
`outputTokens` is this workflow's own output spend; `turnTokensTotal` is the
|
|
504
516
|
whole turn's shared pool (main loop included).
|
|
505
517
|
|
|
506
518
|
Wait for the workflow to complete before proceeding to Step 3.
|
|
507
519
|
|
|
520
|
+
### ENGINE=revmux
|
|
521
|
+
|
|
522
|
+
revmux replaces Review, Dedup, Verify, and Critic with its own multi-agent
|
|
523
|
+
round; Prove still runs inside `workflow.js`. Steps:
|
|
524
|
+
|
|
525
|
+
1. `TASK_SLUG` = `<repo-short-name>-<TARGET_SLUG>`, `RUN` = `01-review`.
|
|
526
|
+
2. Run the engine:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
${CLAUDE_PLUGIN_ROOT}/scripts/revmux-engine.sh \
|
|
530
|
+
--task <TASK_SLUG> --run <RUN> --depth <depth> \
|
|
531
|
+
--workdir <worktreePath> \
|
|
532
|
+
--diff-file <scratchpad>/pr.diff \
|
|
533
|
+
--context-file <scratchpad>/context.json \
|
|
534
|
+
--profile-file ${CLAUDE_PLUGIN_ROOT}/references/revmux/profile.md \
|
|
535
|
+
--config-dir ~/.config/revmux \
|
|
536
|
+
--out <scratchpad>/revmux.json
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
3. Adapt the report:
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/revmux-adapter.mjs \
|
|
543
|
+
<scratchpad>/revmux.json --pricing \
|
|
544
|
+
${CLAUDE_PLUGIN_ROOT}/references/pricing.json \
|
|
545
|
+
--context <scratchpad>/context.json \
|
|
546
|
+
> <scratchpad>/findings.json
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
4. Invoke Workflow(`workflow.js`) with the same args as the `ENGINE=workflow`
|
|
550
|
+
path above, plus `engine: "revmux"` and
|
|
551
|
+
`findingsFile: "<scratchpad>/findings.json"`. It skips Review/Dedup/
|
|
552
|
+
Verify/Critic, runs Prove only, and its return adds `engine, questions,
|
|
553
|
+
agents, degraded, totalTokens, totalUsd` on top of the usual keys.
|
|
554
|
+
5. **Fallback to `ENGINE=workflow` for this run** when either holds: the
|
|
555
|
+
engine script exits `2` (tool error - also raised for a missing profile
|
|
556
|
+
file, the codex guard, or an unsupported depth), or every row in the
|
|
557
|
+
adapter's `agents` array has `degraded: true`. State the fallback in the
|
|
558
|
+
review header (`**Note:** revmux unavailable - fell back to workflow
|
|
559
|
+
engine`) and re-run the `ENGINE=workflow` path from the top of Step 2.
|
|
560
|
+
|
|
561
|
+
**Trust boundary:** always pass `--config-dir ~/.config/revmux` explicitly.
|
|
562
|
+
Without it revmux also reads the reviewed repo's checked-in `.revmux/`, which
|
|
563
|
+
is executed as prompts. `~/.config/revmux` is where
|
|
564
|
+
`scripts/install-revmux-prompts.sh` puts the lekker profiles and lenses; run it
|
|
565
|
+
with `--check` when the engine exits 2 complaining about a missing profile. The task archive lives under the tasks dir
|
|
566
|
+
(`LEKKER_REVMUX_TASKS_DIR`, default `~/code-reviews/revmux-tasks`) and is not
|
|
567
|
+
committed anywhere.
|
|
568
|
+
|
|
508
569
|
---
|
|
509
570
|
|
|
510
571
|
## Step 3 - Synthesize and output
|
|
@@ -600,6 +661,17 @@ requirements:
|
|
|
600
661
|
cost. Input tokens are estimated (diff tokens x agent passes + context
|
|
601
662
|
+ prompt files). Use the pricing table in `references/output-format.md`.
|
|
602
663
|
Real numbers only - no `<N>` placeholders.
|
|
664
|
+
When `ENGINE=revmux`: build the rows from the adapter's `agents` array
|
|
665
|
+
(`name`, `model`, `tokens`, `usd` per row) and report `totalUsd` as the
|
|
666
|
+
total. Read `pricingMissing`; when it is non-empty, explicitly mark `totalUsd`
|
|
667
|
+
as incomplete and name the unpriced models. State plainly that the USD figures are an API list-price estimate
|
|
668
|
+
computed from `references/pricing.json`'s placeholder prices
|
|
669
|
+
(`verified: false`) until that file is verified against real invoices.
|
|
670
|
+
Prove-phase tokens still come from the workflow return's `outputTokens`,
|
|
671
|
+
same as the workflow engine. When `questions` (from the adapter) is
|
|
672
|
+
non-empty, render them under a `## Questions for the author` section. When
|
|
673
|
+
`degraded` is non-empty, add one banner line in the review header naming
|
|
674
|
+
the degraded agents, e.g. `⚠️ degraded: implementation, test-quality`.
|
|
603
675
|
|
|
604
676
|
**Save the review:**
|
|
605
677
|
|
|
@@ -857,6 +929,17 @@ do verification inline per `references/agents/verifier.md`, and state this
|
|
|
857
929
|
fallback in the review output under a
|
|
858
930
|
`**Note:** Workflow tool unavailable - ran agents directly` line in the header.
|
|
859
931
|
|
|
932
|
+
- `ENGINE=revmux` specific: on an exit-2 from `revmux-engine.sh`, or an
|
|
933
|
+
adapter report where every agent came back `degraded`, fall back to
|
|
934
|
+
`ENGINE=workflow` for that run (Step 2) rather than retrying revmux - this
|
|
935
|
+
is a fallback, not a retry loop, so it does not count against the
|
|
936
|
+
two-identical-failures rule above.
|
|
937
|
+
- Never delete or rename anything under the revmux tasks dir
|
|
938
|
+
(`LEKKER_REVMUX_TASKS_DIR`, default `~/code-reviews/revmux-tasks`). A
|
|
939
|
+
duplicate run name (same `TASK_SLUG`/`RUN` pair) is revmux's own hard
|
|
940
|
+
error, by design - pick the next `NN-...` name (e.g. `03-...`) rather than
|
|
941
|
+
clearing the old one.
|
|
942
|
+
|
|
860
943
|
Fix-mode specific:
|
|
861
944
|
- Never claim a fix landed without a `git log` / `git status` receipt from the
|
|
862
945
|
worktree. The review's proposed `fix` text is not an applied fix.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"note": "USD per million tokens, keyed by revmux's actual_model. revmux runs `claude --print` under subscription (Max plan) auth, so nothing here is a real API bill; this is an API-list-price ESTIMATE for the cost block only. Where a model exposes separate input/output prices, usd = tokens/1e6 * output price (output-only, not blended), because revmux reports a single combined `tokens` figure per agent with no input/output split to weight a blend. verified:false entries are not confirmed against a current price sheet (no local claude-api pricing doc found on this machine as of 2026-09-09) and must be checked before this cost block is trusted for a real decision.",
|
|
3
|
+
"claude-opus-5": { "input": 15, "output": 75, "blended": 45, "verified": false },
|
|
4
|
+
"claude-sonnet-5": { "input": 3, "output": 15, "blended": 9, "verified": false },
|
|
5
|
+
"claude-haiku-4-5-20251001": { "input": 1, "output": 5, "blended": 3, "verified": false },
|
|
6
|
+
"opus": { "input": 15, "output": 75, "blended": 45, "verified": false },
|
|
7
|
+
"sonnet": { "input": 3, "output": 15, "blended": 9, "verified": false },
|
|
8
|
+
"haiku": { "input": 1, "output": 5, "blended": 3, "verified": false }
|
|
9
|
+
}
|
package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-conventions.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: deviations from Teifi's own codebase conventions — the "strong teammate" non-blocking lens
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-conventions
|
|
5
|
+
|
|
6
|
+
Review the change for deviations from Teifi's established codebase
|
|
7
|
+
conventions and idioms. This is the "strong teammate" lens: the suggestions a
|
|
8
|
+
senior Teifi engineer leaves — non-blocking, but they make the code match how
|
|
9
|
+
the rest of the codebase is written. Look beyond the diff only for
|
|
10
|
+
convention-specific precedent and reuse searches. Other lenses may inspect the
|
|
11
|
+
runtime context they need for their own cross-file checks.
|
|
12
|
+
|
|
13
|
+
Read `{{PROFILE}}` now, before forming any opinion — it carries the Teifi
|
|
14
|
+
conventions text. Its §1 (naming matrix), §2 (comment policy), §5 (commit
|
|
15
|
+
hygiene) and §6 (generated code) are yours — they are the house style, so a
|
|
16
|
+
deviation needs NO codebase precedent beyond that file (the file IS the
|
|
17
|
+
precedent; cite the section, e.g. "teifi-conventions §1 verbs").
|
|
18
|
+
Everything else in this lens still requires a cited precedent from the code.
|
|
19
|
+
|
|
20
|
+
Axes to cover:
|
|
21
|
+
- Naming (teifi-conventions §1): every symbol the diff INTRODUCES against the
|
|
22
|
+
matrix — boolean without `is`/`has`, async I/O named `get`, a row lock or a
|
|
23
|
+
throw-on-miss or a cache read absent from the name (`…ForUpdate`,
|
|
24
|
+
`…OrThrow`, `…Cached`), a collidable component without its domain prefix, a
|
|
25
|
+
bare generic noun (`line`, `node`, `row`) where the domain has two variants in
|
|
26
|
+
scope. NEVER flag a boundary name (DB column, GraphQL/oRPC field, enum value,
|
|
27
|
+
route string, wire key) — renaming it breaks callers outside the diff.
|
|
28
|
+
- Comments (teifi-conventions §2): one finding per over-commenting offender the
|
|
29
|
+
diff ADDED, with the deletion as the fix. Never a vague "too many comments",
|
|
30
|
+
never a pre-existing comment, never a lint/type pragma or a genuine
|
|
31
|
+
non-obvious "why".
|
|
32
|
+
- Generated code (teifi-conventions §6): a changed `.sql` / `.graphql` /
|
|
33
|
+
`.json` schema / `prisma/schema.prisma` whose generated artifact is absent
|
|
34
|
+
from the diff (or the reverse) — major. Raise NO naming, comment, or
|
|
35
|
+
complexity finding inside a `generated/` directory. A hand-rolled `fetch` to
|
|
36
|
+
the Shopify Admin GraphQL endpoint instead of the genql client — major.
|
|
37
|
+
- Commits (teifi-conventions §5): non-conventional or vague commit subjects, and
|
|
38
|
+
any Claude Code / assistant mention in the commit or PR text.
|
|
39
|
+
- Type-system idioms:
|
|
40
|
+
* A hand-written interface/type that duplicates an existing Zod schema —
|
|
41
|
+
should be `z.infer<typeof zSchema>` so the schema stays the single source
|
|
42
|
+
of truth. (Grep for a matching z-schema in the same feature folder.)
|
|
43
|
+
* Raw `string` used for a Shopify GID or an entity id where a branded
|
|
44
|
+
`ID<'Customer'>` (or similar) type exists and is used elsewhere.
|
|
45
|
+
* A union typed as `as readonly string[]` / a hand-rolled `is...` guard where
|
|
46
|
+
a `z.enum([...])` + `z.infer` would give validation, narrowing, and the
|
|
47
|
+
options array in one declaration.
|
|
48
|
+
* An unnecessary `satisfies` / redundant type annotation the compiler already
|
|
49
|
+
infers.
|
|
50
|
+
* A GID validated/parsed inline where a shared helper exists (e.g.
|
|
51
|
+
`zNamespacedGid`). Grep the shared libs and the repo before asserting.
|
|
52
|
+
- Reuse (search the worktree AND sibling Teifi repos before flagging):
|
|
53
|
+
* Inline fetch/client logic that should reuse — or be promoted into — a
|
|
54
|
+
shared client (e.g. a company-switcher client) that already exists or that
|
|
55
|
+
the codebase clearly wants.
|
|
56
|
+
* A util/helper that already exists elsewhere being re-implemented inline.
|
|
57
|
+
* A symbol defined locally that is (or should be) exported from a shared
|
|
58
|
+
module — "are we not exporting this somewhere?"
|
|
59
|
+
- Consistency:
|
|
60
|
+
* Cache-key / composite-key separators that disagree with the repo's
|
|
61
|
+
prevailing choice (e.g. `:` vs `::`). Grep existing key-building code to
|
|
62
|
+
find the prevailing pattern, then flag the deviation.
|
|
63
|
+
* Ad-hoc error throwing where the repo has an idiom (e.g. `throw new
|
|
64
|
+
HttpError('...', 403)` instead of a bare string / generic Error).
|
|
65
|
+
* Naming/casing that breaks the convention used by sibling files.
|
|
66
|
+
|
|
67
|
+
MANDATORY SWEEP — do this FIRST, before forming any opinion:
|
|
68
|
+
|
|
69
|
+
The axes above are symptom-driven: they only fire once you already suspect a
|
|
70
|
+
duplication. That is how a re-implemented helper slips through — nobody thinks to
|
|
71
|
+
look. So run these enumerations mechanically, whether or not anything looks wrong.
|
|
72
|
+
|
|
73
|
+
1. **Sibling sweep for every file the diff ADDS.** For each added file, list its
|
|
74
|
+
directory and read the exports of its neighbours. A helper that solves the same
|
|
75
|
+
problem is usually sitting in the same folder.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
REVIEW_CONFIG="{{CONTEXT}}/worktree.json"
|
|
79
|
+
WORKTREE="$(jq -er '.worktreePath | select(type == "string" and length > 0)' "$REVIEW_CONFIG")"
|
|
80
|
+
BASE_REV="$(jq -er '(.mergeBase // .baseRef) | select(type == "string" and length > 0)' "$REVIEW_CONFIG")"
|
|
81
|
+
REPO_ROOT="$(jq -er '.repoRoot | select(type == "string" and length > 0)' "$REVIEW_CONFIG")"
|
|
82
|
+
SIBLING_ROOT="$(dirname "$REPO_ROOT")"
|
|
83
|
+
|
|
84
|
+
git -C "$WORKTREE" diff --name-status "$BASE_REV"...HEAD | awk '$1=="A"{print $2}' |
|
|
85
|
+
while IFS= read -r added_file; do
|
|
86
|
+
added_dir="$WORKTREE/$(dirname "$added_file")"
|
|
87
|
+
ls -la "$added_dir" # what already lives beside it
|
|
88
|
+
grep -rn "^export " "$added_dir"/*.ts "$added_dir"/*.tsx 2>/dev/null |
|
|
89
|
+
grep -v -F -- "$added_file"
|
|
90
|
+
done
|
|
91
|
+
grep -rn "^export " "$SIBLING_ROOT" --include='*.ts' --include='*.tsx' \
|
|
92
|
+
--exclude-dir=node_modules 2>/dev/null
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A new `foo/bar-thing.ts` next to an existing `foo/thing.ts` is a finding waiting
|
|
96
|
+
to happen. Read the neighbour, do not just note its name.
|
|
97
|
+
|
|
98
|
+
2. **New-symbol sweep.** For every function/const the diff exports, search the repo
|
|
99
|
+
for something that already does that job, by BEHAVIOUR not just by name. Names
|
|
100
|
+
rarely match; behaviour does.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git -C "$WORKTREE" diff --unified=0 "$BASE_REV"...HEAD | \
|
|
104
|
+
grep '^+' | grep -E 'export (function|const) ' # collect new symbols
|
|
105
|
+
# then for each, search by what it does, e.g. a locale normaliser:
|
|
106
|
+
grep -rln "toLowerCase()\|normalize\|isoCode\|split('-')" "$WORKTREE" \
|
|
107
|
+
--include='*.ts' --include='*.tsx'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Pick 2 or 3 behavioural keywords from the new function's body and grep those.
|
|
111
|
+
Reviewing the diff alone cannot catch this.
|
|
112
|
+
|
|
113
|
+
3. **State what you swept.** In your output, name the directories you listed and the
|
|
114
|
+
behavioural greps you ran, even when they found nothing. A sweep that is not
|
|
115
|
+
reported did not happen, and the next reviewer cannot tell "no duplication exists"
|
|
116
|
+
from "nobody looked".
|
|
117
|
+
|
|
118
|
+
HARD RULES:
|
|
119
|
+
- Only raise a finding when the better pattern PROVABLY ALREADY EXISTS. Cite it:
|
|
120
|
+
the file:line where the helper/type/convention lives, or the sibling file that
|
|
121
|
+
does it the idiomatic way. If you cannot find a concrete precedent, DROP the
|
|
122
|
+
finding — "this would be nicer as X" on taste alone is not allowed.
|
|
123
|
+
- Every finding must still trace to a `+` line in the diff (the deviation must
|
|
124
|
+
be code this change added/changed). The supporting precedent may live outside the
|
|
125
|
+
diff; the deviation may not.
|
|
126
|
+
- These are suggestions, not blockers. Do not inflate severity. Report each as
|
|
127
|
+
`file:line — <deviation> (precedent: <file:line of the existing pattern>)`.
|
|
128
|
+
- ONE EXCEPTION to non-blocking: if the re-implementation DIVERGES in behaviour
|
|
129
|
+
from the helper it duplicates, that is not a style nit, it is two spellings of
|
|
130
|
+
the same value that disagree, and it belongs on lekker-quality's severity
|
|
131
|
+
scale rather than this lens's. Diff the two implementations before deciding:
|
|
132
|
+
same inputs, same outputs? If a real input produces different results, say so
|
|
133
|
+
explicitly and give the input.
|
|
134
|
+
|
|
135
|
+
To find precedents, you may run:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
grep -rn "<symbol or pattern>" "$WORKTREE" --include='*.ts' --include='*.tsx'
|
|
139
|
+
find "$SIBLING_ROOT" \( -name "*.ts" -o -name "*.tsx" \) ! -path "*/node_modules/*" \
|
|
140
|
+
| xargs grep -l "<symbol>" 2>/dev/null | head
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Always include *.tsx. Extension and frontend code lives in .tsx, so a search that
|
|
144
|
+
omits it silently reports "no precedent exists" for whole directories.
|
|
145
|
+
|
|
146
|
+
A mined record of what other bots (Greptile / Gemini / CodeRabbit) have
|
|
147
|
+
commented on in this repo may be present under `{{CONTEXT}}`. Use it as a prior,
|
|
148
|
+
not as a checklist: a high count means "frequently raised here", not "correct" —
|
|
149
|
+
never raise a finding because a bot once said it, only because it is true here.
|
|
150
|
+
|
|
151
|
+
Rules:
|
|
152
|
+
- Report file:line — description with a precedent citation. No positive
|
|
153
|
+
observations. No taste-only suggestions.
|
|
154
|
+
- Quote the verbatim offending line(s) — never paraphrased, never reconstructed
|
|
155
|
+
from memory — and give a concrete drop-in fix, or when the fix is
|
|
156
|
+
architectural, a minimal skeleton plus one sentence on what else must change.
|
|
157
|
+
- A finding you cannot quote and cannot fix is a finding you have not proven —
|
|
158
|
+
drop it instead.
|