@codyswann/lisa 2.189.22 → 2.190.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 (77) hide show
  1. package/npm-package/package-lisa/package.lisa.json +1 -1
  2. package/package.json +1 -1
  3. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  4. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  5. package/plugins/lisa-agy/plugin.json +1 -1
  6. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  9. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  10. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  15. package/plugins/lisa-expo-agy/plugin.json +1 -1
  16. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  21. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  26. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  31. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-phaser/agents/art-director.md +32 -2
  36. package/plugins/lisa-phaser/agents/audio-director.md +3 -1
  37. package/plugins/lisa-phaser/rules/phaser.md +12 -5
  38. package/plugins/lisa-phaser/skills/phaser-asset-pipeline/SKILL.md +94 -20
  39. package/plugins/lisa-phaser/skills/phaser-asset-sourcing/SKILL.md +151 -0
  40. package/plugins/lisa-phaser/skills/phaser-asset-sourcing/agents/openai.yaml +4 -0
  41. package/plugins/lisa-phaser-agy/agents/art-director.md +32 -2
  42. package/plugins/lisa-phaser-agy/agents/audio-director.md +3 -1
  43. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  44. package/plugins/lisa-phaser-agy/skills/phaser-asset-pipeline/SKILL.md +94 -20
  45. package/plugins/lisa-phaser-agy/skills/phaser-asset-sourcing/SKILL.md +151 -0
  46. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser-copilot/agents/art-director.agent.md +32 -2
  48. package/plugins/lisa-phaser-copilot/agents/audio-director.agent.md +3 -1
  49. package/plugins/lisa-phaser-copilot/rules/phaser.md +12 -5
  50. package/plugins/lisa-phaser-copilot/skills/phaser-asset-pipeline/SKILL.md +94 -20
  51. package/plugins/lisa-phaser-copilot/skills/phaser-asset-sourcing/SKILL.md +151 -0
  52. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-phaser-cursor/agents/art-director.md +32 -2
  54. package/plugins/lisa-phaser-cursor/agents/audio-director.md +3 -1
  55. package/plugins/lisa-phaser-cursor/rules/phaser.mdc +12 -5
  56. package/plugins/lisa-phaser-cursor/skills/phaser-asset-pipeline/SKILL.md +94 -20
  57. package/plugins/lisa-phaser-cursor/skills/phaser-asset-sourcing/SKILL.md +151 -0
  58. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-rails-agy/plugin.json +1 -1
  61. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  66. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  71. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/src/phaser/agents/art-director.md +32 -2
  74. package/plugins/src/phaser/agents/audio-director.md +3 -1
  75. package/plugins/src/phaser/rules/phaser.md +12 -5
  76. package/plugins/src/phaser/skills/phaser-asset-pipeline/SKILL.md +94 -20
  77. package/plugins/src/phaser/skills/phaser-asset-sourcing/SKILL.md +151 -0
@@ -3,7 +3,7 @@
3
3
  "scripts": {
4
4
  "prepublishOnly": "$npm_execpath run build",
5
5
  "setup:deploy-key": "setup-deploy-key",
6
- "prepare": "$npm_execpath run build || husky install || true"
6
+ "prepare": "$npm_execpath run build && husky install || true"
7
7
  },
8
8
  "devDependencies": {
9
9
  "vite": "^8.0.16",
package/package.json CHANGED
@@ -95,7 +95,7 @@
95
95
  "ws": ">=8.20.1"
96
96
  },
97
97
  "name": "@codyswann/lisa",
98
- "version": "2.189.22",
98
+ "version": "2.190.0",
99
99
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
100
100
  "main": "dist/index.js",
101
101
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.189.22",
3
+ "version": "2.190.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,12 +1,35 @@
1
1
  ---
2
2
  name: art-director
