buildwithjpegg 1.0.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.
Files changed (64) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/.codex/INSTALL.md +67 -0
  4. package/.opencode/INSTALL.md +118 -0
  5. package/.opencode/plugins/buildwithjpegg.js +95 -0
  6. package/LICENSE +24 -0
  7. package/README.md +134 -0
  8. package/RELEASE-NOTES.md +7 -0
  9. package/agents/code-reviewer.md +48 -0
  10. package/commands/evaluate.md +6 -0
  11. package/commands/run-build.md +6 -0
  12. package/commands/write-blueprint.md +6 -0
  13. package/hooks/hooks.json +16 -0
  14. package/hooks/run-hook.cmd +43 -0
  15. package/hooks/session-start.sh +46 -0
  16. package/lib/skills-core.js +208 -0
  17. package/package.json +39 -0
  18. package/rules/conventions.md +22 -0
  19. package/rules/git.md +18 -0
  20. package/rules/mcp-servers.md +28 -0
  21. package/rules/platform.md +10 -0
  22. package/rules/stack.md +29 -0
  23. package/rules/testing.md +18 -0
  24. package/rules/ui-ux.md +151 -0
  25. package/rules/workflow.md +48 -0
  26. package/skills/auto-release/SKILL.md +176 -0
  27. package/skills/blueprint/SKILL.md +116 -0
  28. package/skills/build/SKILL.md +84 -0
  29. package/skills/ci-loop/SKILL.md +98 -0
  30. package/skills/craft-skill/SKILL.md +655 -0
  31. package/skills/craft-skill/anthropic-best-practices.md +1150 -0
  32. package/skills/craft-skill/examples/CLAUDE_MD_TESTING.md +189 -0
  33. package/skills/craft-skill/graphviz-conventions.dot +172 -0
  34. package/skills/craft-skill/persuasion-principles.md +187 -0
  35. package/skills/craft-skill/render-graphs.js +168 -0
  36. package/skills/craft-skill/testing-skills-with-subagents.md +384 -0
  37. package/skills/delegate/SKILL.md +242 -0
  38. package/skills/delegate/code-quality-reviewer-prompt.md +20 -0
  39. package/skills/delegate/implementer-prompt.md +78 -0
  40. package/skills/delegate/spec-reviewer-prompt.md +61 -0
  41. package/skills/draft-prs/SKILL.md +132 -0
  42. package/skills/evaluate/SKILL.md +96 -0
  43. package/skills/fan-out/SKILL.md +180 -0
  44. package/skills/handle-review/SKILL.md +213 -0
  45. package/skills/onboard/SKILL.md +95 -0
  46. package/skills/pr-stack/SKILL.md +112 -0
  47. package/skills/pre-ship/SKILL.md +139 -0
  48. package/skills/root-cause/CREATION-LOG.md +119 -0
  49. package/skills/root-cause/SKILL.md +296 -0
  50. package/skills/root-cause/condition-based-waiting-example.ts +158 -0
  51. package/skills/root-cause/condition-based-waiting.md +115 -0
  52. package/skills/root-cause/defense-in-depth.md +122 -0
  53. package/skills/root-cause/find-polluter.sh +63 -0
  54. package/skills/root-cause/root-cause-tracing.md +169 -0
  55. package/skills/root-cause/test-academic.md +14 -0
  56. package/skills/root-cause/test-pressure-1.md +58 -0
  57. package/skills/root-cause/test-pressure-2.md +68 -0
  58. package/skills/root-cause/test-pressure-3.md +69 -0
  59. package/skills/seek-review/SKILL.md +105 -0
  60. package/skills/seek-review/code-reviewer.md +146 -0
  61. package/skills/test-first/SKILL.md +371 -0
  62. package/skills/test-first/testing-anti-patterns.md +299 -0
  63. package/skills/worktree/SKILL.md +218 -0
  64. package/skills/wrap-up/SKILL.md +200 -0
