@bitmagic/cli 0.1.65-dev.24 → 0.1.65-dev.3

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 (71) hide show
  1. package/README.md +14 -137
  2. package/dist/assets/materials.d.ts +0 -9
  3. package/dist/assets/materials.js +2 -10
  4. package/dist/assets/materials.js.map +1 -1
  5. package/dist/cli.d.ts +0 -9
  6. package/dist/cli.js +0 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/forge.d.ts +0 -15
  9. package/dist/commands/forge.js +38 -143
  10. package/dist/commands/forge.js.map +1 -1
  11. package/dist/commands/generate.d.ts +13 -31
  12. package/dist/commands/generate.js +56 -48
  13. package/dist/commands/generate.js.map +1 -1
  14. package/dist/commands/trailer.js +1 -1
  15. package/dist/commands/trailer.js.map +1 -1
  16. package/dist/editor/animation-preview-page.js +1 -88
  17. package/dist/editor/animation-preview-page.js.map +1 -1
  18. package/dist/editor/asset-detail-panel.d.ts +2 -2
  19. package/dist/editor/asset-detail-panel.js +0 -21
  20. package/dist/editor/asset-detail-panel.js.map +1 -1
  21. package/dist/editor/asset-viewer.js +8 -31
  22. package/dist/editor/asset-viewer.js.map +1 -1
  23. package/dist/editor/shell-page.js +1 -63
  24. package/dist/editor/shell-page.js.map +1 -1
  25. package/dist/forge/run-pipeline.js +1 -5
  26. package/dist/forge/run-pipeline.js.map +1 -1
  27. package/dist/forge/stream.d.ts +1 -5
  28. package/dist/forge/stream.js +0 -4
  29. package/dist/forge/stream.js.map +1 -1
  30. package/dist/mobile/asset-hosts.d.ts +9 -7
  31. package/dist/mobile/asset-hosts.js +16 -7
  32. package/dist/mobile/asset-hosts.js.map +1 -1
  33. package/dist/scaffold/blender-level-skill-files.js +2 -3
  34. package/dist/scaffold/blender-level-skill-files.js.map +1 -1
  35. package/dist/scaffold/claude-settings.js +0 -4
  36. package/dist/scaffold/claude-settings.js.map +1 -1
  37. package/dist/scaffold/project-files.js +12 -18
  38. package/dist/scaffold/project-files.js.map +1 -1
  39. package/dist/scaffold/project.js +1 -4
  40. package/dist/scaffold/project.js.map +1 -1
  41. package/dist/scaffold/skill-files.d.ts +5 -32
  42. package/dist/scaffold/skill-files.js +63 -102
  43. package/dist/scaffold/skill-files.js.map +1 -1
  44. package/dist/telemetry/creator-prompt.d.ts +2 -18
  45. package/dist/telemetry/creator-prompt.js +3 -24
  46. package/dist/telemetry/creator-prompt.js.map +1 -1
  47. package/dist/telemetry/report.js +16 -31
  48. package/dist/telemetry/report.js.map +1 -1
  49. package/package.json +4 -4
  50. package/assets/skills/trailer-craft.md +0 -180
  51. package/dist/commands/character.d.ts +0 -18
  52. package/dist/commands/character.js +0 -268
  53. package/dist/commands/character.js.map +0 -1
  54. package/dist/forge/edit-target.d.ts +0 -30
  55. package/dist/forge/edit-target.js +0 -88
  56. package/dist/forge/edit-target.js.map +0 -1
  57. package/dist/forge/prune-store.d.ts +0 -46
  58. package/dist/forge/prune-store.js +0 -86
  59. package/dist/forge/prune-store.js.map +0 -1
  60. package/dist/forge/remote-store.d.ts +0 -36
  61. package/dist/forge/remote-store.js +0 -113
  62. package/dist/forge/remote-store.js.map +0 -1
  63. package/dist/generate/character-body.d.ts +0 -79
  64. package/dist/generate/character-body.js +0 -100
  65. package/dist/generate/character-body.js.map +0 -1
  66. package/dist/project/character-search.d.ts +0 -30
  67. package/dist/project/character-search.js +0 -82
  68. package/dist/project/character-search.js.map +0 -1
  69. package/dist/scaffold/asset-pipeline-skill-files.d.ts +0 -42
  70. package/dist/scaffold/asset-pipeline-skill-files.js +0 -207
  71. package/dist/scaffold/asset-pipeline-skill-files.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"creator-prompt.js","sourceRoot":"","sources":["../../src/telemetry/creator-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,sGAAsG;AACtG,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,oFAAoF;UAC7F,iEAAiE;CAC7D,CAAC;AAEX;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,MAAM,KAAK,GAAG,QAAQ,IAAI,cAAc,EAAE,EAAE,aAAa,CAAC;IAC1D,oFAAoF;IACpF,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,IAA6B;IACtD,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,gGAAgG;IAChG,8FAA8F;IAC9F,OAAO,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,QAAkB;IAC/C,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,CAAC,eAAe,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"creator-prompt.js","sourceRoot":"","sources":["../../src/telemetry/creator-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,sGAAsG;AACtG,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,oFAAoF;UAC7F,iEAAiE;CAC7D,CAAC;AAEX;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,MAAM,KAAK,GAAG,QAAQ,IAAI,cAAc,EAAE,EAAE,aAAa,CAAC;IAC1D,oFAAoF;IACpF,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,IAA6B;IACtD,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,gGAAgG;IAChG,8FAA8F;IAC9F,OAAO,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,CAAC,eAAe,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7G,CAAC"}
@@ -16,19 +16,11 @@
16
16
  * bounded to {@link REPORT_TIMEOUT_MS} per request so a finished command is
17
17
  * never held open by a sulking network (the same reasoning as
18
18
  * update/check.ts). It never prints.
19
- * 2. Its ANALYTICS are categories, never messages: the command name, ok/failed,
20
- * the exit code, a `CliError.code`, and a duration. A message can name a
21
- * path; that does not belong in an analytics event. The game id keeps the
22
- * rule (it is an id we minted) and so does the ask DIGEST (opaque by
23
- * construction), and neither reaches GA4.
24
- *
25
- * `creatorPrompt` is the one field that carries words, and it is not
26
- * analytics: api-server posts it to an internal Slack channel and stores it
27
- * nowhere — not GA4, not Postgres, not BigQuery. It is here because the
28
- * commands that send prompt text to a route are the generators, and an
29
- * agent building a GDK project spends most of its commands on the offline
30
- * ones. `DO_NOT_TRACK` drops it with everything else. See
31
- * {@link CommandProvenance}.
19
+ * 2. It sends categories, never messages: the command name, ok/failed, the
20
+ * exit code, a `CliError.code`, and a duration. A message can name a path;
21
+ * that does not belong in an analytics event. The two later additions keep
22
+ * the rule: the project's game id is an id we minted, and the creator's ask
23
+ * travels as an opaque DIGEST, never its words. See {@link CommandProvenance}.
32
24
  * 3. It is authenticated with the creator's own token, so the server can key
33
25
  * the event to the account and look up their real Pro state. No token, no
34
26
  * report — an anonymous beacon would be a spammable endpoint for nothing.
@@ -40,7 +32,7 @@ import { resolveAuth0ClientId } from '../config/environments.js';
40
32
  import { getAccessToken } from '../auth/session.js';
41
33
  import { readProjectGameIdOrNull } from '../project/context.js';
42
34
  import { apiServerHeaders } from './request-headers.js';
43
- import { creatorAskDigest, creatorAskText } from './creator-prompt.js';
35
+ import { creatorAskDigest } from './creator-prompt.js';
44
36
  import { currentCommand, isTelemetryDisabled } from './command-context.js';
45
37
  // Re-exported because this is where the opt-out is enforced for a report that is already
46
38
  // under way; `beginCommand` consults the same predicate to skip collecting anything at all.
@@ -93,28 +85,21 @@ function defaultDeps() {
93
85
  return { fetch: globalThis.fetch, env: process.env };
94
86
  }
95
87
  /**
96
- * The game and the ask, when this command has either. Keys are omitted rather than sent as null:
97
- * api-server validates the body strictly, and an absent key is how "nothing to say" is spelled
98
- * everywhere else on this wire.
99
- *
100
- * Nothing is present unless the command declared `--original-prompt` AND the agent passed it —
101
- * `creatorAskDigest` reads the value `beginCommand` captured, which is also where `DO_NOT_TRACK`
102
- * already dropped it.
88
+ * The game and the ask, when this command has either. Both keys are omitted rather than sent as
89
+ * null: api-server validates the body strictly, and an absent key is how "nothing to say" is
90
+ * spelled everywhere else on this wire.
103
91
  *
104
- * `gameId` and `askDigest` are only sent TOGETHER: a digest with no game cannot be counted, and a
105
- * game with no digest is not an ask. `creatorPrompt` is independent of that pair, because the
106
- * channel it feeds wants the ask from a command run before a game exists just as much — the pair
107
- * is about counting, and counting needs a game to count against.
92
+ * The digest is only ever present when the command declared `--original-prompt` AND the agent
93
+ * passed it — `creatorAskDigest` reads the value `beginCommand` captured, which is also where
94
+ * `DO_NOT_TRACK` already dropped it. The pair is only sent together: a digest with no game
95
+ * cannot be counted, and a game with no digest is not an ask.
108
96
  */
109
97
  function provenance(deps) {
110
- const creatorPrompt = creatorAskText();
111
- if (creatorPrompt === undefined)
98
+ const askDigest = creatorAskDigest();
99
+ if (askDigest === undefined)
112
100
  return {};
113
101
  const gameId = readProjectGameIdOrNull(deps.cwd);
114
- const askDigest = creatorAskDigest();
115
- if (gameId === undefined || askDigest === undefined)
116
- return { creatorPrompt };
117
- return { creatorPrompt, gameId, askDigest };
102
+ return gameId === undefined ? {} : { gameId, askDigest };
118
103
  }
119
104
  /**
120
105
  * Report the running command's `report` to api-server. Resolves when the report
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/telemetry/report.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/telemetry/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,oBAAoB,EAAoB,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhG,yFAAyF;AACzF,4FAA4F;AAC5F,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAE/B;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAEtC,MAAM,CAAC,MAAM,eAAe,GAAG,oBAAoB,CAAC;AA2CpD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,IAAI,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,OAAO,SAAS,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACvE,CAAC;AAKD,qEAAqE;AACrE,SAAS,YAAY,CAAC,SAAkC;IACtD,OAAO,CAAC,KAAiB,EAAE,IAAgB,EAAE,EAAE,CAC7C,SAAS,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;AAClF,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,kBAAkB,CAC/B,WAAwB,EACxB,OAAuB,EACvB,IAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,MAAM,cAAc,CACzB,WAAW,EACX,QAAQ,EACR;YACE,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;YAC/B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACtE,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,EACD,IAAI,CAAC,OAAO,CACb,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;QACvC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,CAAC;AACH,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;AACvD,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,IAAgB;IAClC,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC3D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAqB,EAAE,OAAmB,WAAW,EAAE;IAC9F,IAAI,CAAC;QACH,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO;QAC1C,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;QACjC,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO;QAE7B,2EAA2E;QAC3E,2EAA2E;QAC3E,oCAAoC;QACpC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACxC,IAAI,WAAW,KAAK,IAAI;YAAE,OAAO;QAEjC,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACnE,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO;QAE3B,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,GAAG,eAAe,EAAE,EAAE;YACxE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,GAAG,gBAAgB,EAAE;gBACrB,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;SAChF,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,kEAAkE;IACpE,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitmagic/cli",
3
- "version": "0.1.65-dev.24",
3
+ "version": "0.1.65-dev.3",
4
4
  "description": "Build Bitmagic games from your own agent tool",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "Bitmagic Oy",
@@ -25,9 +25,9 @@
25
25
  "playwright-core": "^1.59.1",
26
26
  "tar": "^7.4.3",
27
27
  "ws": "^8.21.3",
28
- "@bitmagic/animation-forger": "0.1.20-dev.24",
29
- "@bitmagic/asset-core": "0.2.20-dev.24",
30
- "@bitmagic/world-forger": "0.1.25-dev.24"
28
+ "@bitmagic/animation-forger": "0.1.20-dev.3",
29
+ "@bitmagic/asset-core": "0.2.20-dev.3",
30
+ "@bitmagic/world-forger": "0.1.25-dev.3"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@eslint/js": "^9.38.0",
@@ -1,180 +0,0 @@
1
- # Trailer craft — structure, pacing, and on-screen text
2
-
3
- Read this before composing a cut or putting any text on screen. Compressed
4
- from Derek Lieu's trailer-editing essays (trailer editor for Half-Life: Alyx,
5
- Dead Cells, Spelunky 2) plus general trailer-typography guidance; sources at
6
- the bottom.
7
-
8
- ## Ordering: Genre → Hook → Anchors → Content
9
-
10
- - **Genre** in 1–2 shots: the viewer must know what kind of game this is
11
- within the first seconds. For our games one gameplay shot usually does it.
12
- - **Hook** — the thing only this game has — as soon as genre is established:
13
- ideally inside the first 10–15 s, never later than the first third.
14
- Showing generic-but-good content before the hook makes the game read as
15
- "a lesser version of an old game".
16
- - **Anchors** — the features people expect from the genre (for a racer:
17
- overtakes, drifts, crashes, varied tracks) fill the middle.
18
- - **Content** — breadth (levels, modes, vehicles) comes last, once the viewer
19
- already cares.
20
-
21
- ## Timeline layout: constraints first, footage last
22
-
23
- Lay the timeline out as a skeleton and only then hunt for footage — not the
24
- other way round:
25
-
26
- 1. Write the section skeleton: **Cold open → Introduction → Escalation →
27
- Climax → Breath → Button/End card**. Sections have different lengths and
28
- energy; they are not equal slices.
29
- 2. Reserve the **end card** first: 2–3 s, ONE card (logo + tagline + CTA),
30
- never a sequence of closing screens.
31
- 3. Place any **body title cards** next, as placeholders with their copy
32
- written — each one names what the following footage must prove.
33
- 4. Only now fill footage, chosen to demonstrate whatever the neighbouring
34
- card or section claims.
35
-
36
- This turns "pick 15 great clips" (squishy, drifts toward sameness) into
37
- "find the one clip that proves this beat" (constrained, self-diversifying).
38
-
39
- ## Pacing
40
-
41
- - Sections need **ebb and flow**: build to a peak, then give a deliberate
42
- breathing shot (~5 s, calmer, wider) before the next build or the finale.
43
- The stops emphasize the fast; the fast emphasizes the slow.
44
- - **Rule of three**: never more than three similar things in a row — three
45
- crashes, three drift shots, three of anything. Viewers extrapolate after
46
- two; the fourth is dead time.
47
- - **Do not cut on every beat.** Snap cuts to the grid, but vary clip lengths
48
- (2, 3, 4, 6 beats) and sometimes let a musical moment land on an in-clip
49
- action (a landing, an impact) instead of a cut. Constant per-beat cutting
50
- reads as a sizzle reel, and gets nauseating.
51
- - Average shot length is a genre statement: ~1.3 s reads as AAA shooter,
52
- ~4 s reads as contemplative indie. An arcade racer sits around 1.5–3 s
53
- with a few longer holds where one continuous action is the payoff.
54
- - **More editing is not better editing.** A cut must clarify or escalate;
55
- cutting fast to look "like a real trailer" hides the game. If a single
56
- take shows the whole stunt, keep the take.
57
- - Total length: under 90 s unless there's a reason; 30–45 s is a sweet spot
58
- for social/store use.
59
-
60
- ## Variety — the five axes
61
-
62
- Audit the cut on each axis; the labels list in the shots file is the tool
63
- (read the labels top to bottom — repetition is visible in text before you
64
- ever re-watch):
65
-
66
- 1. **Content** — different actions, not the same action in new places.
67
- Escalate variants (jump → bigger jump) rather than repeating.
68
- 2. **Composition** — vary camera distance and framing; chase cam all the way
69
- through is monotone. Watch eye-trace: the viewer's focus point should not
70
- teleport across the frame on a cut.
71
- 3. **Energy** — intense vs. quiet moments; contrast matters more than speed.
72
- 4. **Pacing** — vary section tempo, not just shot tempo.
73
- 5. **Shot length** — vary beats-per-clip even inside a fast section.
74
-
75
- ## Where the shots come from
76
-
77
- Played footage is the default and the bulk of any trailer: it is the only
78
- thing that shows what the game DOES, and a trailer of pure scenery reads as a
79
- tech demo however pretty it is. But two of the beats above are ones a chase
80
- camera almost never hands you:
81
-
82
- - the **cold open**, which wants the level to look composed and deliberate;
83
- - the **breath**, which wants one calm wide shot with nothing happening.
84
-
85
- Both are what an authored **camera fly-through** is for — a path through the
86
- level with the game running underneath, so traffic still drives and animations
87
- still play while the camera moves on rails. Recording one is cheap and nobody
88
- has to play it, so a fly-through is worth having even when there is plenty of
89
- gameplay: cut one or two of its shots into a gameplay trailer rather than
90
- building a trailer out of it.
91
-
92
- A fly-through alone is the right whole trailer in a narrow set of cases — a
93
- world reveal, an architectural or exploration piece, a level that is the
94
- feature — and whenever there is no one available to play. Otherwise it is a
95
- source of shots, not a substitute for showing the game.
96
-
97
- ## On-screen text
98
-
99
- When to use text at all: only when footage cannot say it. A card that
100
- narrates what the next shot visibly shows is noise. Default for our
101
- gameplay trailers: **end card only**; add body cards only when they carry
102
- information the footage can't (game name early, a hook that isn't visual,
103
- release date).
104
-
105
- Writing the cards:
106
- - Copy must sound like this game and no other. Exercise: describe the game
107
- in three words; build cards from what's left after cutting everything
108
- generic.
109
- - ≤ 6 words per card. Evocative verbs beat feature lists ("Forge your own
110
- path", not "Over 40 unique tracks").
111
- - Banned: content quantities ("5 unique levels"), genre-descriptor stacks
112
- ("a 2D rogue-lite action adventure"), player-verb lists ("Race! Drift!
113
- Win!"), and stock phrases ("In a world…", "Get ready…", "Experience…",
114
- "…like never before").
115
-
116
- Showing the cards:
117
- - **Intercut, don't overlay**: a card gets its own beat between gameplay
118
- shots (dimmed still or plain background). Never ask the viewer to read
119
- while action plays — they do neither. Small persistent text (logo bug,
120
- date) is the exception.
121
- - Reading time: ~0.5–0.7 s per word, minimum 2.5 s on screen; end card
122
- 2–3 s (ours may run longer over a music outro).
123
- - Cards ride the same beat grid as the cuts — a card is a clip.
124
- - Typography must match the game's art quality and universe: one typeface
125
- family for the whole trailer, generous letter-spacing for all-caps,
126
- strong contrast against footage (our title raster already adds shadow),
127
- subtle animation only (fade/scale-in) — a static default-font card reads
128
- as a last-minute add-on.
129
-
130
- ## Structure template (30–45 s, gameplay-only, end card close)
131
-
132
- | Section | ~share | What goes here |
133
- |---|---|---|
134
- | Cold open | 5–15% | The single most striking moment; genre obvious at a glance |
135
- | Introduction | 15–20% | Clean, readable driving; establish look + one anchor |
136
- | Escalation | 35–45% | Varied action, rising energy, hook prominent early in it |
137
- | Climax | 15–20% | Densest, loudest stretch; biggest moments back to back |
138
- | Breath | ~5% | One calmer, wider shot; lets the climax land |
139
- | End card | 2–3 s+ | Logo, tagline, CTA — over the music's outro |
140
-
141
- ## Selection rules already paid for
142
-
143
- These came out of review rounds on real cuts. They cost time to learn and
144
- nothing to apply, so apply them before the first render, not after.
145
-
146
- - **Never use slow motion.** On gameplay footage it reads as the game running
147
- at a low framerate, not as drama. `speed` stays 1.0.
148
- - **Numbers are not interest.** Event `severity` / `impactSpeed` ranks
149
- car-stops-dead-against-a-wall highest — the worst possible clip. A crash
150
- earns its slot only if things visibly break (`destruction` events, debris)
151
- and motion continues through the hit.
152
- - **Low speed can BE the drama.** Wheelspin recoveries throwing dirt or snow,
153
- plowing back onto the track, being overtaken while struggling
154
- (`position-change` with `gained: false`), `off-track` +
155
- `hard-acceleration` clusters — these are among the best clips and score
156
- near zero on any speed metric.
157
- - **"Some shots of X" means a few.** The failure mode is 0-or-100: one
158
- standing start is an opener, five is a broken trailer.
159
- - **Quota check before rendering.** Read the shots list labels top to bottom.
160
- Max one standing start; no more than three of any shot type in the whole
161
- cut and never three similar in a row; vary track or biome, camera distance
162
- and action type (the five axes above).
163
- - **Menu and selection clips need `"ui": false`** — the HUD replay only knows
164
- the gameplay HUD and will paint it over menus.
165
- - **Verify on pixels.** Contact sheets and stills for every candidate, and
166
- stills from the RENDERED file for anything you changed. The timeline knows
167
- about events; it cannot see framing, occlusion or beauty.
168
- - **Iterate cheaply, keep history.** Editing the shots and re-rendering is
169
- minutes, and revisions auto-version — so render rather than debate, and
170
- compare against the previous `-rN` before delivering.
171
-
172
- ## Sources
173
-
174
- - [How to Hook the Audience and How Quickly to Do it](https://www.derek-lieu.com/blog/2022/10/24/how-to-hook-the-audience-and-how-quickly-to-do-it)
175
- - [Step Two of Starting a Game Trailer Timeline](https://www.derek-lieu.com/blog/2023/2/19/step-two-of-starting-a-game-trailer-timeline)
176
- - [How to Think About Pacing in a Trailer](https://www.derek-lieu.com/blog/2020/11/16/how-to-think-about-pacing-in-a-trailer)
177
- - [Variety is the Spice of Trailers](https://www.derek-lieu.com/blog/2019/3/27/variety-is-the-spice-of-trailers)
178
- - [More Trailer Editing is not Better Trailer Editing](https://www.derek-lieu.com/blog/2023/4/23/more-trailer-editing-is-not-better-trailer-editing)
179
- - [How to Write Trailer Title Cards With Strong Messaging](https://www.derek-lieu.com/blog/2020/4/19/how-to-write-trailer-title-cards-with-strong-messaging)
180
- - [10 Common Indie Game Trailer Mistakes & How to Fix Them](https://www.derek-lieu.com/blog/2020/9/14/10-common-indie-game-trailer-mistakes-and-how-to-fix-them)
@@ -1,18 +0,0 @@
1
- import { type CharacterSearchRecord } from '../project/character-search.js';
2
- import { type CharacterArgs } from '../generate/character-body.js';
3
- /**
4
- * What `bitmagic character add` will ask for, from its flags and the cached search.
5
- *
6
- * Pure, and separated from the command so the branch that matters — which of the
7
- * two forms was asked for, and whether the cache can answer it — is testable
8
- * without a project, a token or a network. It returns the character args rather
9
- * than a body so the shared `buildCharacterBody` still owns the wire shape.
10
- */
11
- export declare function resolveAddRequest(args: {
12
- name: string;
13
- id?: string;
14
- prompt?: string;
15
- applyToPlayer?: boolean;
16
- record: CharacterSearchRecord | null;
17
- }): CharacterArgs;
18
- export declare const characterCommand: import("citty").CommandDef<import("citty").ArgsDef>;
@@ -1,268 +0,0 @@
1
- /**
2
- * `bitmagic character` — make one, look through the ready-made ones, or add one.
3
- *
4
- * Its own noun group rather than three flags on `bitmagic generate character`,
5
- * because the shelf and the forge are two different requests that happened to
6
- * share a command:
7
- *
8
- * generate costs sparks, makes a new body, may substitute a close stored one
9
- * search free, read-only, shows what the shelf has and how close it is
10
- * add free, puts a stored body in the game, never forges
11
- *
12
- * The old surface expressed "add" as `generate character --library-only`, a
13
- * command that generates nothing, and it carried two flags that existed only to
14
- * be refused on that path (`--head-scale` was accepted, reported and dropped;
15
- * `--mesh-model` was rejected outright). Both refusals disappear here, because a
16
- * command that never forges simply does not offer the flags that pick how a
17
- * forge runs.
18
- *
19
- * `search` exists because no similarity floor can do this job. Measured against
20
- * the live index, genuine matches score 0.624-0.831 while humanoid prompts the
21
- * shelf cannot answer score 0.552-0.698 — overlapping, so a threshold that keeps
22
- * the good ones keeps "a humanoid bee" for "a beekeeper" too. A person reading
23
- * three captions settles in a second what a number cannot settle at all.
24
- */
25
- import { defineCommand } from 'citty';
26
- import { CliError } from '../errors.js';
27
- import { createOutput } from '../output.js';
28
- import { apiGet } from '../http/client.js';
29
- import { resolveArtStyle } from '../project/art-style.js';
30
- import { findCachedCandidate, readCharacterSearch, writeCharacterSearch, } from '../project/character-search.js';
31
- import { ORIGINAL_PROMPT_FLAG, withParent } from '../telemetry/creator-prompt.js';
32
- import { JSON_FLAG, resolveRun, runAssetGeneration } from './generate.js';
33
- import { APPLY_TO_PLAYER_ARG, buildCharacterBody, defaultVoxelizeForProject, FRESH_ARG, HEAD_SCALE_ARG, LIBRARY_ARG, MESH_MODEL_ARG, NAME_ARG, PROMPT_ARG, VOXELIZE_ARG, } from '../generate/character-body.js';
34
- const generateCommand = defineCommand({
35
- meta: {
36
- name: 'generate',
37
- description: 'Forge a new rigged character and add it to world.json assets. Costs sparks.',
38
- },
39
- args: {
40
- name: NAME_ARG,
41
- prompt: PROMPT_ARG,
42
- 'apply-to-player': APPLY_TO_PLAYER_ARG,
43
- 'head-scale': HEAD_SCALE_ARG,
44
- fresh: FRESH_ARG,
45
- library: LIBRARY_ARG,
46
- voxelize: VOXELIZE_ARG,
47
- 'mesh-model': MESH_MODEL_ARG,
48
- 'original-prompt': ORIGINAL_PROMPT_FLAG,
49
- json: JSON_FLAG,
50
- },
51
- async run({ args }) {
52
- const body = buildCharacterBody({
53
- name: args.name,
54
- prompt: args.prompt,
55
- applyToPlayer: args['apply-to-player'],
56
- headScale: args['head-scale'],
57
- fresh: args.fresh,
58
- library: args.library,
59
- voxelize: args.voxelize ?? defaultVoxelizeForProject(),
60
- meshModel: args['mesh-model'],
61
- });
62
- await runAssetGeneration('character', 'character', withParent(body), args.json === true);
63
- },
64
- });
65
- /** `62%` — the only form a similarity is ever shown in, so the table lines up. */
66
- function percent(value) {
67
- return `${Math.round(value * 100)}%`;
68
- }
69
- function renderCandidates(candidates, threshold, log) {
70
- const width = Math.max(2, ...candidates.map((row) => row.sid.length));
71
- log('');
72
- log(` # ${'id'.padEnd(width)} match auto description`);
73
- candidates.forEach((row, index) => {
74
- // The "auto" column is why the floor is reported rather than applied: it
75
- // says which of these automatic substitution would have taken, so a creator
76
- // can see both that a body cleared the bar and that a weaker one exists.
77
- const auto = row.similarity >= threshold ? ' yes' : ' -';
78
- const label = row.subject ?? row.description ?? '';
79
- log(` ${String(index + 1).padStart(1)} ${row.sid.padEnd(width)} ${percent(row.similarity).padStart(5)} ${auto} ${label.slice(0, 60)}`);
80
- });
81
- log('');
82
- }
83
- const searchCommand = defineCommand({
84
- meta: {
85
- name: 'search',
86
- description: 'List the ready-made characters closest to a description. Free; changes nothing.',
87
- },
88
- args: {
89
- prompt: {
90
- type: 'string',
91
- required: true,
92
- description: 'What the character should look like.',
93
- },
94
- limit: {
95
- type: 'string',
96
- description: 'How many candidates to list, 1-16. Default 5.',
97
- },
98
- representation: {
99
- type: 'string',
100
- description: "Which shelf to search: 'voxel' or 'lowpoly'. Omit to follow the project's art style.",
101
- },
102
- json: JSON_FLAG,
103
- },
104
- async run({ args }) {
105
- const json = args.json === true;
106
- const { output, runDeps, context, environment, token } = await resolveRun(json);
107
- const representation = (() => {
108
- const asked = args.representation;
109
- // `resolveArtStyle` also reports a game.json value that is neither word,
110
- // which is the same note every other art-style-aware command prints.
111
- if (asked === undefined)
112
- return resolveArtStyle(context.root, output.info) === 'low-poly' ? 'lowpoly' : 'voxel';
113
- if (asked !== 'voxel' && asked !== 'lowpoly') {
114
- throw new CliError("--representation must be 'voxel' or 'lowpoly'.", 1, 'invalid_flag');
115
- }
116
- return asked;
117
- })();
118
- const query = new URLSearchParams({
119
- gameId: context.metadata.gameId,
120
- prompt: args.prompt,
121
- representation,
122
- });
123
- if (args.limit !== undefined)
124
- query.set('limit', args.limit);
125
- const response = await apiGet(environment, `/api/cli/v1/characters/search?${query.toString()}`, token, runDeps);
126
- // Written even when empty: "the shelf has nothing like this" is a result a
127
- // re-run should not have to rediscover, and an empty cache makes `add --id`
128
- // say "search first" rather than "no such id".
129
- writeCharacterSearch(context.root, {
130
- prompt: response.prompt,
131
- representation,
132
- threshold: response.threshold,
133
- searchedAt: new Date().toISOString(),
134
- candidates: response.candidates,
135
- });
136
- if (response.candidates.length === 0) {
137
- const nearest = response.topSimilarity !== undefined
138
- ? ` The closest was ${percent(response.topSimilarity)}, which had no usable file.`
139
- : '';
140
- output.info(`No ready-made ${representation} character matches "${response.prompt}".${nearest}`);
141
- output.info(`Forge one instead: bitmagic character generate --name <name> --prompt "${response.prompt}"`);
142
- output.result({ ok: true, kind: 'character search', representation, threshold: response.threshold, candidates: [], ...(response.reason ? { reason: response.reason } : {}) });
143
- return;
144
- }
145
- output.info(`${response.candidates.length} ready-made ${representation} character(s) for "${response.prompt}":`);
146
- renderCandidates(response.candidates, response.threshold, output.info);
147
- const best = response.candidates[0];
148
- if (best.similarity < response.threshold) {
149
- // Said plainly rather than hidden: on this corpus a confident-looking
150
- // caption can still be the wrong character, and the creator is the only
151
- // one who can tell.
152
- output.info(`None clears the ${percent(response.threshold)} bar automatic substitution requires — read the captions before adding one.`);
153
- }
154
- output.info(`Add one: bitmagic character add --id ${best.sid} --name <name>`);
155
- output.result({
156
- ok: true,
157
- kind: 'character search',
158
- representation,
159
- threshold: response.threshold,
160
- candidates: response.candidates,
161
- });
162
- },
163
- });
164
- /**
165
- * What `bitmagic character add` will ask for, from its flags and the cached search.
166
- *
167
- * Pure, and separated from the command so the branch that matters — which of the
168
- * two forms was asked for, and whether the cache can answer it — is testable
169
- * without a project, a token or a network. It returns the character args rather
170
- * than a body so the shared `buildCharacterBody` still owns the wire shape.
171
- */
172
- export function resolveAddRequest(args) {
173
- if (args.id === undefined) {
174
- if (args.prompt === undefined) {
175
- throw new CliError('Pass --id (from `bitmagic character search`) or --prompt to take the closest match.', 1, 'invalid_flag');
176
- }
177
- // The no-id form: the server searches with no floor and adds the best usable
178
- // body, or fails without charging. Deliberately still available — "any of
179
- // these will do" is a real request, and it is one call rather than two.
180
- return {
181
- name: args.name,
182
- prompt: args.prompt,
183
- applyToPlayer: args.applyToPlayer,
184
- libraryOnly: true,
185
- voxelize: defaultVoxelizeForProject(),
186
- };
187
- }
188
- if (args.record === null) {
189
- throw new CliError('No character search to pick from. Run `bitmagic character search --prompt "..."` first.', 1, 'no_search');
190
- }
191
- const candidate = findCachedCandidate(args.record, args.id);
192
- if (candidate === null) {
193
- const known = args.record.candidates.map((row) => row.sid).join(', ');
194
- throw new CliError(`"${args.id}" was not in the last character search`
195
- + (known === '' ? ' (it found nothing).' : `. It offered: ${known}.`)
196
- + ' Run `bitmagic character search` again to pick from a fresh list.', 1, 'unknown_candidate');
197
- }
198
- return {
199
- name: args.name,
200
- // The CREATOR's brief, not the library row's own caption: this is what a
201
- // regeneration re-drives, so it must describe what they asked for.
202
- prompt: args.prompt ?? args.record.prompt,
203
- applyToPlayer: args.applyToPlayer,
204
- libraryPick: {
205
- sid: candidate.sid,
206
- assetUrl: candidate.assetUrl,
207
- similarity: candidate.similarity,
208
- ...(candidate.description ? { description: candidate.description } : {}),
209
- },
210
- // The shelf that produced the row decides the medium, not the project's art
211
- // style at the moment of adding: mixing them would hand a low-poly game a
212
- // pile of cubes.
213
- voxelize: args.record.representation === 'voxel',
214
- };
215
- }
216
- const addCommand = defineCommand({
217
- meta: {
218
- name: 'add',
219
- description: 'Add a ready-made character from the library to world.json assets. Free; never forges.',
220
- },
221
- args: {
222
- name: NAME_ARG,
223
- id: {
224
- type: 'string',
225
- description: 'The id of a candidate from `bitmagic character search`. Omit to take the closest match to --prompt.',
226
- },
227
- prompt: {
228
- type: 'string',
229
- description: 'What the character should look like. Required without --id; with --id it defaults to the search that found it.',
230
- },
231
- 'apply-to-player': APPLY_TO_PLAYER_ARG,
232
- 'original-prompt': ORIGINAL_PROMPT_FLAG,
233
- json: JSON_FLAG,
234
- },
235
- async run({ args }) {
236
- const json = args.json === true;
237
- const { context } = await resolveRun(json);
238
- const output = createOutput(json);
239
- // Only the --id form consults the cache; the --prompt form is a fresh ask.
240
- const record = args.id === undefined ? null : readCharacterSearch(context.root);
241
- const request = resolveAddRequest({
242
- name: args.name,
243
- id: args.id,
244
- prompt: args.prompt,
245
- applyToPlayer: args['apply-to-player'],
246
- record,
247
- });
248
- if (request.libraryPick) {
249
- // The search's own date, not an expiry: age is worth seeing, and the
250
- // catalogue only changes when somebody promotes to it.
251
- output.info(`Adding ${request.libraryPick.sid} (${percent(request.libraryPick.similarity)} match`
252
- + `${record?.searchedAt ? `, from the search on ${record.searchedAt.slice(0, 10)}` : ''}).`);
253
- }
254
- await runAssetGeneration('character', 'character', withParent(buildCharacterBody(request)), json);
255
- },
256
- });
257
- export const characterCommand = defineCommand({
258
- meta: {
259
- name: 'character',
260
- description: 'Forge a character, search the ready-made ones, or add one to your game.',
261
- },
262
- subCommands: {
263
- generate: generateCommand,
264
- search: searchCommand,
265
- add: addCommand,
266
- },
267
- });
268
- //# sourceMappingURL=character.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"character.js","sourceRoot":"","sources":["../../src/commands/character.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GAGrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EAEzB,SAAS,EACT,cAAc,EACd,WAAW,EACX,cAAc,EACd,QAAQ,EACR,UAAU,EACV,YAAY,GACb,MAAM,+BAA+B,CAAC;AAcvC,MAAM,eAAe,GAAG,aAAa,CAAC;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,6EAA6E;KAC3F;IACD,IAAI,EAAE;QACJ,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,UAAU;QAClB,iBAAiB,EAAE,mBAAmB;QACtC,YAAY,EAAE,cAAc;QAC5B,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,WAAW;QACpB,QAAQ,EAAE,YAAY;QACtB,YAAY,EAAE,cAAc;QAC5B,iBAAiB,EAAE,oBAAoB;QACvC,IAAI,EAAE,SAAS;KAChB;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,IAAI,GAAG,kBAAkB,CAAC;YAC9B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,iBAAiB,CAAC;YACtC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;YAC7B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,yBAAyB,EAAE;YACtD,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;SAC9B,CAAC,CAAC;QACH,MAAM,kBAAkB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC3F,CAAC;CACF,CAAC,CAAC;AAEH,kFAAkF;AAClF,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;AACvC,CAAC;AAED,SAAS,gBAAgB,CACvB,UAAgC,EAChC,SAAiB,EACjB,GAA8B;IAE9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,GAAG,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC5D,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QAChC,yEAAyE;QACzE,4EAA4E;QAC5E,yEAAyE;QACzE,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;QACnD,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9I,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,EAAE,CAAC,CAAC;AACV,CAAC;AAED,MAAM,aAAa,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE;QACJ,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,iFAAiF;KAC/F;IACD,IAAI,EAAE;QACJ,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,sCAAsC;SACpD;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,+CAA+C;SAC7D;QACD,cAAc,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,sFAAsF;SACpG;QACD,IAAI,EAAE,SAAS;KAChB;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAChC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QAEhF,MAAM,cAAc,GAAG,CAAC,GAAmB,EAAE;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC;YAClC,yEAAyE;YACzE,qEAAqE;YACrE,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;YAChH,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7C,MAAM,IAAI,QAAQ,CAAC,gDAAgD,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC;YAC1F,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,EAAE,CAAC;QAEL,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC;YAChC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;YAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,cAAc;SACf,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAE7D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAC3B,WAAW,EACX,iCAAiC,KAAK,CAAC,QAAQ,EAAE,EAAE,EACnD,KAAK,EACL,OAAO,CACR,CAAC;QAEF,2EAA2E;QAC3E,4EAA4E;QAC5E,+CAA+C;QAC/C,oBAAoB,CAAC,OAAO,CAAC,IAAI,EAAE;YACjC,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,cAAc;YACd,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,UAAU,EAAE,QAAQ,CAAC,UAAU;SAChC,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,KAAK,SAAS;gBAClD,CAAC,CAAC,oBAAoB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,6BAA6B;gBAClF,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,iBAAiB,cAAc,uBAAuB,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;YACjG,MAAM,CAAC,IAAI,CAAC,2EAA2E,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC3G,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,kBAAkB,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9K,OAAO;QACT,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,eAAe,cAAc,sBAAsB,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC;QACjH,gBAAgB,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAuB,CAAC;QAC1D,IAAI,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;YACzC,sEAAsE;YACtE,wEAAwE;YACxE,oBAAoB;YACpB,MAAM,CAAC,IAAI,CAAC,mBAAmB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,6EAA6E,CAAC,CAAC;QAC3I,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,yCAAyC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC;QAC/E,MAAM,CAAC,MAAM,CAAC;YACZ,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,kBAAkB;YACxB,cAAc;YACd,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;SAChC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAMjC;IACC,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,IAAI,QAAQ,CAChB,qFAAqF,EACrF,CAAC,EACD,cAAc,CACf,CAAC;QACJ,CAAC;QACD,6EAA6E;QAC7E,0EAA0E;QAC1E,wEAAwE;QACxE,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,yBAAyB,EAAE;SACtC,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,QAAQ,CAChB,yFAAyF,EACzF,CAAC,EACD,WAAW,CACZ,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,MAAM,IAAI,QAAQ,CAChB,IAAI,IAAI,CAAC,EAAE,wCAAwC;cACjD,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,iBAAiB,KAAK,GAAG,CAAC;cACnE,mEAAmE,EACrE,CAAC,EACD,mBAAmB,CACpB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,yEAAyE;QACzE,mEAAmE;QACnE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;QACzC,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,WAAW,EAAE;YACX,GAAG,EAAE,SAAS,CAAC,GAAG;YAClB,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,UAAU,EAAE,SAAS,CAAC,UAAU;YAChC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzE;QACD,4EAA4E;QAC5E,0EAA0E;QAC1E,iBAAiB;QACjB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,OAAO;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,aAAa,CAAC;IAC/B,IAAI,EAAE;QACJ,IAAI,EAAE,KAAK;QACX,WAAW,EAAE,uFAAuF;KACrG;IACD,IAAI,EAAE;QACJ,IAAI,EAAE,QAAQ;QACd,EAAE,EAAE;YACF,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,qGAAqG;SACnH;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,gHAAgH;SAC9H;QACD,iBAAiB,EAAE,mBAAmB;QACtC,iBAAiB,EAAE,oBAAoB;QACvC,IAAI,EAAE,SAAS;KAChB;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAChC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAElC,2EAA2E;QAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChF,MAAM,OAAO,GAAG,iBAAiB,CAAC;YAChC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,iBAAiB,CAAC;YACtC,MAAM;SACP,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,qEAAqE;YACrE,uDAAuD;YACvD,MAAM,CAAC,IAAI,CACT,UAAU,OAAO,CAAC,WAAW,CAAC,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ;kBACnF,GAAG,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,wBAAwB,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAC5F,CAAC;QACJ,CAAC;QACD,MAAM,kBAAkB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACpG,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;IAC5C,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,yEAAyE;KACvF;IACD,WAAW,EAAE;QACX,QAAQ,EAAE,eAAe;QACzB,MAAM,EAAE,aAAa;QACrB,GAAG,EAAE,UAAU;KAChB;CACF,CAAC,CAAC"}
@@ -1,30 +0,0 @@
1
- /**
2
- * `bitmagic forge --edit` — which level is being edited, and the request an
3
- * edit job is frozen with.
4
- *
5
- * Pure over the project's world.json so the resolution rules are testable
6
- * without a project on disk: `--level` names a level by id, by exact name, or
7
- * by a unique case-insensitive name; omitted, it means the ONE level this
8
- * project could edit, and anything else is refused with the candidates named.
9
- * A level can be edited when its `.vwld` asset records the forge job that
10
- * built it (or, for older levels, carries the job id in its source GLB key —
11
- * see `parentForgeJobId`).
12
- */
13
- import { type ForgeJobInput, type ForgeReusePlan, type ForgeArtifact } from '@bitmagic/world-forger/pipeline/index.js';
14
- import { type JsonObject } from '../project/world-json.js';
15
- export interface EditTarget {
16
- levelId: string;
17
- name: string;
18
- vwldAssetId: string;
19
- parentJobId: string;
20
- }
21
- export declare function resolveEditTarget(world: JsonObject, levelArg: string | undefined): EditTarget;
22
- /** The frozen request of an edit job: the level's own name, the parent it edits, the level it replaces. */
23
- export declare function buildEditJobInput(options: {
24
- target: EditTarget;
25
- editPrompt: string;
26
- gameId: string;
27
- groundPlane?: boolean;
28
- }): ForgeJobInput;
29
- /** A plan that reuses nothing — what a terminal frame without one means. */
30
- export declare function bakeEverythingPlan(artifact: ForgeArtifact, reason: string): ForgeReusePlan;