@bitmagic/cli 0.1.15 → 0.1.17

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 (36) hide show
  1. package/README.md +50 -16
  2. package/dist/cli.d.ts +26 -0
  3. package/dist/cli.js +3 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/cover.d.ts +100 -0
  6. package/dist/commands/cover.js +321 -0
  7. package/dist/commands/cover.js.map +1 -0
  8. package/dist/commands/generate.d.ts +13 -3
  9. package/dist/commands/generate.js +12 -2
  10. package/dist/commands/generate.js.map +1 -1
  11. package/dist/commands/init.d.ts +4 -0
  12. package/dist/commands/init.js +21 -7
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/publish.d.ts +23 -0
  15. package/dist/commands/publish.js +72 -23
  16. package/dist/commands/publish.js.map +1 -1
  17. package/dist/commands/upgrade.d.ts +18 -0
  18. package/dist/commands/upgrade.js +33 -0
  19. package/dist/commands/upgrade.js.map +1 -1
  20. package/dist/commands/verify.js +2 -3
  21. package/dist/commands/verify.js.map +1 -1
  22. package/dist/publish/fingerprint.js +13 -5
  23. package/dist/publish/fingerprint.js.map +1 -1
  24. package/dist/render/inline-image.d.ts +45 -0
  25. package/dist/render/inline-image.js +70 -0
  26. package/dist/render/inline-image.js.map +1 -0
  27. package/dist/scaffold/project-files.d.ts +15 -0
  28. package/dist/scaffold/project-files.js +94 -6
  29. package/dist/scaffold/project-files.js.map +1 -1
  30. package/dist/scaffold/project.d.ts +17 -3
  31. package/dist/scaffold/project.js +22 -7
  32. package/dist/scaffold/project.js.map +1 -1
  33. package/dist/scaffold/upgrade-project.d.ts +12 -4
  34. package/dist/scaffold/upgrade-project.js +19 -5
  35. package/dist/scaffold/upgrade-project.js.map +1 -1
  36. package/package.json +2 -2
package/README.md CHANGED
@@ -18,6 +18,7 @@ bitmagic --version
18
18
  bitmagic login # device-code auth in your browser
19
19
  bitmagic init my-game # scaffold a new project into ./my-game
20
20
  cd my-game
21
+ bitmagic cover # generate cover art from GAME-DESIGN.md
21
22
  bitmagic check # typecheck against the vendored engine
22
23
  bitmagic dev # build, then serve with live rebuilds
23
24
  bitmagic verify # boot the game in a real browser and report what broke
@@ -31,14 +32,15 @@ bitmagic publish # verify-gate, build, and ship — private by defa
31
32
  | `bitmagic login [--env <env>]` | Device-code auth against Auth0; stores credentials per environment. |
32
33
  | `bitmagic logout [--env <env>]` | Remove stored credentials for an environment. |
33
34
  | `bitmagic whoami [--env <env>]` | Show the identity the CLI is authenticated as. |
34
- | `bitmagic init [dir] [--template <id>] [--name <name>] [--env <env>] [--force]` | Scaffold a new project: mints a game, downloads the vendored engine, writes `AGENTS.md` and templates. |
35
+ | `bitmagic init [dir] [--template <id>] [--name <name>] [--idea "<pitch>"] [--env <env>] [--force]` | Scaffold a new project: mints a game, downloads the vendored engine, writes `AGENTS.md`, `GAME-DESIGN.md` and templates. `--idea` seeds the design doc's pitch. |
35
36
  | `bitmagic check` | Typecheck (`tsc --noEmit`) against the vendored engine. Fast; does not prove the game runs. |
36
37
  | `bitmagic dev [--port <port>]` | Build, then serve with `tsc --watch` + `vite`, default port 3010. |
37
38
  | `bitmagic verify [--timeout <ms>]` | Build, boot the game in a headless browser, click the engine's Play button like a player would, and report what broke. A game without the button passes as long as gameplay starts by itself (auto-starting genres, custom start UIs); a run where gameplay never starts fails. `--timeout` is settle time in ms measured after gameplay starts, so the screenshot captures gameplay. Writes the artifacts the publish gate reads (see below). |
38
39
  | `bitmagic build` | Bundle the game into one self-contained `.bitmagic/build/index.html`. Rarely needs to be run by hand — `publish` builds automatically when the project changed. |
39
40
  | `bitmagic publish [--visibility public\|private] [--name <name>] [--description <desc>] [--force]` | Verify-gate, build if needed, and upload straight to GCS. **Private unless `--visibility public` is passed.** |