@@ -0,0 +1,208 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { execSync } from 'child_process';
4
+
5
+ /**
6
+ * Extract YAML frontmatter from a skill file.
7
+ * Current format:
8
+ * ---
9
+ * name: skill-name
10
+ * description: Use when [condition] - [what it does]
11
+ * ---
12
+ *
13
+ * @param {string} filePath - Path to SKILL.md file
14
+ * @returns {{name: string, description: string}}
15
+ */
16
+ function extractFrontmatter(filePath) {
17
+ try {
18
+ const content = fs.readFileSync(filePath, 'utf8');
19
+ const lines = content.split('\n');
20
+
21
+ let inFrontmatter = false;
22
+ let name = '';
23
+ let description = '';
24
+
25
+ for (const line of lines) {
26
+ if (line.trim() === '---') {
27
+ if (inFrontmatter) break;
28
+ inFrontmatter = true;
29
+ continue;
30
+ }
31
+
32
+ if (inFrontmatter) {
33
+ const match = line.match(/^(\w+):\s*(.*)$/);
34
+ if (match) {
35
+ const [, key, value] = match;
36
+ switch (key) {
37
+ case 'name':
38
+ name = value.trim();
39
+ break;
40
+ case 'description':
41
+ description = value.trim();
42
+ break;
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ return { name, description };
49
+ } catch (error) {
50
+ return { name: '', description: '' };
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Find all SKILL.md files in a directory recursively.
56
+ *
57
+ * @param {string} dir - Directory to search
58
+ * @param {string} sourceType - 'personal' or 'buildwithjpegg' for namespacing
59
+ * @param {number} maxDepth - Maximum recursion depth (default: 3)
60
+ * @returns {Array<{path: string, name: string, description: string, sourceType: string}>}
61
+ */
62
+ function findSkillsInDir(dir, sourceType, maxDepth = 3) {
63
+ const skills = [];
64
+
65
+ if (!fs.existsSync(dir)) return skills;
66
+
67
+ function recurse(currentDir, depth) {
68
+ if (depth > maxDepth) return;
69
+
70
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
71
+
72
+ for (const entry of entries) {
73
+ const fullPath = path.join(currentDir, entry.name);
74
+
75
+ if (entry.isDirectory()) {
76
+ // Check for SKILL.md in this directory
77
+ const skillFile = path.join(fullPath, 'SKILL.md');
78
+ if (fs.existsSync(skillFile)) {
79
+ const { name, description } = extractFrontmatter(skillFile);
80
+ skills.push({
81
+ path: fullPath,
82
+ skillFile: skillFile,
83
+ name: name || entry.name,
84
+ description: description || '',
85
+ sourceType: sourceType
86
+ });
87
+ }
88
+
89
+ // Recurse into subdirectories
90
+ recurse(fullPath, depth + 1);
91
+ }
92
+ }
93
+ }
94
+
95
+ recurse(dir, 0);
96
+ return skills;
97
+ }
98
+
99
+ /**
100
+ * Resolve a skill name to its file path, handling shadowing
101
+ * (personal skills override buildwithjpegg skills).
102
+ *
103
+ * @param {string} skillName - Name like "jpegg:evaluate" or "my-skill"
104
+ * @param {string} pluginDir - Path to buildwithjpegg skills directory
105
+ * @param {string} personalDir - Path to personal skills directory
106
+ * @returns {{skillFile: string, sourceType: string, skillPath: string} | null}
107
+ */
108
+ function resolveSkillPath(skillName, pluginDir, personalDir) {
109
+ // Strip jpegg: prefix if present
110
+ const forcePlugin = skillName.startsWith('jpegg:');
111
+ const actualSkillName = forcePlugin ? skillName.replace(/^jpegg:/, '') : skillName;
112
+
113
+ // Try personal skills first (unless explicitly jpegg:)
114
+ if (!forcePlugin && personalDir) {
115
+ const personalPath = path.join(personalDir, actualSkillName);
116
+ const personalSkillFile = path.join(personalPath, 'SKILL.md');
117
+ if (fs.existsSync(personalSkillFile)) {
118
+ return {
119
+ skillFile: personalSkillFile,
120
+ sourceType: 'personal',
121
+ skillPath: actualSkillName
122
+ };
123
+ }
124
+ }
125
+
126
+ // Try buildwithjpegg skills
127
+ if (pluginDir) {
128
+ const pluginPath = path.join(pluginDir, actualSkillName);
129
+ const pluginSkillFile = path.join(pluginPath, 'SKILL.md');
130
+ if (fs.existsSync(pluginSkillFile)) {
131
+ return {
132
+ skillFile: pluginSkillFile,
133
+ sourceType: 'buildwithjpegg',
134
+ skillPath: actualSkillName
135
+ };
136
+ }
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ /**
143
+ * Check if a git repository has updates available.
144
+ *
145
+ * @param {string} repoDir - Path to git repository
146
+ * @returns {boolean} - True if updates are available
147
+ */
148
+ function checkForUpdates(repoDir) {
149
+ try {
150
+ // Quick check with 3 second timeout to avoid delays if network is down
151
+ const output = execSync('git fetch origin && git status --porcelain=v1 --branch', {
152
+ cwd: repoDir,
153
+ timeout: 3000,
154
+ encoding: 'utf8',
155
+ stdio: 'pipe'
156
+ });
157
+
158
+ // Parse git status output to see if we're behind
159
+ const statusLines = output.split('\n');
160
+ for (const line of statusLines) {
161
+ if (line.startsWith('## ') && line.includes('[behind ')) {
162
+ return true; // We're behind remote
163
+ }
164
+ }
165
+ return false; // Up to date
166
+ } catch (error) {
167
+ // Network down, git error, timeout, etc. - don't block bootstrap
168
+ return false;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Strip YAML frontmatter from skill content, returning just the content.
174
+ *
175
+ * @param {string} content - Full content including frontmatter
176
+ * @returns {string} - Content without frontmatter
177
+ */
178
+ function stripFrontmatter(content) {
179
+ const lines = content.split('\n');
180
+ let inFrontmatter = false;
181
+ let frontmatterEnded = false;
182
+ const contentLines = [];
183
+
184
+ for (const line of lines) {
185
+ if (line.trim() === '---') {
186
+ if (inFrontmatter) {
187
+ frontmatterEnded = true;
188
+ continue;
189
+ }
190
+ inFrontmatter = true;
191
+ continue;
192
+ }
193
+
194
+ if (frontmatterEnded || !inFrontmatter) {
195
+ contentLines.push(line);
196
+ }
197
+ }
198
+
199
+ return contentLines.join('\n').trim();
200
+ }
201
+
202
+ export {
203
+ extractFrontmatter,
204
+ findSkillsInDir,
205
+ resolveSkillPath,
206
+ checkForUpdates,
207
+ stripFrontmatter
208
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "buildwithjpegg",
3
+ "version": "1.0.0",
4
+ "description": "Development workflow skills for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
5
+ "author": "jpegg",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/jpeggdev/buildwithjpegg.git"
10
+ },
11
+ "homepage": "https://github.com/jpeggdev/buildwithjpegg",
12
+ "bugs": {
13
+ "url": "https://github.com/jpeggdev/buildwithjpegg/issues"
14
+ },
15
+ "keywords": [
16
+ "claude-code",
17
+ "skills",
18
+ "tdd",
19
+ "debugging",
20
+ "collaboration",
21
+ "best-practices",
22
+ "workflows",
23
+ "ai-agent"
24
+ ],
25
+ "files": [
26
+ "agents/",
27
+ "commands/",
28
+ "hooks/",
29
+ "lib/",
30
+ "rules/",
31
+ "skills/",
32
+ ".claude-plugin/",
33
+ ".codex/",
34
+ ".opencode/",
35
+ "LICENSE",
36
+ "README.md",
37
+ "RELEASE-NOTES.md"
38
+ ]
39
+ }
@@ -0,0 +1,22 @@
1
+ # Project Conventions
2
+
3
+ ## Package Manager
4
+
5
+ Use `pnpm` for all package management commands (not npm or yarn).
6
+
7
+ Exception: Global install instructions for end users should use `npm install -g` since it's universal.
8
+
9
+ ## Dependencies
10
+
11
+ Always check for the latest npm version when adding dependencies. Use `pnpm add <package>` (without version) to get the latest, or verify with `npm view <package> version` first.
12
+
13
+ ## No Emojis
14
+
15
+ Do not use emojis anywhere (code, comments, output, docs).
16
+
17
+ ## Docs Updates
18
+
19
+ When a change affects how humans or agents use a codebase (new/changed/removed commands, flags, behavior, or config), update all of these:
20
+
21
+ 1. `README.md` -- user-facing documentation
22
+ 2. `CLAUDE.md` -- agent-facing documentation
package/rules/git.md ADDED
@@ -0,0 +1,18 @@
1
+ # Git Conventions
2
+
3
+ ## Commits
4
+ - Conventional commits: `type(scope): message`
5
+ - Types: feat, fix, refactor, test, docs, chore, ci
6
+ - Message in imperative mood, lowercase, no period
7
+ - Keep commits atomic -- one logical change per commit
8
+
9
+ ## Branches
10
+ - `main` is production
11
+ - Feature branches: `feat/short-description`
12
+ - Fix branches: `fix/short-description`
13
+ - Never force-push to main
14
+
15
+ ## PRs
16
+ - Title matches conventional commit format
17
+ - Body: summary bullets + test plan
18
+ - Squash merge to main
@@ -0,0 +1,28 @@
1
+ # MCP Server Configuration
2
+
3
+ On Windows, MCP servers using npx must go through `cmd /c` since MINGW64 bash cannot run npx directly as a stdio command.
4
+
5
+ **Pattern:**
6
+ ```json
7
+ "<server-name>": {
8
+ "type": "stdio",
9
+ "command": "cmd",
10
+ "args": ["/c", "npx", "-y", "<package>@latest"],
11
+ "env": {}
12
+ }
13
+ ```
14
+
15
+ **Example (Context7):**
16
+ ```json
17
+ "context7": {
18
+ "type": "stdio",
19
+ "command": "cmd",
20
+ "args": ["/c", "npx", "-y", "@upstash/context7-mcp@latest"],
21
+ "env": {}
22
+ }
23
+ ```
24
+
25
+ Key points:
26
+ - Always use `"command": "cmd"` with `"/c"` as the first arg.
27
+ - Use `-y` with npx to auto-confirm install prompts.
28
+ - The `env` object can pass environment variables the server needs (API keys, etc.).
@@ -0,0 +1,10 @@
1
+ # Platform specific
2
+
3
+ On Windows (MINGW64/Git Bash):
4
+
5
+ - Use forward slashes in paths for cross-platform compatibility.
6
+ - Shell commands run in bash (MINGW64), not cmd.exe or PowerShell.
7
+ - Avoid `NUL` -- use `/dev/null` instead.
8
+ - Quote paths containing spaces with double quotes.
9
+ - The `/c` flag for cmd.exe must not be converted to `C:/`.
10
+ - Always test MCP server registrations and hook commands for Windows compatibility.
package/rules/stack.md ADDED
@@ -0,0 +1,29 @@
1
+ # Default Tech Stack
2
+
3
+ When starting a new project or the user hasn't specified preferences:
4
+
5
+ ## Desktop Applications (default for this workspace)
6
+ - **Language**: Python for everything — UI and backend in one language
7
+ - **UI framework**: Flet (Python + Flutter renderer) — `flet build` produces native binaries
8
+ - **Embedded server**: aiohttp or FastAPI for the local HTTP server
9
+ - **Database**: SQLite via `aiosqlite` for async access, raw `sqlite3` for sync
10
+ - **Validation**: Pydantic
11
+ - **Templates**: Jinja2
12
+ - **Testing**: pytest + pytest-asyncio
13
+ - **MCP integration**: Official Anthropic MCP Python SDK
14
+ - **Packaging**: `flet build windows/macos/linux` for native binaries
15
+
16
+ ## Web Applications
17
+ - **Language**: TypeScript for everything
18
+ - **Frontend**: React + Next.js (App Router), Tailwind CSS, Zustand or TanStack Query
19
+ - **Backend**: Hono (lightweight) or Next.js API routes, PostgreSQL + Drizzle ORM, Zod
20
+ - **Testing**: Vitest + Testing Library + Playwright
21
+
22
+ ## Infra (both)
23
+ - GitHub Actions for CI/CD
24
+ - Docker + Docker Compose for services that need it
25
+
26
+ ## AI/ML
27
+ - Anthropic SDK (Claude) as primary LLM — Python or TypeScript depending on project type
28
+
29
+ Always ask before deviating from these defaults on new projects.
@@ -0,0 +1,18 @@
1
+ # Testing Conventions
2
+
3
+ ## Philosophy
4
+ - Test behavior, not implementation
5
+ - Integration tests over unit tests for most code
6
+ - Unit tests for pure logic and edge cases
7
+ - E2E tests for critical user flows only
8
+
9
+ ## Approach
10
+ - New features: write tests alongside implementation
11
+ - Bug fixes: write a failing test first, then fix
12
+ - Refactors: ensure tests pass before and after
13
+
14
+ ## Tools (defaults)
15
+ - Vitest for unit/integration
16
+ - Playwright for E2E
17
+ - Testing Library for component tests
18
+ - pytest for Python projects
package/rules/ui-ux.md ADDED
@@ -0,0 +1,151 @@
1
+ # UI/UX Guidelines
2
+
3
+ Concise rules for building accessible, fast, delightful UIs. Use MUST/SHOULD/NEVER to guide decisions.
4
+
5
+ ## Interactions
6
+
7
+ ### Keyboard
8
+
9
+ - MUST: Full keyboard support per [WAI-ARIA APG](https://www.w3.org/WAI/ARIA/apg/patterns/)
10
+ - MUST: Visible focus rings (`:focus-visible`; group with `:focus-within`)
11
+ - MUST: Manage focus (trap, move, return) per APG patterns
12
+ - NEVER: `outline: none` without visible focus replacement
13
+
14
+ ### Targets & Input
15
+
16
+ - MUST: Hit target >=24px (mobile >=44px); if visual <24px, expand hit area
17
+ - MUST: Mobile `<input>` font-size >=16px to prevent iOS zoom
18
+ - NEVER: Disable browser zoom (`user-scalable=no`, `maximum-scale=1`)
19
+ - MUST: `touch-action: manipulation` to prevent double-tap zoom
20
+ - SHOULD: Set `-webkit-tap-highlight-color` to match design
21
+
22
+ ### Forms
23
+
24
+ - MUST: Hydration-safe inputs (no lost focus/value)
25
+ - NEVER: Block paste in `<input>`/`<textarea>`
26
+ - MUST: Loading buttons show spinner and keep original label
27
+ - MUST: Enter submits focused input; in `<textarea>`, Cmd/Ctrl+Enter submits
28
+ - MUST: Keep submit enabled until request starts; then disable with spinner
29
+ - MUST: Accept free text, validate after--don't block typing
30
+ - MUST: Allow incomplete form submission to surface validation
31
+ - MUST: Errors inline next to fields; on submit, focus first error
32
+ - MUST: `autocomplete` + meaningful `name`; correct `type` and `inputmode`
33
+ - SHOULD: Disable spellcheck for emails/codes/usernames
34
+ - SHOULD: Placeholders end with `...` and show example pattern
35
+ - MUST: Warn on unsaved changes before navigation
36
+ - MUST: Compatible with password managers & 2FA; allow pasting codes
37
+ - MUST: Trim values to handle text expansion trailing spaces
38
+ - MUST: No dead zones on checkboxes/radios; label+control share one hit target
39
+
40
+ ### State & Navigation
41
+
42
+ - MUST: URL reflects state (deep-link filters/tabs/pagination/expanded panels)
43
+ - MUST: Back/Forward restores scroll position
44
+ - MUST: Links use `<a>`/`<Link>` for navigation (support Cmd/Ctrl/middle-click)
45
+ - NEVER: Use `<div onClick>` for navigation
46
+
47
+ ### Feedback
48
+
49
+ - SHOULD: Optimistic UI; reconcile on response; on failure rollback or offer Undo
50
+ - MUST: Confirm destructive actions or provide Undo window
51
+ - MUST: Use polite `aria-live` for toasts/inline validation
52
+ - SHOULD: Ellipsis (`...`) for options opening follow-ups ("Rename...") and loading states ("Loading...")
53
+
54
+ ### Touch & Drag
55
+
56
+ - MUST: Generous targets, clear affordances; avoid finicky interactions
57
+ - MUST: Delay first tooltip; subsequent peers instant
58
+ - MUST: `overscroll-behavior: contain` in modals/drawers
59
+ - MUST: During drag, disable text selection and set `inert` on dragged elements
60
+ - MUST: If it looks clickable, it must be clickable
61
+
62
+ ### Autofocus
63
+
64
+ - SHOULD: Autofocus on desktop with single primary input; rarely on mobile
65
+
66
+ ## Animation
67
+
68
+ - MUST: Honor `prefers-reduced-motion` (provide reduced variant or disable)
69
+ - SHOULD: Prefer CSS > Web Animations API > JS libraries
70
+ - MUST: Animate compositor-friendly props (`transform`, `opacity`) only
71
+ - NEVER: Animate layout props (`top`, `left`, `width`, `height`)
72
+ - NEVER: `transition: all`--list properties explicitly
73
+ - SHOULD: Animate only to clarify cause/effect or add deliberate delight
74
+ - SHOULD: Choose easing to match the change (size/distance/trigger)
75
+ - MUST: Animations interruptible and input-driven (no autoplay)
76
+ - MUST: Correct `transform-origin` (motion starts where it "physically" should)
77
+ - MUST: SVG transforms on `<g>` wrapper with `transform-box: fill-box`
78
+
79
+ ## Layout
80
+
81
+ - SHOULD: Optical alignment; adjust +/-1px when perception beats geometry
82
+ - MUST: Deliberate alignment to grid/baseline/edges--no accidental placement
83
+ - SHOULD: Balance icon/text lockups (weight/size/spacing/color)
84
+ - MUST: Verify mobile, laptop, ultra-wide (simulate ultra-wide at 50% zoom)
85
+ - MUST: Respect safe areas (`env(safe-area-inset-*)`)
86
+ - MUST: Avoid unwanted scrollbars; fix overflows
87
+ - SHOULD: Flex/grid over JS measurement for layout
88
+
89
+ ## Content & Accessibility
90
+
91
+ - SHOULD: Inline help first; tooltips last resort
92
+ - MUST: Skeletons mirror final content to avoid layout shift
93
+ - MUST: `<title>` matches current context
94
+ - MUST: No dead ends; always offer next step/recovery
95
+ - MUST: Design empty/sparse/dense/error states
96
+ - SHOULD: Curly quotes; avoid widows/orphans (`text-wrap: balance`)
97
+ - MUST: `font-variant-numeric: tabular-nums` for number comparisons
98
+ - MUST: Redundant status cues (not color-only); icons have text labels
99
+ - MUST: Accessible names exist even when visuals omit labels
100
+ - MUST: Use `...` character (not `...` three dots)
101
+ - MUST: `scroll-margin-top` on headings; "Skip to content" link; hierarchical `<h1>`-`<h6>`
102
+ - MUST: Resilient to user-generated content (short/avg/very long)
103
+ - MUST: Locale-aware dates/times/numbers (`Intl.DateTimeFormat`, `Intl.NumberFormat`)
104
+ - MUST: Accurate `aria-label`; decorative elements `aria-hidden`
105
+ - MUST: Icon-only buttons have descriptive `aria-label`
106
+ - MUST: Prefer native semantics (`button`, `a`, `label`, `table`) before ARIA
107
+ - MUST: Non-breaking spaces: `10&nbsp;MB`, `Cmd&nbsp;K`, brand names
108
+
109
+ ## Content Handling
110
+
111
+ - MUST: Text containers handle long content (`truncate`, `line-clamp-*`, `break-words`)
112
+ - MUST: Flex children need `min-w-0` to allow truncation
113
+ - MUST: Handle empty states--no broken UI for empty strings/arrays
114
+
115
+ ## Performance
116
+
117
+ - SHOULD: Test iOS Low Power Mode and macOS Safari
118
+ - MUST: Measure reliably (disable extensions that skew runtime)
119
+ - MUST: Track and minimize re-renders (React DevTools/React Scan)
120
+ - MUST: Profile with CPU/network throttling
121
+ - MUST: Batch layout reads/writes; avoid reflows/repaints
122
+ - MUST: Mutations (`POST`/`PATCH`/`DELETE`) target <500ms
123
+ - SHOULD: Prefer uncontrolled inputs; controlled inputs cheap per keystroke
124
+ - MUST: Virtualize large lists (>50 items)
125
+ - MUST: Preload above-fold images; lazy-load the rest
126
+ - MUST: Prevent CLS (explicit image dimensions)
127
+ - SHOULD: `<link rel="preconnect">` for CDN domains
128
+ - SHOULD: Critical fonts: `<link rel="preload" as="font">` with `font-display: swap`
129
+
130
+ ## Dark Mode & Theming
131
+
132
+ - MUST: `color-scheme: dark` on `<html>` for dark themes
133
+ - SHOULD: `<meta name="theme-color">` matches page background
134
+ - MUST: Native `<select>`: explicit `background-color` and `color` (Windows fix)
135
+
136
+ ## Hydration
137
+
138
+ - MUST: Inputs with `value` need `onChange` (or use `defaultValue`)
139
+ - SHOULD: Guard date/time rendering against hydration mismatch
140
+
141
+ ## Design
142
+
143
+ - SHOULD: Layered shadows (ambient + direct)
144
+ - SHOULD: Crisp edges via semi-transparent borders + shadows
145
+ - SHOULD: Nested radii: child <= parent; concentric
146
+ - SHOULD: Hue consistency: tint borders/shadows/text toward bg hue
147
+ - MUST: Accessible charts (color-blind-friendly palettes)
148
+ - MUST: Meet contrast--prefer [APCA](https://apcacontrast.com/) over WCAG 2
149
+ - MUST: Increase contrast on `:hover`/`:active`/`:focus`
150
+ - SHOULD: Match browser UI to bg
151
+ - SHOULD: Avoid dark color gradient banding (use background images when needed)
@@ -0,0 +1,48 @@
1
+ # Development Workflow
2
+
3
+ ## Skill Invocation Sequence
4
+
5
+ For any new feature or fix, skills fire in this order — do not skip steps:
6
+
7
+ 1. **`jpegg:evaluate`** — always first. Explores intent, asks clarifying questions, surfaces unstated requirements. Fires when a feature or fix is described.
8
+ 2. **`jpegg:blueprint`** — after evaluation. Produces the plan document. User reviews and approves before any code is written.
9
+ 3. **`jpegg:build`** — after plan approval. Works through the plan with PR-level checkpoints. One PR per logical chunk.
10
+
11
+ Within each implementation chunk:
12
+
13
+ 4. **`jpegg:worktree`** — before writing code. Creates an isolated workspace for the branch.
14
+ 5. **`jpegg:test-first`** — before writing implementation code. Tests first, always.
15
+ 6. **`jpegg:pre-ship`** — before claiming done. Runs verification commands, reads actual output.
16
+ 7. **`jpegg:seek-review`** — before opening the PR. Internal review pass first.
17
+ 8. **`jpegg:draft-prs`** — when creating a PR in a stack. Downstream PRs are draft; promote on base merge.
18
+ 9. **`jpegg:ci-loop`** — after PR creation. Monitors CI and fixes failures before reporting back.
19
+ 10. **`jpegg:pr-stack`** — after every PR creation or merge. Updates `.claude/stack.json`.
20
+
21
+ When receiving feedback on an open PR:
22
+
23
+ 11. **`jpegg:handle-review`** — fires before implementing any review comment. Evaluates feedback before acting.
24
+
25
+ ## Stacked PR Workflow
26
+
27
+ - Each branch is created from the tip of the previous branch, not from `main`
28
+ - Each PR targets the previous branch as its base, not `main`
29
+ - Downstream PRs are always created as drafts until their base merges
30
+ - When a base merges: rebase the next branch, update its PR base to `main`, promote from draft to ready
31
+ - Session state is tracked in `.claude/stack.json` — read this at session start if it exists
32
+
33
+ ## Resuming After a Break
34
+
35
+ 1. Read `.claude/stack.json` to understand current stack state
36
+ 2. Read the plan document referenced in the stack file
37
+ 3. Check open PRs with `gh pr list`
38
+ 4. Continue from the current branch
39
+
40
+ ## After a PR is Merged
41
+
42
+ When the user reports a PR is merged:
43
+ 1. `git fetch`
44
+ 2. Rebase the next branch in the stack onto `main`
45
+ 3. `git push --force-with-lease --force-if-includes`
46
+ 4. Update the PR base on GitHub: `gh pr edit <number> --base main`
47
+ 5. Promote from draft: `gh pr ready <number>`
48
+ 6. Update `.claude/stack.json`