3
- description: Art director persona agent (opt-in). Critiques visual cohesion, style-guide adherence, readability, and the art-pipeline implications for a Phaser game against the project's art direction. Reviews visual design, not engine code.
3
+ description: Art director persona agent (opt-in). Critiques visual cohesion, style-guide adherence, readability, and the art-pipeline implications for a Phaser game against the project's art direction. Demands real rendered art (not placeholders) and reviews rendered screenshots of the live game. Reviews visual design, not engine code.
4
+ skills:
5
+ - phaser-asset-sourcing
4
6
  ---
5
7
 
6
8
  # Art Director Persona Agent
7
9
 
8
10
  You are the game's art director. You guard visual cohesion and make sure everything on screen reads and belongs together. You are a **critic**; you do not produce final art or write code.
9
11
 
12
+ ## Review the RENDERED game, not the code
13
+
14
+ Your review is worthless if you only read source. **Look at the actual pixels**:
15
+ run the game (`bun run dev`) and capture rendered screenshots of each scene at
16
+ gameplay resolution (a Playwright screenshot, or ask for them). Critique what is
17
+ on screen, not what the code claims will be on screen. If you cannot see rendered
18
+ output, say so and treat the review as blocked — do not sign off from code alone.
19
+
20
+ ## Placeholder art is a BLOCKING finding
21
+
22
+ Programmer-art placeholders — `generateTexture` rectangles, solid-color fills,
23
+ untextured shapes standing in for characters/props/UI — are a **top-severity,
24
+ blocking** result, not a stylistic nit. A scene rendered in placeholders is not
25
+ "on style," it is **unfinished**. Flag every placeholder-only scene explicitly,
26
+ point the team at **`phaser-asset-sourcing`** (route real, properly-licensed art
27
+ through the pipeline), and require a linked art-debt issue for any placeholder that must
28
+ ship temporarily. Never accept "zero binary assets / zero licensing risk" as a
29
+ virtue — that framing is how the game ended up as wireframes. Characters must
30
+ render with a **playing idle/walk animation**; a frozen single frame is a
31
+ placeholder smell, flag it.
32
+
10
33
  ## Source of Truth — the style guide is binding
11
34
 
12
35
  - `wiki/design/art-direction.md` — the visual style guide: palette, shape language, lighting, scale, mood. Treat it as binding.
@@ -31,7 +54,12 @@ If `art-direction.md` is absent, critique against general visual-cohesion and re
31
54
  ## Art Direction Review
32
55
 
33
56
  ### Verdict
34
- [ON STYLE / NEEDS REVISION / OFF MODEL] — one sentence
57
+ [ON STYLE / NEEDS REVISION / OFF MODEL / PLACEHOLDER-BLOCKED] — one sentence
58
+ (use PLACEHOLDER-BLOCKED whenever any reviewed scene renders in placeholder art)
59
+
60
+ ### Rendered evidence
61
+ - [screenshots reviewed, per scene — or "BLOCKED: no rendered output available"]
62
+ - Placeholder/`generateTexture` art present? [list scenes] → art-debt issue linked? [#NNN / NO]
35
63
 
36
64
  ### Style-guide check
37
65
  - Palette: ... | Shape language: ... | Lighting/mood: ... | Scale/proportion: ...
@@ -53,6 +81,8 @@ If `art-direction.md` is absent, critique against general visual-cohesion and re
53
81
 
54
82
  ## Rules
55
83
 
84
+ - Review rendered screenshots of the live game, not source code; block the review if you cannot see rendered output.
85
+ - Treat placeholder-only / `generateTexture` art as a top-severity blocking finding; point at `phaser-asset-sourcing` and require a linked art-debt issue for anything shipping temporarily.
56
86
  - Hold every asset to the documented style guide; cite the guideline you're applying.
57
87
  - Judge cohesion and readability first — a beautiful asset that doesn't belong is a failure.
58
88
  - Always check the asset *in context* (next to existing art, at gameplay scale), not in isolation.
@@ -33,7 +33,8 @@ If `audio-direction.md` is absent, critique against general game-audio principle
33
33
  ## Audio Direction Review
34
34
 
35
35
  ### Verdict
36
- [ON STYLE / NEEDS WORK / SILENT GAPS] — one sentence
36
+ [ON STYLE / NEEDS WORK / SILENT GAPS / PLACEHOLDER-BLOCKED] — one sentence
37
+ (use PLACEHOLDER-BLOCKED when the game ships beeps/temp audio with no tracked debt)
37
38
 
38
39
  ### Audio identity check
39
40
  - Musical fit: ... | SFX language: ... | Mix priorities: ...
@@ -57,6 +58,7 @@ If `audio-direction.md` is absent, critique against general game-audio principle
57
58
  ## Rules
58
59
 
59
60
  - Map feedback coverage first — every meaningful action should be audible; flag silent state changes at top severity.
61
+ - Treat temp/placeholder audio (synth beeps, un-sourced SFX) as tracked debt: acceptable only with a linked audio-debt issue, never as the finished mix; real audio must be sourced (CC0 or human-approved) and run through the audiosprite pipeline. Never frame "no audio assets" as zero-risk best practice.
60
62
  - Judge the mix by whether critical cues survive over music and ambience.
61
63
  - Tie issues to feedback clarity, mood, or fatigue, citing the audio-direction guideline you're applying.
62
64
  - Respect the baked audio pipeline and the mobile audio-unlock-on-first-gesture constraint; flag anything that breaks them.
@@ -55,11 +55,18 @@ tools for scenes/assets/tilemaps) is defined for this project type but
55
55
  and create no objects/tweens/timers inside `update()`**; atlas everything; use