40
- | `bitmagic upgrade [--engine <version>] [--force]` | Refresh vendored platform files to the currently published engine. Refuses a dirty git tree unless `--force`. |
41
+ | `bitmagic upgrade [--engine <version>] [--force]` | Refresh vendored platform files and the CLI's own skills to the currently published engine. Refuses a dirty git tree unless `--force`. |
41
42
  | `bitmagic generate <skybox\|sound\|image\|character\|animation> ...` | Generate one asset directly into `src/work/world.json`. Costs sparks. |
43
+ | `bitmagic cover [--prompt "..."] [--force] [--no-start-screen]` | Generate the game's cover art from `GAME-DESIGN.md`: sets the start screen, saves `.bitmagic/cover.webp`, becomes the publish thumbnail. Costs sparks. |
42
44
  | `bitmagic forge --prompt "..." [--city\|--dungeon\|--platformer\|--freeform] [--name <name>] [--resume <jobId>]` | Design and bake a whole playable level into `world.json`. Slow and expensive; supports `--resume`. |
43
45
 
44
46
  Every command exits non-zero on failure with a single-line message (no stack trace) and, where a
@@ -56,16 +58,35 @@ my-game/
56
58
  src/work/ # your game — Game.ts, game.json, world.json
57
59
  engine/ # vendored engine, read-only, replaced wholesale by `bitmagic upgrade`
58
60
  AGENTS.md # guidance for the agent editing this project
61
+ GAME-DESIGN.md # what the game is — yours to write; bitmagic cover reads it
59
62
  .bitmagic/
63
+ cover.webp # bitmagic cover's artwork — the publish thumbnail when present
64
+ cover.json # its URL and the design hash it was generated from
60
65
  build/
61
- index.html # bitmagic build's output bundle
62
- manifest.json # engineVersion, fingerprint, bytes, sha256, builtAt
66
+ index.html # bitmagic build's output bundle
67
+ manifest.json # engineVersion, fingerprint, bytes, sha256, builtAt
63
68
  verify/
