@joystack/cli 0.1.43 → 0.1.44

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 CHANGED
@@ -14,6 +14,14 @@ Versions: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
14
14
 
15
15
  ## [Unreleased]
16
16
 
17
+ ## [0.1.44] - 2026-10-09
18
+
19
+ ### Added
20
+
21
+ - When `joystack update` moves a game made before the rename, it ends with a
22
+ short checklist for your agent: the links, tests and publishing tokens in
23
+ your game's own files that the update cannot move itself.
24
+
17
25
  ### Removed
18
26
 
19
27
  - `joystack dev` no longer starts a services proxy on port 8788. Nothing in the
@@ -22,6 +30,9 @@ Versions: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
22
30
 
23
31
  ### Changed
24
32
 
33
+ - `joystack create` marks the new game as ES modules (`"type": "module"` in
34
+ package.json), so `joystack publish` no longer prints Node's
35
+ `MODULE_TYPELESS_PACKAGE_JSON` warning.
25
36
  - **The CLI is `@joystack/cli`, and its command is `joystack`** (`npx joystack
26
37
  dev`, `npx joystack test`, `npx joystack publish`, …). Nothing in your game has
27
38
  to change: the previous command name runs the same CLI, so the scripts in your
package/lib/create.mjs CHANGED
@@ -112,6 +112,8 @@ async function scaffold({ dir, name, install, cliSpec, apiBase, log, ensure, rec
112
112
  {
113
113
  name,
114
114
  private: true,
115
+ // Every file a game is made of is an ES module.
116
+ type: 'module',
115
117
  description: `A Joystack game. Play at https://${name}.joystack.games/`,
116
118
  // The scaffold is a framework-v2 game (entities + traits + a world root,
117
119
  // lowered to the engine runtime on this machine). This flag is what makes
@@ -244,3 +244,32 @@ export const LEGACY_MANAGED_MARKERS = Object.freeze({
244
244
  start: `<!-- ${OLD}:managed:start -->`,
245
245
  end: `<!-- ${OLD}:managed:end -->`,
246
246
  });
247
+
248
+ // Whether this update moved a game made under the old name: the CLI dependency
249
+ // was switched over, or the old update skill was retired. `cli` and `surface`
250
+ // are what update.mjs got from syncCli and the agent-surface reconcile.
251
+ export function movedFromLegacy(cli, surface) {
252
+ return Boolean(cli?.switched) || (surface?.removed ?? []).some((f) => /(^|\/)skills\/update-looop(\/|$)/.test(f));
253
+ }
254
+
255
+ // What `joystack update` prints after such a move, once. It is addressed to
256
+ // the creator's agent, which reads the update's output: the update moved what
257
+ // is Joystack's, and these are what the game itself may still say about Looop.
258
+ export const MOVED_FROM_LEGACY_NOTE = [
259
+ 'This game just moved from Looop to Joystack. The update moved what is',
260
+ "Joystack's; check the game's own files for what it could not move, and",
261
+ 'fix what you find before calling the update done:',
262
+ ' • Links to play.looop.games/g/<slug>: the game lives at',
263
+ ' https://<slug>.joystack.games/ from its first `npx joystack publish`.',
264
+ ' The Looop copy stays as it was.',
265
+ ' • Tests or scripts matching `[looop]`, `looop:` keys or `.looop/`: the',
266
+ ' engine now writes `[joystack]`, `joystack:` and `.joystack/`.',
267
+ ' • Automation that publishes (GitHub Actions, scripts) with a Looop token:',
268
+ ' Joystack does not accept it. Make one for the job by signing in with an',
269
+ ' empty folder (mktemp -d) as the home: `JOYSTACK_HOME=<folder> npx joystack login`.',
270
+ ' With the creator\'s OK, store it without ever displaying it:',
271
+ ' node -e "process.stdout.write(require(\'<folder>/config.json\').token)" | gh secret set <NAME>',
272
+ ' Without gh, pipe the same into pbcopy (Mac) and walk them through',
273
+ ' GitHub → Settings → Secrets and variables → Actions → <NAME> → Update.',
274
+ ' Then delete the folder.',
275
+ ];
package/lib/update.mjs CHANGED
@@ -16,6 +16,7 @@ import { login } from './login.mjs';
16
16
  import { compareVersions, renderReleases } from './changelog.mjs';
17
17
  import { syncCli, reportCli } from './self-update.mjs';
18
18
  import { fetchStatus, landedIn } from './feedback-sync.mjs';
19
+ import { MOVED_FROM_LEGACY_NOTE, movedFromLegacy } from './legacy-names.mjs';
19
20
 
20
21
  // The report is the point. A creator reading this must be able to answer, with
21
22
  // no further digging: what changed, was any of it mine, and what do I do now.
@@ -69,6 +70,20 @@ function report(log, engineVersion, surface) {
69
70
  log(" changes you didn't make. Review them and save them with your next commit.");
70
71
  }
71
72
 
73
+ // A game made before the rename moves over in this update. What is Joystack's
74
+ // has moved; what the game itself says (its links, its tests, a token its
75
+ // automation publishes with) has not, and the note lists it for the agent
76
+ // reading this output. Neither condition holds on a later update, so it is
77
+ // printed this once only.
78
+ function noteMove(log, cli, surface) {
79
+ const moved = movedFromLegacy(cli, surface);
80
+ if (moved) {
81
+ log('');
82
+ for (const line of MOVED_FROM_LEGACY_NOTE) log(` ${line}`);
83
+ }
84
+ return moved;
85
+ }
86
+
72
87
  export async function update({
73
88
  cwd = process.cwd(),
74
89
  apiBase = getApiBase(DEFAULT_API_BASE),
@@ -146,7 +161,8 @@ export async function update({
146
161
  log(' This is a pre-release candidate — its changelog and any migration');
147
162
  log(' notes ship inside the engine (read the handbook / engine docs), not');
148
163
  log(' via `joystack changelog`. Republish (`npx joystack publish`) when ready.');
149
- return { from, to: engine.version, updated: from !== engine.version, surface, crossed: null, cli, candidate: true };
164
+ const movedIn = noteMove(log, cli, surface);
165
+ return { from, to: engine.version, updated: from !== engine.version, surface, crossed: null, cli, candidate: true, movedIn };
150
166
  }
151
167
 
152
168
  const res = await fetchImpl(`${apiBase}/api/creator/engine`, {
@@ -295,5 +311,6 @@ export async function update({
295
311
  log('');
296
312
  log(' Republish (`npx joystack publish`) when you want the live game on it.');
297
313
  }
298
- return { from, to: latest, updated, surface, crossed, cli, landed };
314
+ const movedIn = noteMove(log, cli, surface);
315
+ return { from, to: latest, updated, surface, crossed, cli, landed, movedIn };
299
316
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joystack/cli",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
4
4
  "description": "Joystack game development CLI \u2014 dev server, login, and publishing for standalone Joystack games.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",