@hopla/claude-setup 1.17.0 → 1.19.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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +18 -7
- package/agents/system-reviewer.md +4 -4
- package/cli.js +142 -1
- package/commands/archive.md +137 -0
- package/commands/execute.md +1 -1
- package/commands/guides/ai-optimized-codebase.md +1 -1
- package/commands/guides/mcp-integration.md +2 -2
- package/commands/guides/remote-coding.md +1 -1
- package/commands/guides/validation-pyramid.md +1 -1
- package/commands/init-project.md +38 -17
- package/commands/plan-feature.md +50 -2
- package/commands/system-review.md +11 -15
- package/commands/validate.md +1 -1
- package/hooks/session-prime.js +12 -4
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +17 -1
- package/skills/code-review/checklist.md +1 -1
- package/skills/execution-report/SKILL.md +1 -1
- package/skills/execution-report/report-structure.md +2 -2
- package/skills/git/commit.md +2 -2
- package/skills/git/pr.md +1 -1
- package/skills/hook-audit/SKILL.md +135 -0
- package/skills/hook-audit/checklist.md +210 -0
- package/skills/hook-audit/tests/fixtures/use-bad.ts.example +53 -0
- package/skills/hook-audit/tests/fixtures/use-good.ts.example +64 -0
- package/skills/hook-audit/tests/manual-test.sh +73 -0
- package/skills/prime/SKILL.md +2 -2
- package/skills/refactoring/SKILL.md +1 -1
- package/skills/subagent-execution/SKILL.md +2 -2
- package/skills/worktree/SKILL.md +2 -2
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// SYNTHETIC FIXTURE — triggers all 4 hook-audit rules.
|
|
2
|
+
// Filename is *.example so TypeScript ignores it. This file is read as text by the hook-audit skill.
|
|
3
|
+
|
|
4
|
+
import { useState, useRef } from 'react';
|
|
5
|
+
|
|
6
|
+
// Rule D-1: module-level cache without companion inFlight map. BUG.
|
|
7
|
+
const cache = new Map<string, FooResult>();
|
|
8
|
+
|
|
9
|
+
type FooResult = { ok: boolean; value: string };
|
|
10
|
+
|
|
11
|
+
export function useFooLookup(fooId: string) {
|
|
12
|
+
const [data, setData] = useState<FooResult | null>(null);
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
const currentFooRef = useRef<string>(fooId);
|
|
15
|
+
|
|
16
|
+
async function load(id: string) {
|
|
17
|
+
setLoading(true);
|
|
18
|
+
currentFooRef.current = id;
|
|
19
|
+
try {
|
|
20
|
+
const cached = cache.get(id);
|
|
21
|
+
if (cached) {
|
|
22
|
+
setData(cached);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const result = await fakeFetch(id);
|
|
26
|
+
cache.set(id, result);
|
|
27
|
+
if (currentFooRef.current === id) {
|
|
28
|
+
setData(result);
|
|
29
|
+
}
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
// Rule E-1: substring match — too permissive. BUG.
|
|
32
|
+
if (error.message.includes('imei')) {
|
|
33
|
+
setData({ ok: false, value: 'imei-error' });
|
|
34
|
+
}
|
|
35
|
+
} finally {
|
|
36
|
+
// Rule S-8: setLoading(false) inside stale-id guard. BUG.
|
|
37
|
+
if (currentFooRef.current === id) {
|
|
38
|
+
setLoading(false);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Rule P-5: object literal return without useMemo. BUG.
|
|
44
|
+
return {
|
|
45
|
+
data,
|
|
46
|
+
loading,
|
|
47
|
+
load,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function fakeFetch(id: string): Promise<FooResult> {
|
|
52
|
+
return { ok: true, value: id };
|
|
53
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// SYNTHETIC FIXTURE — clean hook that passes all 4 rules.
|
|
2
|
+
// Mirrors the well-formed consumer-project shape (e.g. useGradingDefinitions.ts).
|
|
3
|
+
|
|
4
|
+
import { useState, useRef, useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
// Rule D-1: cache + companion inFlight map. CLEAN.
|
|
7
|
+
const cache = new Map<string, BarResult>();
|
|
8
|
+
const inFlight = new Map<string, Promise<BarResult>>();
|
|
9
|
+
|
|
10
|
+
type BarResult = { ok: boolean; value: string };
|
|
11
|
+
|
|
12
|
+
export function useBarLookup(barId: string) {
|
|
13
|
+
const [data, setData] = useState<BarResult | null>(null);
|
|
14
|
+
const [loading, setLoading] = useState(false);
|
|
15
|
+
const currentBarRef = useRef<string>(barId);
|
|
16
|
+
|
|
17
|
+
async function load(id: string) {
|
|
18
|
+
setLoading(true);
|
|
19
|
+
currentBarRef.current = id;
|
|
20
|
+
try {
|
|
21
|
+
const cached = cache.get(id);
|
|
22
|
+
if (cached) {
|
|
23
|
+
setData(cached);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const inflight = inFlight.get(id);
|
|
27
|
+
const promise = inflight ?? fakeFetch(id).then((result) => {
|
|
28
|
+
cache.set(id, result);
|
|
29
|
+
inFlight.delete(id);
|
|
30
|
+
return result;
|
|
31
|
+
});
|
|
32
|
+
if (!inflight) inFlight.set(id, promise);
|
|
33
|
+
const result = await promise;
|
|
34
|
+
if (currentBarRef.current === id) {
|
|
35
|
+
setData(result);
|
|
36
|
+
}
|
|
37
|
+
} catch (error: any) {
|
|
38
|
+
// Rule E-1: anchored regex — precise. CLEAN.
|
|
39
|
+
if (/^(invalid )?bar( format)?\b/i.test(error.message)) {
|
|
40
|
+
setData({ ok: false, value: 'bar-error' });
|
|
41
|
+
}
|
|
42
|
+
} finally {
|
|
43
|
+
if (currentBarRef.current === id) {
|
|
44
|
+
setData((prev) => prev);
|
|
45
|
+
}
|
|
46
|
+
// Rule S-8: setLoading(false) OUTSIDE the stale-id guard. CLEAN.
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Rule P-5: return wrapped in useMemo with full deps. CLEAN.
|
|
52
|
+
return useMemo(
|
|
53
|
+
() => ({
|
|
54
|
+
data,
|
|
55
|
+
loading,
|
|
56
|
+
load,
|
|
57
|
+
}),
|
|
58
|
+
[data, loading, load],
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function fakeFetch(id: string): Promise<BarResult> {
|
|
63
|
+
return { ok: true, value: id };
|
|
64
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Manual sanity test for the hook-audit skill's detection layer.
|
|
4
|
+
#
|
|
5
|
+
# The skill itself is markdown — an agent (Claude) interprets it. This script
|
|
6
|
+
# cannot exercise the full agent flow. What it CAN do is verify that the
|
|
7
|
+
# grep-based detection commands from `checklist.md` produce the expected
|
|
8
|
+
# candidate matches against the bundled fixtures.
|
|
9
|
+
#
|
|
10
|
+
# Rules covered by this script:
|
|
11
|
+
# - E-1 (substring error matching) — exact match count
|
|
12
|
+
# - D-1 (cache + inFlight) — presence check
|
|
13
|
+
#
|
|
14
|
+
# Rules requiring agent-level structural verification (NOT covered here, by design):
|
|
15
|
+
# - P-5 (return-not-memoized) — needs hook-scope + useMemo-wrapper filter
|
|
16
|
+
# - S-8 (setLoading inside guard) — needs brace-balance / above-context filter
|
|
17
|
+
# Their grep stage is exercised below but only as a "candidates exist" assertion.
|
|
18
|
+
#
|
|
19
|
+
# Usage: bash skills/hook-audit/tests/manual-test.sh
|
|
20
|
+
# Exits 0 on success, 1 on the first failed assertion.
|
|
21
|
+
|
|
22
|
+
set -e
|
|
23
|
+
set -u
|
|
24
|
+
|
|
25
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
26
|
+
BAD="$DIR/fixtures/use-bad.ts.example"
|
|
27
|
+
GOOD="$DIR/fixtures/use-good.ts.example"
|
|
28
|
+
PASS=0
|
|
29
|
+
FAIL=0
|
|
30
|
+
|
|
31
|
+
run() {
|
|
32
|
+
local label="$1"
|
|
33
|
+
local expected="$2"
|
|
34
|
+
local actual="$3"
|
|
35
|
+
if [ "$expected" = "$actual" ]; then
|
|
36
|
+
echo " ✓ $label"
|
|
37
|
+
PASS=$((PASS + 1))
|
|
38
|
+
else
|
|
39
|
+
echo " ✗ $label — expected '$expected', got '$actual'"
|
|
40
|
+
FAIL=$((FAIL + 1))
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
count() {
|
|
45
|
+
# `grep -c` already outputs the match count (0 if none). Its exit code is
|
|
46
|
+
# 1 on no matches; the `|| true` keeps `set -e` happy without duplicating
|
|
47
|
+
# output via an explicit `echo 0` fallback.
|
|
48
|
+
grep -cE "$1" "$2" 2>/dev/null || true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
echo "=== hook-audit detection sanity test ==="
|
|
52
|
+
echo
|
|
53
|
+
|
|
54
|
+
[ -f "$BAD" ] || { echo "Missing fixture: $BAD"; exit 1; }
|
|
55
|
+
[ -f "$GOOD" ] || { echo "Missing fixture: $GOOD"; exit 1; }
|
|
56
|
+
|
|
57
|
+
echo "BAD fixture ($BAD):"
|
|
58
|
+
run "P-5 — return literal candidate present" "yes" "$([ $(count 'return[[:space:]]*\{' "$BAD") -ge 1 ] && echo yes || echo no)"
|
|
59
|
+
run "S-8 — setLoading(false) candidate present" "yes" "$([ $(count 'setLoading\(false\)' "$BAD") -ge 1 ] && echo yes || echo no)"
|
|
60
|
+
run "E-1 — substring matcher detected exactly 1" "1" "$(count "\.message\??\.includes\([\"'][a-z]+[\"']\)" "$BAD")"
|
|
61
|
+
run "D-1 — module cache present" "yes" "$([ $(count '^const cache[[:space:]]*=[[:space:]]*new Map' "$BAD") -ge 1 ] && echo yes || echo no)"
|
|
62
|
+
run "D-1 — inFlight ABSENT" "yes" "$([ $(count '^const inFlight[[:space:]]*=[[:space:]]*new Map' "$BAD") -eq 0 ] && echo yes || echo no)"
|
|
63
|
+
|
|
64
|
+
echo
|
|
65
|
+
echo "GOOD fixture ($GOOD):"
|
|
66
|
+
run "E-1 — no substring matcher (count is 0)" "0" "$(count "\.message\??\.includes\([\"'][a-z]+[\"']\)" "$GOOD")"
|
|
67
|
+
run "D-1 — module cache present" "yes" "$([ $(count '^const cache[[:space:]]*=[[:space:]]*new Map' "$GOOD") -ge 1 ] && echo yes || echo no)"
|
|
68
|
+
run "D-1 — inFlight present" "yes" "$([ $(count '^const inFlight[[:space:]]*=[[:space:]]*new Map' "$GOOD") -ge 1 ] && echo yes || echo no)"
|
|
69
|
+
|
|
70
|
+
echo
|
|
71
|
+
echo "=== Summary: $PASS passed, $FAIL failed ==="
|
|
72
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
73
|
+
echo "All hook-audit detection tests pass."
|
package/skills/prime/SKILL.md
CHANGED
|
@@ -14,14 +14,14 @@ Use the Glob tool to list project files (up to 60):
|
|
|
14
14
|
- Pattern: `**/*` with head_limit: 60
|
|
15
15
|
|
|
16
16
|
Use the Glob tool to find key config files:
|
|
17
|
-
- `**/CLAUDE.md`
|
|
18
17
|
- `**/AGENTS.md`
|
|
18
|
+
- `**/CLAUDE.md`
|
|
19
19
|
- `**/README.md`
|
|
20
20
|
|
|
21
21
|
## Step 2: Read Key Files
|
|
22
22
|
|
|
23
23
|
Read in this order:
|
|
24
|
-
1. `
|
|
24
|
+
1. `AGENTS.md` or `CLAUDE.md` at project root (project-specific rules — AGENTS.md is canonical when both exist; CLAUDE.md may be a thin `@AGENTS.md` alias)
|
|
25
25
|
2. `README.md` (project overview)
|
|
26
26
|
3. `package.json` or `pyproject.toml` (dependencies and scripts)
|
|
27
27
|
|
|
@@ -23,7 +23,7 @@ If the answers reveal a missing test covering the target, **write the test first
|
|
|
23
23
|
|
|
24
24
|
## Step 2: Capture the Baseline
|
|
25
25
|
|
|
26
|
-
Run the project's validation commands from `
|
|
26
|
+
Run the project's validation commands from `AGENTS.md` (or `CLAUDE.md` as fallback, or use `/hopla:validate`). Record:
|
|
27
27
|
|
|
28
28
|
- Lint / format — current state
|
|
29
29
|
- Types — current state
|
|
@@ -19,7 +19,7 @@ Instead of executing all tasks in one conversation (where context degrades), dis
|
|
|
19
19
|
1. **Dispatch implementer agent** with:
|
|
20
20
|
- The specific task spec (not the full plan)
|
|
21
21
|
- Relevant file paths from the plan
|
|
22
|
-
- Project CLAUDE.md for conventions
|
|
22
|
+
- Project AGENTS.md (or CLAUDE.md as fallback) for conventions
|
|
23
23
|
- Clear success criteria
|
|
24
24
|
|
|
25
25
|
2. **Agent implements, tests, and reports** with status:
|
|
@@ -48,7 +48,7 @@ Implement this task from the plan:
|
|
|
48
48
|
**Tests**: [tests to write]
|
|
49
49
|
**Validation**: [command to verify]
|
|
50
50
|
|
|
51
|
-
Project conventions are in CLAUDE.md. Follow them strictly.
|
|
51
|
+
Project conventions are in AGENTS.md (or CLAUDE.md for legacy projects). Follow them strictly.
|
|
52
52
|
Report your status as DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, or BLOCKED.
|
|
53
53
|
```
|
|
54
54
|
|
package/skills/worktree/SKILL.md
CHANGED
|
@@ -44,7 +44,7 @@ Use `../git/flow-detection.md` (Steps 1–2) to resolve the base branch from the
|
|
|
44
44
|
Check in order:
|
|
45
45
|
|
|
46
46
|
1. Existing `.worktrees/` or `worktrees/` directory
|
|
47
|
-
2. `CLAUDE.md` preference (if configured)
|
|
47
|
+
2. `AGENTS.md` / `CLAUDE.md` preference (if configured)
|
|
48
48
|
3. Ask the user (default: `worktrees/<kebab-name>`)
|
|
49
49
|
|
|
50
50
|
### Step 3: Ensure the worktree directory is gitignored
|
|
@@ -91,7 +91,7 @@ If none match, skip.
|
|
|
91
91
|
|
|
92
92
|
### Step 6: Verify clean baseline (optional)
|
|
93
93
|
|
|
94
|
-
If `CLAUDE.md` or `package.json` defines a test command, run it once inside the worktree to confirm the baseline is green. If no test command is defined, skip.
|
|
94
|
+
If `AGENTS.md` / `CLAUDE.md` or `package.json` defines a test command, run it once inside the worktree to confirm the baseline is green. If no test command is defined, skip.
|
|
95
95
|
|
|
96
96
|
If tests fail **before any changes**, STOP and report — the baseline is broken.
|
|
97
97
|
|