64
- result.json # bitmagic verify's verdict — ok, failures, warnings, fingerprint, at
65
- screenshot.png # becomes the publish thumbnail
66
- console.log # captured browser console output
69
+ result.json # bitmagic verify's verdict — ok, failures, warnings, fingerprint, at
70
+ screenshot.png # the publish thumbnail when there is no cover art
71
+ console.log # captured browser console output
67
72
  ```
68
73
 
74
+ ## Cover art
75
+
76
+ `bitmagic cover` generates the game's cover from `GAME-DESIGN.md` — the same artwork the web
77
+ Creator's Planning Mode produces, from the same style direction. It sets `hud.startScreen.imageUrl`
78
+ so the image appears in the game itself, saves `.bitmagic/cover.webp`, and hands that to
79
+ `bitmagic publish` as the portal thumbnail.
80
+
81
+ Run it **early** — right after the design doc says what the game is, before implementing it. The
82
+ first build takes a while, and the cover is what there is to look at meanwhile. In iTerm2, WezTerm
83
+ and Konsole it is drawn straight into the terminal; everywhere else the path and URL are printed
84
+ and `.bitmagic/cover.webp` is there to open (`BITMAGIC_NO_INLINE_IMAGE=1` turns the drawing off).
85
+
86
+ Re-running on an unchanged design generates nothing and costs nothing — it reports the existing
87
+ cover. Edit the design and run again, or pass `--force`, to replace it. Because it writes
88
+ `world.json`, it invalidates a passing verify: run it before `bitmagic verify`, not after.
89
+
69
90
  ## Publishing
70
91
 
71
92
  `bitmagic publish` is two gated steps in one command:
@@ -76,7 +97,8 @@ my-game/
76
97
  record stale immediately, while an unmodified project stays fresh no matter how much time has
77
98
  passed. There is no way to check freshness except running `bitmagic verify` again.
78
99
  `.git/`, `node_modules/`, `dist/`, `.bitmagic/` and `.vite-cache/` are excluded, so committing
79
- between `bitmagic verify` and `bitmagic publish` does **not** invalidate the verify.
100
+ between `bitmagic verify` and `bitmagic publish` does **not** invalidate the verify. Neither
101
+ does editing `GAME-DESIGN.md`, which no build step reads.
80
102
  2. **The build.** If the project has changed since the last `.bitmagic/build/manifest.json`,
81
103
  publish rebuilds automatically; a prior manual `bitmagic build` is never required, only useful
82
104
  if you want to inspect `.bitmagic/build/index.html` before it ships.
@@ -89,9 +111,9 @@ service, so a previously-public game re-published bare is gone from bitmagic.ai
89
111
  publicly again.
90
112
 
91
113
  `--force` overrides a **stale or failed** verify verdict. It does **not** override a missing
92
- verify record — publish's thumbnail comes from the verify screenshot, so `bitmagic verify` must
93
- have produced at least one `.bitmagic/verify/screenshot.png`, ever, before `--force` has anything
94
- to work with.
114
+ verify record — a project with no cover art has only the verify screenshot to publish a thumbnail
115
+ from, so `bitmagic verify` must have produced at least one `.bitmagic/verify/screenshot.png`, ever,
116
+ before `--force` has anything to work with.
95
117
 
96
118
  `--name` and `--description` apply to that one publish call only. They are never written back to
97
119
  `src/work/game.json`, so pass them again on every future `bitmagic publish` if you want them to
@@ -115,11 +137,23 @@ invalidate the verify you just checked.
115
137
 
116
138
  ## Upgrading
117
139
 
118
- `bitmagic upgrade` refreshes the vendored `engine/`, the generated config files, and `index.html`
119
- to the currently published engine version. It never touches `src/`, `world.json`, `game.json`,
120
- `package.json`, or the agent guidance files (`AGENTS.md`, `CLAUDE.md`, `.claude/skills/`) those
121
- are yours. It refuses to run against a dirty git working tree unless `--force`, so the upgrade
122
- always shows up as its own reviewable diff.
140
+ `bitmagic upgrade` refreshes the vendored `engine/`, the generated config files, `index.html`, and
141
+ **the skills this CLI ships** (`.claude/skills/`) to the currently published engine version. It
142
+ refuses to run against a dirty git working tree unless `--force`, so the upgrade always shows up as
143
+ its own reviewable diff.
144
+
145
+ What is yours and never touched: `src/`, `world.json`, `game.json`, `package.json`, `AGENTS.md`,
146
+ `CLAUDE.md`, `GAME-DESIGN.md`, `.gitignore`, and any skill you wrote yourself.
147
+
148
+ The shipped skills are on the refreshed side deliberately. They describe what the **CLI** can do,
149
+ not what your game is, so a project holding the version that came with its scaffold has an agent
150
+ that cannot discover anything added since — which is exactly what happened when `bitmagic cover`
151
+ shipped. The cost is that edits to *our* skill files are lost on upgrade; put your own guidance in
152
+ `AGENTS.md` or a skill of your own, both of which are left alone.
153
+
154
+ Where an upgrade leaves a project unable to use something the CLI can now do, it says so rather
155
+ than fixing it silently: a project with no `GAME-DESIGN.md` is told what to write and that
156
+ `bitmagic cover` needs it.
123
157
 
124
158
  `upgrade` **reports** missing or outdated dependencies rather than installing them — it prints the
125
159
  exact install command to run, using your project's own package manager (detected from
package/dist/cli.d.ts CHANGED
@@ -52,6 +52,10 @@ declare const rawCommands: {
52
52
  readonly type: "string";
53
53
  readonly description: "Game name.";
54
54
  };
55
+ readonly idea: {
56
+ readonly type: "string";
57
+ readonly description: "One-line pitch, seeded into GAME-DESIGN.md. Optional — you can write it yourself.";
58
+ };
55
59
  readonly env: {
56
60
  readonly type: "string";
57
61
  readonly description: "Environment (dev, prod, local).";
@@ -108,6 +112,28 @@ declare const rawCommands: {
108
112
  };
109
113
  }>;
110
114
  generate: CommandDef<ArgsDef>;
115
+ cover: CommandDef<{
116
+ readonly prompt: {
117
+ readonly type: "string";
118
+ readonly description: "Describe the game for this call only, instead of reading GAME-DESIGN.md. Never written to the file.";
119
+ };
120
+ readonly force: {
121
+ readonly type: "boolean";
122
+ readonly description: "Generate a new cover even when the design has not changed since the last one.";
123
+ };
124
+ readonly 'start-screen': {
125
+ readonly type: "boolean";
126
+ readonly negativeDescription: "Leave hud.startScreen.imageUrl alone — still saves the cover and uses it as the publish thumbnail.";
127
+ };
128
+ readonly 'reference-image-url': {
129
+ readonly type: "string";
130
+ readonly description: "Style/composition reference image URL forwarded to the image model.";
131
+ };
132
+ readonly json: {
133
+ readonly type: "boolean";
134
+ readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
135
+ };
136
+ }>;
111
137
  forge: CommandDef<{
112
138
  readonly prompt: {
113
139
  readonly type: "string";
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import { devCommand } from './commands/dev.js';
11
11
  import { verifyCommand } from './commands/verify.js';
12
12
  import { buildCommand } from './commands/build.js';
13
13
  import { generateCommand } from './commands/generate.js';
14
+ import { coverCommand } from './commands/cover.js';
14
15
  import { forgeCommand } from './commands/forge.js';
15
16
  import { publishCommand } from './commands/publish.js';
16
17
  // citty's runMain() catches every error thrown from a command's `run()` internally and calls
@@ -88,7 +89,7 @@ export function isWrappedWithCleanErrors(command) {
88
89
  if (!subs)
89
90
  return false;
90
91
  const entries = Object.values(subs);
91
- return entries.length > 0 && entries.every((sub) => isWrappedWithCleanErrors(sub));
92
+ return entries.length > 0 && entries.every(isWrappedWithCleanErrors);
92
93
  }
93
94
  // Wrapping happens once here, over the whole map, rather than per-entry at each call site —
94
95
  // a future command added to `rawCommands` is wrapped automatically, so there is no per-command
@@ -104,6 +105,7 @@ const rawCommands = {
104
105
  verify: verifyCommand,
105
106
  build: buildCommand,
106
107
  generate: generateCommand,
108
+ cover: coverCommand,
107
109
  forge: forgeCommand,
108
110
  publish: publishCommand,
109
111
  };
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ import { Buffer } from 'buffer';
2
+ import { type WorldPatch } from '@bitmagic/asset-core';
3
+ import type { GenerationOutcome } from '../generate/stream.js';
4
+ import { type GenerateRunDeps } from './generate.js';
5
+ /** The design document `bitmagic init` scaffolds and the coding agent maintains. */
6
+ export declare const GAME_DESIGN_FILE = "GAME-DESIGN.md";
7
+ export declare function coverImagePath(root: string): string;
8
+ export interface CoverRecord {
9
+ /** The public image URL, as written into world.json. */
10
+ url: string;
11
+ /** Hash of the design text this cover was drawn from — see `readCoverRecord`. */
12
+ designHash: string;
13
+ generatedAt: string;
14
+ }
15
+ /**
16
+ * Identifies the design text a cover was generated from, so a repeat `bitmagic cover` on an
17
+ * unchanged design can decline to spend sparks on the same picture twice. This is the local
18
+ * equivalent of `PLAN_COVER_CACHE` in game-play-agent's PromptToGameController, which does the
19
+ * same for Planning Mode, keyed the same way — on the design text rather than the game.
20
+ */
21
+ export declare function hashDesign(designText: string): string;
22
+ export declare function readCoverRecord(root: string): CoverRecord | null;
23
+ /**
24
+ * The prose the cover is drawn from: `--prompt` when given, otherwise GAME-DESIGN.md.
25
+ *
26
+ * `--prompt` is a one-call override and is never written to the file — the design document belongs
27
+ * to the creator, and a flag silently rewriting it would be a surprise the next `bitmagic cover`
28
+ * inherits.
29
+ *
30
+ * The error path matters as much as the happy one: projects scaffolded before GAME-DESIGN.md
31
+ * existed have no such file, and an agent that only sees "not found" cannot tell whether it should
32
+ * create one or has run the command in the wrong place. So the message says both what to write and
33
+ * what to run instead.
34
+ */
35
+ export declare function resolveDesignText(options: {
36
+ promptFlag?: string | undefined;
37
+ root: string;
38
+ readFile?: (file: string) => string;
39
+ }): string;
40
+ /**
41
+ * The generated image's URL, read back out of the patch the image generator returned.
42
+ *
43
+ * The generator's terminal frame carries only `{ success, message, patches }` — the URL is not a
44
+ * separate field on the wire — so the patch it upserts into `assets[]` is where it lives. Matched
45
+ * on the patch's own shape rather than on the message text, which is prose and not a contract.
46
+ */
47
+ export declare function coverUrlFromPatches(patches: WorldPatch[]): string | null;
48
+ /**
49
+ * The patch that puts the cover behind the game's start screen.
50
+ *
51
+ * `set`, NOT `setRoot`: `set` addresses a path inside `worldProfileData`, which is where the
52
+ * engine's GameRuntimeController reads `hud.startScreen.imageUrl` from. A root-level write would
53
+ * land somewhere nothing ever looks and report success — the exact trap apply-patches.ts documents.
54
+ */
55
+ export declare function startScreenPatch(url: string): WorldPatch;
56
+ export interface CoverArgs {
57
+ prompt?: string;
58
+ force?: boolean;
59
+ 'reference-image-url'?: string;
60
+ /** citty's negative form of the boolean flag: `--no-start-screen` arrives here as `false`. */
61
+ 'start-screen'?: boolean;
62
+ json?: boolean;
63
+ }
64
+ export interface CoverRunDeps extends GenerateRunDeps {
65
+ /**
66
+ * Seam over `runAssetGeneration`, so a test can drive this without a network or a token.
67
+ *
68
+ * Deliberately narrower than `runAssetGeneration`'s own signature: it omits that function's
69
+ * `json` parameter, because this command always passes `false` there. Under `--json` stdout must
70
+ * carry exactly one document, and this command emits its own.
71
+ */
72
+ generate: (type: string, label: string, body: Record<string, unknown>, json: boolean, deps: GenerateRunDeps) => Promise<GenerationOutcome>;
73
+ /** Returns whether the terminal actually drew the image. */
74
+ renderImage: (bytes: Buffer) => boolean;
75
+ /** The machine-readable result, emitted only under `--json`. */
76
+ result: (value: unknown) => void;
77
+ }
78
+ export declare function runCover(args: CoverArgs, deps?: CoverRunDeps): Promise<void>;
79
+ export declare const coverCommand: import("citty").CommandDef<{
80
+ readonly prompt: {
81
+ readonly type: "string";
82
+ readonly description: "Describe the game for this call only, instead of reading GAME-DESIGN.md. Never written to the file.";
83
+ };
84
+ readonly force: {
85
+ readonly type: "boolean";
86
+ readonly description: "Generate a new cover even when the design has not changed since the last one.";
87
+ };
88
+ readonly 'start-screen': {
89
+ readonly type: "boolean";
90
+ readonly negativeDescription: "Leave hud.startScreen.imageUrl alone — still saves the cover and uses it as the publish thumbnail.";
91
+ };
92
+ readonly 'reference-image-url': {
93
+ readonly type: "string";
94
+ readonly description: "Style/composition reference image URL forwarded to the image model.";
95
+ };
96
+ readonly json: {
97
+ readonly type: "boolean";
98
+ readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
99
+ };
100
+ }>;
@@ -0,0 +1,321 @@
1
+ /**
2
+ * `bitmagic cover` — generate this game's cover art from its design document.
3
+ *
4
+ * The web lane's Planning Mode does this automatically for every game it builds, which is why a
5
+ * game made in Creator arrives with real artwork and a game made here arrived with whatever
6
+ * `bitmagic verify` happened to screenshot. This is the pro lane's equivalent, with one deliberate
7
+ * difference: it is a command the creator (or their agent) runs, not something `bitmagic init`
8
+ * fires. At init there is no design yet, and a cover generated from nothing is generic.
9
+ *
10
+ * Run it EARLY — as soon as GAME-DESIGN.md says what the game is, before implementing it. The art
11
+ * then exists during the long first coding pass rather than after it, which is the other half of
12
+ * what Planning Mode's cover is for.
13
+ *
14
+ * It is built entirely on the existing `generate image` lane (`runAssetGeneration`), so ownership,
15
+ * spark billing, the SSE keepalive and the server-side deadline all behave identically to every
16
+ * other generation. Only two things differ, and both are this side of the wire: the prompt is
17
+ * composed by `@bitmagic/asset-core`'s `buildCoverArtPrompt` (the same one the web lane uses, so
18
+ * covers from both lanes look like the same product), and the result is additionally written to
19
+ * the start screen, saved locally, and picked up by `bitmagic publish` as the thumbnail.
20
+ */
21
+ import { defineCommand } from 'citty';
22
+ import { Buffer } from 'buffer';
23
+ import { createHash } from 'crypto';
24
+ import * as fs from 'fs';
25
+ import * as path from 'path';
26
+ import { buildCoverArtPrompt, isUpsertRootPatch } from '@bitmagic/asset-core';
27
+ import { CliError } from '../errors.js';
28
+ import { loadProjectContext } from '../project/context.js';
29
+ import { aliasSourceDir } from '../scaffold/aliases.js';
30
+ import { applyPatchesToWorld } from '../generate/apply-patches.js';
31
+ import { runAssetGeneration } from './generate.js';
32
+ import { renderInlineImage } from '../render/inline-image.js';
33
+ import { createOutput } from '../output.js';
34
+ /** The design document `bitmagic init` scaffolds and the coding agent maintains. */
35
+ export const GAME_DESIGN_FILE = 'GAME-DESIGN.md';
36
+ /**
37
+ * The `assets[]` entry name the cover is stored under. Fixed, not derived from the game name,
38
+ * because the image generator upserts on `['name', 'type']` — a stable name means regenerating
39
+ * REPLACES the previous cover instead of leaving a trail of orphaned images in world.json.
40
+ */
41
+ const COVER_ASSET_NAME = 'cover_art';
42
+ /** Square, matching the web lane's cover size. The portal card and start screen both crop to fit. */
43
+ const COVER_SIZE = 1024;
44
+ /** Under `.bitmagic/`: gitignored, and excluded from the publish fingerprint. */
45
+ const COVER_IMAGE_FILE = path.join('.bitmagic', 'cover.webp');
46
+ const COVER_RECORD_FILE = path.join('.bitmagic', 'cover.json');
47
+ export function coverImagePath(root) {
48
+ return path.join(root, COVER_IMAGE_FILE);
49
+ }
50
+ function coverRecordPath(root) {
51
+ return path.join(root, COVER_RECORD_FILE);
52
+ }
53
+ /**
54
+ * Identifies the design text a cover was generated from, so a repeat `bitmagic cover` on an
55
+ * unchanged design can decline to spend sparks on the same picture twice. This is the local
56
+ * equivalent of `PLAN_COVER_CACHE` in game-play-agent's PromptToGameController, which does the
57
+ * same for Planning Mode, keyed the same way — on the design text rather than the game.
58
+ */
59
+ export function hashDesign(designText) {
60
+ return createHash('sha256').update(designText.trim(), 'utf-8').digest('hex');
61
+ }
62
+ export function readCoverRecord(root) {
63
+ let raw;
64
+ try {
65
+ raw = fs.readFileSync(coverRecordPath(root), 'utf-8');
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(raw);
73
+ }
74
+ catch {
75
+ // A corrupt record is not worth an error: the worst case is one regeneration.
76
+ return null;
77
+ }
78
+ if (typeof parsed !== 'object' || parsed === null)
79
+ return null;
80
+ const { url, designHash, generatedAt } = parsed;
81
+ if (typeof url !== 'string' || typeof designHash !== 'string' || typeof generatedAt !== 'string') {
82
+ return null;
83
+ }
84
+ return { url, designHash, generatedAt };
85
+ }
86
+ function writeCoverRecord(root, record) {
87
+ fs.writeFileSync(coverRecordPath(root), `${JSON.stringify(record, null, 2)}\n`);
88
+ }
89
+ // ============================================================================
90
+ // Design text
91
+ // ============================================================================
92
+ /**
93
+ * The prose the cover is drawn from: `--prompt` when given, otherwise GAME-DESIGN.md.
94
+ *
95
+ * `--prompt` is a one-call override and is never written to the file — the design document belongs
96
+ * to the creator, and a flag silently rewriting it would be a surprise the next `bitmagic cover`
97
+ * inherits.
98
+ *
99
+ * The error path matters as much as the happy one: projects scaffolded before GAME-DESIGN.md
100
+ * existed have no such file, and an agent that only sees "not found" cannot tell whether it should
101
+ * create one or has run the command in the wrong place. So the message says both what to write and
102
+ * what to run instead.
103
+ */
104
+ export function resolveDesignText(options) {
105
+ const flag = options.promptFlag?.trim();
106
+ if (flag)
107
+ return flag;
108
+ const readFile = options.readFile ?? ((file) => fs.readFileSync(file, 'utf-8'));
109
+ const designPath = path.join(options.root, GAME_DESIGN_FILE);
110
+ let contents;
111
+ try {
112
+ contents = readFile(designPath);
113
+ }
114
+ catch {
115
+ throw new CliError(`No ${GAME_DESIGN_FILE} at ${designPath}, and no --prompt given. Cover art is generated from ` +
116
+ 'the game design, so there must be one or the other.\n' +
117
+ `Write ${GAME_DESIGN_FILE} describing the pitch, the core loop and — most usefully for the ` +
118
+ 'artwork — the world and its look, then run `bitmagic cover` again. To generate from a ' +
119
+ 'one-off description instead, run `bitmagic cover --prompt "..."`.');
120
+ }
121
+ if (!contents.trim()) {
122
+ throw new CliError(`${designPath} is empty. Describe the game — especially its world and look — or pass ` +
123
+ '`bitmagic cover --prompt "..."` to generate from a one-off description.');
124
+ }
125
+ return contents;
126
+ }
127
+ // ============================================================================
128
+ // Reading the result
129
+ // ============================================================================
130
+ /**
131
+ * The generated image's URL, read back out of the patch the image generator returned.
132
+ *
133
+ * The generator's terminal frame carries only `{ success, message, patches }` — the URL is not a
134
+ * separate field on the wire — so the patch it upserts into `assets[]` is where it lives. Matched
135
+ * on the patch's own shape rather than on the message text, which is prose and not a contract.
136
+ */
137
+ export function coverUrlFromPatches(patches) {
138
+ for (const patch of patches) {
139
+ if (!isUpsertRootPatch(patch))
140
+ continue;
141
+ if (patch.path.join('.') !== 'assets')
142
+ continue;
143
+ const value = patch.value;
144
+ if (typeof value !== 'object' || value === null)
145
+ continue;
146
+ const url = value.url;
147
+ if (typeof url === 'string' && url.length > 0)
148
+ return url;
149
+ }
150
+ return null;
151
+ }
152
+ /**
153
+ * The patch that puts the cover behind the game's start screen.
154
+ *
155
+ * `set`, NOT `setRoot`: `set` addresses a path inside `worldProfileData`, which is where the
156
+ * engine's GameRuntimeController reads `hud.startScreen.imageUrl` from. A root-level write would
157
+ * land somewhere nothing ever looks and report success — the exact trap apply-patches.ts documents.
158
+ */
159
+ export function startScreenPatch(url) {
160
+ return { type: 'set', path: ['hud', 'startScreen', 'imageUrl'], value: url };
161
+ }
162
+ function defaultDeps(json) {
163
+ const output = createOutput(json);
164
+ return {
165
+ fetch: globalThis.fetch,
166
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
167
+ log: output.info,
168
+ result: output.result,
169
+ generate: runAssetGeneration,
170
+ renderImage: (bytes) => renderInlineImage(bytes),
171
+ };
172
+ }
173
+ /**
174
+ * Download the generated image so the agent driving this CLI can look at it, and so
175
+ * `bitmagic publish` has bytes to upload as the thumbnail.
176
+ *
177
+ * Best-effort by design: the URL is already in world.json by the time this runs, so a failed
178
+ * download costs the local copy and nothing else. It must not turn a paid, successful generation
179
+ * into a failed command.
180
+ */
181
+ async function saveCoverImage(root, url, deps) {
182
+ try {
183
+ const response = await deps.fetch(url);
184
+ if (!response.ok) {
185
+ deps.log(` could not download the image for a local copy (HTTP ${response.status}).`);
186
+ return null;
187
+ }
188
+ const bytes = Buffer.from(await response.arrayBuffer());
189
+ fs.mkdirSync(path.dirname(coverImagePath(root)), { recursive: true });
190
+ fs.writeFileSync(coverImagePath(root), bytes);
191
+ return bytes;
192
+ }
193
+ catch (error) {
194
+ const message = error instanceof Error ? error.message : String(error);
195
+ deps.log(` could not download the image for a local copy: ${message}`);
196
+ return null;
197
+ }
198
+ }
199
+ /** Prints the cover's location, and draws it where the terminal can show it. */
200
+ function reportCover(root, url, bytes, deps) {
201
+ if (bytes)
202
+ deps.log(`Cover art: ${coverImagePath(root)}`);
203
+ deps.log(` ${url}`);
204
+ // Plain lines first, always: they are what an agent reads, and the image is strictly extra.
205
+ if (bytes)
206
+ deps.renderImage(bytes);
207
+ }
208
+ export async function runCover(args, deps = defaultDeps(args.json === true)) {
209
+ const context = loadProjectContext(deps.startDir);
210
+ const root = context.root;
211
+ const designText = resolveDesignText({ promptFlag: args.prompt, root });
212
+ const designHash = hashDesign(designText);
213
+ // Same design, same picture: decline rather than bill for it twice. The local image has to be
214
+ // there too — without it there is nothing for `bitmagic publish` to upload, and "up to date"
215
+ // would be a lie.
216
+ const existing = readCoverRecord(root);
217
+ if (args.force !== true &&
218
+ existing?.designHash === designHash &&
219
+ fs.existsSync(coverImagePath(root))) {
220
+ deps.log('Cover art is already up to date for this design.');
221
+ reportCover(root, existing.url, fs.readFileSync(coverImagePath(root)), deps);
222
+ deps.log('Pass --force to generate a new one anyway.');
223
+ // `generated: false` is the whole point of this branch for an automated caller: the command
224
+ // succeeded, nothing was billed, and there is no new picture to look at.
225
+ deps.result({
226
+ ok: true,
227
+ generated: false,
228
+ url: existing.url,
229
+ imagePath: coverImagePath(root),
230
+ startScreenSet: false,
231
+ });
232
+ return;
233
+ }
234
+ const body = {
235
+ name: COVER_ASSET_NAME,
236
+ prompt: buildCoverArtPrompt(designText),
237
+ width: COVER_SIZE,
238
+ height: COVER_SIZE,
239
+ ...(args['reference-image-url'] !== undefined
240
+ ? { referenceImageUrl: args['reference-image-url'] }
241
+ : {}),
242
+ };
243
+ // 'image' is the wire type; everything past this point is what makes it a COVER rather than one
244
+ // more entry in the assets catalog. `false` suppresses the generator's own result document —
245
+ // this command emits the one, and its prose is already routed by the deps it hands over.
246
+ const outcome = await deps.generate('image', 'cover art', body, false, deps);
247
+ const url = coverUrlFromPatches(outcome.patches);
248
+ if (url === null) {
249
+ throw new CliError('The cover image was generated and added to world.json, but the response carried no image ' +
250
+ 'URL, so the start screen was not set and no local copy was saved. Re-run `bitmagic cover ' +
251
+ '--force`; if it happens again, report it.');
252
+ }
253
+ const setsStartScreen = args['start-screen'] !== false;
254
+ if (setsStartScreen) {
255
+ const worldPath = path.join(root, aliasSourceDir('work'), 'world.json');
256
+ applyPatchesToWorld(worldPath, [startScreenPatch(url)]);
257
+ // A full sentence, not a bullet: it lands after runAssetGeneration's own patch summary has
258
+ // already closed with "world.json updated", where another bullet would read as part of that list.
259
+ deps.log("Set as the game's start screen (hud.startScreen.imageUrl).");
260
+ }
261
+ const bytes = await saveCoverImage(root, url, deps);
262
+ if (bytes) {
263
+ writeCoverRecord(root, { url, designHash, generatedAt: new Date().toISOString() });
264
+ }
265
+ reportCover(root, url, bytes, deps);
266
+ deps.log(bytes
267
+ ? 'This is now the publish thumbnail as well — `bitmagic publish` prefers it over the verify screenshot.'
268
+ : 'No local copy was saved, so `bitmagic publish` will still use the verify screenshot as the thumbnail.');
269
+ // Said plainly, and NOT conditional on --start-screen: the generation writes the image into
270
+ // `assets[]` either way, so world.json — and the fingerprint the verify gate checks — has moved
271
+ // regardless. An agent that publishes next would otherwise hit exit 3 and have to work out why.
272
+ deps.log('world.json changed — run `bitmagic verify` again before publishing.');
273
+ deps.result({
274
+ ok: true,
275
+ generated: true,
276
+ url,
277
+ // Null, not omitted: a caller that wanted to display the art needs to know it is not there,
278
+ // and an absent key reads as "look at the default path" rather than "there is no file".
279
+ imagePath: bytes ? coverImagePath(root) : null,
280
+ startScreenSet: setsStartScreen,
281
+ verifyStale: true,
282
+ });
283
+ }
284
+ export const coverCommand = defineCommand({
285
+ meta: {
286
+ name: 'cover',
287
+ description: "Generate the game's cover art from GAME-DESIGN.md and set it as the start screen.",
288
+ },
289
+ args: {
290
+ prompt: {
291
+ type: 'string',
292
+ description: `Describe the game for this call only, instead of reading ${GAME_DESIGN_FILE}. Never written to the file.`,
293
+ },
294
+ force: {
295
+ type: 'boolean',
296
+ description: 'Generate a new cover even when the design has not changed since the last one.',
297
+ },
298
+ 'start-screen': {
299
+ type: 'boolean',
300
+ negativeDescription: 'Leave hud.startScreen.imageUrl alone — still saves the cover and uses it as the publish thumbnail.',
301
+ },
302
+ 'reference-image-url': {
303
+ type: 'string',
304
+ description: 'Style/composition reference image URL forwarded to the image model.',
305
+ },
306
+ json: {
307
+ type: 'boolean',
308
+ description: 'Print the result as JSON on stdout; all progress goes to stderr.',
309
+ },
310
+ },
311
+ async run({ args }) {
312
+ await runCover({
313
+ prompt: args.prompt,
314
+ force: args.force,
315
+ 'start-screen': args['start-screen'],
316
+ 'reference-image-url': args['reference-image-url'],
317
+ json: args.json,
318
+ });
319
+ },
320
+ });
321
+ //# sourceMappingURL=cover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cover.js","sourceRoot":"","sources":["../../src/commands/cover.ts"],"names":[],"mappings":"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"}