@codyswann/lisa 2.161.0 → 2.163.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 (175) hide show
  1. package/.agents/plugins/marketplace.json +12 -0
  2. package/.claude-plugin/marketplace.json +24 -0
  3. package/dist/codex/plugin-marketplace-installer.d.ts.map +1 -1
  4. package/dist/codex/plugin-marketplace-installer.js +2 -0
  5. package/dist/codex/plugin-marketplace-installer.js.map +1 -1
  6. package/dist/configs/eslint/index.d.ts +2 -0
  7. package/dist/configs/eslint/index.d.ts.map +1 -1
  8. package/dist/configs/eslint/index.js +2 -0
  9. package/dist/configs/eslint/index.js.map +1 -1
  10. package/dist/configs/eslint/phaser.d.ts +29 -0
  11. package/dist/configs/eslint/phaser.d.ts.map +1 -0
  12. package/dist/configs/eslint/phaser.js +87 -0
  13. package/dist/configs/eslint/phaser.js.map +1 -0
  14. package/dist/configs/vitest/index.d.ts +3 -2
  15. package/dist/configs/vitest/index.d.ts.map +1 -1
  16. package/dist/configs/vitest/index.js +3 -2
  17. package/dist/configs/vitest/index.js.map +1 -1
  18. package/dist/configs/vitest/phaser.d.ts +29 -0
  19. package/dist/configs/vitest/phaser.d.ts.map +1 -0
  20. package/dist/configs/vitest/phaser.js +36 -0
  21. package/dist/configs/vitest/phaser.js.map +1 -0
  22. package/dist/core/config.d.ts +1 -1
  23. package/dist/core/config.d.ts.map +1 -1
  24. package/dist/core/config.js +2 -0
  25. package/dist/core/config.js.map +1 -1
  26. package/dist/detection/detectors/phaser.d.ts +15 -0
  27. package/dist/detection/detectors/phaser.d.ts.map +1 -0
  28. package/dist/detection/detectors/phaser.js +24 -0
  29. package/dist/detection/detectors/phaser.js.map +1 -0
  30. package/dist/detection/index.d.ts.map +1 -1
  31. package/dist/detection/index.js +2 -0
  32. package/dist/detection/index.js.map +1 -1
  33. package/dist/migrations/reconcile-claude-stack-plugins.d.ts.map +1 -1
  34. package/dist/migrations/reconcile-claude-stack-plugins.js +1 -0
  35. package/dist/migrations/reconcile-claude-stack-plugins.js.map +1 -1
  36. package/dist/strategies/package-lisa.d.ts.map +1 -1
  37. package/dist/strategies/package-lisa.js +4 -0
  38. package/dist/strategies/package-lisa.js.map +1 -1
  39. package/oxlint/phaser.json +11 -0
  40. package/package.json +7 -2
  41. package/phaser/copy-contents/.prettierignore +7 -0
  42. package/phaser/copy-overwrite/.github/workflows/ci.yml +23 -0
  43. package/phaser/copy-overwrite/eslint.config.ts +38 -0
  44. package/phaser/copy-overwrite/knip.json +17 -0
  45. package/phaser/copy-overwrite/tsconfig.eslint.json +20 -0
  46. package/phaser/copy-overwrite/tsconfig.json +7 -0
  47. package/phaser/copy-overwrite/vitest.config.ts +30 -0
  48. package/phaser/deletions.json +3 -0
  49. package/phaser/merge/.claude/settings.json +34 -0
  50. package/phaser/merge/.oxlintrc.json +16 -0
  51. package/phaser/package-lisa/package.lisa.json +73 -0
  52. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-agy/plugin.json +1 -1
  55. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  58. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-expo-agy/plugin.json +1 -1
  65. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +12 -1
  68. package/plugins/lisa-harper-fabric/.codex-plugin/hooks.json +11 -0
  69. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-harper-fabric/hooks/enforce-config-extensions.mjs +143 -0
  71. package/plugins/lisa-harper-fabric/hooks/enforce-config-extensions.sh +19 -0
  72. package/plugins/lisa-harper-fabric/rules/harper-fabric.md +1 -0
  73. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  74. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +12 -1
  75. package/plugins/lisa-harper-fabric-copilot/hooks/enforce-config-extensions.mjs +143 -0
  76. package/plugins/lisa-harper-fabric-copilot/hooks/enforce-config-extensions.sh +19 -0
  77. package/plugins/lisa-harper-fabric-copilot/rules/harper-fabric.md +1 -0
  78. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-harper-fabric-cursor/hooks/enforce-config-extensions.mjs +143 -0
  80. package/plugins/lisa-harper-fabric-cursor/hooks/enforce-config-extensions.sh +19 -0
  81. package/plugins/lisa-harper-fabric-cursor/hooks/hooks.json +6 -0
  82. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +1 -0
  83. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  86. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  91. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-phaser/.claude-plugin/plugin.json +35 -0
  94. package/plugins/lisa-phaser/.codex-plugin/hooks.json +26 -0
  95. package/plugins/lisa-phaser/.codex-plugin/plugin.json +30 -0
  96. package/plugins/lisa-phaser/hooks/inject-rules.sh +16 -0
  97. package/plugins/lisa-phaser/rules/phaser.md +59 -0
  98. package/plugins/lisa-phaser/skills/phaser-assets/SKILL.md +96 -0
  99. package/plugins/lisa-phaser/skills/phaser-assets/agents/openai.yaml +4 -0
  100. package/plugins/lisa-phaser/skills/phaser-gameobjects/SKILL.md +94 -0
  101. package/plugins/lisa-phaser/skills/phaser-gameobjects/agents/openai.yaml +4 -0
  102. package/plugins/lisa-phaser/skills/phaser-physics/SKILL.md +86 -0
  103. package/plugins/lisa-phaser/skills/phaser-physics/agents/openai.yaml +4 -0
  104. package/plugins/lisa-phaser/skills/phaser-project-structure/SKILL.md +89 -0
  105. package/plugins/lisa-phaser/skills/phaser-project-structure/agents/openai.yaml +4 -0
  106. package/plugins/lisa-phaser/skills/phaser-rendering/SKILL.md +89 -0
  107. package/plugins/lisa-phaser/skills/phaser-rendering/agents/openai.yaml +4 -0
  108. package/plugins/lisa-phaser/skills/phaser-scenes/SKILL.md +86 -0
  109. package/plugins/lisa-phaser/skills/phaser-scenes/agents/openai.yaml +4 -0
  110. package/plugins/lisa-phaser/skills/phaser-testing/SKILL.md +99 -0
  111. package/plugins/lisa-phaser/skills/phaser-testing/agents/openai.yaml +4 -0
  112. package/plugins/lisa-phaser/skills/phaser-v3-migration/SKILL.md +81 -0
  113. package/plugins/lisa-phaser/skills/phaser-v3-migration/agents/openai.yaml +4 -0
  114. package/plugins/lisa-phaser-agy/plugin.json +11 -0
  115. package/plugins/lisa-phaser-agy/skills/phaser-assets/SKILL.md +96 -0
  116. package/plugins/lisa-phaser-agy/skills/phaser-gameobjects/SKILL.md +94 -0
  117. package/plugins/lisa-phaser-agy/skills/phaser-physics/SKILL.md +86 -0
  118. package/plugins/lisa-phaser-agy/skills/phaser-project-structure/SKILL.md +89 -0
  119. package/plugins/lisa-phaser-agy/skills/phaser-rendering/SKILL.md +89 -0
  120. package/plugins/lisa-phaser-agy/skills/phaser-scenes/SKILL.md +86 -0
  121. package/plugins/lisa-phaser-agy/skills/phaser-testing/SKILL.md +99 -0
  122. package/plugins/lisa-phaser-agy/skills/phaser-v3-migration/SKILL.md +81 -0
  123. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +24 -0
  124. package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +16 -0
  125. package/plugins/lisa-phaser-copilot/rules/phaser.md +59 -0
  126. package/plugins/lisa-phaser-copilot/skills/phaser-assets/SKILL.md +96 -0
  127. package/plugins/lisa-phaser-copilot/skills/phaser-gameobjects/SKILL.md +94 -0
  128. package/plugins/lisa-phaser-copilot/skills/phaser-physics/SKILL.md +86 -0
  129. package/plugins/lisa-phaser-copilot/skills/phaser-project-structure/SKILL.md +89 -0
  130. package/plugins/lisa-phaser-copilot/skills/phaser-rendering/SKILL.md +89 -0
  131. package/plugins/lisa-phaser-copilot/skills/phaser-scenes/SKILL.md +86 -0
  132. package/plugins/lisa-phaser-copilot/skills/phaser-testing/SKILL.md +99 -0
  133. package/plugins/lisa-phaser-copilot/skills/phaser-v3-migration/SKILL.md +81 -0
  134. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +11 -0
  135. package/plugins/lisa-phaser-cursor/rules/phaser.mdc +64 -0
  136. package/plugins/lisa-phaser-cursor/skills/phaser-assets/SKILL.md +96 -0
  137. package/plugins/lisa-phaser-cursor/skills/phaser-gameobjects/SKILL.md +94 -0
  138. package/plugins/lisa-phaser-cursor/skills/phaser-physics/SKILL.md +86 -0
  139. package/plugins/lisa-phaser-cursor/skills/phaser-project-structure/SKILL.md +89 -0
  140. package/plugins/lisa-phaser-cursor/skills/phaser-rendering/SKILL.md +89 -0
  141. package/plugins/lisa-phaser-cursor/skills/phaser-scenes/SKILL.md +86 -0
  142. package/plugins/lisa-phaser-cursor/skills/phaser-testing/SKILL.md +99 -0
  143. package/plugins/lisa-phaser-cursor/skills/phaser-v3-migration/SKILL.md +81 -0
  144. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  145. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  146. package/plugins/lisa-rails-agy/plugin.json +1 -1
  147. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  148. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  149. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  151. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  152. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  153. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  154. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  156. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  157. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  158. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  159. package/plugins/src/harper-fabric/.claude-plugin/plugin.json +8 -0
  160. package/plugins/src/harper-fabric/hooks/enforce-config-extensions.mjs +143 -0
  161. package/plugins/src/harper-fabric/hooks/enforce-config-extensions.sh +19 -0
  162. package/plugins/src/harper-fabric/rules/harper-fabric.md +1 -0
  163. package/plugins/src/phaser/.claude-plugin/plugin.json +15 -0
  164. package/plugins/src/phaser/hooks/inject-rules.sh +16 -0
  165. package/plugins/src/phaser/rules/phaser.md +59 -0
  166. package/plugins/src/phaser/skills/phaser-assets/SKILL.md +96 -0
  167. package/plugins/src/phaser/skills/phaser-gameobjects/SKILL.md +94 -0
  168. package/plugins/src/phaser/skills/phaser-physics/SKILL.md +86 -0
  169. package/plugins/src/phaser/skills/phaser-project-structure/SKILL.md +89 -0
  170. package/plugins/src/phaser/skills/phaser-rendering/SKILL.md +89 -0
  171. package/plugins/src/phaser/skills/phaser-scenes/SKILL.md +86 -0
  172. package/plugins/src/phaser/skills/phaser-testing/SKILL.md +99 -0
  173. package/plugins/src/phaser/skills/phaser-v3-migration/SKILL.md +81 -0
  174. package/scripts/build-plugins.sh +1 -1
  175. package/tsconfig/phaser.json +14 -0
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: phaser-v3-migration
3
+ description: This skill should be used when migrating a Phaser 3 game to Phaser 4, reviewing code that contains Phaser 3 idioms, or answering "does this v3 API still exist" questions — pipelines→render nodes, preFX/postFX/BitmapMask→Filters, tintFill→TintModes, removed namespaces (Geom.Point, Struct, Mesh/Plane, Camera3D, bundled Spine), config default changes, and texture-orientation changes. Pairs with phaser-rendering and phaser-project-structure.
4
+ ---
5
+
6
+ # Phaser 3 → Phaser 4 Migration
7
+
8
+ ## Overview
9
+
10
+ Phaser 4.0.0 "Caladan" (April 2026; current line v4.1 "Salusa") keeps the core
11
+ game-facing API stable — scenes, input, Arcade/Matter physics, tweens,
12
+ animations, audio, and the scale manager are **unchanged** — and replaces the
13
+ rendering internals. The official estimate for a standard-API game is hours,
14
+ not weeks. The npm package is still `phaser`; v4 ships its own
15
+ `types/phaser.d.ts`.
16
+
17
+ Upstream references for deeper detail: the official migration guide in the
18
+ phaser repo (`changelog/v4/4.0/MIGRATION-GUIDE.md`) and the shader-specific
19
+ guide. This skill is original Lisa content written against those primary
20
+ sources — it is not a port of any upstream plugin or skill package.
21
+
22
+ ## What did NOT change (don't "migrate" these)
23
+
24
+ Scene lifecycle (`init/preload/create/update`), `this.scene` control, Loader
25
+ API (plus new `atlasPCT`), Input, Arcade & Matter physics, Tweens/Timelines,
26
+ `anims`, Scale Manager, Sound managers, Groups, Containers, particle emitter
27
+ API (v3.60 style).
28
+
29
+ ## The breaking changes, by frequency of impact
30
+
31
+ | Phaser 3 | Phaser 4 |
32
+ | --- | --- |
33
+ | `setPipeline(...)` / `setPostPipeline(...)` / `resetPipeline()` | RenderNodes (`render.renderNodes` config) — rewrite required |
34
+ | `preFX` / `postFX` controllers | Unified **Filter** system (internal/external lists) |
35
+ | `BitmapMask` / `GeometryMask` | `Mask` filter; `Phaser.Actions.AddMaskShape()` |
36
+ | Bloom/Shine/Circle FX | `Actions.AddEffectBloom()` / `AddEffectShine()` |
37
+ | Gradient FX | `Gradient` GameObject |
38
+ | `setTintFill(c)` / `tintFill` | `setTint(c)` + `setTintMode(Phaser.TintModes.FILL)` |
39
+ | `setPipeline('Light2D')` | `gameObject.setLighting(true)`; lights gained `z` |
40
+ | `Phaser.Geom.Point` | Removed — `Phaser.Math.Vector2` (geometry returns Vector2) |
41
+ | `Phaser.Struct.Set` / `Struct.Map` | Native `Set` / `Map` |
42
+ | Shadertoy-style `Shader` uniforms | Rewritten Shader: `ShaderQuadConfig`, `setUniform()`, `#pragma` |
43
+ | RenderTexture/DynamicTexture draw-executes-immediately | **Buffered** — call `render()`; `preserve()`, `renderMode` |
44
+ | Bundled Spine 3/4 plugins | Removed — Esoteric's official Phaser Spine runtime |
45
+ | `Mesh`, `Plane`, OBJ loader, `Camera3D`, `Layer3D` | Removed, no replacement |
46
+ | `Math.TAU` = π/2 (wrong) | `Math.TAU` = 2π (correct); `PI_OVER_2` added; `PI2` removed |
47
+ | `Create.GenerateTexture`, polyfills, IE9 entry | Removed |
48
+
49
+ ## Behavior/default changes that silently alter a port
50
+
51
+ - `roundPixels` default flipped **true → false**. If a pixel-art game looks
52
+ blurry after porting, set `pixelArt: true` (or `render.smoothPixelArt`) —
53
+ don't blanket-restore roundPixels.
54
+ - **Texture Y-flip**: UVs are GL-oriented (Y=0 bottom). Standard usage is
55
+ translated automatically, but custom shaders and **compressed textures** must
56
+ be updated/re-encoded.
57
+ - `Camera#matrix` no longer includes position (`matrixExternal` does;
58
+ `matrixCombined` removed) — affects code doing manual camera-space math.
59
+ - `Grid` "outline" properties renamed to "stroke". TileSprite was rebuilt
60
+ (atlas frames, `tileRotation`; cropping removed). `DOMElement` without a
61
+ container parent now throws.
62
+
63
+ ## Migration procedure
64
+
65
+ 1. Bump `phaser` to `^4.1.0`; remove any `@types/phaser`.
66
+ 2. Typecheck — removed APIs surface as compile errors; fix using the table.
67
+ 3. Grep for the silent ones types won't catch: `Light2D`, `tintFill`,
68
+ `Math.TAU`, custom shader GLSL, compressed-texture loads.
69
+ 4. Rewrite pipelines/FX as RenderNodes/Filters ([[phaser-rendering]]); re-add
70
+ `render()` calls after DynamicTexture drawing.
71
+ 5. Swap Spine to the Esoteric runtime; check third-party plugins (`rex` users:
72
+ the v4 line is the separate `phaser4-rex-plugins` package, which also ships
73
+ `p3-fx` ports of the dropped v3 FX).
74
+ 6. Verify visually per [[phaser-rendering]] — renderer migrations produce
75
+ wrong-pixels bugs, not exceptions.
76
+
77
+ ## Project conventions
78
+
79
+ This stack's lint config hard-bans the left column of the table in `src/**` —
80
+ a migration is not complete until lint passes with those rules on, with zero
81
+ disables.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.161.0",
3
+ "version": "2.163.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -13,6 +13,14 @@
13
13
  ]
