@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.
- package/.agents/plugins/marketplace.json +12 -0
- package/.claude-plugin/marketplace.json +24 -0
- package/dist/codex/plugin-marketplace-installer.d.ts.map +1 -1
- package/dist/codex/plugin-marketplace-installer.js +2 -0
- package/dist/codex/plugin-marketplace-installer.js.map +1 -1
- package/dist/configs/eslint/index.d.ts +2 -0
- package/dist/configs/eslint/index.d.ts.map +1 -1
- package/dist/configs/eslint/index.js +2 -0
- package/dist/configs/eslint/index.js.map +1 -1
- package/dist/configs/eslint/phaser.d.ts +29 -0
- package/dist/configs/eslint/phaser.d.ts.map +1 -0
- package/dist/configs/eslint/phaser.js +87 -0
- package/dist/configs/eslint/phaser.js.map +1 -0
- package/dist/configs/vitest/index.d.ts +3 -2
- package/dist/configs/vitest/index.d.ts.map +1 -1
- package/dist/configs/vitest/index.js +3 -2
- package/dist/configs/vitest/index.js.map +1 -1
- package/dist/configs/vitest/phaser.d.ts +29 -0
- package/dist/configs/vitest/phaser.d.ts.map +1 -0
- package/dist/configs/vitest/phaser.js +36 -0
- package/dist/configs/vitest/phaser.js.map +1 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +2 -0
- package/dist/core/config.js.map +1 -1
- package/dist/detection/detectors/phaser.d.ts +15 -0
- package/dist/detection/detectors/phaser.d.ts.map +1 -0
- package/dist/detection/detectors/phaser.js +24 -0
- package/dist/detection/detectors/phaser.js.map +1 -0
- package/dist/detection/index.d.ts.map +1 -1
- package/dist/detection/index.js +2 -0
- package/dist/detection/index.js.map +1 -1
- package/dist/migrations/reconcile-claude-stack-plugins.d.ts.map +1 -1
- package/dist/migrations/reconcile-claude-stack-plugins.js +1 -0
- package/dist/migrations/reconcile-claude-stack-plugins.js.map +1 -1
- package/dist/strategies/package-lisa.d.ts.map +1 -1
- package/dist/strategies/package-lisa.js +4 -0
- package/dist/strategies/package-lisa.js.map +1 -1
- package/oxlint/phaser.json +11 -0
- package/package.json +7 -2
- package/phaser/copy-contents/.prettierignore +7 -0
- package/phaser/copy-overwrite/.github/workflows/ci.yml +23 -0
- package/phaser/copy-overwrite/eslint.config.ts +38 -0
- package/phaser/copy-overwrite/knip.json +17 -0
- package/phaser/copy-overwrite/tsconfig.eslint.json +20 -0
- package/phaser/copy-overwrite/tsconfig.json +7 -0
- package/phaser/copy-overwrite/vitest.config.ts +30 -0
- package/phaser/deletions.json +3 -0
- package/phaser/merge/.claude/settings.json +34 -0
- package/phaser/merge/.oxlintrc.json +16 -0
- package/phaser/package-lisa/package.lisa.json +73 -0
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +12 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/hooks.json +11 -0
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/hooks/enforce-config-extensions.mjs +143 -0
- package/plugins/lisa-harper-fabric/hooks/enforce-config-extensions.sh +19 -0
- package/plugins/lisa-harper-fabric/rules/harper-fabric.md +1 -0
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +12 -1
- package/plugins/lisa-harper-fabric-copilot/hooks/enforce-config-extensions.mjs +143 -0
- package/plugins/lisa-harper-fabric-copilot/hooks/enforce-config-extensions.sh +19 -0
- package/plugins/lisa-harper-fabric-copilot/rules/harper-fabric.md +1 -0
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/hooks/enforce-config-extensions.mjs +143 -0
- package/plugins/lisa-harper-fabric-cursor/hooks/enforce-config-extensions.sh +19 -0
- package/plugins/lisa-harper-fabric-cursor/hooks/hooks.json +6 -0
- package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +1 -0
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +35 -0
- package/plugins/lisa-phaser/.codex-plugin/hooks.json +26 -0
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +30 -0
- package/plugins/lisa-phaser/hooks/inject-rules.sh +16 -0
- package/plugins/lisa-phaser/rules/phaser.md +59 -0
- package/plugins/lisa-phaser/skills/phaser-assets/SKILL.md +96 -0
- package/plugins/lisa-phaser/skills/phaser-assets/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-gameobjects/SKILL.md +94 -0
- package/plugins/lisa-phaser/skills/phaser-gameobjects/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-physics/SKILL.md +86 -0
- package/plugins/lisa-phaser/skills/phaser-physics/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-project-structure/SKILL.md +89 -0
- package/plugins/lisa-phaser/skills/phaser-project-structure/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-rendering/SKILL.md +89 -0
- package/plugins/lisa-phaser/skills/phaser-rendering/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-scenes/SKILL.md +86 -0
- package/plugins/lisa-phaser/skills/phaser-scenes/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-testing/SKILL.md +99 -0
- package/plugins/lisa-phaser/skills/phaser-testing/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser/skills/phaser-v3-migration/SKILL.md +81 -0
- package/plugins/lisa-phaser/skills/phaser-v3-migration/agents/openai.yaml +4 -0
- package/plugins/lisa-phaser-agy/plugin.json +11 -0
- package/plugins/lisa-phaser-agy/skills/phaser-assets/SKILL.md +96 -0
- package/plugins/lisa-phaser-agy/skills/phaser-gameobjects/SKILL.md +94 -0
- package/plugins/lisa-phaser-agy/skills/phaser-physics/SKILL.md +86 -0
- package/plugins/lisa-phaser-agy/skills/phaser-project-structure/SKILL.md +89 -0
- package/plugins/lisa-phaser-agy/skills/phaser-rendering/SKILL.md +89 -0
- package/plugins/lisa-phaser-agy/skills/phaser-scenes/SKILL.md +86 -0
- package/plugins/lisa-phaser-agy/skills/phaser-testing/SKILL.md +99 -0
- package/plugins/lisa-phaser-agy/skills/phaser-v3-migration/SKILL.md +81 -0
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +24 -0
- package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +16 -0
- package/plugins/lisa-phaser-copilot/rules/phaser.md +59 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-assets/SKILL.md +96 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-gameobjects/SKILL.md +94 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-physics/SKILL.md +86 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-project-structure/SKILL.md +89 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-rendering/SKILL.md +89 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-scenes/SKILL.md +86 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-testing/SKILL.md +99 -0
- package/plugins/lisa-phaser-copilot/skills/phaser-v3-migration/SKILL.md +81 -0
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +11 -0
- package/plugins/lisa-phaser-cursor/rules/phaser.mdc +64 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-assets/SKILL.md +96 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-gameobjects/SKILL.md +94 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-physics/SKILL.md +86 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-project-structure/SKILL.md +89 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-rendering/SKILL.md +89 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-scenes/SKILL.md +86 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-testing/SKILL.md +99 -0
- package/plugins/lisa-phaser-cursor/skills/phaser-v3-migration/SKILL.md +81 -0
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/harper-fabric/.claude-plugin/plugin.json +8 -0
- package/plugins/src/harper-fabric/hooks/enforce-config-extensions.mjs +143 -0
- package/plugins/src/harper-fabric/hooks/enforce-config-extensions.sh +19 -0
- package/plugins/src/harper-fabric/rules/harper-fabric.md +1 -0
- package/plugins/src/phaser/.claude-plugin/plugin.json +15 -0
- package/plugins/src/phaser/hooks/inject-rules.sh +16 -0
- package/plugins/src/phaser/rules/phaser.md +59 -0
- package/plugins/src/phaser/skills/phaser-assets/SKILL.md +96 -0
- package/plugins/src/phaser/skills/phaser-gameobjects/SKILL.md +94 -0
- package/plugins/src/phaser/skills/phaser-physics/SKILL.md +86 -0
- package/plugins/src/phaser/skills/phaser-project-structure/SKILL.md +89 -0
- package/plugins/src/phaser/skills/phaser-rendering/SKILL.md +89 -0
- package/plugins/src/phaser/skills/phaser-scenes/SKILL.md +86 -0
- package/plugins/src/phaser/skills/phaser-testing/SKILL.md +99 -0
- package/plugins/src/phaser/skills/phaser-v3-migration/SKILL.md +81 -0
- package/scripts/build-plugins.sh +1 -1
- 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.
|
|
@@ -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.
|