@looop-games/cli 0.1.8 → 0.1.10
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/CHANGELOG.md +75 -0
- package/bin/looop.mjs +8 -0
- package/lib/agent-surface.mjs +14 -32
- package/lib/changelog.mjs +171 -0
- package/lib/create.mjs +132 -86
- package/lib/update.mjs +29 -2
- package/package.json +2 -2
- package/template/.claude/skills/build/SKILL.md +0 -266
- package/template/.claude/skills/engine/SKILL.md +0 -100
- package/template/.claude/skills/feedback/SKILL.md +0 -125
- package/template/.claude/skills/handbook/SKILL.md +0 -142
- package/template/.claude/skills/qa/SKILL.md +0 -53
- package/template/.claude/skills/todo/SKILL.md +0 -54
- package/template/.claude/skills/update-looop/SKILL.md +0 -74
- package/template/AGENTS.md +0 -93
- package/template/CLAUDE.md +0 -4
- package/template/GEMINI.md +0 -4
- package/template/boot.smoke.mjs +0 -21
- package/template/game.js +0 -53
- package/template/gitignore +0 -2
- package/template/handbook/design.md +0 -8
- package/template/handbook/feel.md +0 -8
- package/template/handbook/qa.md +0 -9
- package/template/index.html +0 -26
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: handbook
|
|
3
|
-
description: The entry point to this game's handbook — its durable truth. Use when you need to know what this game already believes (before building, before proposing anything), and when something durable emerges that belongs in it — a design pillar, a blessed feel value, or a playtest catch that needs converting into an automated check. Also use when durable truth appears that has no chapter yet (the game's vision, its world, its cast, its economy) and one should be started.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# /handbook — what this game knows about itself
|
|
7
|
-
|
|
8
|
-
`handbook/` is this game's **durable truth**: the things a future session must
|
|
9
|
-
not violate, and must not have to re-derive. It is the game's own layer on top
|
|
10
|
-
of the engine's read-only craft docs — where they touch the same topic, the
|
|
11
|
-
handbook is *this game's* answer.
|
|
12
|
-
|
|
13
|
-
Three places knowledge lives here; keep them straight:
|
|
14
|
-
|
|
15
|
-
| | Holds | Lifespan |
|
|
16
|
-
|---|---|---|
|
|
17
|
-
| **`handbook/`** | what this game IS and has decided | durable — outlives every plan |
|
|
18
|
-
| **`notes/`** | what we're building or might build (`plans/`, `todos/`) | transient — closed when the work is |
|
|
19
|
-
| the engine's `shared/practices/` | how Looop games are built in general | read-only, ships with the engine |
|
|
20
|
-
|
|
21
|
-
The test for whether something belongs here: **would a future session need to
|
|
22
|
-
*not violate* this?** → handbook. *Is it something we're doing, or might do?* →
|
|
23
|
-
`notes/`.
|
|
24
|
-
|
|
25
|
-
## The chapters
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
handbook/
|
|
29
|
-
design.md the pillars — what this game IS, checked against new ideas
|
|
30
|
-
feel.md locked feel values the creator has blessed
|
|
31
|
-
qa.md checks this game has earned (mostly from playtests that caught something)
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**The set is open, and these three are only where it starts.** They ship with
|
|
35
|
-
every game because every game accumulates them. They are not the limit, and they
|
|
36
|
-
are not a template to fill in — what else a handbook holds depends entirely on
|
|
37
|
-
what this game turns out to need. See *Starting a new chapter*.
|
|
38
|
-
|
|
39
|
-
## Reading it — do this before you build
|
|
40
|
-
|
|
41
|
-
**`ls handbook/` and read what's relevant before writing game code or proposing
|
|
42
|
-
a direction.** Every chapter opens with a line saying what it's for, so the
|
|
43
|
-
folder listing plus the first two lines of each file is a cheap orientation.
|
|
44
|
-
Reading is free; contradicting the handbook and being caught later is not.
|
|
45
|
-
|
|
46
|
-
If what you're about to propose **contradicts something the handbook already
|
|
47
|
-
says**, that is not a detail to smooth over — stop and say so. Either the idea is
|
|
48
|
-
wrong, or what's written is out of date and the creator needs to say so out loud.
|
|
49
|
-
Never quietly build against it.
|
|
50
|
-
|
|
51
|
-
An empty chapter is honest — an early game hasn't decided much yet, and a
|
|
52
|
-
speculative pillar nobody has lived is worse than none.
|
|
53
|
-
|
|
54
|
-
## Growing it
|
|
55
|
-
|
|
56
|
-
**Every handbook write needs the creator's approval first.** The handbook is
|
|
57
|
-
*their* game's truth — propose the exact entry ("I'd like to record: …") and
|
|
58
|
-
write it only after they say yes. Never slip an entry in as a side effect of
|
|
59
|
-
other work. (Smoke/test FILES don't need this gate — they're regression tests,
|
|
60
|
-
not blessed truth; only `handbook/` writes do.)
|
|
61
|
-
|
|
62
|
-
### A design principle emerged → `handbook/design.md`
|
|
63
|
-
|
|
64
|
-
When a decision reveals what this game IS ("never text tutorials — the world
|
|
65
|
-
teaches", "death must always be the player's fault"), write the pillar down.
|
|
66
|
-
Future builds check new ideas against these.
|
|
67
|
-
|
|
68
|
-
### A playtest caught a defect → an automated check
|
|
69
|
-
|
|
70
|
-
The premise (from the engine's `shared/practices/qa.md`): **a human catching a
|
|
71
|
-
defect means an automated check was missing.** Convert the *class* of defect,
|
|
72
|
-
not the instance:
|
|
73
|
-
|
|
74
|
-
1. Name the miss precisely — not "the door was broken" but "doors can lose
|
|
75
|
-
their collision when the room resets, and nothing checks collision after a
|
|
76
|
-
reset."
|
|
77
|
-
2. Prefer an **executable check**: write a `<aspect>.smoke.mjs` (or
|
|
78
|
-
`*.test.mjs`) that reproduces the defect — confirm it fails RED on the
|
|
79
|
-
broken state, then goes green on the fix. `npx looop test` discovers it
|
|
80
|
-
forever after; a guard you never saw fail is a guard you can't trust.
|
|
81
|
-
3. **If the check needs to SEE the game's internals** (collision boxes, depth
|
|
82
|
-
order, hit areas) and the game has no debug overlay yet, **build one as part
|
|
83
|
-
of the conversion** — a keyboard-toggled draw of the real boxes/order. It's
|
|
84
|
-
a small one-time cost, and every later screenshot-verify reuses it (master
|
|
85
|
-
list row R2).
|
|
86
|
-
4. Only if it truly can't be executed (needs human perception), add it as a
|
|
87
|
-
procedural step in **`handbook/qa.md`** — `/qa` runs those by hand each time.
|
|
88
|
-
|
|
89
|
-
## Starting a new chapter
|
|
90
|
-
|
|
91
|
-
The four standing chapters won't fit everything. When durable truth appears
|
|
92
|
-
that belongs in none of them, **start a chapter** — that is the handbook
|
|
93
|
-
working as intended, not a special case.
|
|
94
|
-
|
|
95
|
-
A subject earns a chapter when it is: **durable** (it outlives the current
|
|
96
|
-
plan), **referred back to** (future sessions need it to stay consistent), and
|
|
97
|
-
**not a fit** for an existing chapter. It can be anything this game actually
|
|
98
|
-
needs: `vision.md` (what the game is *for*, once the creator has said it out
|
|
99
|
-
loud), `world.md` (a setting an agent must not contradict), `characters.md`,
|
|
100
|
-
`economy.md` (numbers that have to balance), `controls.md`. Don't shop from that
|
|
101
|
-
list — reach for whatever this game keeps needing to remember.
|
|
102
|
-
|
|
103
|
-
To start one:
|
|
104
|
-
|
|
105
|
-
1. **Propose it** — the name, and the exact first entry. Same approval gate as
|
|
106
|
-
any handbook write; creating a chapter is a bigger act than adding a line,
|
|
107
|
-
not a smaller one.
|
|
108
|
-
2. Create `handbook/<subject>.md` with the standard shape:
|
|
109
|
-
|
|
110
|
-
```markdown
|
|
111
|
-
# <Subject> — <what this chapter is for, in half a line>
|
|
112
|
-
|
|
113
|
-
<One or two sentences: what belongs here, what doesn't.>
|
|
114
|
-
|
|
115
|
-
## YYYY-MM-DD — <the entry>
|
|
116
|
-
<The truth itself. Short. Actionable cold, by someone who wasn't there.>
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
3. Don't pre-create chapters "in case", and don't propose one because the game
|
|
120
|
-
"ought to" have it. An empty speculative chapter is a trap — it invites
|
|
121
|
-
invented content, and a creator filling in a template is not the same as a
|
|
122
|
-
creator telling you something true. A chapter starts the day it has something
|
|
123
|
-
true to hold.
|
|
124
|
-
|
|
125
|
-
## The upstream half
|
|
126
|
-
|
|
127
|
-
Before writing, ask: **is this lesson specific to this game, or would every
|
|
128
|
-
Looop game want it?** A generic hole (an engine component that breaks a
|
|
129
|
-
universal expectation, a check every game should run) belongs in the engine's
|
|
130
|
-
master list, not just this repo — offer `/feedback` so it lands upstream for
|
|
131
|
-
everyone. Do both when in doubt: the handbook entry protects this game now; the
|
|
132
|
-
feedback fixes it everywhere later.
|
|
133
|
-
|
|
134
|
-
## Rules
|
|
135
|
-
|
|
136
|
-
- **One lesson per invocation, converted fully** — an entry someone can act on
|
|
137
|
-
cold, not a vague reminder.
|
|
138
|
-
- **Date entries.** When a later decision supersedes one, update it **in place**
|
|
139
|
-
rather than stacking contradictions — a handbook that argues with itself is
|
|
140
|
-
worse than no handbook, because a future session will pick the wrong side.
|
|
141
|
-
- **Short.** Every line in here is read by every future session. It earns its
|
|
142
|
-
place or it goes.
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: qa
|
|
3
|
-
description: Verify the current change — run every automated check that applies and hand the creator only the shortest possible list of things that genuinely need human eyes. Use when the creator asks "does it work?", before any playtest handback, or whenever work is about to be called done.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# /qa — run the game's checks, hand back only what needs a human
|
|
7
|
-
|
|
8
|
-
The QA framework lives in ONE place: the engine's master doc at
|
|
9
|
-
`node_modules/@looop-games/engine/shared/practices/qa.md` — what counts as an
|
|
10
|
-
**automated** step (you run it, you report it) vs a **manual** one (only the
|
|
11
|
-
creator can judge it), the ratchet, and the master steps list. **Read it
|
|
12
|
-
first, every time. This skill is the procedure; the steps live there.**
|
|
13
|
-
|
|
14
|
-
The governing rule (from that doc): **maximize automated, drive manual toward
|
|
15
|
-
zero.** Anything you *can* run, you run and report — it never goes on the
|
|
16
|
-
creator's list.
|
|
17
|
-
|
|
18
|
-
## The three sources of checks (merge all three)
|
|
19
|
-
|
|
20
|
-
1. **Executable checks — `npx looop test`.** Discovers and runs every
|
|
21
|
-
`*.test.mjs` and `*.smoke.mjs` in this folder against a real dev stack.
|
|
22
|
-
The files ARE the list; always the whole gate, not just new tests.
|
|
23
|
-
2. **This game's earned checks — `handbook/qa.md`.** Procedural steps this
|
|
24
|
-
specific game accumulated (past playtest catches, fragile spots).
|
|
25
|
-
3. **The engine master list** — the doc above. Generic steps every Looop game
|
|
26
|
-
is checked against.
|
|
27
|
-
|
|
28
|
-
## Procedure
|
|
29
|
-
|
|
30
|
-
1. **Scope the change** — which surfaces did it touch? That selects which
|
|
31
|
-
master-list rows and handbook steps apply.
|
|
32
|
-
2. **Run every automated step that applies**, from all three sources. Where
|
|
33
|
-
the master doc calls for fresh reviewer or adjudicator sub-agents, spawn
|
|
34
|
-
them yourself with the briefs it describes.
|
|
35
|
-
3. **Report results concretely** — how you verified, not "tested": suite
|
|
36
|
-
counts, which URL you drove, what the screenshot showed, findings resolved.
|
|
37
|
-
4. **Emit the minimal human list.** Only feel/look/judgment items and things
|
|
38
|
-
you genuinely couldn't verify — each against a game that is **already
|
|
39
|
-
running** (start or reuse the dev stack yourself; the creator gets the
|
|
40
|
-
URL to open, never a command to run). If everything was automatable, say
|
|
41
|
-
so plainly; "couldn't test it" must never read as "tested and fine".
|
|
42
|
-
5. **Close the ratchet.** If the creator's pass catches something your run
|
|
43
|
-
missed, that's a hole in the checks — run `/handbook` to convert
|
|
44
|
-
that defect class into an automated check.
|
|
45
|
-
|
|
46
|
-
## Notes
|
|
47
|
-
|
|
48
|
-
- `/qa` verifies; it never commits or publishes.
|
|
49
|
-
- Smokes drive a real browser with Playwright — **already in this repo's
|
|
50
|
-
devDependencies** (`looop create` ships it and pre-fetches Chromium, and
|
|
51
|
-
`looop test` re-fetches the browser if the cache was pruned). Only a repo
|
|
52
|
-
scaffolded before playwright shipped needs the one-time
|
|
53
|
-
`npm i -D playwright && npx playwright install chromium`.
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: todo
|
|
3
|
-
description: Capture a bug, idea, or improvement for later without acting on it now. Use when the creator (or you, mid-build) spots something worth remembering — "we should fix that jump later", "idea — a boss level", "note that down" — and when they want to see what's captured or start work on one.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# /todo — capture now, decide later
|
|
7
|
-
|
|
8
|
-
A **todo** is the one lightweight capture type in this repo: bug, idea,
|
|
9
|
-
improvement — all the same thing until someone decides to act. Capturing must
|
|
10
|
-
be nearly free; never derail the current work to file one.
|
|
11
|
-
|
|
12
|
-
## Capture
|
|
13
|
-
|
|
14
|
-
Write one small file per todo at `notes/todos/<YYYY-MM-DD-HHMM>-<short-slug>.md`
|
|
15
|
-
(creation datetime first, always — the whole `notes/` tree names files this way
|
|
16
|
-
so they list in order):
|
|
17
|
-
|
|
18
|
-
```markdown
|
|
19
|
-
---
|
|
20
|
-
status: open # open | done | promoted | abandoned
|
|
21
|
-
created: YYYY-MM-DD
|
|
22
|
-
---
|
|
23
|
-
# <One-line summary>
|
|
24
|
-
|
|
25
|
-
What was seen / imagined, in a couple of sentences. Enough that a future
|
|
26
|
-
session understands it cold. Reference files by path if relevant.
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Then get back to what you were doing. If the creator says something mid-build
|
|
30
|
-
that is clearly a "later" item, offer to capture it in one line — don't stop
|
|
31
|
-
the milestone.
|
|
32
|
-
|
|
33
|
-
## Browse
|
|
34
|
-
|
|
35
|
-
"What's on the list?" → read `notes/todos/`, summarize the open ones in a few
|
|
36
|
-
lines each. Group bugs vs ideas by content, not by folder — there is
|
|
37
|
-
deliberately only one folder.
|
|
38
|
-
|
|
39
|
-
## Promote
|
|
40
|
-
|
|
41
|
-
When the creator decides to actually DO a todo, it becomes a **plan**: start
|
|
42
|
-
`/build`, which creates the plan in `notes/plans/` through its discussion
|
|
43
|
-
phase, seeded from the todo's content. Mark the todo `status: promoted` with a
|
|
44
|
-
line pointing at the plan file. Small fixes don't need promotion — just do
|
|
45
|
-
them and mark the todo `done`.
|
|
46
|
-
|
|
47
|
-
## Rules
|
|
48
|
-
|
|
49
|
-
- Never delete a todo — mark it `done` (with a line saying what happened),
|
|
50
|
-
`promoted`, or — when the creator decides NOT to do it — `abandoned` **with
|
|
51
|
-
a one-line why**. The why is the value: it saves re-deciding the same thing
|
|
52
|
-
next month. "Won't do" and "did it" must never collapse into one status.
|
|
53
|
-
- A todo about **Looop itself** (the engine, the CLI, the platform — not this
|
|
54
|
-
game) is `/feedback`'s job, not a todo.
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: update-looop
|
|
3
|
-
description: Update Looop itself — the engine, and the skills and instructions this repo runs on. Use when the creator asks for the latest Looop ("is there an update?", "update looop", "get the newest version"), when a Looop feature they've heard about is missing, or when something looks like a Looop bug that may already be fixed. Not for updating their GAME — that's just building.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# /update-looop — get the latest Looop, and explain what changed
|
|
7
|
-
|
|
8
|
-
`npx looop update` moves this game to the newest Looop release. It is the **only**
|
|
9
|
-
command that writes to Looop's files inside this repo — `dev` and `publish` never
|
|
10
|
-
do. That is deliberate: an update is something the creator *asks* for.
|
|
11
|
-
|
|
12
|
-
But it does write into **their** repo. After it runs, `git status` shows changes
|
|
13
|
-
they did not make. **Your job is to make sure that is never a surprise.**
|
|
14
|
-
|
|
15
|
-
## Before
|
|
16
|
-
|
|
17
|
-
Tell them what an update touches, in one or two sentences:
|
|
18
|
-
|
|
19
|
-
- **The engine** — the game code they import from (`/shared/...`). Their game
|
|
20
|
-
keeps running on the old engine until they publish again, so an update is safe
|
|
21
|
-
to take at any time.
|
|
22
|
-
- **The skills and instructions** — the files in `.claude/skills/` and the Looop
|
|
23
|
-
section of `AGENTS.md`. These are Looop's, living in their repo.
|
|
24
|
-
- **Never their game.** Not `index.html`, not `game.js`, not `handbook/`, not
|
|
25
|
-
`notes/`. And never a skill they wrote, or an edit they made to one of ours —
|
|
26
|
-
where they've changed something, their version wins and the update says so.
|
|
27
|
-
|
|
28
|
-
If they have uncommitted work, say so first — the update's changes will land
|
|
29
|
-
alongside it, and a mixed diff is harder to read. Offer to save theirs first.
|
|
30
|
-
|
|
31
|
-
## Run it
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npx looop update
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
It prints exactly what it did: every file updated, every file removed, and every
|
|
38
|
-
file it **kept because the creator had changed it**. Read that output — it is the
|
|
39
|
-
source of truth for the next step, not your assumptions about what a release
|
|
40
|
-
contains.
|
|
41
|
-
|
|
42
|
-
## After — the part that matters
|
|
43
|
-
|
|
44
|
-
**Report the change in their terms, not ours.** Don't paste the file list back at
|
|
45
|
-
them; tell them what is now different about what you and they can do:
|
|
46
|
-
|
|
47
|
-
> *"Looop updated to engine 0.1.8. The `/update-handbook` skill was replaced by a
|
|
48
|
-
> broader `/handbook` — same job, plus it now keeps your game's vision and design
|
|
49
|
-
> pillars. Your own `/my-thing` skill was left alone."*
|
|
50
|
-
|
|
51
|
-
Then **offer the commit** — never run it unasked:
|
|
52
|
-
|
|
53
|
-
> *"That changed 3 of Looop's files in your repo. Want me to save them?
|
|
54
|
-
> (`git add -A && git commit -m "update Looop to engine 0.1.8"`)"*
|
|
55
|
-
|
|
56
|
-
Two things to volunteer without being asked:
|
|
57
|
-
|
|
58
|
-
- **If they'd edited a Looop file**, the update kept *their* version and did not
|
|
59
|
-
apply ours. Say which file, and that they may want to re-apply their change on
|
|
60
|
-
top of the new one — otherwise they'll silently miss the update to that file
|
|
61
|
-
forever.
|
|
62
|
-
- **If the update mentions a backup folder** (`.looop/backup-<version>/`), that
|
|
63
|
-
is the pre-update copy of files whose history we couldn't be sure of. Tell them
|
|
64
|
-
it exists. They can delete it once they're happy.
|
|
65
|
-
|
|
66
|
-
## Rules
|
|
67
|
-
|
|
68
|
-
- **Never run `looop update` as a side effect of something else.** It is its own
|
|
69
|
-
act, with its own consent. If a bug looks like it might already be fixed
|
|
70
|
-
upstream, *propose* the update — don't just do it mid-build.
|
|
71
|
-
- **Never commit on their behalf** without asking, even when the only changes are
|
|
72
|
-
Looop's own files. It is their repo and their history.
|
|
73
|
-
- The live game at `play.looop.games` does **not** change until they run
|
|
74
|
-
`npx looop publish`. Say so — creators reasonably assume an update is live.
|
package/template/AGENTS.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# {{name}} — a Looop game
|
|
2
|
-
|
|
3
|
-
<!-- looop:managed:start -->
|
|
4
|
-
<!-- Everything between the looop:managed markers belongs to the Looop
|
|
5
|
-
platform and is REWRITTEN when the engine updates. Add your own
|
|
6
|
-
instructions BELOW the end marker — updates never touch them. -->
|
|
7
|
-
|
|
8
|
-
This folder is a standalone Looop game. You (the agent) build it for a person
|
|
9
|
-
who is probably **not a developer** — the "creator". They describe the game in
|
|
10
|
-
plain language and make the judgment calls; you own everything else: process,
|
|
11
|
-
code, and verification. They should never need to learn git, npm, or servers.
|
|
12
|
-
|
|
13
|
-
## Where things go
|
|
14
|
-
|
|
15
|
-
The workflows live as **skills** in `.claude/skills/` (aliased at
|
|
16
|
-
`.agents/skills/` — same files); each skill's `description` says when it
|
|
17
|
-
applies. This table routes only what has no skill:
|
|
18
|
-
|
|
19
|
-
| The creator wants… | Do this |
|
|
20
|
-
|---|---|
|
|
21
|
-
| To "save my work" | `git add -A && git commit` (and push, if the repo has a remote). Saving is local; it is **not** publishing. |
|
|
22
|
-
| To update Looop / the engine | `/update-looop`. It is the ONLY thing that writes Looop's files into this repo (`.claude/skills/`, the managed block of this file) — `dev` and `publish` never do, so an update is never a surprise in their git. The live game changes only on the next publish. |
|
|
23
|
-
| Better game feel (tuning speeds, jumps, timings) | The engine's **tweaks** library (`node_modules/@looop-games/engine/shared/ui/tweaks/`) + `shared/practices/feel.md`. |
|
|
24
|
-
| To change how an engine file behaves | Copy it to `overrides/shared/<same path>` and edit the copy — dev and publish serve your override instead of the official module. Imports stay `/shared/...`; never rewrite them. An override is a fork: that file stops receiving engine updates until you re-port it. |
|
|
25
|
-
|
|
26
|
-
## Commands (run from this folder)
|
|
27
|
-
|
|
28
|
-
- `npx looop dev` — the full local stack: the game at
|
|
29
|
-
http://localhost:8000/games/{{name}}/index.html, multiplayer on :1999,
|
|
30
|
-
platform services (LLM etc.) on :8788. Leave it running; edits hot-reload.
|
|
31
|
-
**On a new machine the first `dev` signs the creator in: a browser window
|
|
32
|
-
opens, they log in and click Approve (~30 seconds, once per machine). Tell
|
|
33
|
-
them it's coming BEFORE you run it** — an unexplained browser popup mid-build
|
|
34
|
-
reads as something going wrong.
|
|
35
|
-
- `npx looop test` — the per-change gate: runs every `*.test.mjs` (unit, under
|
|
36
|
-
node) and every `*.smoke.mjs` (against a real dev stack it boots itself on
|
|
37
|
-
free ports) in this folder.
|
|
38
|
-
- `npx looop login` — connect this machine to the creator's Looop account
|
|
39
|
-
(once per machine; the first `looop dev` runs it automatically).
|
|
40
|
-
- `npx looop publish` — ship to https://play.looop.games/g/{{name}}.
|
|
41
|
-
**The first publish permanently claims the name `{{name}}` for the creator's
|
|
42
|
-
account** — from then on only they can update it. `--slug <alt>` publishes an
|
|
43
|
-
A/B copy without touching the live game.
|
|
44
|
-
|
|
45
|
-
## The rules that are physics (not preference)
|
|
46
|
-
|
|
47
|
-
1. **Multiplayer-first.** Any state other players can see (positions, physics,
|
|
48
|
-
scores, world objects) rides the room's authority — `room.update(...)`,
|
|
49
|
-
`room.send('input', ...)` — never local mutation only one client sees.
|
|
50
|
-
Verify multiplayer behaviour with TWO browser contexts, not one.
|
|
51
|
-
2. **The engine is read-only — and it is not the ceiling.** `/shared/...`
|
|
52
|
-
imports come from the installed engine at `node_modules/@looop-games/engine`
|
|
53
|
-
(not an npm dependency — `looop dev` downloads the version pinned in
|
|
54
|
-
`package.json`'s `looop.engine` and reinstalls it if an `npm install` prunes
|
|
55
|
-
it). Never edit files in `node_modules` — use `overrides/shared/` (see the
|
|
56
|
-
table above). But read-only means *don't edit it*, **not** *don't exceed
|
|
57
|
-
it*: a Looop game is a web page, so **anything the browser can do, this game
|
|
58
|
-
can do.** The library is a bag of conveniences you draw from, not a fence
|
|
59
|
-
around what the game may be — what it doesn't have, you build, here in the
|
|
60
|
-
game folder. **Never tell the creator that Looop "can't" do something**
|
|
61
|
-
(see `/engine` — it is almost always false, and it retires their idea
|
|
62
|
-
without their consent).
|
|
63
|
-
3. **Never hand-roll a server.** Only `looop dev` (or `looop test`) serves
|
|
64
|
-
this game: they alias `/shared/...` and inject the platform layer. A plain
|
|
65
|
-
static server 404s every engine import and the game silently never boots.
|
|
66
|
-
4. **Never handle tokens or keys.** Login is a browser device flow; platform
|
|
67
|
-
services (LLM etc.) are already wired through the local shim. If something
|
|
68
|
-
seems to need an API key, you're on the wrong path.
|
|
69
|
-
5. **Read what's already known before writing game code.** First this game's
|
|
70
|
-
own **`handbook/`** (its locked truths — qa.md, feel.md, design.md), then
|
|
71
|
-
the engine craft docs at
|
|
72
|
-
`node_modules/@looop-games/engine/shared/practices/` — `feel.md`,
|
|
73
|
-
`architecture.md`, `multiplayer.md`, `qa.md` (and `eval.md` when anything
|
|
74
|
-
is LLM-generated), which apply to every game.
|
|
75
|
-
The handbook augments the generic docs; where they touch the same topic,
|
|
76
|
-
the handbook is this game's answer.
|
|
77
|
-
6. **Anything with an assertion is a regression test.** Playwright checks live
|
|
78
|
-
in `<aspect>.smoke.mjs` files (excluded from publish, discovered by
|
|
79
|
-
`looop test`). Keep them; delete only assertion-free scratch.
|
|
80
|
-
|
|
81
|
-
## This repo's own knowledge
|
|
82
|
-
|
|
83
|
-
- **`handbook/`** — durable truth about THIS game: `qa.md` (its checks),
|
|
84
|
-
`feel.md` (locked feel values), `design.md` (its pillars). Consult it before
|
|
85
|
-
working; grow it with `/handbook`.
|
|
86
|
-
- **`notes/`** — work tracking: `notes/plans/` (what's being built — `/build`
|
|
87
|
-
runs from these), `notes/todos/` (captured bugs/ideas), and
|
|
88
|
-
`notes/feedback/` (reports for the Looop team, written by `/feedback`).
|
|
89
|
-
Nothing under `notes/` is ever published.
|
|
90
|
-
|
|
91
|
-
<!-- looop:managed:end -->
|
|
92
|
-
|
|
93
|
-
<!-- Your own instructions go below this line. -->
|
package/template/CLAUDE.md
DELETED
package/template/GEMINI.md
DELETED
package/template/boot.smoke.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Boot smoke — the game's first regression test, shipped with the scaffold.
|
|
2
|
-
// Proves the dev stack serves the game page AND the engine alias (a plain
|
|
3
|
-
// static server would 404 every /shared/... import and the game would
|
|
4
|
-
// silently never boot). Runs under `npx looop test`, which boots a real dev
|
|
5
|
-
// stack and exports its URL. Grow your own checks as `<aspect>.smoke.mjs`
|
|
6
|
-
// files next to this one.
|
|
7
|
-
const url = process.env.LOOOP_TEST_GAME_URL || process.env.URL;
|
|
8
|
-
if (!url) {
|
|
9
|
-
console.error('boot smoke: no game URL — run this via `npx looop test`.');
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const page = await fetch(url);
|
|
14
|
-
if (!page.ok) throw new Error(`game page: ${page.status} for ${url}`);
|
|
15
|
-
if (!(await page.text()).includes('<canvas')) throw new Error('game page served, but no canvas in it');
|
|
16
|
-
|
|
17
|
-
const engineModule = new URL('/shared/ui/room/client.js', url);
|
|
18
|
-
const engine = await fetch(engineModule);
|
|
19
|
-
if (!engine.ok) throw new Error(`engine alias broken: ${engineModule.pathname} → ${engine.status}`);
|
|
20
|
-
|
|
21
|
-
console.log('boot smoke: game page + engine alias OK');
|
package/template/game.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// A tiny multiplayer plaza — walk around, see everyone else.
|
|
2
|
-
// This is a REAL Looop game: it already syncs across browsers and works
|
|
3
|
-
// published. Reshape it into your game; the room + identity plumbing stays.
|
|
4
|
-
import { createRoom, getIdentity } from '/shared/ui/room/client.js';
|
|
5
|
-
|
|
6
|
-
const me = getIdentity(); // injected by the platform (fails closed if absent)
|
|
7
|
-
const canvas = document.getElementById('game');
|
|
8
|
-
const ctx = canvas.getContext('2d');
|
|
9
|
-
const size = () => { canvas.width = innerWidth; canvas.height = innerHeight; };
|
|
10
|
-
addEventListener('resize', size); size();
|
|
11
|
-
|
|
12
|
-
const self = { name: me.name, color: me.color, x: Math.random() * 400 - 200, y: Math.random() * 400 - 200 };
|
|
13
|
-
// One room per game: the slug (injected by the serve path) is the room id.
|
|
14
|
-
const room = createRoom({ room: window.GAME_SLUG ?? 'dev', self });
|
|
15
|
-
|
|
16
|
-
let others = new Map();
|
|
17
|
-
room.onPlayers = (players) => { others = new Map(players); };
|
|
18
|
-
// Handy for smokes: how many players does this client see (self included)?
|
|
19
|
-
window.__looopPlayers = () => others.size + 1;
|
|
20
|
-
|
|
21
|
-
const keys = new Set();
|
|
22
|
-
addEventListener('keydown', (e) => keys.add(e.key.toLowerCase()));
|
|
23
|
-
addEventListener('keyup', (e) => keys.delete(e.key.toLowerCase()));
|
|
24
|
-
|
|
25
|
-
const SPEED = 220; // px/s — tune the feel!
|
|
26
|
-
let last = performance.now();
|
|
27
|
-
function tick(now) {
|
|
28
|
-
const dt = Math.min((now - last) / 1000, 0.05); last = now;
|
|
29
|
-
const dx = (keys.has('d') || keys.has('arrowright')) - (keys.has('a') || keys.has('arrowleft'));
|
|
30
|
-
const dy = (keys.has('s') || keys.has('arrowdown')) - (keys.has('w') || keys.has('arrowup'));
|
|
31
|
-
if (dx || dy) {
|
|
32
|
-
const n = Math.hypot(dx, dy);
|
|
33
|
-
self.x += (dx / n) * SPEED * dt;
|
|
34
|
-
self.y += (dy / n) * SPEED * dt;
|
|
35
|
-
room.update({ x: self.x, y: self.y });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
ctx.fillStyle = '#0f1220';
|
|
39
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
40
|
-
const cx = canvas.width / 2, cy = canvas.height / 2;
|
|
41
|
-
const drawAvatar = (p) => {
|
|
42
|
-
ctx.fillStyle = p.color ?? '#888';
|
|
43
|
-
ctx.fillRect(cx + (p.x ?? 0) - self.x - 14, cy + (p.y ?? 0) - self.y - 14, 28, 28);
|
|
44
|
-
ctx.fillStyle = '#e7e9f4';
|
|
45
|
-
ctx.font = '12px system-ui';
|
|
46
|
-
ctx.textAlign = 'center';
|
|
47
|
-
ctx.fillText(p.name ?? '?', cx + (p.x ?? 0) - self.x, cy + (p.y ?? 0) - self.y - 22);
|
|
48
|
-
};
|
|
49
|
-
for (const p of others.values()) drawAvatar(p);
|
|
50
|
-
drawAvatar(self);
|
|
51
|
-
requestAnimationFrame(tick);
|
|
52
|
-
}
|
|
53
|
-
requestAnimationFrame(tick);
|
package/template/gitignore
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Design — the pillars
|
|
2
|
-
|
|
3
|
-
The rules that follow from the vision: the principles new ideas get checked
|
|
4
|
-
against. Written as
|
|
5
|
-
they emerge from real decisions (via `/handbook`), not invented up
|
|
6
|
-
front — an empty file is honest; a speculative pillar is a trap.
|
|
7
|
-
|
|
8
|
-
_No pillars yet._
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Feel — locked values
|
|
2
|
-
|
|
3
|
-
Feel calls the creator has blessed: the value, where it lives, and why it's
|
|
4
|
-
right — so no future session "improves" them away. General feel craft lives in
|
|
5
|
-
the engine's `shared/practices/feel.md`; this file is only what THIS game has
|
|
6
|
-
locked. `/handbook` adds entries.
|
|
7
|
-
|
|
8
|
-
_Nothing locked yet._
|
package/template/handbook/qa.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# QA — this game's own checks
|
|
2
|
-
|
|
3
|
-
Checks THIS game has earned, on top of the engine's master list
|
|
4
|
-
(`node_modules/@looop-games/engine/shared/practices/qa.md`) and the executable
|
|
5
|
-
tests `npx looop test` discovers. `/qa` runs all three sources; `/handbook`
|
|
6
|
-
adds entries here when a playtest catches something no automated check saw —
|
|
7
|
-
but prefer writing a `*.smoke.mjs` when the check can be executed.
|
|
8
|
-
|
|
9
|
-
_No game-specific checks yet. The first playtest catch starts this list._
|
package/template/index.html
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<!-- Mobile baseline (engine practices, architecture.md): locked viewport,
|
|
6
|
-
no double-tap/pinch zoom, no text selection on game UI. Required on
|
|
7
|
-
every Looop game — keep all four pieces when reshaping this file. -->
|
|
8
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
9
|
-
<title>{{name}}</title>
|
|
10
|
-
<style>
|
|
11
|
-
html, body { margin: 0; height: 100%; background: #0f1220; overflow: hidden; touch-action: manipulation; }
|
|
12
|
-
* { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; }
|
|
13
|
-
input, textarea, [contenteditable] { -webkit-user-select: text; user-select: text; }
|
|
14
|
-
canvas { display: block; width: 100vw; height: 100vh; }
|
|
15
|
-
</style>
|
|
16
|
-
<script>
|
|
17
|
-
// Safari ignores user-scalable=no — block iOS gesture-zoom too.
|
|
18
|
-
for (const ev of ['gesturestart', 'gesturechange', 'gestureend'])
|
|
19
|
-
document.addEventListener(ev, (e) => e.preventDefault());
|
|
20
|
-
</script>
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<canvas id="game"></canvas>
|
|
24
|
-
<script type="module" src="game.js"></script>
|
|
25
|
-
</body>
|
|
26
|
-
</html>
|