56
56
  `BitmapText` for high-churn text; use `SpriteGPULayer` / `TilemapGPULayer` for
57
57
  mass rendering. See the official `game-object-components` skill.
58
- 7. **Baked-in asset pipeline.** Raw art in `assets/src` is packed at build time
59
- (free-tex-packer-core atlases, audiosprite, BMFont) into `public/assets`, and
60
- a codegen step emits typed keys to `src/assets.ts` so a missing or renamed key
61
- is a **compile error**. No raw string asset / scene / event keys anywhere.
62
- See `phaser-asset-pipeline`.
58
+ 7. **Baked-in asset pipeline with REAL art.** Raw art in `assets/src` is packed
59
+ at build time (free-tex-packer-core atlases, audiosprite, BMFont) into
60
+ `public/assets`, and a codegen step emits typed keys to `src/assets.ts` so a
61
+ missing or renamed key is a **compile error**. No raw string asset / scene /
62
+ event keys anywhere. The art itself must be **real and licensed** — sourced
63
+ per `phaser-asset-sourcing` (CC0-or-equivalent only, with an exact license
64
+ quote + evidence URL in `assets/LICENSES.md` and committed provenance).
65
+ Procedural `generateTexture` placeholders are **tracked art debt**: a scene
66
+ may ship them **only** with a linked art-debt issue, never as the finished
67
+ state and never described as "zero-asset best practice." Characters ship real
68
+ idle+walk animation, not static frames. See `phaser-asset-sourcing` and
69
+ `phaser-asset-pipeline`.
63
70
  8. **PWA on by default** via `vite-plugin-pwa` (`registerType: "autoUpdate"`,
64
71
  `globPatterns` extended to cover game assets). See `phaser-build-deploy`.
65
72
  9. **Cross-cutting services are scaffolded** in `src/services/**`: SoundService
@@ -1,27 +1,42 @@
1
1
  ---
2
2
  name: phaser-asset-pipeline
3
- description: This skill should be used when setting up or changing the build-time asset pipeline of a Phaser 4 game — packing raw art in assets/src into public/assets (free-tex-packer-core texture atlases, audiosprite audio sprites, BMFont bitmap fonts) and the codegen step that emits typed keys to src/assets.ts so a missing or renamed key is a compile error. Use it when adding source art, wiring packing into the build, regenerating typed keys, or eliminating raw string asset keys. Pairs with the official loading-assets skill, phaser-build-deploy, and phaser-services.
3
+ description: This skill should be used when setting up or changing the build-time asset pipeline of a Phaser 4 game — packing raw art in assets/src into public/assets (free-tex-packer-core texture atlases, audiosprite audio sprites, BMFont bitmap fonts) and the codegen step that emits typed keys to src/assets.ts so a missing or renamed key is a compile error. Use it when adding source art, wiring packing into the build, regenerating typed keys, or eliminating raw string asset keys. Assumes the raw art already exists — get it first via phaser-asset-sourcing (CC0 sourcing + license gate). Pairs with phaser-asset-sourcing, the official loading-assets skill, phaser-build-deploy, and phaser-services.
4
4
  ---
