@chankov/agent-skills 0.2.0 → 0.3.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/commands/{doctor.md → doctor-agent-skills.md} +1 -1
- package/.pi/extensions/agent-skills-update-check/README.md +4 -4
- package/.pi/prompts/{doctor.md → doctor-agent-skills.md} +1 -1
- package/.versions/0.2.0/.claude/commands/{doctor.md → doctor-agent-skills.md} +1 -1
- package/.versions/0.2.0/.pi/extensions/agent-skills-update-check/README.md +4 -4
- package/.versions/0.2.0/.pi/prompts/{doctor.md → doctor-agent-skills.md} +1 -1
- package/.versions/0.2.0/skills/guided-workspace-setup/SKILL.md +40 -2
- package/.versions/0.3.0/.claude/commands/build.md +18 -0
- package/.versions/0.3.0/.claude/commands/code-simplify.md +22 -0
- package/.versions/0.3.0/.claude/commands/design-agent.md +14 -0
- package/.versions/0.3.0/.claude/commands/doctor-agent-skills.md +13 -0
- package/.versions/0.3.0/.claude/commands/plan.md +16 -0
- package/.versions/0.3.0/.claude/commands/prime.md +22 -0
- package/.versions/0.3.0/.claude/commands/review.md +16 -0
- package/.versions/0.3.0/.claude/commands/setup-agent-skills.md +19 -0
- package/.versions/0.3.0/.claude/commands/ship.md +17 -0
- package/.versions/0.3.0/.claude/commands/spec.md +15 -0
- package/.versions/0.3.0/.claude/commands/test.md +19 -0
- package/.versions/0.3.0/.opencode/commands/as-build.md +17 -0
- package/.versions/0.3.0/.opencode/commands/as-code-simplify.md +16 -0
- package/.versions/0.3.0/.opencode/commands/as-design-agent.md +15 -0
- package/.versions/0.3.0/.opencode/commands/as-doctor-agent-skills.md +11 -0
- package/.versions/0.3.0/.opencode/commands/as-plan.md +16 -0
- package/.versions/0.3.0/.opencode/commands/as-prime.md +22 -0
- package/.versions/0.3.0/.opencode/commands/as-review.md +15 -0
- package/.versions/0.3.0/.opencode/commands/as-setup-agent-skills.md +11 -0
- package/.versions/0.3.0/.opencode/commands/as-ship.md +16 -0
- package/.versions/0.3.0/.opencode/commands/as-spec.md +16 -0
- package/.versions/0.3.0/.opencode/commands/as-test.md +21 -0
- package/.versions/0.3.0/.pi/agents/agent-chain.yaml +49 -0
- package/.versions/0.3.0/.pi/agents/bowser.md +19 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/agent-expert.md +98 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/cli-expert.md +41 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/config-expert.md +63 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/ext-expert.md +43 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/keybinding-expert.md +134 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/pi-orchestrator.md +57 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/prompt-expert.md +70 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/skill-expert.md +42 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/theme-expert.md +40 -0
- package/.versions/0.3.0/.pi/agents/pi-pi/tui-expert.md +85 -0
- package/.versions/0.3.0/.pi/agents/teams.yaml +31 -0
- package/.versions/0.3.0/.pi/damage-control-rules.yaml +278 -0
- package/.versions/0.3.0/.pi/extensions/agent-skills-update-check/README.md +58 -0
- package/.versions/0.3.0/.pi/extensions/agent-skills-update-check/index.ts +161 -0
- package/.versions/0.3.0/.pi/extensions/agent-skills-update-check/package.json +6 -0
- package/.versions/0.3.0/.pi/extensions/chrome-devtools-mcp/README.md +39 -0
- package/.versions/0.3.0/.pi/extensions/chrome-devtools-mcp/index.ts +61 -0
- package/.versions/0.3.0/.pi/extensions/chrome-devtools-mcp/package.json +6 -0
- package/.versions/0.3.0/.pi/extensions/compact-and-continue/README.md +42 -0
- package/.versions/0.3.0/.pi/extensions/compact-and-continue/index.ts +120 -0
- package/.versions/0.3.0/.pi/extensions/compact-and-continue/package.json +6 -0
- package/.versions/0.3.0/.pi/extensions/mcp-bridge/README.md +46 -0
- package/.versions/0.3.0/.pi/extensions/mcp-bridge/index.ts +206 -0
- package/.versions/0.3.0/.pi/extensions/mcp-bridge/package.json +6 -0
- package/.versions/0.3.0/.pi/extensions/package-lock.json +1143 -0
- package/.versions/0.3.0/.pi/extensions/package.json +9 -0
- package/.versions/0.3.0/.pi/harnesses/agent-chain/README.md +37 -0
- package/.versions/0.3.0/.pi/harnesses/agent-chain/index.ts +795 -0
- package/.versions/0.3.0/.pi/harnesses/agent-chain/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/agent-team/README.md +38 -0
- package/.versions/0.3.0/.pi/harnesses/agent-team/index.ts +732 -0
- package/.versions/0.3.0/.pi/harnesses/agent-team/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/coms/README.md +36 -0
- package/.versions/0.3.0/.pi/harnesses/coms/index.ts +1595 -0
- package/.versions/0.3.0/.pi/harnesses/coms/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/coms-net/README.md +46 -0
- package/.versions/0.3.0/.pi/harnesses/coms-net/index.ts +1637 -0
- package/.versions/0.3.0/.pi/harnesses/coms-net/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/damage-control/README.md +38 -0
- package/.versions/0.3.0/.pi/harnesses/damage-control/index.ts +207 -0
- package/.versions/0.3.0/.pi/harnesses/damage-control/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/damage-control-continue/README.md +37 -0
- package/.versions/0.3.0/.pi/harnesses/damage-control-continue/index.ts +234 -0
- package/.versions/0.3.0/.pi/harnesses/damage-control-continue/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/minimal/README.md +27 -0
- package/.versions/0.3.0/.pi/harnesses/minimal/index.ts +32 -0
- package/.versions/0.3.0/.pi/harnesses/minimal/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/package-lock.json +35 -0
- package/.versions/0.3.0/.pi/harnesses/package.json +9 -0
- package/.versions/0.3.0/.pi/harnesses/pi-pi/README.md +39 -0
- package/.versions/0.3.0/.pi/harnesses/pi-pi/index.ts +631 -0
- package/.versions/0.3.0/.pi/harnesses/pi-pi/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/purpose-gate/README.md +27 -0
- package/.versions/0.3.0/.pi/harnesses/purpose-gate/index.ts +82 -0
- package/.versions/0.3.0/.pi/harnesses/purpose-gate/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/session-replay/README.md +28 -0
- package/.versions/0.3.0/.pi/harnesses/session-replay/index.ts +214 -0
- package/.versions/0.3.0/.pi/harnesses/session-replay/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/subagent-widget/README.md +36 -0
- package/.versions/0.3.0/.pi/harnesses/subagent-widget/index.ts +479 -0
- package/.versions/0.3.0/.pi/harnesses/subagent-widget/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/system-select/README.md +39 -0
- package/.versions/0.3.0/.pi/harnesses/system-select/index.ts +165 -0
- package/.versions/0.3.0/.pi/harnesses/system-select/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/tilldone/README.md +35 -0
- package/.versions/0.3.0/.pi/harnesses/tilldone/index.ts +724 -0
- package/.versions/0.3.0/.pi/harnesses/tilldone/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/tool-counter/README.md +31 -0
- package/.versions/0.3.0/.pi/harnesses/tool-counter/index.ts +100 -0
- package/.versions/0.3.0/.pi/harnesses/tool-counter/package.json +6 -0
- package/.versions/0.3.0/.pi/harnesses/tool-counter-widget/README.md +27 -0
- package/.versions/0.3.0/.pi/harnesses/tool-counter-widget/index.ts +66 -0
- package/.versions/0.3.0/.pi/harnesses/tool-counter-widget/package.json +6 -0
- package/.versions/0.3.0/.pi/prompts/build.md +24 -0
- package/.versions/0.3.0/.pi/prompts/code-simplify.md +22 -0
- package/.versions/0.3.0/.pi/prompts/doctor-agent-skills.md +13 -0
- package/.versions/0.3.0/.pi/prompts/plan.md +16 -0
- package/.versions/0.3.0/.pi/prompts/review.md +16 -0
- package/.versions/0.3.0/.pi/prompts/setup-agent-skills.md +19 -0
- package/.versions/0.3.0/.pi/prompts/ship.md +17 -0
- package/.versions/0.3.0/.pi/prompts/spec.md +15 -0
- package/.versions/0.3.0/.pi/prompts/test.md +19 -0
- package/.versions/0.3.0/.pi/skills/bowser/SKILL.md +114 -0
- package/.versions/0.3.0/.version +1 -0
- package/.versions/0.3.0/agents/builder.md +6 -0
- package/.versions/0.3.0/agents/code-reviewer.md +93 -0
- package/.versions/0.3.0/agents/documenter.md +6 -0
- package/.versions/0.3.0/agents/plan-reviewer.md +22 -0
- package/.versions/0.3.0/agents/planner.md +6 -0
- package/.versions/0.3.0/agents/scout.md +6 -0
- package/.versions/0.3.0/agents/security-auditor.md +97 -0
- package/.versions/0.3.0/agents/test-engineer.md +89 -0
- package/.versions/0.3.0/hooks/SIMPLIFY-IGNORE.md +90 -0
- package/.versions/0.3.0/hooks/hooks.json +14 -0
- package/.versions/0.3.0/hooks/session-start.sh +74 -0
- package/.versions/0.3.0/hooks/simplify-ignore-test.sh +247 -0
- package/.versions/0.3.0/hooks/simplify-ignore.sh +302 -0
- package/.versions/0.3.0/references/accessibility-checklist.md +159 -0
- package/.versions/0.3.0/references/performance-checklist.md +121 -0
- package/.versions/0.3.0/references/prompting-patterns.md +380 -0
- package/.versions/0.3.0/references/security-checklist.md +134 -0
- package/.versions/0.3.0/references/testing-patterns.md +236 -0
- package/.versions/0.3.0/skills/api-and-interface-design/SKILL.md +294 -0
- package/.versions/0.3.0/skills/browser-testing-with-devtools/SKILL.md +335 -0
- package/.versions/0.3.0/skills/ci-cd-and-automation/SKILL.md +390 -0
- package/.versions/0.3.0/skills/code-review-and-quality/SKILL.md +347 -0
- package/.versions/0.3.0/skills/code-simplification/SKILL.md +331 -0
- package/.versions/0.3.0/skills/context-engineering/SKILL.md +291 -0
- package/.versions/0.3.0/skills/debugging-and-error-recovery/SKILL.md +300 -0
- package/.versions/0.3.0/skills/deprecation-and-migration/SKILL.md +206 -0
- package/.versions/0.3.0/skills/designing-agents/SKILL.md +394 -0
- package/.versions/0.3.0/skills/designing-agents/pi-harness-authoring.md +213 -0
- package/.versions/0.3.0/skills/documentation-and-adrs/SKILL.md +278 -0
- package/.versions/0.3.0/skills/frontend-ui-engineering/SKILL.md +322 -0
- package/.versions/0.3.0/skills/git-workflow-and-versioning/SKILL.md +316 -0
- package/.versions/0.3.0/skills/guided-workspace-setup/SKILL.md +331 -0
- package/.versions/0.3.0/skills/idea-refine/SKILL.md +178 -0
- package/.versions/0.3.0/skills/idea-refine/examples.md +238 -0
- package/.versions/0.3.0/skills/idea-refine/frameworks.md +99 -0
- package/.versions/0.3.0/skills/idea-refine/refinement-criteria.md +113 -0
- package/.versions/0.3.0/skills/idea-refine/scripts/idea-refine.sh +15 -0
- package/.versions/0.3.0/skills/incremental-implementation/SKILL.md +279 -0
- package/.versions/0.3.0/skills/performance-optimization/SKILL.md +350 -0
- package/.versions/0.3.0/skills/planning-and-task-breakdown/SKILL.md +237 -0
- package/.versions/0.3.0/skills/security-and-hardening/SKILL.md +349 -0
- package/.versions/0.3.0/skills/shipping-and-launch/SKILL.md +309 -0
- package/.versions/0.3.0/skills/source-driven-development/SKILL.md +194 -0
- package/.versions/0.3.0/skills/spec-driven-development/SKILL.md +237 -0
- package/.versions/0.3.0/skills/test-driven-development/SKILL.md +379 -0
- package/.versions/0.3.0/skills/using-agent-skills/SKILL.md +176 -0
- package/CHANGELOG.md +72 -0
- package/README.md +5 -5
- package/bin/cli.js +100 -24
- package/bin/lib/bootstrap.js +254 -0
- package/bin/lib/doctor.js +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/npm-install.md +34 -11
- package/package.json +1 -1
- package/skills/guided-workspace-setup/SKILL.md +40 -2
- /package/.claude/commands/{setup.md → setup-agent-skills.md} +0 -0
- /package/.opencode/commands/{as-doctor.md → as-doctor-agent-skills.md} +0 -0
- /package/.opencode/commands/{as-setup.md → as-setup-agent-skills.md} +0 -0
- /package/.pi/prompts/{setup.md → setup-agent-skills.md} +0 -0
- /package/.versions/0.2.0/.claude/commands/{setup.md → setup-agent-skills.md} +0 -0
- /package/.versions/0.2.0/.opencode/commands/{as-doctor.md → as-doctor-agent-skills.md} +0 -0
- /package/.versions/0.2.0/.opencode/commands/{as-setup.md → as-setup-agent-skills.md} +0 -0
- /package/.versions/0.2.0/.pi/prompts/{setup.md → setup-agent-skills.md} +0 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# simplify-ignore.sh — Hook for Read (PreToolUse), Edit|Write (PostToolUse), Stop
|
|
3
|
+
#
|
|
4
|
+
# PreToolUse Read → backs up file, replaces blocks with BLOCK_<hash> in-place
|
|
5
|
+
# PostToolUse Edit → expands placeholders, re-filters so file stays hidden
|
|
6
|
+
# PostToolUse Write → expands placeholders, re-filters so file stays hidden
|
|
7
|
+
# Stop → restores real file content from backup
|
|
8
|
+
#
|
|
9
|
+
# The file on disk ALWAYS has placeholders while the session is active.
|
|
10
|
+
# The real content (with model's changes applied) lives in the backup.
|
|
11
|
+
#
|
|
12
|
+
# Dependencies: jq, shasum or sha1sum (auto-detected)
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
17
|
+
printf '%s\n' "error: missing jq" >&2; exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
CACHE="${CLAUDE_PROJECT_DIR:-.}/.claude/.simplify-ignore-cache"
|
|
21
|
+
if [ -t 0 ]; then INPUT="{}"; else INPUT=$(cat); fi
|
|
22
|
+
|
|
23
|
+
# Parse hook input — trap errors explicitly so set -e doesn't cause
|
|
24
|
+
# a silent exit on malformed JSON, and surface a useful diagnostic.
|
|
25
|
+
parse_error=""
|
|
26
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null) || {
|
|
27
|
+
parse_error="failed to parse .tool_name from hook input"
|
|
28
|
+
TOOL_NAME=""
|
|
29
|
+
}
|
|
30
|
+
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null) || {
|
|
31
|
+
parse_error="failed to parse .tool_input.file_path from hook input"
|
|
32
|
+
FILE_PATH=""
|
|
33
|
+
}
|
|
34
|
+
if [ -n "$parse_error" ]; then
|
|
35
|
+
printf 'Warning: %s (input: %.120s)\n' "$parse_error" "$INPUT" >&2
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
hash_cmd() {
|
|
39
|
+
if command -v shasum >/dev/null 2>&1; then shasum
|
|
40
|
+
elif command -v sha1sum >/dev/null 2>&1; then sha1sum
|
|
41
|
+
else printf '%s\n' "error: missing shasum or sha1sum" >&2; exit 1; fi
|
|
42
|
+
}
|
|
43
|
+
file_id() { printf '%s' "$1" | hash_cmd | cut -c1-16; }
|
|
44
|
+
block_hash() { printf '%s' "$1" | hash_cmd | cut -c1-8; }
|
|
45
|
+
# Escape glob metacharacters so ${var/pattern/repl} treats pattern as literal.
|
|
46
|
+
# Needed for Bash 3.2 (macOS) where quotes don't suppress globbing in PE patterns.
|
|
47
|
+
escape_glob() {
|
|
48
|
+
local s="$1"
|
|
49
|
+
s="${s//\\/\\\\}"
|
|
50
|
+
s="${s//\*/\\*}"
|
|
51
|
+
s="${s//\?/\\?}"
|
|
52
|
+
s="${s//\[/\\[}"
|
|
53
|
+
printf '%s' "$s"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# ── filter_file: replace simplify-ignore blocks with BLOCK_<hash> placeholders ─
|
|
57
|
+
# Reads $1 (source), writes filtered version to $2 (dest), saves blocks to cache.
|
|
58
|
+
# Returns 0 if blocks were found, 1 if none.
|
|
59
|
+
filter_file() {
|
|
60
|
+
local src="$1" dest="$2" fid="$3"
|
|
61
|
+
: > "$dest"
|
|
62
|
+
rm -f "$CACHE/${fid}".block.* "$CACHE/${fid}".reason.* "$CACHE/${fid}".prefix.* "$CACHE/${fid}".suffix.*
|
|
63
|
+
|
|
64
|
+
local count=0 in_block=0 buf="" reason="" prefix="" suffix=""
|
|
65
|
+
|
|
66
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
67
|
+
# Check for start marker (no fork — uses bash case)
|
|
68
|
+
if [ $in_block -eq 0 ]; then
|
|
69
|
+
case "$line" in *simplify-ignore-start*)
|
|
70
|
+
in_block=1
|
|
71
|
+
buf="$line"
|
|
72
|
+
# Extract comment prefix/suffix to preserve language-appropriate syntax
|
|
73
|
+
prefix="${line%%simplify-ignore-start*}"
|
|
74
|
+
suffix=""
|
|
75
|
+
case "$line" in *'*/'*) suffix=" */" ;; *'-->'*) suffix=" -->" ;; esac
|
|
76
|
+
reason=$(printf '%s' "$line" | sed -n 's/.*simplify-ignore-start:[[:space:]]*//p' \
|
|
77
|
+
| sed 's/[[:space:]]*\*\/.*$//' | sed 's/[[:space:]]*-->.*$//' | sed 's/[[:space:]]*$//')
|
|
78
|
+
# Handle single-line block (start + end on same line)
|
|
79
|
+
case "$line" in *simplify-ignore-end*)
|
|
80
|
+
in_block=0
|
|
81
|
+
# Write single-line block immediately and skip to next line
|
|
82
|
+
# to avoid the end-marker check below firing again
|
|
83
|
+
local h; h=$(block_hash "$buf")
|
|
84
|
+
count=$((count + 1))
|
|
85
|
+
printf '%s' "$buf" > "$CACHE/${fid}.block.${h}"
|
|
86
|
+
[ -n "$reason" ] && printf '%s' "$reason" > "$CACHE/${fid}.reason.${h}"
|
|
87
|
+
printf '%s' "$prefix" > "$CACHE/${fid}.prefix.${h}"
|
|
88
|
+
printf '%s' "$suffix" > "$CACHE/${fid}.suffix.${h}"
|
|
89
|
+
if [ -n "$reason" ]; then
|
|
90
|
+
printf '%s\n' "${prefix}BLOCK_${h}: ${reason}${suffix}" >> "$dest"
|
|
91
|
+
else
|
|
92
|
+
printf '%s\n' "${prefix}BLOCK_${h}${suffix}" >> "$dest"
|
|
93
|
+
fi
|
|
94
|
+
buf=""; reason=""; prefix=""; suffix=""
|
|
95
|
+
continue
|
|
96
|
+
;; *)
|
|
97
|
+
continue
|
|
98
|
+
;;
|
|
99
|
+
esac
|
|
100
|
+
;; esac
|
|
101
|
+
fi
|
|
102
|
+
# Accumulate block content
|
|
103
|
+
if [ $in_block -eq 1 ]; then
|
|
104
|
+
buf="${buf}
|
|
105
|
+
${line}"
|
|
106
|
+
fi
|
|
107
|
+
# Check for end marker
|
|
108
|
+
case "$line" in *simplify-ignore-end*)
|
|
109
|
+
if [ $in_block -eq 1 ]; then
|
|
110
|
+
local h; h=$(block_hash "$buf")
|
|
111
|
+
count=$((count + 1))
|
|
112
|
+
printf '%s' "$buf" > "$CACHE/${fid}.block.${h}"
|
|
113
|
+
[ -n "$reason" ] && printf '%s' "$reason" > "$CACHE/${fid}.reason.${h}"
|
|
114
|
+
printf '%s' "$prefix" > "$CACHE/${fid}.prefix.${h}"
|
|
115
|
+
printf '%s' "$suffix" > "$CACHE/${fid}.suffix.${h}"
|
|
116
|
+
if [ -n "$reason" ]; then
|
|
117
|
+
printf '%s\n' "${prefix}BLOCK_${h}: ${reason}${suffix}" >> "$dest"
|
|
118
|
+
else
|
|
119
|
+
printf '%s\n' "${prefix}BLOCK_${h}${suffix}" >> "$dest"
|
|
120
|
+
fi
|
|
121
|
+
in_block=0; buf=""; reason=""; prefix=""; suffix=""
|
|
122
|
+
continue
|
|
123
|
+
fi
|
|
124
|
+
;;
|
|
125
|
+
esac
|
|
126
|
+
[ $in_block -eq 0 ] && printf '%s\n' "$line" >> "$dest"
|
|
127
|
+
done < "$src"
|
|
128
|
+
|
|
129
|
+
# Unclosed block → flush as-is
|
|
130
|
+
if [ $in_block -eq 1 ] && [ -n "$buf" ]; then
|
|
131
|
+
printf 'Warning: unclosed simplify-ignore-start in %s (block not hidden)\n' "$src" >&2
|
|
132
|
+
printf '%s\n' "$buf" >> "$dest"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Preserve trailing newline status of source
|
|
136
|
+
if [ -s "$dest" ] && [ -s "$src" ] && [ -n "$(tail -c 1 "$src")" ]; then
|
|
137
|
+
perl -pe 'chomp if eof' "$dest" > "${dest}.nnl" && \
|
|
138
|
+
cat "${dest}.nnl" > "$dest" && rm -f "${dest}.nnl"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
[ $count -gt 0 ] && return 0 || return 1
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# ── Stop: restore all files from backup ───────────────────────────────────────
|
|
145
|
+
if [ -z "$TOOL_NAME" ]; then
|
|
146
|
+
[ -d "$CACHE" ] || exit 0
|
|
147
|
+
for bak in "$CACHE"/*.bak; do
|
|
148
|
+
[ -f "$bak" ] || continue
|
|
149
|
+
fid="${bak##*/}"; fid="${fid%.bak}"
|
|
150
|
+
pathfile="$CACHE/${fid}.path"
|
|
151
|
+
[ -f "$pathfile" ] || { rm -f "$bak"; continue; }
|
|
152
|
+
orig=$(cat "$pathfile")
|
|
153
|
+
if [ -f "$orig" ]; then
|
|
154
|
+
cat "$bak" > "$orig"
|
|
155
|
+
rm -f "$bak" "$pathfile" "$CACHE/${fid}".block.* "$CACHE/${fid}".reason.* "$CACHE/${fid}".prefix.* "$CACHE/${fid}".suffix.*
|
|
156
|
+
rmdir "$CACHE/${fid}.lock" 2>/dev/null
|
|
157
|
+
else
|
|
158
|
+
# File was moved/deleted — save backup as .recovered, don't destroy it
|
|
159
|
+
mkdir -p "$(dirname "${orig}.recovered")"
|
|
160
|
+
mv "$bak" "${orig}.recovered"
|
|
161
|
+
rm -f "$pathfile" "$CACHE/${fid}".block.* "$CACHE/${fid}".reason.* "$CACHE/${fid}".prefix.* "$CACHE/${fid}".suffix.*
|
|
162
|
+
rmdir "$CACHE/${fid}.lock" 2>/dev/null
|
|
163
|
+
printf 'Warning: %s was moved/deleted. Recovered original to %s.recovered\n' "$orig" "$orig" >&2
|
|
164
|
+
fi
|
|
165
|
+
done
|
|
166
|
+
# Clean orphan locks (created but crash before backup)
|
|
167
|
+
for lockdir in "$CACHE"/*.lock; do
|
|
168
|
+
[ -d "$lockdir" ] || continue
|
|
169
|
+
rmdir "$lockdir" 2>/dev/null
|
|
170
|
+
done
|
|
171
|
+
exit 0
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
175
|
+
|
|
176
|
+
# ── PreToolUse Read: filter in-place ──────────────────────────────────────────
|
|
177
|
+
if [ "$TOOL_NAME" = "Read" ]; then
|
|
178
|
+
[ -f "$FILE_PATH" ] || exit 0
|
|
179
|
+
case "$(basename "$FILE_PATH")" in simplify-ignore*|SIMPLIFY-IGNORE*) exit 0 ;; esac
|
|
180
|
+
|
|
181
|
+
mkdir -p "$CACHE"
|
|
182
|
+
ID=$(file_id "$FILE_PATH")
|
|
183
|
+
|
|
184
|
+
# If backup exists, file is already filtered — skip
|
|
185
|
+
[ -f "$CACHE/${ID}.bak" ] && exit 0
|
|
186
|
+
|
|
187
|
+
grep -q 'simplify-ignore-start' -- "$FILE_PATH" || exit 0
|
|
188
|
+
|
|
189
|
+
# Atomic lock: mkdir fails if another session races us
|
|
190
|
+
if ! mkdir "$CACHE/${ID}.lock" 2>/dev/null; then
|
|
191
|
+
# Lock exists — reclaim only if stale (>60s old, no backup = crash leftover)
|
|
192
|
+
if [ ! -f "$CACHE/${ID}.bak" ] && \
|
|
193
|
+
[ -n "$(find "$CACHE/${ID}.lock" -maxdepth 0 -mmin +1 2>/dev/null)" ]; then
|
|
194
|
+
rmdir "$CACHE/${ID}.lock" 2>/dev/null || true
|
|
195
|
+
mkdir "$CACHE/${ID}.lock" 2>/dev/null || exit 0
|
|
196
|
+
else
|
|
197
|
+
exit 0
|
|
198
|
+
fi
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# Back up the original (preserve trailing newline status)
|
|
202
|
+
cp -p "$FILE_PATH" "$CACHE/${ID}.bak" 2>/dev/null || cp "$FILE_PATH" "$CACHE/${ID}.bak"
|
|
203
|
+
printf '%s' "$FILE_PATH" > "$CACHE/${ID}.path"
|
|
204
|
+
|
|
205
|
+
# Filter in-place (cat > preserves inode and permissions)
|
|
206
|
+
FILTERED="$CACHE/${ID}.$$.tmp"
|
|
207
|
+
rm -f "$FILTERED"
|
|
208
|
+
if filter_file "$FILE_PATH" "$FILTERED" "$ID"; then
|
|
209
|
+
cat "$FILTERED" > "$FILE_PATH"
|
|
210
|
+
rm -f "$FILTERED"
|
|
211
|
+
else
|
|
212
|
+
rm -f "$FILTERED" "$CACHE/${ID}.bak" "$CACHE/${ID}.path"
|
|
213
|
+
rmdir "$CACHE/${ID}.lock" 2>/dev/null
|
|
214
|
+
fi
|
|
215
|
+
exit 0
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# ── PostToolUse Edit|Write: expand, then re-filter ────────────────────────────
|
|
219
|
+
if [ "$TOOL_NAME" = "Edit" ] || [ "$TOOL_NAME" = "Write" ]; then
|
|
220
|
+
ID=$(file_id "$FILE_PATH")
|
|
221
|
+
[ -f "$CACHE/${ID}.bak" ] || exit 0
|
|
222
|
+
ls "$CACHE/${ID}".block.* >/dev/null 2>&1 || exit 0
|
|
223
|
+
|
|
224
|
+
# Expand placeholders, preserving any inline code the model added around them
|
|
225
|
+
EXPANDED="$CACHE/${ID}.$$.expanded"
|
|
226
|
+
rm -f "$EXPANDED"
|
|
227
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
228
|
+
case "$line" in *BLOCK_*)
|
|
229
|
+
# Expand all placeholders on this line (supports multiple per line)
|
|
230
|
+
for bf in "$CACHE/${ID}".block.*; do
|
|
231
|
+
[ -f "$bf" ] || continue
|
|
232
|
+
h="${bf##*.}"
|
|
233
|
+
case "$line" in *"BLOCK_${h}"*)
|
|
234
|
+
# Reconstruct the exact placeholder pattern
|
|
235
|
+
bp=""; bs=""; br=""
|
|
236
|
+
[ -f "$CACHE/${ID}.prefix.${h}" ] && bp=$(cat "$CACHE/${ID}.prefix.${h}")
|
|
237
|
+
[ -f "$CACHE/${ID}.suffix.${h}" ] && bs=$(cat "$CACHE/${ID}.suffix.${h}")
|
|
238
|
+
[ -f "$CACHE/${ID}.reason.${h}" ] && br=$(cat "$CACHE/${ID}.reason.${h}")
|
|
239
|
+
if [ -n "$br" ]; then
|
|
240
|
+
placeholder="${bp}BLOCK_${h}: ${br}${bs}"
|
|
241
|
+
else
|
|
242
|
+
placeholder="${bp}BLOCK_${h}${bs}"
|
|
243
|
+
fi
|
|
244
|
+
block_content=$(cat "$bf"; printf x); block_content="${block_content%x}"
|
|
245
|
+
# Escape glob metacharacters (* ? [ \) in the pattern
|
|
246
|
+
esc_placeholder=$(escape_glob "$placeholder")
|
|
247
|
+
# Bash native substitution (// = global replace): replace placeholder, keep surrounding code
|
|
248
|
+
line="${line//$esc_placeholder/$block_content}"
|
|
249
|
+
# Fallback: if model altered the reason text, try without reason
|
|
250
|
+
# (only trigger if BLOCK_hash is still present AND wasn't in the original block content)
|
|
251
|
+
case "$block_content" in *"BLOCK_${h}"*) ;; *)
|
|
252
|
+
case "$line" in *"BLOCK_${h}"*)
|
|
253
|
+
printf 'Warning: placeholder BLOCK_%s was modified by model, using fuzzy match\n' "$h" >&2
|
|
254
|
+
esc_fuzzy=$(escape_glob "${bp}BLOCK_${h}${bs}")
|
|
255
|
+
line="${line//$esc_fuzzy/$block_content}"
|
|
256
|
+
# Last resort: match just the hash token
|
|
257
|
+
case "$line" in *"BLOCK_${h}"*)
|
|
258
|
+
line="${line//BLOCK_${h}/$block_content}"
|
|
259
|
+
;; esac
|
|
260
|
+
;; esac
|
|
261
|
+
;; esac
|
|
262
|
+
;; esac
|
|
263
|
+
done
|
|
264
|
+
;; esac
|
|
265
|
+
printf '%s\n' "$line" >> "$EXPANDED"
|
|
266
|
+
done < "$FILE_PATH"
|
|
267
|
+
# Preserve trailing newline status
|
|
268
|
+
if [ -s "$EXPANDED" ] && [ -s "$FILE_PATH" ] && [ -n "$(tail -c 1 "$FILE_PATH")" ]; then
|
|
269
|
+
perl -pe 'chomp if eof' "$EXPANDED" > "${EXPANDED}.nnl" && \
|
|
270
|
+
cat "${EXPANDED}.nnl" > "$EXPANDED" && rm -f "${EXPANDED}.nnl"
|
|
271
|
+
fi
|
|
272
|
+
# Warn if model deleted a protected block entirely
|
|
273
|
+
for bf in "$CACHE/${ID}".block.*; do
|
|
274
|
+
[ -f "$bf" ] || continue
|
|
275
|
+
bh="${bf##*.}"
|
|
276
|
+
# After expansion, blocks appear as original code (simplify-ignore-start).
|
|
277
|
+
# If neither the expanded code nor the placeholder is in EXPANDED, it was deleted.
|
|
278
|
+
if ! grep -qF "BLOCK_${bh}" "$EXPANDED" 2>/dev/null; then
|
|
279
|
+
# Get first line of block to check if it was expanded back
|
|
280
|
+
first_line=$(head -1 "$bf")
|
|
281
|
+
if ! grep -qF "$first_line" "$EXPANDED" 2>/dev/null; then
|
|
282
|
+
printf 'Warning: protected block BLOCK_%s was deleted by model\n' "$bh" >&2
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
done
|
|
286
|
+
# Preserve inode and permissions
|
|
287
|
+
cat "$EXPANDED" > "$FILE_PATH"
|
|
288
|
+
rm -f "$EXPANDED"
|
|
289
|
+
|
|
290
|
+
# Save expanded version as new backup (this is the "real" file with model's changes)
|
|
291
|
+
cp "$FILE_PATH" "$CACHE/${ID}.bak"
|
|
292
|
+
|
|
293
|
+
# Re-filter in-place so the file on disk stays with placeholders
|
|
294
|
+
FILTERED="$CACHE/${ID}.$$.tmp"
|
|
295
|
+
rm -f "$FILTERED"
|
|
296
|
+
if filter_file "$FILE_PATH" "$FILTERED" "$ID"; then
|
|
297
|
+
cat "$FILTERED" > "$FILE_PATH"
|
|
298
|
+
rm -f "$FILTERED"
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
exit 0
|
|
302
|
+
fi
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Accessibility Checklist
|
|
2
|
+
|
|
3
|
+
Quick reference for WCAG 2.1 AA compliance. Use alongside the `frontend-ui-engineering` skill.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Essential Checks](#essential-checks)
|
|
8
|
+
- [Common HTML Patterns](#common-html-patterns)
|
|
9
|
+
- [Testing Tools](#testing-tools)
|
|
10
|
+
- [Quick Reference: ARIA Live Regions](#quick-reference-aria-live-regions)
|
|
11
|
+
- [Common Anti-Patterns](#common-anti-patterns)
|
|
12
|
+
|
|
13
|
+
## Essential Checks
|
|
14
|
+
|
|
15
|
+
### Keyboard Navigation
|
|
16
|
+
- [ ] All interactive elements focusable via Tab key
|
|
17
|
+
- [ ] Focus order follows visual/logical order
|
|
18
|
+
- [ ] Focus is visible (outline/ring on focused elements)
|
|
19
|
+
- [ ] Custom widgets have keyboard support (Enter to activate, Escape to close)
|
|
20
|
+
- [ ] No keyboard traps (user can always Tab away from a component)
|
|
21
|
+
- [ ] Skip-to-content link at top of page
|
|
22
|
+
- [ ] Modals trap focus while open, return focus on close
|
|
23
|
+
|
|
24
|
+
### Screen Readers
|
|
25
|
+
- [ ] All images have `alt` text (or `alt=""` for decorative images)
|
|
26
|
+
- [ ] All form inputs have associated labels (`<label>` or `aria-label`)
|
|
27
|
+
- [ ] Buttons and links have descriptive text (not "Click here")
|
|
28
|
+
- [ ] Icon-only buttons have `aria-label`
|
|
29
|
+
- [ ] Page has one `<h1>` and headings don't skip levels
|
|
30
|
+
- [ ] Dynamic content changes announced (`aria-live` regions)
|
|
31
|
+
- [ ] Tables have `<th>` headers with scope
|
|
32
|
+
|
|
33
|
+
### Visual
|
|
34
|
+
- [ ] Text contrast ≥ 4.5:1 (normal text) or ≥ 3:1 (large text, 18px+)
|
|
35
|
+
- [ ] UI components contrast ≥ 3:1 against background
|
|
36
|
+
- [ ] Color is not the only way to convey information
|
|
37
|
+
- [ ] Text resizable to 200% without breaking layout
|
|
38
|
+
- [ ] No content that flashes more than 3 times per second
|
|
39
|
+
|
|
40
|
+
### Forms
|
|
41
|
+
- [ ] Every input has a visible label
|
|
42
|
+
- [ ] Required fields indicated (not by color alone)
|
|
43
|
+
- [ ] Error messages specific and associated with the field
|
|
44
|
+
- [ ] Error state visible by more than color (icon, text, border)
|
|
45
|
+
- [ ] Form submission errors summarized and focusable
|
|
46
|
+
|
|
47
|
+
### Content
|
|
48
|
+
- [ ] Language declared (`<html lang="en">`)
|
|
49
|
+
- [ ] Page has a descriptive `<title>`
|
|
50
|
+
- [ ] Links distinguish from surrounding text (not by color alone)
|
|
51
|
+
- [ ] Touch targets ≥ 44x44px on mobile
|
|
52
|
+
- [ ] Meaningful empty states (not blank screens)
|
|
53
|
+
|
|
54
|
+
## Common HTML Patterns
|
|
55
|
+
|
|
56
|
+
### Buttons vs. Links
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<!-- Use <button> for actions -->
|
|
60
|
+
<button onClick={handleDelete}>Delete Task</button>
|
|
61
|
+
|
|
62
|
+
<!-- Use <a> for navigation -->
|
|
63
|
+
<a href="/tasks/123">View Task</a>
|
|
64
|
+
|
|
65
|
+
<!-- NEVER use div/span as buttons -->
|
|
66
|
+
<div onClick={handleDelete}>Delete</div> <!-- BAD -->
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Form Labels
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<!-- Explicit label association -->
|
|
73
|
+
<label htmlFor="email">Email address</label>
|
|
74
|
+
<input id="email" type="email" required />
|
|
75
|
+
|
|
76
|
+
<!-- Implicit wrapping -->
|
|
77
|
+
<label>
|
|
78
|
+
Email address
|
|
79
|
+
<input type="email" required />
|
|
80
|
+
</label>
|
|
81
|
+
|
|
82
|
+
<!-- Hidden label (visible label preferred) -->
|
|
83
|
+
<input type="search" aria-label="Search tasks" />
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### ARIA Roles
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<!-- Navigation -->
|
|
90
|
+
<nav aria-label="Main navigation">...</nav>
|
|
91
|
+
<nav aria-label="Footer links">...</nav>
|
|
92
|
+
|
|
93
|
+
<!-- Status messages -->
|
|
94
|
+
<div role="status" aria-live="polite">Task saved</div>
|
|
95
|
+
|
|
96
|
+
<!-- Alert messages -->
|
|
97
|
+
<div role="alert">Error: Title is required</div>
|
|
98
|
+
|
|
99
|
+
<!-- Modal dialogs -->
|
|
100
|
+
<dialog aria-modal="true" aria-labelledby="dialog-title">
|
|
101
|
+
<h2 id="dialog-title">Confirm Delete</h2>
|
|
102
|
+
...
|
|
103
|
+
</dialog>
|
|
104
|
+
|
|
105
|
+
<!-- Loading states -->
|
|
106
|
+
<div aria-busy="true" aria-label="Loading tasks">
|
|
107
|
+
<Spinner />
|
|
108
|
+
</div>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Accessible Lists
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<ul role="list" aria-label="Tasks">
|
|
115
|
+
<li>
|
|
116
|
+
<input type="checkbox" id="task-1" aria-label="Complete: Buy groceries" />
|
|
117
|
+
<label htmlFor="task-1">Buy groceries</label>
|
|
118
|
+
</li>
|
|
119
|
+
</ul>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Testing Tools
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Automated audit
|
|
126
|
+
npx axe-core # Programmatic accessibility testing
|
|
127
|
+
npx pa11y # CLI accessibility checker
|
|
128
|
+
|
|
129
|
+
# In browser
|
|
130
|
+
# Chrome DevTools → Lighthouse → Accessibility
|
|
131
|
+
# Chrome DevTools → Elements → Accessibility tree
|
|
132
|
+
|
|
133
|
+
# Screen reader testing
|
|
134
|
+
# macOS: VoiceOver (Cmd + F5)
|
|
135
|
+
# Windows: NVDA (free) or JAWS
|
|
136
|
+
# Linux: Orca
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Quick Reference: ARIA Live Regions
|
|
140
|
+
|
|
141
|
+
| Value | Behavior | Use For |
|
|
142
|
+
|-------|----------|---------|
|
|
143
|
+
| `aria-live="polite"` | Announced at next pause | Status updates, saved confirmations |
|
|
144
|
+
| `aria-live="assertive"` | Announced immediately | Errors, time-sensitive alerts |
|
|
145
|
+
| `role="status"` | Same as `polite` | Status messages |
|
|
146
|
+
| `role="alert"` | Same as `assertive` | Error messages |
|
|
147
|
+
|
|
148
|
+
## Common Anti-Patterns
|
|
149
|
+
|
|
150
|
+
| Anti-Pattern | Problem | Fix |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `div` as button | Not focusable, no keyboard support | Use `<button>` |
|
|
153
|
+
| Missing `alt` text | Images invisible to screen readers | Add descriptive `alt` |
|
|
154
|
+
| Color-only states | Invisible to color-blind users | Add icons, text, or patterns |
|
|
155
|
+
| Autoplaying media | Disorienting, can't be stopped | Add controls, don't autoplay |
|
|
156
|
+
| Custom dropdown with no ARIA | Unusable by keyboard/screen reader | Use native `<select>` or proper ARIA listbox |
|
|
157
|
+
| Removing focus outlines | Users can't see where they are | Style outlines, don't remove them |
|
|
158
|
+
| Empty links/buttons | "Link" announced with no description | Add text or `aria-label` |
|
|
159
|
+
| `tabindex > 0` | Breaks natural tab order | Use `tabindex="0"` or `-1` only |
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Performance Checklist
|
|
2
|
+
|
|
3
|
+
Quick reference checklist for web application performance. Use alongside the `performance-optimization` skill.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Core Web Vitals Targets](#core-web-vitals-targets)
|
|
8
|
+
- [Frontend Checklist](#frontend-checklist)
|
|
9
|
+
- [Backend Checklist](#backend-checklist)
|
|
10
|
+
- [Measurement Commands](#measurement-commands)
|
|
11
|
+
- [Common Anti-Patterns](#common-anti-patterns)
|
|
12
|
+
|
|
13
|
+
## Core Web Vitals Targets
|
|
14
|
+
|
|
15
|
+
| Metric | Good | Needs Work | Poor |
|
|
16
|
+
|--------|------|------------|------|
|
|
17
|
+
| LCP (Largest Contentful Paint) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
|
|
18
|
+
| INP (Interaction to Next Paint) | ≤ 200ms | ≤ 500ms | > 500ms |
|
|
19
|
+
| CLS (Cumulative Layout Shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
|
|
20
|
+
|
|
21
|
+
## TTFB Diagnosis
|
|
22
|
+
|
|
23
|
+
When TTFB is slow (> 800ms), check each component in DevTools Network waterfall:
|
|
24
|
+
|
|
25
|
+
- [ ] **DNS resolution** slow → add `<link rel="dns-prefetch">` or `<link rel="preconnect">` for known origins
|
|
26
|
+
- [ ] **TCP/TLS handshake** slow → enable HTTP/2, consider edge deployment, verify keep-alive
|
|
27
|
+
- [ ] **Server processing** slow → profile backend, check slow queries, add caching
|
|
28
|
+
|
|
29
|
+
## Frontend Checklist
|
|
30
|
+
|
|
31
|
+
### Images
|
|
32
|
+
- [ ] Images use modern formats (WebP, AVIF)
|
|
33
|
+
- [ ] Images are responsively sized (`srcset` and `sizes`)
|
|
34
|
+
- [ ] Images and `<source>` elements have explicit `width` and `height` (prevents CLS in art direction)
|
|
35
|
+
- [ ] Below-the-fold images use `loading="lazy"` and `decoding="async"`
|
|
36
|
+
- [ ] Hero/LCP images use `fetchpriority="high"` and no lazy loading
|
|
37
|
+
|
|
38
|
+
### JavaScript
|
|
39
|
+
- [ ] Bundle size under 200KB gzipped (initial load)
|
|
40
|
+
- [ ] Code splitting with dynamic `import()` for routes and heavy features
|
|
41
|
+
- [ ] Tree shaking enabled (verify dependency ships ESM and marks `sideEffects: false`)
|
|
42
|
+
- [ ] No blocking JavaScript in `<head>` (use `defer` or `async`)
|
|
43
|
+
- [ ] Heavy computation offloaded to Web Workers (if applicable)
|
|
44
|
+
- [ ] `React.memo()` on expensive components that re-render with same props
|
|
45
|
+
- [ ] `useMemo()` / `useCallback()` only where profiling shows benefit
|
|
46
|
+
|
|
47
|
+
### CSS
|
|
48
|
+
- [ ] Critical CSS inlined or preloaded
|
|
49
|
+
- [ ] No render-blocking CSS for non-critical styles
|
|
50
|
+
- [ ] No CSS-in-JS runtime cost in production (use extraction)
|
|
51
|
+
- [ ] Font display strategy set (`font-display: swap` or `optional`)
|
|
52
|
+
- [ ] System font stack considered before custom fonts
|
|
53
|
+
|
|
54
|
+
### Network
|
|
55
|
+
- [ ] Static assets cached with long `max-age` + content hashing
|
|
56
|
+
- [ ] API responses cached where appropriate (`Cache-Control`)
|
|
57
|
+
- [ ] HTTP/2 or HTTP/3 enabled
|
|
58
|
+
- [ ] Resources preconnected (`<link rel="preconnect">`) for known origins
|
|
59
|
+
- [ ] No unnecessary redirects
|
|
60
|
+
|
|
61
|
+
### Rendering
|
|
62
|
+
- [ ] No layout thrashing (forced synchronous layouts)
|
|
63
|
+
- [ ] Animations use `transform` and `opacity` (GPU-accelerated)
|
|
64
|
+
- [ ] Long lists use virtualization (e.g., `react-window`)
|
|
65
|
+
- [ ] No unnecessary full-page re-renders
|
|
66
|
+
|
|
67
|
+
## Backend Checklist
|
|
68
|
+
|
|
69
|
+
### Database
|
|
70
|
+
- [ ] No N+1 query patterns (use eager loading / joins)
|
|
71
|
+
- [ ] Queries have appropriate indexes
|
|
72
|
+
- [ ] List endpoints paginated (never `SELECT * FROM table`)
|
|
73
|
+
- [ ] Connection pooling configured
|
|
74
|
+
- [ ] Slow query logging enabled
|
|
75
|
+
|
|
76
|
+
### API
|
|
77
|
+
- [ ] Response times < 200ms (p95)
|
|
78
|
+
- [ ] No synchronous heavy computation in request handlers
|
|
79
|
+
- [ ] Bulk operations instead of loops of individual calls
|
|
80
|
+
- [ ] Response compression (gzip/brotli)
|
|
81
|
+
- [ ] Appropriate caching (in-memory, Redis, CDN)
|
|
82
|
+
|
|
83
|
+
### Infrastructure
|
|
84
|
+
- [ ] CDN for static assets
|
|
85
|
+
- [ ] Server located close to users (or edge deployment)
|
|
86
|
+
- [ ] Horizontal scaling configured (if needed)
|
|
87
|
+
- [ ] Health check endpoint for load balancer
|
|
88
|
+
|
|
89
|
+
## Measurement Commands
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Lighthouse CLI
|
|
93
|
+
npx lighthouse https://localhost:3000 --output json --output-path ./report.json
|
|
94
|
+
|
|
95
|
+
# Bundle analysis
|
|
96
|
+
npx webpack-bundle-analyzer stats.json
|
|
97
|
+
# or for Vite:
|
|
98
|
+
npx vite-bundle-visualizer
|
|
99
|
+
|
|
100
|
+
# Check bundle size
|
|
101
|
+
npx bundlesize
|
|
102
|
+
|
|
103
|
+
# Web Vitals in code
|
|
104
|
+
import { onLCP, onINP, onCLS } from 'web-vitals';
|
|
105
|
+
onLCP(console.log);
|
|
106
|
+
onINP(console.log);
|
|
107
|
+
onCLS(console.log);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Common Anti-Patterns
|
|
111
|
+
|
|
112
|
+
| Anti-Pattern | Impact | Fix |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| N+1 queries | Linear DB load growth | Use joins, includes, or batch loading |
|
|
115
|
+
| Unbounded queries | Memory exhaustion, timeouts | Always paginate, add LIMIT |
|
|
116
|
+
| Missing indexes | Slow reads as data grows | Add indexes for filtered/sorted columns |
|
|
117
|
+
| Layout thrashing | Jank, dropped frames | Batch DOM reads, then batch writes |
|
|
118
|
+
| Unoptimized images | Slow LCP, wasted bandwidth | Use WebP, responsive sizes, lazy load |
|
|
119
|
+
| Large bundles | Slow Time to Interactive | Code split, tree shake, audit deps |
|
|
120
|
+
| Blocking main thread | Poor INP, unresponsive UI | Use Web Workers, defer work |
|
|
121
|
+
| Memory leaks | Growing memory, eventual crash | Clean up listeners, intervals, refs |
|