@hopla/claude-setup 1.14.1 → 1.16.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/.mcp.json.example +41 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +22 -0
- package/cli.js +65 -50
- package/commands/execute.md +5 -44
- package/commands/guides/validation-pyramid.md +74 -0
- package/commands/plan-feature.md +15 -15
- package/commands/validate.md +3 -30
- package/global-rules.md +15 -5
- package/hooks/hooks.json +22 -0
- package/hooks/precompact-snapshot.js +65 -0
- package/hooks/prompt-route.js +125 -0
- package/hooks/session-prime.js +35 -3
- package/hooks/statusline.js +83 -0
- package/package.json +5 -1
- package/skills/code-review/SKILL.md +15 -38
- package/skills/code-review/checklist.md +44 -0
- package/skills/execution-report/SKILL.md +18 -85
- package/skills/execution-report/report-structure.md +88 -0
- package/skills/verify/SKILL.md +12 -7
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreCompact hook: snapshot the session's work state so it survives /compact.
|
|
3
|
+
// The SessionStart hook (session-prime.js) re-injects this when it exists and is recent.
|
|
4
|
+
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
|
|
9
|
+
function run(cmd) {
|
|
10
|
+
try {
|
|
11
|
+
return execSync(cmd, { cwd: process.cwd(), stdio: "pipe" }).toString().trim();
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function findActivePlan() {
|
|
18
|
+
const plansDir = path.join(process.cwd(), ".agents", "plans");
|
|
19
|
+
if (!fs.existsSync(plansDir)) return null;
|
|
20
|
+
try {
|
|
21
|
+
const files = fs
|
|
22
|
+
.readdirSync(plansDir)
|
|
23
|
+
.filter((f) => f.endsWith(".md") && !f.startsWith("."))
|
|
24
|
+
.map((f) => ({ name: f, mtime: fs.statSync(path.join(plansDir, f)).mtimeMs }))
|
|
25
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
26
|
+
return files[0]?.name || null;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function detectWorktree() {
|
|
33
|
+
const gitDir = run("git rev-parse --git-dir");
|
|
34
|
+
const commonDir = run("git rev-parse --git-common-dir");
|
|
35
|
+
if (!gitDir || !commonDir) return false;
|
|
36
|
+
return path.resolve(gitDir) !== path.resolve(commonDir);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function main() {
|
|
40
|
+
// Drain stdin (hook contract)
|
|
41
|
+
const chunks = [];
|
|
42
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
43
|
+
|
|
44
|
+
const snapshot = {
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
branch: run("git branch --show-current"),
|
|
47
|
+
uncommitted: run("git status --short"),
|
|
48
|
+
activePlan: findActivePlan(),
|
|
49
|
+
inWorktree: detectWorktree(),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const targetDir = path.join(process.cwd(), ".claude");
|
|
53
|
+
try {
|
|
54
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
55
|
+
fs.writeFileSync(
|
|
56
|
+
path.join(targetDir, "compact-snapshot.json"),
|
|
57
|
+
JSON.stringify(snapshot, null, 2) + "\n"
|
|
58
|
+
);
|
|
59
|
+
} catch {
|
|
60
|
+
// Best-effort — never block /compact
|
|
61
|
+
}
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main();
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// UserPromptSubmit hook: scan the user's prompt for skill keywords and inject
|
|
3
|
+
// a short routing hint. Keeps skill suggestions visible mid-session even after
|
|
4
|
+
// compaction or long conversations where the initial skill list gets buried.
|
|
5
|
+
|
|
6
|
+
const SKILL_TRIGGERS = [
|
|
7
|
+
{
|
|
8
|
+
skill: "git",
|
|
9
|
+
patterns: [
|
|
10
|
+
/\bcommit\b/i,
|
|
11
|
+
/save (my |the )?changes/i,
|
|
12
|
+
/\bcreate (a )?pr\b/i,
|
|
13
|
+
/pull request/i,
|
|
14
|
+
/\bpush\b/i,
|
|
15
|
+
/merge request/i,
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
skill: "worktree",
|
|
20
|
+
patterns: [
|
|
21
|
+
/\bworktree\b/i,
|
|
22
|
+
/isolated branch/i,
|
|
23
|
+
/parallel (feature|development)/i,
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
skill: "prime",
|
|
28
|
+
patterns: [
|
|
29
|
+
/\borient(\b| yourself)/i,
|
|
30
|
+
/catch me up/i,
|
|
31
|
+
/get context/i,
|
|
32
|
+
/load project/i,
|
|
33
|
+
/what is this project/i,
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
skill: "brainstorm",
|
|
38
|
+
patterns: [
|
|
39
|
+
/\bbrainstorm\b/i,
|
|
40
|
+
/explore (options|approaches)/i,
|
|
41
|
+
/how should we/i,
|
|
42
|
+
/trade[- ]offs?/i,
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
skill: "debug",
|
|
47
|
+
patterns: [
|
|
48
|
+
/\bbug\b/i,
|
|
49
|
+
/\berror\b/i,
|
|
50
|
+
/\bdebug\b/i,
|
|
51
|
+
/not working/i,
|
|
52
|
+
/\bfailing\b/i,
|
|
53
|
+
/\bbroken\b/i,
|
|
54
|
+
/\bno funciona\b/i,
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
skill: "verify",
|
|
59
|
+
patterns: [
|
|
60
|
+
/\bverify\b/i,
|
|
61
|
+
/all tests? pass/i,
|
|
62
|
+
/\blisto\b/i,
|
|
63
|
+
/\bterminé\b/i,
|
|
64
|
+
/\bya está\b/i,
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
skill: "code-review",
|
|
69
|
+
patterns: [
|
|
70
|
+
/review (the |my )?code/i,
|
|
71
|
+
/code review/i,
|
|
72
|
+
/audit (the |my )?code/i,
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
skill: "execution-report",
|
|
77
|
+
patterns: [
|
|
78
|
+
/generate (the |a )?report/i,
|
|
79
|
+
/document what was done/i,
|
|
80
|
+
/execution report/i,
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
skill: "tdd",
|
|
85
|
+
patterns: [
|
|
86
|
+
/\btdd\b/i,
|
|
87
|
+
/test[- ]first/i,
|
|
88
|
+
/red[- ]green[- ]refactor/i,
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
async function main() {
|
|
94
|
+
const chunks = [];
|
|
95
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
96
|
+
|
|
97
|
+
let input;
|
|
98
|
+
try {
|
|
99
|
+
input = JSON.parse(Buffer.concat(chunks).toString());
|
|
100
|
+
} catch {
|
|
101
|
+
// Malformed payload — skip silently so we never block legitimate prompts
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Cap prompt length so regex matching stays cheap on large paste-ins
|
|
106
|
+
const prompt = (input.prompt || "").slice(0, 4000);
|
|
107
|
+
if (!prompt) process.exit(0);
|
|
108
|
+
|
|
109
|
+
const matched = [];
|
|
110
|
+
for (const { skill, patterns } of SKILL_TRIGGERS) {
|
|
111
|
+
if (patterns.some((p) => p.test(prompt))) matched.push(skill);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (matched.length === 0) process.exit(0);
|
|
115
|
+
|
|
116
|
+
const list = matched.map((s) => `\`${s}\``).join(", ");
|
|
117
|
+
const plural = matched.length > 1;
|
|
118
|
+
process.stdout.write(
|
|
119
|
+
`📦 HOPLA routing hint: this prompt matches skill${plural ? "s" : ""} ${list}. ` +
|
|
120
|
+
`Use ${plural ? "them" : "it"} if applicable to the current task.`
|
|
121
|
+
);
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main();
|
package/hooks/session-prime.js
CHANGED
|
@@ -13,6 +13,18 @@ function run(cmd) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function excerptClaudeMd(content) {
|
|
17
|
+
const lines = content.split("\n");
|
|
18
|
+
// Prefer the first `---` separator after the opening heading and first section
|
|
19
|
+
for (let i = 5; i < Math.min(lines.length, 120); i++) {
|
|
20
|
+
if (lines[i].trim() === "---") {
|
|
21
|
+
return lines.slice(0, i).join("\n").trimEnd();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// No separator within a reasonable window — cap at 60 lines
|
|
25
|
+
return lines.slice(0, Math.min(lines.length, 60)).join("\n").trimEnd();
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
function discoverSkills() {
|
|
17
29
|
// Try plugin context first: ../skills/ relative to this script
|
|
18
30
|
const hookDir = import.meta.dirname;
|
|
@@ -65,11 +77,31 @@ async function main() {
|
|
|
65
77
|
lines.push("Working tree is clean.");
|
|
66
78
|
}
|
|
67
79
|
|
|
68
|
-
//
|
|
80
|
+
// Re-inject pre-compact snapshot if recent (< 2 hours old)
|
|
81
|
+
const snapshotPath = path.join(process.cwd(), ".claude", "compact-snapshot.json");
|
|
82
|
+
if (fs.existsSync(snapshotPath)) {
|
|
83
|
+
try {
|
|
84
|
+
const snap = JSON.parse(fs.readFileSync(snapshotPath, "utf8"));
|
|
85
|
+
const ageMs = Date.now() - Date.parse(snap.timestamp);
|
|
86
|
+
if (Number.isFinite(ageMs) && ageMs < 2 * 60 * 60 * 1000) {
|
|
87
|
+
const parts = [
|
|
88
|
+
`Resuming from pre-compact snapshot (${Math.round(ageMs / 60000)} min ago):`,
|
|
89
|
+
];
|
|
90
|
+
if (snap.branch) parts.push(`- branch: ${snap.branch}${snap.inWorktree ? " (worktree)" : ""}`);
|
|
91
|
+
if (snap.activePlan) parts.push(`- active plan: .agents/plans/${snap.activePlan}`);
|
|
92
|
+
if (snap.uncommitted) parts.push(`- uncommitted at snapshot:\n${snap.uncommitted}`);
|
|
93
|
+
lines.push(parts.join("\n"));
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
// Ignore malformed snapshot
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// CLAUDE.md excerpt — cut at a natural boundary, not a fixed line count
|
|
69
101
|
const claudeMdPath = path.join(process.cwd(), "CLAUDE.md");
|
|
70
102
|
if (fs.existsSync(claudeMdPath)) {
|
|
71
|
-
const
|
|
72
|
-
lines.push(`Project rules (CLAUDE.md excerpt):\n${
|
|
103
|
+
const excerpt = excerptClaudeMd(fs.readFileSync(claudeMdPath, "utf8"));
|
|
104
|
+
lines.push(`Project rules (CLAUDE.md excerpt):\n${excerpt}`);
|
|
73
105
|
}
|
|
74
106
|
|
|
75
107
|
// Auto-discover available skills
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Hopla statusline: branch · worktree indicator · uncommitted count · active plan.
|
|
3
|
+
// Wire it up by adding to ~/.claude/settings.json:
|
|
4
|
+
// "statusLine": {
|
|
5
|
+
// "type": "command",
|
|
6
|
+
// "command": "node ~/.claude/plugins/marketplaces/hopla-marketplace/hooks/statusline.js"
|
|
7
|
+
// }
|
|
8
|
+
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
|
|
13
|
+
const CYAN = "\x1b[36m";
|
|
14
|
+
const YELLOW = "\x1b[33m";
|
|
15
|
+
const MAGENTA = "\x1b[35m";
|
|
16
|
+
const DIM = "\x1b[2m";
|
|
17
|
+
const RESET = "\x1b[0m";
|
|
18
|
+
|
|
19
|
+
function run(cmd, cwd) {
|
|
20
|
+
try {
|
|
21
|
+
return execSync(cmd, { cwd, stdio: "pipe" }).toString().trim();
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findActivePlan(cwd) {
|
|
28
|
+
const plansDir = path.join(cwd, ".agents", "plans");
|
|
29
|
+
if (!fs.existsSync(plansDir)) return null;
|
|
30
|
+
try {
|
|
31
|
+
const files = fs
|
|
32
|
+
.readdirSync(plansDir)
|
|
33
|
+
.filter((f) => f.endsWith(".md") && !f.startsWith("."))
|
|
34
|
+
.map((f) => ({ name: f, mtime: fs.statSync(path.join(plansDir, f)).mtimeMs }))
|
|
35
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
36
|
+
return files[0]?.name.replace(/\.md$/, "") || null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function main() {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
45
|
+
|
|
46
|
+
let input = {};
|
|
47
|
+
try {
|
|
48
|
+
input = JSON.parse(Buffer.concat(chunks).toString());
|
|
49
|
+
} catch {
|
|
50
|
+
// Malformed payload — render nothing
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const cwd = input.workspace?.current_dir || input.cwd || process.cwd();
|
|
55
|
+
const parts = [];
|
|
56
|
+
|
|
57
|
+
const branch = run("git branch --show-current", cwd);
|
|
58
|
+
if (branch) {
|
|
59
|
+
const gitDir = run("git rev-parse --git-dir", cwd);
|
|
60
|
+
const commonDir = run("git rev-parse --git-common-dir", cwd);
|
|
61
|
+
const isWorktree =
|
|
62
|
+
gitDir && commonDir && path.resolve(cwd, gitDir) !== path.resolve(cwd, commonDir);
|
|
63
|
+
parts.push(`${CYAN}${isWorktree ? "⎇ " : " "}${branch}${RESET}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const status = run("git status --short", cwd);
|
|
67
|
+
if (status) {
|
|
68
|
+
const count = status.split("\n").length;
|
|
69
|
+
parts.push(`${YELLOW}${count}M${RESET}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const plan = findActivePlan(cwd);
|
|
73
|
+
if (plan) {
|
|
74
|
+
parts.push(`${MAGENTA}📋 ${plan}${RESET}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (parts.length === 0) process.exit(0);
|
|
78
|
+
|
|
79
|
+
process.stdout.write(parts.join(` ${DIM}·${RESET} `));
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hopla/claude-setup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Hopla team agentic coding system for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"hooks/",
|
|
17
17
|
".claude-plugin/"
|
|
18
18
|
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prepublishOnly": "node scripts/check-versions.js",
|
|
21
|
+
"check-versions": "node scripts/check-versions.js"
|
|
22
|
+
},
|
|
19
23
|
"engines": {
|
|
20
24
|
"node": ">=18"
|
|
21
25
|
},
|
|
@@ -14,7 +14,7 @@ Read `CLAUDE.md` or `AGENTS.md` to understand project standards and patterns.
|
|
|
14
14
|
|
|
15
15
|
If `.agents/guides/` exists, read any guides relevant to the files being reviewed (e.g. `@.agents/guides/api-guide.md` when reviewing API changes). These guides define the expected patterns for specific task types.
|
|
16
16
|
|
|
17
|
-
If `.agents/guides/review-checklist.md` exists, read it and apply the project-specific checks it defines in addition to the standard checks
|
|
17
|
+
If `.agents/guides/review-checklist.md` exists, read it and apply the project-specific checks it defines in addition to the standard checks. Project-specific checklists cover framework gotchas and domain anti-patterns unique to the project (e.g., grid stale closures, route ordering).
|
|
18
18
|
|
|
19
19
|
## Step 2: Identify Changed Files
|
|
20
20
|
|
|
@@ -28,42 +28,16 @@ Read each changed or new file in its entirety — not just the diff.
|
|
|
28
28
|
|
|
29
29
|
## Step 3: Analyze for Issues
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
**2. Security Issues**
|
|
43
|
-
- Exposed secrets or API keys
|
|
44
|
-
- SQL/command injection vulnerabilities
|
|
45
|
-
- Missing input validation on API endpoints — required fields, format constraints (regex, length), payload size limits
|
|
46
|
-
- Insecure data handling — raw user input in queries, responses exposing internal data or stack traces
|
|
47
|
-
- XSS vulnerabilities (frontend)
|
|
48
|
-
- Multi-user authorization context — for multi-tenant apps, verify each endpoint filters by the correct context (e.g., active org vs personal org, admin vs viewer). Check that middleware/auth guards match the intended audience for each route
|
|
49
|
-
|
|
50
|
-
**3. Performance Problems**
|
|
51
|
-
- Unnecessary re-renders (React)
|
|
52
|
-
- N+1 queries — database queries or API calls inside loops (`for`, `.map`, `.forEach`), duplicate existence checks before mutations, sequential operations that could use `Promise.all()` or batch SQL. This was found in 5 of 13 recent implementations
|
|
53
|
-
- Memory leaks
|
|
54
|
-
|
|
55
|
-
**4. Code Quality**
|
|
56
|
-
- DRY violations — before flagging, search for similar functions/constants elsewhere in the codebase; suggest extraction to a shared module if the same logic exists in multiple places
|
|
57
|
-
- Poor naming or overly complex functions
|
|
58
|
-
- Missing TypeScript types or `any` usage
|
|
59
|
-
|
|
60
|
-
**5. Pattern Adherence**
|
|
61
|
-
- Follows project conventions from CLAUDE.md
|
|
62
|
-
- Consistent with existing codebase style
|
|
63
|
-
|
|
64
|
-
**6. Route & Middleware Ordering**
|
|
65
|
-
- Static routes defined AFTER parameterized routes (e.g., `/users/all` after `/users/:id`) causing shadowing — the parameterized route captures requests meant for the static one
|
|
66
|
-
- Middleware applied in incorrect order (e.g., auth after route handler, CORS after response sent)
|
|
31
|
+
Apply the full checklist in `checklist.md` (same directory). It covers:
|
|
32
|
+
|
|
33
|
+
1. Logic errors (stale closures, unhandled rejections, missing deps)
|
|
34
|
+
2. Security (secrets, injection, input validation, multi-tenant auth)
|
|
35
|
+
3. Performance (N+1, re-renders, memory leaks)
|
|
36
|
+
4. Code quality (DRY, naming, types)
|
|
37
|
+
5. Pattern adherence (project conventions)
|
|
38
|
+
6. Route & middleware ordering
|
|
39
|
+
|
|
40
|
+
Read `checklist.md` before reviewing so you apply every category.
|
|
67
41
|
|
|
68
42
|
## Step 4: Verify Issues Are Real
|
|
69
43
|
|
|
@@ -73,9 +47,10 @@ Before reporting, confirm each issue is legitimate:
|
|
|
73
47
|
|
|
74
48
|
## Step 5: Output Report
|
|
75
49
|
|
|
76
|
-
Save to `.agents/code-reviews/[descriptive-name].md
|
|
50
|
+
Save to `.agents/code-reviews/[descriptive-name].md`.
|
|
77
51
|
|
|
78
52
|
**Format for each issue:**
|
|
53
|
+
|
|
79
54
|
```
|
|
80
55
|
severity: critical | high | medium | low
|
|
81
56
|
file: path/to/file.ts
|
|
@@ -88,6 +63,7 @@ suggestion: [how to fix it]
|
|
|
88
63
|
If no issues found: "Code review passed. No technical issues detected."
|
|
89
64
|
|
|
90
65
|
**Rules:**
|
|
66
|
+
|
|
91
67
|
- Be specific — line numbers, not vague complaints
|
|
92
68
|
- Focus on real bugs, not style preferences (linting handles that)
|
|
93
69
|
- Flag security issues as `critical`
|
|
@@ -96,4 +72,5 @@ If no issues found: "Code review passed. No technical issues detected."
|
|
|
96
72
|
## Next Step
|
|
97
73
|
|
|
98
74
|
After the review, suggest:
|
|
75
|
+
|
|
99
76
|
> "Code review saved to `.agents/code-reviews/[name].md`. If issues were found, run `/hopla:code-review-fix .agents/code-reviews/[name].md` to fix them. If the review passed clean, proceed to the `execution-report` skill."
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Code Review Checklist
|
|
2
|
+
|
|
3
|
+
Apply every category to every changed file. Severity guidance is in the parent `SKILL.md`.
|
|
4
|
+
|
|
5
|
+
## 1. Logic Errors
|
|
6
|
+
|
|
7
|
+
- Off-by-one errors, incorrect conditionals
|
|
8
|
+
- Missing error handling, unhandled edge cases
|
|
9
|
+
- Race conditions or async issues
|
|
10
|
+
- Stale closures — callbacks passed to imperative APIs (grids, charts, maps) that capture stale state instead of using refs or stable references
|
|
11
|
+
- Unhandled promise rejections — `.then()` without `.catch()`, async calls without `try/catch` in non-void contexts
|
|
12
|
+
- Side effects inside JSX render — mutations of arrays/objects inside `.map()` in JSX (breaks React strict mode, causes double-execution bugs)
|
|
13
|
+
- Stale dependency arrays — for every new `useState`/`useRef` variable introduced in the diff, verify it appears in the dependency arrays of `useEffect`, `useCallback`, or `useMemo` that reference it. Missing deps cause stale closures and are a recurring source of React bugs.
|
|
14
|
+
|
|
15
|
+
## 2. Security Issues
|
|
16
|
+
|
|
17
|
+
- Exposed secrets or API keys
|
|
18
|
+
- SQL/command injection vulnerabilities
|
|
19
|
+
- Missing input validation on API endpoints — required fields, format constraints (regex, length), payload size limits
|
|
20
|
+
- Insecure data handling — raw user input in queries, responses exposing internal data or stack traces
|
|
21
|
+
- XSS vulnerabilities (frontend)
|
|
22
|
+
- Multi-user authorization context — for multi-tenant apps, verify each endpoint filters by the correct context (e.g., active org vs personal org, admin vs viewer). Check that middleware/auth guards match the intended audience for each route.
|
|
23
|
+
|
|
24
|
+
## 3. Performance Problems
|
|
25
|
+
|
|
26
|
+
- Unnecessary re-renders (React)
|
|
27
|
+
- N+1 queries — database queries or API calls inside loops (`for`, `.map`, `.forEach`), duplicate existence checks before mutations, sequential operations that could use `Promise.all()` or batch SQL
|
|
28
|
+
- Memory leaks (event listeners not detached, timers not cleared, closures holding large objects)
|
|
29
|
+
|
|
30
|
+
## 4. Code Quality
|
|
31
|
+
|
|
32
|
+
- DRY violations — before flagging, search for similar functions/constants elsewhere in the codebase; suggest extraction to a shared module if the same logic exists in multiple places
|
|
33
|
+
- Poor naming or overly complex functions
|
|
34
|
+
- Missing TypeScript types or `any` usage
|
|
35
|
+
|
|
36
|
+
## 5. Pattern Adherence
|
|
37
|
+
|
|
38
|
+
- Follows project conventions from `CLAUDE.md`
|
|
39
|
+
- Consistent with existing codebase style
|
|
40
|
+
|
|
41
|
+
## 6. Route & Middleware Ordering
|
|
42
|
+
|
|
43
|
+
- Static routes defined AFTER parameterized routes (e.g., `/users/all` after `/users/:id`) causing shadowing — the parameterized route captures requests meant for the static one
|
|
44
|
+
- Middleware applied in incorrect order (e.g., auth after route handler, CORS after response sent)
|
|
@@ -22,97 +22,30 @@ Also check for recent code reviews:
|
|
|
22
22
|
ls -t .agents/code-reviews/ 2>/dev/null | head -5
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
If a code review exists for this feature, note its path for the Code Review Findings section
|
|
25
|
+
If a code review exists for this feature, note its path for the Code Review Findings section.
|
|
26
26
|
|
|
27
|
-
## Step 2: Generate Report
|
|
27
|
+
## Step 2: Generate the Report
|
|
28
28
|
|
|
29
|
-
Save to: `.agents/execution-reports/[feature-name].md
|
|
29
|
+
Save to: `.agents/execution-reports/[feature-name].md`.
|
|
30
30
|
|
|
31
|
-
Use the
|
|
31
|
+
Use the full structure documented in `report-structure.md` (same directory). It covers:
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- Syntax & Linting: ✓/✗ [details if failed]
|
|
45
|
-
- Type Checking: ✓/✗ [details if failed]
|
|
46
|
-
- Unit Tests: ✓/✗ [X passed, Y failed]
|
|
47
|
-
- Integration Tests: ✓/✗ [X passed, Y failed]
|
|
48
|
-
|
|
49
|
-
### Code Review Findings
|
|
50
|
-
|
|
51
|
-
- **Code review file:** [path to `.agents/code-reviews/[name].md`, or "Not run"]
|
|
52
|
-
- **Issues found:** [count by severity: X critical, Y high, Z medium, W low]
|
|
53
|
-
- **Issues fixed before this report:** [count]
|
|
54
|
-
- **Key findings:** [1-2 sentence summary of the most significant issues found]
|
|
55
|
-
|
|
56
|
-
### What Went Well
|
|
57
|
-
|
|
58
|
-
List specific things that worked smoothly:
|
|
59
|
-
- [concrete examples]
|
|
60
|
-
|
|
61
|
-
### Challenges Encountered
|
|
62
|
-
|
|
63
|
-
List specific difficulties encountered:
|
|
64
|
-
- [what was difficult and why]
|
|
65
|
-
|
|
66
|
-
### Bugs Encountered
|
|
67
|
-
|
|
68
|
-
Categorize each bug found during implementation:
|
|
69
|
-
|
|
70
|
-
| Bug | Category | Found By | Severity |
|
|
71
|
-
|-----|----------|----------|----------|
|
|
72
|
-
| [description] | stale closure / validation / race condition / styling / scope mismatch / type error / route ordering / other | lint / types / tests / code review / manual testing | critical / high / medium / low |
|
|
33
|
+
- Meta information (plan file, files added/modified, lines changed)
|
|
34
|
+
- Validation results
|
|
35
|
+
- Code review findings
|
|
36
|
+
- What went well
|
|
37
|
+
- Challenges encountered
|
|
38
|
+
- Bugs encountered (with categorization table)
|
|
39
|
+
- Divergences from plan
|
|
40
|
+
- Scope assessment
|
|
41
|
+
- Skipped items
|
|
42
|
+
- Technical patterns discovered (with ready-to-paste CLAUDE.md entry)
|
|
43
|
+
- Recommendations
|
|
73
44
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
### Divergences from Plan
|
|
77
|
-
|
|
78
|
-
For each divergence from the original plan:
|
|
79
|
-
|
|
80
|
-
**[Divergence Title]**
|
|
81
|
-
- **Planned:** [what the plan specified]
|
|
82
|
-
- **Actual:** [what was implemented instead]
|
|
83
|
-
- **Reason:** [why this divergence occurred]
|
|
84
|
-
- **Type:** Better approach found | Plan assumption wrong | Security concern | Performance issue | Other
|
|
85
|
-
|
|
86
|
-
### Scope Assessment
|
|
87
|
-
|
|
88
|
-
- **Planned tasks:** [number of tasks in the original plan]
|
|
89
|
-
- **Executed tasks:** [number of tasks actually completed]
|
|
90
|
-
- **Unplanned additions:** [count and brief description of work not in the original plan]
|
|
91
|
-
- **Scope accuracy:** On target | Under-scoped (took more work than planned) | Over-scoped (simpler than expected)
|
|
92
|
-
|
|
93
|
-
### Skipped Items
|
|
94
|
-
|
|
95
|
-
List anything from the plan that was not implemented:
|
|
96
|
-
- [what was skipped] — Reason: [why]
|
|
97
|
-
|
|
98
|
-
### Technical Patterns Discovered
|
|
99
|
-
|
|
100
|
-
New gotchas, patterns, or conventions learned during this implementation that should be documented:
|
|
101
|
-
|
|
102
|
-
- **Pattern/Gotcha:** [description]
|
|
103
|
-
- **Where it applies:** [what type of feature or context triggers this]
|
|
104
|
-
- **Ready-to-paste CLAUDE.md entry:** [Write the EXACT text that should be added to the project's CLAUDE.md to prevent this gotcha in future features. Format it as a bullet point under the appropriate section. If it belongs in a guide instead, write the exact text for the guide. Do not write vague suggestions like "document this" — write the actual content so the system reviewer can apply it directly.]
|
|
105
|
-
|
|
106
|
-
If nothing new was discovered, write "No new patterns discovered."
|
|
107
|
-
|
|
108
|
-
### Recommendations
|
|
109
|
-
|
|
110
|
-
Based on this implementation, what should change for next time?
|
|
111
|
-
- Plan command improvements: [suggestions]
|
|
112
|
-
- Execute command improvements: [suggestions]
|
|
113
|
-
- CLAUDE.md additions: [suggestions]
|
|
45
|
+
Read `report-structure.md` before writing so every section is filled correctly.
|
|
114
46
|
|
|
115
47
|
## Next Step
|
|
116
48
|
|
|
117
49
|
After the report is saved, suggest:
|
|
118
|
-
|
|
50
|
+
|
|
51
|
+
> "Execution report saved to `.agents/execution-reports/[name].md`. Use the `git` skill (say 'commit') to save your changes."
|