5
5
 
6
6
  # Phaser 4 Asset Pipeline
7
7
 
8
+ ## Where the raw art comes from (read this first)
9
+
10
+ This skill **packs** raw art; it does not conjure it. The inputs under
11
+ `assets/src/**` are **real, licensed source art you sourced first** — they do not
12
+ magically pre-exist, and they are not procedural `generateTexture` rectangles.
13
+ Before you can pack anything, use **[[phaser-asset-sourcing]]**: it covers where
14
+ game art comes from (curated CC0 packs like Kenney and Ninja Adventure → deriving
15
+ CC0 art → asking the human), the strict CC0-or-equivalent license gate with
16
+ `assets/LICENSES.md` evidence, provenance via a committed ingest script, and why
17
+ `generateTexture` placeholders are tracked art debt rather than a finished state.
18
+ The rule of thumb: **source → `assets/src` → pack (this skill) → typed keys.**
19
+
8
20
  ## Overview
9
21
 
10
- Runtime assets are **generated, not hand-placed**. Raw source art lives in
11
- `assets/src/**` (PNGs, audio, font sources); a build step packs it into
12
- `public/assets/**` (atlases, audio sprites, bitmap fonts) and **codegens typed
13
- key constants into `src/assets.ts`**. The payoff: a missing or renamed asset is a
14
- **compile error**, not a green square or silent-missing audio at runtime. No raw
15
- string asset / scene / event keys anywhere they all come from the generated
16
- module (the official `loading-assets` skill covers loading those keys at runtime).
22
+ The pipeline turns committed source art into runtime form it is a build step,
23
+ not a substitute for having art. Raw source art lives in `assets/src/**` (PNGs,
24
+ audio, font sources); a build step packs it into `public/assets/**` (atlases,
25
+ audio sprites, bitmap fonts) and **codegens typed key constants into
26
+ `src/assets.ts`**. The payoff: a missing or renamed asset is a **compile error**,
27
+ not a green square or silent-missing audio at runtime. No raw string asset /
28
+ scene / event keys anywhere they all come from the generated module (the
29
+ official `loading-assets` skill covers loading those keys at runtime).
17
30
 
18
31
  ## Layout
19
32
 
20
33
  | Path | Role |
21
34
  | --- | --- |
22
- | `assets/src/sprites/**` | Raw individual PNGs — the art source of truth |
35
+ | `assets/src/sprites/**` | Raw individual PNGs — the art source of truth (sourced per [[phaser-asset-sourcing]]) |
23
36
  | `assets/src/audio/**` | Raw audio clips (wav/ogg sources) |
24
37
  | `assets/src/fonts/**` | Font sources for BMFont generation |
38
+ | `assets/LICENSES.md` | License evidence per source pack (CC0 quote + evidence URL) — see [[phaser-asset-sourcing]] |
39
+ | `scripts/ingest-assets.mjs` | The provenance record — how each frame was carved from its source pack into `assets/src` (re-runnable) |
25
40
  | `public/assets/**` | **Generated** packed output (atlases, audiosprites, fonts, `pack.json`) — git-ignored or committed as a build artifact |
26
41
  | `src/assets.ts` | **Generated** typed key constants — never edited by hand |
27
42
  | `scripts/pack-assets.mjs` | The pipeline driver (runs the three packers + codegen) |
@@ -43,16 +58,34 @@ const images = await Promise.all(
43
58
  })));
44
59
 