14
14
  }
15
15
  ],
16
+ "PostToolUse": [
17
+ {
18
+ "matcher": "Write|Edit|MultiEdit",
19
+ "hooks": [
20
+ { "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-config-extensions.sh" }
21
+ ]
22
+ }
23
+ ],
16
24
  "SessionStart": [
17
25
  { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] }
18
26
  ],
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { readFileSync } from "node:fs";
4
+ import { spawnSync } from "node:child_process";
5
+ import path from "node:path";
6
+
7
+ const BLOCKED = 2;
8
+ const ALLOWED = 0;
9
+ const CONFIG_PATH = "harper-app/config.yaml";
10
+ const ALLOWLIST_PATH = ".lisa/harper-config-extension-allowlist.json";
11
+
12
+ const readStdin = () => {
13
+ try {
14
+ return readFileSync(0, "utf8");
15
+ } catch {
16
+ return "";
17
+ }
18
+ };
19
+
20
+ const parseHookInput = raw => {
21
+ try {
22
+ return JSON.parse(raw);
23
+ } catch {
24
+ return {};
25
+ }
26
+ };
27
+
28
+ const normalizePath = filePath =>
29
+ filePath.replace(/\\/g, "/").replace(/^\.\//, "");
30
+
31
+ const isConfigPath = filePath => {
32
+ const normalized = normalizePath(filePath);
33
+ return normalized === CONFIG_PATH || normalized.endsWith(`/${CONFIG_PATH}`);
34
+ };
35
+
36
+ const repoRelativeConfigPath = filePath => {
37
+ const normalized = normalizePath(filePath);
38
+ const index = normalized.lastIndexOf(CONFIG_PATH);
39
+ return index === -1 ? normalized : normalized.slice(index);
40
+ };
41
+
42
+ const loadYaml = () => {
43
+ const require = createRequire(import.meta.url);
44
+ return require("js-yaml");
45
+ };
46
+
47
+ const topLevelExtensionKeys = yamlText => {
48
+ const yaml = loadYaml();
49
+ let parsed;
50
+ try {
51
+ parsed = yaml.load(yamlText);
52
+ } catch {
53
+ return null;
54
+ }
55
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [];
56
+ return Object.keys(parsed).sort();
57
+ };
58
+
59
+ const gitEnv = () =>
60
+ Object.fromEntries(
61
+ Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_"))
62
+ );
63
+
64
+ const readGitBlob = repoRoot => {
65
+ const result = spawnSync(
66
+ "git",
67
+ ["-C", repoRoot, "show", `HEAD:${CONFIG_PATH}`],
68
+ {
69
+ encoding: "utf8",
70
+ env: gitEnv(),
71
+ }
72
+ );
73
+ return result.status === 0 ? result.stdout : null;
74
+ };
75
+
76
+ const readAllowlist = (repoRoot, configPath) => {
77
+ const allowlistFile = path.join(repoRoot, ALLOWLIST_PATH);
78
+ let parsed;
79
+ try {
80
+ parsed = JSON.parse(readFileSync(allowlistFile, "utf8"));
81
+ } catch {
82
+ return new Set();
83
+ }
84
+
85
+ const entry = parsed?.[configPath] ?? parsed?.[CONFIG_PATH];
86
+ const values = Array.isArray(entry)
87
+ ? entry
88
+ : Array.isArray(entry?.allowedRemovedExtensions)
89
+ ? entry.allowedRemovedExtensions
90
+ : [];
91
+ return new Set(values.filter(value => typeof value === "string"));
92
+ };
93
+
94
+ const main = () => {
95
+ const input = parseHookInput(readStdin());
96
+ const filePath = input?.tool_input?.file_path;
97
+ if (typeof filePath !== "string" || !isConfigPath(filePath)) return ALLOWED;
98
+
99
+ const repoRoot = process.cwd();
100
+ const configPath = repoRelativeConfigPath(filePath);
101
+ let currentText;
102
+ try {
103
+ currentText = readFileSync(path.join(repoRoot, configPath), "utf8");
104
+ } catch {
105
+ return ALLOWED;
106
+ }
107
+
108
+ const previousText = readGitBlob(repoRoot);
109
+ if (previousText === null) return ALLOWED;
110
+
111
+ const previousExtensions = topLevelExtensionKeys(previousText);
112
+ const currentExtensionKeys = topLevelExtensionKeys(currentText);
113
+ if (previousExtensions === null || currentExtensionKeys === null)
114
+ return ALLOWED;
115
+ const currentExtensions = new Set(currentExtensionKeys);
116
+ const allowedRemovals = readAllowlist(repoRoot, configPath);
117
+ const missing = previousExtensions.filter(
118
+ extension =>
119
+ !currentExtensions.has(extension) && !allowedRemovals.has(extension)
120
+ );
121
+
122
+ if (missing.length === 0) return ALLOWED;
123
+
124
+ process.stderr
125
+ .write(`Blocked: harper-app/config.yaml dropped required Harper extension(s).
126
+
127
+ Missing extension(s): ${missing.join(", ")}
128
+
129
+ Harper does not merge a custom config.yaml with defaults. Removing a top-level
130
+ extension silently disables that runtime surface and may only fail after deploy.
131
+ Re-add the missing extension(s), or document an intentional removal in
132
+ ${ALLOWLIST_PATH}:
133
+
134
+ {
135
+ "${CONFIG_PATH}": {
136
+ "allowedRemovedExtensions": ["${missing[0]}"]
137
+ }
138
+ }
139
+ `);
140
+ return BLOCKED;
141
+ };
142
+
143
+ process.exitCode = main();
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # This file is managed by Lisa.
3
+ # Do not edit directly - changes will be overwritten on the next `lisa` run.
4
+
5
+ # PostToolUse hook: after a harper-app/config.yaml edit, compare the edited
6
+ # extension set against HEAD and block silent removals. Harper does not merge a
7
+ # custom config.yaml with defaults, so removing a top-level extension can disable
8
+ # runtime surfaces without a build-time failure.
9
+
10
+ PLUGIN_ROOT=${CLAUDE_PLUGIN_ROOT:-}
11
+ if [ -z "$PLUGIN_ROOT" ]; then
12
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
13
+ fi
14
+
15
+ if command -v bun >/dev/null 2>&1; then
16
+ exec bun "$PLUGIN_ROOT/hooks/enforce-config-extensions.mjs"
17
+ fi
18
+
19
+ exec node "$PLUGIN_ROOT/hooks/enforce-config-extensions.mjs"
@@ -12,6 +12,7 @@ These rules apply to Harper/Fabric component apps managed by Lisa.
12
12
 
13
13
  - TypeScript under `src/` is the source of truth for Harper resources, browser modules, shared libraries, and operational scripts.
14
14
  - `harper-app/config.yaml`, `harper-app/schema.graphql`, HTML, CSS, docs, and research fixtures are source assets.
15
+ - `harper-app/config.yaml` does not merge with Harper defaults. Keep every required top-level extension declared when editing it; the Harper Fabric hook blocks accidental extension drops unless the removal is documented in `.lisa/harper-config-extension-allowlist.json`.
15
16
  - `harper-app/resources.js` and `harper-app/web/**/*.js` are generated deploy artifacts. Never edit them directly; change the matching TypeScript and run `bun run build`.
16
17
  - Deployment, bootstrap, smoke, seed, verify, preview, token, crawl, ingest, and extraction commands must run from compiled JavaScript or generated Harper assets, not stale checked-in JavaScript.
17
18
 
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "lisa-phaser",
3
+ "version": "1.0.0",
4
+ "description": "Phaser 4 game-development rules for TypeScript projects",
5
+ "author": { "name": "Cody Swann" },
6
+ "dependencies": ["lisa-typescript"],
7
+ "hooks": {
8
+ "SessionStart": [
9
+ { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] }
10
+ ],
11
+ "SubagentStart": [
12
+ { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+ # Reads all .md files from the plugin's rules/ directory and injects them
3
+ # into Claude context at session/subagent start.
4
+ set -euo pipefail
5
+
6
+ ROOT="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}}"
7
+ RULES_DIR="$ROOT/rules"
8
+
9
+ [ -d "$RULES_DIR" ] || exit 0
10
+
11
+ for rule in "$RULES_DIR"/*.md; do
12
+ [ -f "$rule" ] || continue
13
+ printf '\n<lisa-phaser-rule path="%s">\n' "$rule"
14
+ cat "$rule"
15
+ printf '\n</lisa-phaser-rule>\n'
16
+ done
@@ -0,0 +1,59 @@
1
+ # Phaser 4 Project Rules
2
+
3
+ This is a **Phaser 4** (v4.1+ "Salusa") TypeScript game project. Phaser 4 is the
4
+ only supported target — never introduce Phaser 3 idioms. The lint config enforces
5
+ the bans below; do not disable those rules, fix the code.
6
+
7
+ ## Phaser 4 Only — Banned v3 Idioms
8
+
9
+ - **No pipelines.** `setPipeline` / `setPostPipeline` / `resetPipeline` are gone.
10
+ Custom rendering is a **RenderNode** (registered via `render.renderNodes` in the
11
+ game config); post-processing is a **Filter** (`gameObject.filters` /
12
+ `camera.filters`). See the `phaser-rendering` skill.
13
+ - **No preFX/postFX.** The v3 FX controllers were replaced by the unified Filter
14
+ system. `BitmapMask` → `Mask` filter.
15
+ - **No `setTintFill`/`tintFill`.** Use `setTint()` + `setTintMode(Phaser.TintModes.FILL)`.
16
+ - **No `Phaser.Geom.Point`** (removed — use `Phaser.Math.Vector2`) and **no
17
+ `Phaser.Struct.Set/Map`** (removed — use native `Set`/`Map`).
18
+ - **No raw WebGL calls.** External GL work goes through the `Extern` game object.
19
+ - **No `setPipeline('Light2D')`.** Lighting is `gameObject.setLighting(true)`.
20
+
21
+ ## Architecture
22
+
23
+ - **One scene class per file** under `src/scenes/`, named after the scene key.
24
+ The scene flow is `Boot → Preloader → MainMenu → Game` (plus overlays). Boot
25
+ loads only what the Preloader needs; the Preloader loads everything else.
26
+ - **Pure game logic lives outside scenes** in `src/logic/` (or `src/core/`) with
27
+ **no `phaser` imports** — plain TypeScript functions and classes that take and
28
+ return data. Scenes are thin orchestrators that wire logic to GameObjects.
29
+ This is what makes the game unit-testable; see the `phaser-testing` skill.
30
+ - **Asset keys are typed constants** in `src/assets.ts` — never inline magic
31
+ strings for texture/audio/animation keys. Load via asset-pack manifests
32
+ (`this.load.pack`), not ad-hoc `load.image` lists scattered across scenes.
33
+
34
+ ## Determinism
35
+
36
+ - **No `Math.random()` in game code.** Use the scene-seeded
37
+ `Phaser.Math.RandomDataGenerator` (`Phaser.Math.RND` or a local instance with
38
+ an explicit seed) so replays and tests are reproducible.
39
+ - Arcade physics keeps its v4 default `fixedStep: true`. Do not switch physics to
40
+ variable step to "fix" a tunneling bug — fix the body/velocity configuration.
41
+
42
+ ## Performance
43
+
44
+ - **No allocations in `update()`.** No `new`, array literals, closures, or
45
+ `.filter/.map` chains in per-frame paths — hoist scratch objects, reuse vectors.
46
+ - **Pool, don't churn.** Bullets, enemies, particles, and pickups come from
47
+ `Group` pools (`get`/`killAndHide`), never `new`/`destroy` per spawn.
48
+ - **Mass rendering uses the GPU layers.** Large static/animated sprite fields →
49
+ `SpriteGPULayer`; large tile maps → `TilemapGPULayer`. Texture atlases (PCT
50
+ where possible) over loose images — see the `phaser-assets` skill.
51
+ - Target WebGL; the Canvas renderer is deprecated in v4 and only acceptable as an
52
+ explicit, documented fallback decision.
53
+
54
+ ## Verification
55
+
56
+ Before reporting any change complete: run `bun run typecheck`, `bun run test`,
57
+ and `bun run build`. For changes that affect rendering or input, verify in the
58
+ real browser — `bun run dev` plus a Playwright check (the game boots, the canvas
59
+ renders, no console errors). A green typecheck alone is not proof a game works.
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: phaser-assets
3
+ description: This skill should be used when loading or organizing assets in a Phaser 4 game — the Loader (images, texture atlases, spritesheets, audio, fonts), asset-pack manifests via this.load.pack, the new PCT compact atlas format (load.atlasPCT), typed asset keys, and where files live in the Vite project. Use it when adding any asset, restructuring loading, fixing a missing-texture/green-square bug, or optimizing load size. Pairs with phaser-project-structure, phaser-scenes, and phaser-gameobjects.
4
+ ---
5
+
6
+ # Phaser 4 Assets and Loading
7
+
8
+ ## Overview
9
+
10
+ All runtime assets live under `public/assets/` (served verbatim by Vite — never
11
+ `import` game assets through the bundler) and are loaded by Phaser's Loader in a
12
+ scene's `preload()`. The opinionated pattern: **one asset-pack manifest, loaded
13
+ by the Preloader, with every key defined as a typed constant.**
14
+
15
+ ## Asset packs (the default loading strategy)
16
+
17
+ A pack is a JSON manifest the Loader consumes wholesale:
18
+
19
+ ```json
20
+ {
21
+ "main": {
22
+ "files": [
23
+ { "type": "atlasPCT", "key": "game-atlas", "url": "atlases/game.pct" },
24
+ { "type": "image", "key": "background", "url": "images/background.png" },
25
+ { "type": "audio", "key": "sfx-jump", "url": ["audio/jump.ogg", "audio/jump.m4a"] },
26
+ { "type": "spritesheet", "key": "explosion", "url": "sheets/explosion.png",
27
+ "frameConfig": { "frameWidth": 64, "frameHeight": 64 } }
28
+ ]
29
+ }
30
+ }
31
+ ```
32
+
33
+ ```ts
34
+ // Preloader.preload()
35
+ this.load.pack("game-pack", "assets/pack.json");
36
+ ```
37
+
38
+ Why packs: loading is declared in one reviewable file instead of scattered
39
+ `load.*` calls; adding an asset never touches scene code beyond using the key.
40
+
41
+ ## Texture atlases — the rule, not the suggestion
42
+
43
+ Individual images break sprite batching. Everything that renders together ships
44
+ in an atlas:
45
+
46
+ - **PCT (Phaser Compact Texture)** is the preferred v4 atlas format —
47
+ `this.load.atlasPCT(key, url)` — its manifests are 90–95% smaller than the
48
+ JSON-hash equivalent. JSON/XML/Unity/multi-atlas loaders still exist for
49
+ third-party toolchains.
50
+ - Spritesheets (`load.spritesheet`) are acceptable only for uniform-grid
51
+ animation strips; anything else is an atlas.
52
+
53
+ ## Audio
54
+
55
+ - Provide at least two encodings (`.ogg` + `.m4a`) in the url array; Phaser
56
+ picks the first the browser can play.
57
+ - Web Audio is the default manager; it unlocks on first user gesture — never
58
+ autoplay sound before input (it will silently fail and "work on my machine").
59
+ - Use audio sprites (`load.audioSprite`) for large sets of short SFX.
60
+
61
+ ## Typed keys
62
+
63
+ Every key lives in `src/assets.ts`:
64
+
65
+ ```ts
66
+ export const Tex = { GameAtlas: "game-atlas", Background: "background" } as const;
67
+ export const Sfx = { Jump: "sfx-jump" } as const;
68
+ export const SceneKeys = { Boot: "Boot", Preloader: "Preloader", Game: "Game" } as const;
69
+ ```
70
+
71
+ Scenes use `Tex.GameAtlas`, never `"game-atlas"` inline. A bad inline key fails
72
+ at runtime as a green square or silent missing audio; a bad constant fails in
73
+ review and in tests that assert the pack manifest covers every constant.
74
+
75
+ ## Loading UX and failure handling
76
+
77
+ - The Preloader binds `this.load.on("progress", …)` to a progress bar built from
78
+ Boot-loaded art ([[phaser-project-structure]]).
79
+ - Handle `loaderror`: `this.load.on(Phaser.Loader.Events.FILE_LOAD_ERROR, …)` —
80
+ log the key and URL; a missing asset must fail loudly in dev, not render as a
81
+ placeholder in production.
82
+
83
+ ## Project conventions
84
+
85
+ - `public/assets/` subdirs: `atlases/`, `images/`, `audio/`, `sheets/`, `fonts/`,
86
+ plus `pack.json` at the root of `assets/`.
87
+ - Generated atlas files (PCT/JSON + PNG pages) are build inputs, committed to the
88
+ repo; their source art lives wherever the art pipeline keeps it.
89
+ - A test should assert that every key constant in `src/assets.ts` appears in
90
+ `pack.json` (cheap manifest-coverage check; see [[phaser-testing]]).
91
+
92
+ ## Verification
93
+
94
+ Asset changes are verified by booting the game and watching the network panel /
95
+ console: every file 200s, no `FILE_LOAD_ERROR`, and the new asset visibly renders
96
+ or audibly plays in the scene that uses it.