45
60
  const files = await packAsync(images, {
46
- textureName: "game", width: 2048, height: 2048,
47
- padding: 2, allowRotation: false, allowTrim: true,
48
- exporter: "JsonHash", removeFileExtension: true,
61
+ textureName: "game", width: 4096, height: 4096,
62
+ packer: "MaxRectsPacker", packerMethod: "Smart", // see gotcha below
63
+ padding: 2, extrude: 1, // 1px extrude kills bleeding at integer scales
64
+ allowRotation: false, allowTrim: false, // pixel-art-safe (see below)
65
+ detectIdentical: true,
66
+ exporter: "JsonHash", removeFileExtension: true, prependFolderName: true,
49
67
  });
50
68
  for (const f of files) await writeFile(`public/assets/atlases/${f.name}`, f.buffer ?? f.content);
51
69
  ```
52
70
 
53
71
  The frame names in the manifest become the atlas frame keys you reference as
54
72
  generated constants. Atlas everything that renders together — loose images break
55
- sprite batching (the official `loading-assets` skill).
73
+ sprite batching (the official `loading-assets` skill). Full-screen backdrops and
74
+ parallax layers are the exception: copy them verbatim into `public/assets/images`
75
+ and `load.image` them — they would bloat an atlas. Even this exception keeps the
76
+ no-raw-string promise: the codegen step emits one generated constant per copied
77
+ image (the `Img` record below), and the loader derives the file path from the
78
+ key in exactly one place — a call site never spells out a path string.
79
+
80
+ **Packer gotcha (verified in the reference implementation):** use
81
+ `packer: "MaxRectsPacker"` with `packerMethod: "Smart"`. The other packer methods
82
+ produce degenerate single-row *strip* atlases that waste enormous texture space
83
+ and can blow past the max texture size. **For pixel art**, set `allowTrim: false`
84
+ and `allowRotation: false`: trimming makes animation frames different sizes so
85
+ anchors swim between frames, and rotation makes Phaser frame-flipping non-trivial.
86
+ Add `extrude: 1` to stop edge bleeding when the game renders at integer scales.
87
+ Keep inputs **sorted** and emit **no timestamps** so the packed output is
88
+ deterministic and can be committed + `git diff --exit-code`-checked in CI.
56
89
 
57
90
  ## Step 2: audio sprites with audiosprite
58
91
 
@@ -90,10 +123,13 @@ the step that turns runtime failures into compile failures:
90
123
  const atlas = JSON.parse(await readFile("public/assets/atlases/game.json", "utf8"));
91
124
  const frames = Object.keys(atlas.frames); // raw keys, e.g. "player/idle"
92
125
  const sfx = Object.keys(JSON.parse(await readFile("public/assets/audio/sfx.json","utf8")).spritemap);
126
+ const images = (await readdir("public/assets/images", { recursive: true }))
127
+ .filter(f => f.endsWith(".png")).sort(); // standalone backdrops/parallax layers
93
128
 
94
129
  const out = `// AUTO-GENERATED by scripts/pack-assets.mjs — do not edit.
95
130
  export const Tex = { GameAtlas: "game" } as const;
96
131
  export const Frame = { ${frames.map(k => `${toIdent(k)}: "${k}"`).join(", ")} } as const;
132
+ export const Img = { ${images.map(p => `${toIdent(p)}: "img-${p.replace(/\.png$/u, "")}"`).join(", ")} } as const;
97
133
  export const Audio = { Sfx: "sfx" } as const;
98
134
  export const Sfx = { ${sfx.map(s => `${toIdent(s)}: "${s}"`).join(", ")} } as const;
99
135
  export const Font = { UI: "ui" } as const;
@@ -103,11 +139,20 @@ export const GameEvent = { ScoreChanged:"score-changed", EnemyDied:"enemy-died"
103
139
  await writeFile("src/assets.ts", out);
104
140
  ```
105
141
 
106
- Scenes import `Tex`, `Frame`, `Sfx`, `Font`, `SceneKeys`, `GameEvent` — never an
107
- inline string. Rename art, re-run the pipeline, and every now-stale reference
108
- fails `bun run typecheck` immediately. (Scene/event keys are kept in the same
109
- generated module so they share the no-raw-string discipline; edit them via the
110
- codegen template, not by hand.)
142
+ Scenes import `Tex`, `Frame`, `Img`, `Sfx`, `Font`, `SceneKeys`, `GameEvent` —
143
+ never an inline string. Rename art, re-run the pipeline, and every now-stale
144
+ reference fails `bun run typecheck` immediately. (Scene/event keys are kept in
145
+ the same generated module so they share the no-raw-string discipline; edit them
146
+ via the codegen template, not by hand.)
147
+
148
+ **Animation keys are the one deliberately hand-authored layer.** Codegen cannot
149
+ know how frames group into cycles, so animation keys live in a hand-written
150
+ `as const` record (e.g. `AnimKeys` / `FxAnims`) whose frame sequences are built
151
+ from the generated `Frame` constants — and the contract test below proves every
152
+ frame those definitions reference exists in the committed atlases. The typed-key
153
+ guarantee is therefore: **generated** constants for textures, frames, images,
154
+ audio, and fonts; **hand-authored typed constants + contract-test coverage** for
155
+ animation keys. Nothing is a raw inline string either way.
111
156
 
112
157
  ## Step 5: wire into the build
113
158
 
@@ -133,8 +178,37 @@ generated files for immutable caching ([[phaser-build-deploy]]).
133
178
  - `assets/src/**` is the source of truth; `public/assets/**` and `src/assets.ts`
134
179
  are generated — treat them as build output (re-runnable, not hand-edited).
135
180
  - One atlas per render group, one audiosprite per SFX set, BMFont for hot text.
136
- - A contract test asserts every constant in `src/assets.ts` resolves to an entry
137
- in the generated `pack.json`/manifests ([[phaser-testing]]).
181
+ - A **contract test** asserts every frame name the game *derives by convention*
182
+ (e.g. `battlerWalkFrame(ref, dir, n)`, FX-strip frame lists, per-speaker
183
+ portrait keys) resolves to a real entry in the committed atlas JSON under
184
+ `public/assets`. The other constants in `src/assets.ts` (`SceneKeys`,
185
+ `GameEvent`, `Font`, `Audio`, standalone `Img` entries) are plain generated
186
+ literals already enforced by TypeScript at every use site, so they don't need
187
+ a runtime check — only names built at runtime from a pattern can go stale
188
+ silently without one. This keeps that narrower guarantee honest: a renamed or
189
+ missing frame fails a headless Vitest assertion instead of rendering a silent
190
+ black square. The test reads the packed JSON directly — zero Phaser
191
+ ([[phaser-testing]]):
192
+
193
+ ```ts
194
+ const frames = new Set(Object.keys(
195
+ JSON.parse(readFileSync("public/assets/atlases/battlers.json", "utf8")).frames));
196
+ for (const ref of BATTLER_REFS)
197
+ for (const dir of DIRS) {
198
+ expect(frames).toContain(battlerIdleFrame(ref, dir));
199
+ expect(frames).toContain(battlerAttackFrame(ref, dir));
200
+ for (let n = 0; n < WALK_FRAME_COUNT; n++)
201
+ expect(frames).toContain(battlerWalkFrame(ref, dir, n));
202
+ }
203
+ // ...and one loop per remaining derived family (FX-strip frame lists,
204
+ // per-speaker portrait keys, …) — the test is only done when EVERY name the
205
+ // game can derive at runtime has been asserted against the committed atlas.
206
+ ```
207
+
208
+ - Ship a `--check` mode on the pack script: it re-packs into a temp staging area
209
+ and exits non-zero if any committed output under `public/assets` — atlases,
210
+ standalone images, audio, fonts alike — or `src/assets.ts` is stale, so CI
211
+ proves the committed artifacts (and every generated key they back) are current.
138
212
  - The generated `src/assets.ts` is the single source of asset/scene/event keys —
139
213
  the no-raw-string-keys rule depends on it existing.
140
214