lpc-forge 1.0.0 → 1.0.1

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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Asset manager — downloads, caches, and verifies LPC sprite assets
3
+ * for npm-installed CLI users who don't have the full repo checkout.
4
+ */
5
+ export interface AssetManifest {
6
+ version: string;
7
+ generatedAt: string;
8
+ chunks: AssetChunk[];
9
+ totalSize: number;
10
+ }
11
+ export interface AssetChunk {
12
+ name: string;
13
+ filename: string;
14
+ url: string;
15
+ size: number;
16
+ sha256: string;
17
+ required: boolean;
18
+ }
19
+ /** Platform-aware asset cache directory */
20
+ export declare function getAssetDir(): string;
21
+ /**
22
+ * Resolve the root directory where `spritesheets/` and `sheet_definitions/` live.
23
+ *
24
+ * Priority:
25
+ * 1. LPC_FORGE_ASSETS env var
26
+ * 2. Local repo checkout (spritesheets/ next to package root)
27
+ * 3. Cached asset directory (~/.lpc-forge/assets or platform equivalent)
28
+ */
29
+ export declare function resolveAssetRoot(packageRoot: string): string;
30
+ /** Check if assets are installed and match the expected version */
31
+ export declare function isAssetsInstalled(version?: string): Promise<{
32
+ installed: boolean;
33
+ version?: string;
34
+ chunks?: string[];
35
+ path: string;
36
+ }>;
37
+ /** Fetch the remote asset manifest from the latest GitHub release */
38
+ export declare function fetchRemoteManifest(version?: string): Promise<AssetManifest>;
39
+ export interface DownloadOptions {
40
+ /** Only download required chunks (body + definitions) */
41
+ minimal?: boolean;
42
+ /** Custom asset directory (overrides default) */
43
+ path?: string;
44
+ /** Callback for progress updates */
45
+ onProgress?: (state: DownloadProgress) => void;
46
+ /** Skip interactive prompts */
47
+ nonInteractive?: boolean;
48
+ }
49
+ export interface DownloadProgress {
50
+ phase: 'fetching-manifest' | 'downloading' | 'verifying' | 'extracting' | 'done';
51
+ currentChunk?: string;
52
+ chunkIndex?: number;
53
+ totalChunks?: number;
54
+ bytesDownloaded?: number;
55
+ bytesTotal?: number | null;
56
+ overallDownloaded?: number;
57
+ overallTotal?: number;
58
+ }
59
+ export declare function downloadAssets(options?: DownloadOptions): Promise<void>;
60
+ export declare function cleanAssets(assetDir?: string): Promise<void>;
61
+ /** Ask user yes/no in the terminal. Returns true for yes. */
62
+ export declare function promptYesNo(question: string): Promise<boolean>;
63
+ /**
64
+ * Ensures assets are available. If missing, prompts the user to download them.
65
+ * Returns the resolved asset root path.
66
+ * Throws if assets are unavailable and user declines download.
67
+ */
68
+ export declare function ensureAssets(packageRoot: string): Promise<string>;
69
+ export declare function downloadWithProgress(options?: DownloadOptions): Promise<void>;
@@ -0,0 +1,452 @@
1
+ /**
2
+ * Asset manager — downloads, caches, and verifies LPC sprite assets
3
+ * for npm-installed CLI users who don't have the full repo checkout.
4
+ */
5
+ import { createHash } from 'node:crypto';
6
+ import { existsSync } from 'node:fs';
7
+ import { mkdir, readFile, writeFile, rm, } from 'node:fs/promises';
8
+ import { get as httpsGet } from 'node:https';
9
+ import { get as httpGet } from 'node:http';
10
+ import { homedir, platform } from 'node:os';
11
+ import { join, resolve } from 'node:path';
12
+ import { createReadStream, createWriteStream } from 'node:fs';
13
+ import { pipeline } from 'node:stream/promises';
14
+ import { createInterface } from 'node:readline';
15
+ import { execFile } from 'node:child_process';
16
+ import { promisify } from 'node:util';
17
+ const execFileAsync = promisify(execFile);
18
+ // ──────────────────────────────────────────────
19
+ // Constants
20
+ // ──────────────────────────────────────────────
21
+ const GITHUB_REPO = 'LiberatedPixelCup/Universal-LPC-Spritesheet-Character-Generator';
22
+ const MANIFEST_FILENAME = 'assets-manifest.json';
23
+ const LOCAL_MANIFEST = '.lpc-forge-manifest.json';
24
+ // ──────────────────────────────────────────────
25
+ // Asset directory resolution
26
+ // ──────────────────────────────────────────────
27
+ /** Platform-aware asset cache directory */
28
+ export function getAssetDir() {
29
+ if (process.env.LPC_FORGE_ASSETS) {
30
+ return resolve(process.env.LPC_FORGE_ASSETS);
31
+ }
32
+ const p = platform();
33
+ if (p === 'darwin') {
34
+ return join(homedir(), 'Library', 'Application Support', 'lpc-forge', 'assets');
35
+ }
36
+ if (p === 'win32') {
37
+ return join(process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'), 'lpc-forge', 'assets');
38
+ }
39
+ // Linux / other — respect XDG
40
+ return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'lpc-forge', 'assets');
41
+ }
42
+ /**
43
+ * Resolve the root directory where `spritesheets/` and `sheet_definitions/` live.
44
+ *
45
+ * Priority:
46
+ * 1. LPC_FORGE_ASSETS env var
47
+ * 2. Local repo checkout (spritesheets/ next to package root)
48
+ * 3. Cached asset directory (~/.lpc-forge/assets or platform equivalent)
49
+ */
50
+ export function resolveAssetRoot(packageRoot) {
51
+ if (process.env.LPC_FORGE_ASSETS) {
52
+ return resolve(process.env.LPC_FORGE_ASSETS);
53
+ }
54
+ // Check if running from a git clone / dev checkout
55
+ if (existsSync(join(packageRoot, 'spritesheets'))) {
56
+ return packageRoot;
57
+ }
58
+ return getAssetDir();
59
+ }
60
+ // ──────────────────────────────────────────────
61
+ // Status checks
62
+ // ──────────────────────────────────────────────
63
+ /** Check if assets are installed and match the expected version */
64
+ export async function isAssetsInstalled(version) {
65
+ const assetDir = getAssetDir();
66
+ const manifestPath = join(assetDir, LOCAL_MANIFEST);
67
+ if (!existsSync(manifestPath)) {
68
+ return { installed: false, path: assetDir };
69
+ }
70
+ try {
71
+ const raw = await readFile(manifestPath, 'utf-8');
72
+ const manifest = JSON.parse(raw);
73
+ // Check if critical directories actually exist
74
+ const hasSpritesheets = existsSync(join(assetDir, 'spritesheets'));
75
+ const hasDefinitions = existsSync(join(assetDir, 'sheet_definitions'));
76
+ if (!hasSpritesheets || !hasDefinitions) {
77
+ return { installed: false, path: assetDir };
78
+ }
79
+ // Version mismatch check
80
+ if (version && manifest.version !== version) {
81
+ return {
82
+ installed: false,
83
+ version: manifest.version,
84
+ chunks: manifest.chunks,
85
+ path: assetDir,
86
+ };
87
+ }
88
+ return {
89
+ installed: true,
90
+ version: manifest.version,
91
+ chunks: manifest.chunks,
92
+ path: assetDir,
93
+ };
94
+ }
95
+ catch {
96
+ return { installed: false, path: assetDir };
97
+ }
98
+ }
99
+ // ──────────────────────────────────────────────
100
+ // Download helpers
101
+ // ──────────────────────────────────────────────
102
+ function followRedirects(url, maxRedirects = 5) {
103
+ return new Promise((resolve, reject) => {
104
+ if (maxRedirects <= 0) {
105
+ reject(new Error('Too many redirects'));
106
+ return;
107
+ }
108
+ const getter = url.startsWith('https') ? httpsGet : httpGet;
109
+ const req = getter(url, {
110
+ headers: { 'User-Agent': 'lpc-forge-cli' },
111
+ }, (res) => {
112
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
113
+ res.resume(); // drain the response
114
+ followRedirects(res.headers.location, maxRedirects - 1).then(resolve, reject);
115
+ return;
116
+ }
117
+ if (res.statusCode && res.statusCode >= 400) {
118
+ res.resume();
119
+ reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
120
+ return;
121
+ }
122
+ resolve(res);
123
+ });
124
+ req.on('error', reject);
125
+ });
126
+ }
127
+ async function downloadToFile(url, destPath, onProgress) {
128
+ const res = await followRedirects(url);
129
+ const total = res.headers['content-length'] ? parseInt(res.headers['content-length'], 10) : null;
130
+ await mkdir(join(destPath, '..'), { recursive: true });
131
+ const tmpPath = destPath + '.tmp';
132
+ const ws = createWriteStream(tmpPath);
133
+ let downloaded = 0;
134
+ res.on('data', (chunk) => {
135
+ downloaded += chunk.length;
136
+ onProgress?.(downloaded, total);
137
+ });
138
+ try {
139
+ await pipeline(res, ws);
140
+ // Atomic rename from tmp to final
141
+ const { rename } = await import('node:fs/promises');
142
+ await rename(tmpPath, destPath);
143
+ }
144
+ catch (err) {
145
+ // Clean up partial file
146
+ await rm(tmpPath, { force: true });
147
+ throw err;
148
+ }
149
+ }
150
+ async function computeSha256(filePath) {
151
+ const hash = createHash('sha256');
152
+ const stream = createReadStream(filePath);
153
+ for await (const chunk of stream) {
154
+ hash.update(chunk);
155
+ }
156
+ return hash.digest('hex');
157
+ }
158
+ // ──────────────────────────────────────────────
159
+ // Manifest fetching
160
+ // ──────────────────────────────────────────────
161
+ async function fetchJson(url) {
162
+ const res = await followRedirects(url);
163
+ const chunks = [];
164
+ for await (const chunk of res) {
165
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
166
+ }
167
+ return JSON.parse(Buffer.concat(chunks).toString('utf-8'));
168
+ }
169
+ /** Fetch the remote asset manifest from the latest GitHub release */
170
+ export async function fetchRemoteManifest(version) {
171
+ const tag = version || 'latest';
172
+ // For 'latest', use the GitHub API to resolve the actual tag
173
+ if (tag === 'latest') {
174
+ const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
175
+ const release = await fetchJson(releaseUrl);
176
+ const manifestAsset = release.assets.find(a => a.name === MANIFEST_FILENAME);
177
+ if (!manifestAsset) {
178
+ throw new Error(`No ${MANIFEST_FILENAME} found in latest release (${release.tag_name}). ` +
179
+ `The release may not have been packaged correctly.`);
180
+ }
181
+ return fetchJson(manifestAsset.browser_download_url);
182
+ }
183
+ const url = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${MANIFEST_FILENAME}`;
184
+ return fetchJson(url);
185
+ }
186
+ export async function downloadAssets(options = {}) {
187
+ const assetDir = options.path ? resolve(options.path) : getAssetDir();
188
+ // Check available disk space (best-effort)
189
+ await checkDiskSpace(assetDir);
190
+ // 1. Fetch manifest
191
+ options.onProgress?.({ phase: 'fetching-manifest' });
192
+ const manifest = await fetchRemoteManifest();
193
+ // 2. Filter chunks
194
+ let chunks = manifest.chunks;
195
+ if (options.minimal) {
196
+ chunks = chunks.filter(c => c.required);
197
+ }
198
+ const totalSize = chunks.reduce((s, c) => s + c.size, 0);
199
+ let overallDownloaded = 0;
200
+ // 3. Download and extract each chunk
201
+ await mkdir(assetDir, { recursive: true });
202
+ const downloadedChunks = [];
203
+ for (let i = 0; i < chunks.length; i++) {
204
+ const chunk = chunks[i];
205
+ const archivePath = join(assetDir, chunk.filename);
206
+ // Download
207
+ options.onProgress?.({
208
+ phase: 'downloading',
209
+ currentChunk: chunk.name,
210
+ chunkIndex: i,
211
+ totalChunks: chunks.length,
212
+ bytesDownloaded: 0,
213
+ bytesTotal: chunk.size,
214
+ overallDownloaded,
215
+ overallTotal: totalSize,
216
+ });
217
+ await downloadToFile(chunk.url, archivePath, (downloaded, total) => {
218
+ options.onProgress?.({
219
+ phase: 'downloading',
220
+ currentChunk: chunk.name,
221
+ chunkIndex: i,
222
+ totalChunks: chunks.length,
223
+ bytesDownloaded: downloaded,
224
+ bytesTotal: total ?? chunk.size,
225
+ overallDownloaded: overallDownloaded + downloaded,
226
+ overallTotal: totalSize,
227
+ });
228
+ });
229
+ overallDownloaded += chunk.size;
230
+ // Verify checksum
231
+ options.onProgress?.({
232
+ phase: 'verifying',
233
+ currentChunk: chunk.name,
234
+ chunkIndex: i,
235
+ totalChunks: chunks.length,
236
+ });
237
+ const actualHash = await computeSha256(archivePath);
238
+ if (actualHash !== chunk.sha256) {
239
+ await rm(archivePath, { force: true });
240
+ throw new Error(`Checksum mismatch for ${chunk.filename}. ` +
241
+ `Expected ${chunk.sha256}, got ${actualHash}. ` +
242
+ `The file may be corrupted — try again.`);
243
+ }
244
+ // Extract
245
+ options.onProgress?.({
246
+ phase: 'extracting',
247
+ currentChunk: chunk.name,
248
+ chunkIndex: i,
249
+ totalChunks: chunks.length,
250
+ });
251
+ await execFileAsync('tar', ['xzf', archivePath, '-C', assetDir]);
252
+ // Remove archive after extraction to save space
253
+ await rm(archivePath, { force: true });
254
+ downloadedChunks.push(chunk.name);
255
+ }
256
+ // 4. Write local manifest
257
+ const localManifest = {
258
+ version: manifest.version,
259
+ installedAt: new Date().toISOString(),
260
+ chunks: downloadedChunks,
261
+ };
262
+ await writeFile(join(assetDir, LOCAL_MANIFEST), JSON.stringify(localManifest, null, 2));
263
+ options.onProgress?.({ phase: 'done' });
264
+ }
265
+ // ──────────────────────────────────────────────
266
+ // Disk space check (best-effort)
267
+ // ──────────────────────────────────────────────
268
+ async function checkDiskSpace(dir) {
269
+ // Ensure parent exists for statvfs
270
+ await mkdir(dir, { recursive: true });
271
+ try {
272
+ // Use df on Unix-like systems
273
+ if (platform() !== 'win32') {
274
+ const { stdout } = await execFileAsync('df', ['-k', dir]);
275
+ const lines = stdout.trim().split('\n');
276
+ if (lines.length >= 2) {
277
+ const parts = lines[1].split(/\s+/);
278
+ const availKB = parseInt(parts[3], 10);
279
+ if (!isNaN(availKB)) {
280
+ const availMB = availKB / 1024;
281
+ if (availMB < 2000) { // Less than 2GB available
282
+ throw new Error(`Insufficient disk space: ${Math.round(availMB)} MB available, ` +
283
+ `but assets require ~1.3 GB. Free up space or use --path to specify a different location.`);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+ catch (err) {
290
+ // If it's our own insufficient space error, re-throw
291
+ if (err instanceof Error && err.message.includes('Insufficient disk space')) {
292
+ throw err;
293
+ }
294
+ // Otherwise ignore — best-effort check
295
+ }
296
+ }
297
+ // ──────────────────────────────────────────────
298
+ // Clean up
299
+ // ──────────────────────────────────────────────
300
+ export async function cleanAssets(assetDir) {
301
+ const dir = assetDir || getAssetDir();
302
+ if (existsSync(dir)) {
303
+ await rm(dir, { recursive: true, force: true });
304
+ }
305
+ }
306
+ // ──────────────────────────────────────────────
307
+ // Interactive prompt
308
+ // ──────────────────────────────────────────────
309
+ /** Ask user yes/no in the terminal. Returns true for yes. */
310
+ export async function promptYesNo(question) {
311
+ // Non-interactive / CI environments
312
+ if (process.env.LPC_FORGE_AUTO_DOWNLOAD === 'true') {
313
+ return true;
314
+ }
315
+ if (!process.stdin.isTTY) {
316
+ return false;
317
+ }
318
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
319
+ return new Promise((resolve) => {
320
+ rl.question(question, (answer) => {
321
+ rl.close();
322
+ const a = answer.trim().toLowerCase();
323
+ resolve(a === '' || a === 'y' || a === 'yes');
324
+ });
325
+ });
326
+ }
327
+ // ──────────────────────────────────────────────
328
+ // Ensure assets (called before sprite commands)
329
+ // ──────────────────────────────────────────────
330
+ /**
331
+ * Ensures assets are available. If missing, prompts the user to download them.
332
+ * Returns the resolved asset root path.
333
+ * Throws if assets are unavailable and user declines download.
334
+ */
335
+ export async function ensureAssets(packageRoot) {
336
+ const assetRoot = resolveAssetRoot(packageRoot);
337
+ // If we found a local checkout with spritesheets, we're good
338
+ if (existsSync(join(assetRoot, 'spritesheets')) && existsSync(join(assetRoot, 'sheet_definitions'))) {
339
+ return assetRoot;
340
+ }
341
+ // Assets not found — need to download
342
+ const chalk = (await import('chalk')).default;
343
+ console.log('');
344
+ console.log(chalk.yellow('⚠️ LPC Forge assets not found.') + ' These are required for sprite generation.');
345
+ console.log('');
346
+ console.log(` Total download: ${chalk.bold('~1.3 GB')} (305,680 sprite files)`);
347
+ console.log(` Install location: ${chalk.gray(getAssetDir())}`);
348
+ console.log('');
349
+ const proceed = await promptYesNo(` Download now? (Y/n): `);
350
+ if (!proceed) {
351
+ console.log('');
352
+ console.log(chalk.gray(' Run ') + chalk.cyan('lpc-forge setup') + chalk.gray(' when ready to download.'));
353
+ console.log(chalk.gray(' Run ') + chalk.cyan('lpc-forge setup --help') + chalk.gray(' for more options.'));
354
+ console.log('');
355
+ process.exit(1);
356
+ }
357
+ // Download with progress display
358
+ await downloadWithProgress();
359
+ return getAssetDir();
360
+ }
361
+ // ──────────────────────────────────────────────
362
+ // Progress display
363
+ // ──────────────────────────────────────────────
364
+ function formatBytes(bytes) {
365
+ if (bytes < 1024)
366
+ return `${bytes} B`;
367
+ if (bytes < 1024 * 1024)
368
+ return `${(bytes / 1024).toFixed(1)} KB`;
369
+ if (bytes < 1024 * 1024 * 1024)
370
+ return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;
371
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
372
+ }
373
+ function progressBar(fraction, width = 20) {
374
+ const filled = Math.round(fraction * width);
375
+ const empty = width - filled;
376
+ return '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';
377
+ }
378
+ export async function downloadWithProgress(options = {}) {
379
+ const chalk = (await import('chalk')).default;
380
+ const ora = (await import('ora')).default;
381
+ let manifest;
382
+ const manifestSpinner = ora('Fetching asset manifest...').start();
383
+ try {
384
+ manifest = await fetchRemoteManifest();
385
+ manifestSpinner.succeed('Asset manifest fetched');
386
+ }
387
+ catch (err) {
388
+ manifestSpinner.fail('Failed to fetch asset manifest');
389
+ throw err;
390
+ }
391
+ let chunks = manifest.chunks;
392
+ if (options.minimal) {
393
+ chunks = chunks.filter(c => c.required);
394
+ }
395
+ const totalSize = chunks.reduce((s, c) => s + c.size, 0);
396
+ const assetDir = options.path ? resolve(options.path) : getAssetDir();
397
+ console.log('');
398
+ console.log(chalk.bold('📦 Downloading LPC Forge assets...'));
399
+ console.log('');
400
+ await mkdir(assetDir, { recursive: true });
401
+ await checkDiskSpace(assetDir);
402
+ const downloadedChunks = [];
403
+ let overallDownloaded = 0;
404
+ for (let i = 0; i < chunks.length; i++) {
405
+ const chunk = chunks[i];
406
+ const archivePath = join(assetDir, chunk.filename);
407
+ // Show which chunk we're downloading
408
+ const spinner = ora({
409
+ text: `${chunk.name} (${formatBytes(chunk.size)})`,
410
+ prefixText: ' ',
411
+ }).start();
412
+ try {
413
+ // Download
414
+ await downloadToFile(chunk.url, archivePath, (downloaded, total) => {
415
+ const t = total ?? chunk.size;
416
+ const pct = Math.round((downloaded / t) * 100);
417
+ spinner.text = `${chunk.name} (${formatBytes(chunk.size)}) ${progressBar(downloaded / t)} ${pct}%`;
418
+ });
419
+ // Verify
420
+ spinner.text = `${chunk.name} — verifying checksum...`;
421
+ const actualHash = await computeSha256(archivePath);
422
+ if (actualHash !== chunk.sha256) {
423
+ await rm(archivePath, { force: true });
424
+ spinner.fail(`${chunk.name} — checksum mismatch`);
425
+ throw new Error(`Checksum mismatch for ${chunk.filename}. ` +
426
+ `Expected ${chunk.sha256}, got ${actualHash}.`);
427
+ }
428
+ // Extract
429
+ spinner.text = `${chunk.name} — extracting...`;
430
+ await execFileAsync('tar', ['xzf', archivePath, '-C', assetDir]);
431
+ await rm(archivePath, { force: true });
432
+ overallDownloaded += chunk.size;
433
+ spinner.succeed(`${chunk.name} (${formatBytes(chunk.size)})`);
434
+ downloadedChunks.push(chunk.name);
435
+ }
436
+ catch (err) {
437
+ spinner.fail(`${chunk.name} — failed`);
438
+ throw err;
439
+ }
440
+ }
441
+ // Write local manifest
442
+ const localManifest = {
443
+ version: manifest.version,
444
+ installedAt: new Date().toISOString(),
445
+ chunks: downloadedChunks,
446
+ };
447
+ await writeFile(join(assetDir, LOCAL_MANIFEST), JSON.stringify(localManifest, null, 2));
448
+ console.log('');
449
+ console.log(chalk.green(`✅ Assets installed (${formatBytes(overallDownloaded)}) → ${assetDir}`));
450
+ console.log('');
451
+ }
452
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/assets/manager.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -3,8 +3,10 @@ import { Command } from 'commander';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { mkdir, writeFile } from 'node:fs/promises';
5
5
  import { fileURLToPath } from 'node:url';
6
+ import { resolveAssetRoot, ensureAssets } from './assets/manager.js';
6
7
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
7
- const REPO_ROOT = resolve(__dirname, '..');
8
+ const PACKAGE_ROOT = resolve(__dirname, '..');
9
+ const REPO_ROOT = resolveAssetRoot(PACKAGE_ROOT);
8
10
  const program = new Command();
9
11
  program
10
12
  .name('lpc-forge')
@@ -29,8 +31,9 @@ program
29
31
  const chalk = (await import('chalk')).default;
30
32
  const ora = (await import('ora')).default;
31
33
  if (opts.listLayers) {
34
+ const assetRoot = await ensureAssets(PACKAGE_ROOT);
32
35
  const { loadDefinitions, listLayers } = await import('./character/definitions.js');
33
- const registry = await loadDefinitions(REPO_ROOT);
36
+ const registry = await loadDefinitions(assetRoot);
34
37
  const layers = listLayers(registry);
35
38
  for (const [category, items] of Object.entries(layers)) {
36
39
  console.log(chalk.bold.cyan(`\n${category}`));
@@ -40,6 +43,7 @@ program
40
43
  }
41
44
  return;
42
45
  }
46
+ const assetRoot = await ensureAssets(PACKAGE_ROOT);
43
47
  const spinner = ora('Composing character...').start();
44
48
  try {
45
49
  const { composeCharacter } = await import('./character/composer.js');
@@ -76,7 +80,7 @@ program
76
80
  layers,
77
81
  };
78
82
  }
79
- const buffer = await composeCharacter(spec, REPO_ROOT);
83
+ const buffer = await composeCharacter(spec, assetRoot);
80
84
  const outputDir = resolve(opts.output);
81
85
  await mkdir(outputDir, { recursive: true });
82
86
  await writeFile(join(outputDir, 'spritesheet.png'), buffer);
@@ -111,8 +115,9 @@ program
111
115
  const chalk = (await import('chalk')).default;
112
116
  const ora = (await import('ora')).default;
113
117
  const { runBatch } = await import('./character/batch.js');
118
+ const assetRoot = await ensureAssets(PACKAGE_ROOT);
114
119
  const spinner = ora('Running batch generation...').start();
115
- const results = await runBatch(resolve(configPath), REPO_ROOT, resolve(opts.output));
120
+ const results = await runBatch(resolve(configPath), assetRoot, resolve(opts.output));
116
121
  const succeeded = results.filter(r => r.success).length;
117
122
  const failed = results.filter(r => !r.success).length;
118
123
  spinner.succeed(`Batch complete: ${succeeded} succeeded, ${failed} failed`);
@@ -134,8 +139,9 @@ program
134
139
  .option('--json', 'Output as JSON')
135
140
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
141
  .action(async (opts) => {
142
+ const assetRoot = await ensureAssets(PACKAGE_ROOT);
137
143
  const { loadDefinitions, listLayers } = await import('./character/definitions.js');
138
- const registry = await loadDefinitions(REPO_ROOT);
144
+ const registry = await loadDefinitions(assetRoot);
139
145
  const layers = listLayers(registry);
140
146
  if (opts.category) {
141
147
  const filtered = {};
@@ -335,7 +341,8 @@ program
335
341
  charSpinner.fail(`Unknown preset: ${opts.character}`);
336
342
  process.exit(1);
337
343
  }
338
- const charBuffer = await composeCharacter(preset.spec, REPO_ROOT);
344
+ const assetRoot = await ensureAssets(PACKAGE_ROOT);
345
+ const charBuffer = await composeCharacter(preset.spec, assetRoot);
339
346
  await exportCharacterToGodot(charBuffer, outputDir, opts.character);
340
347
  charSpinner.succeed(`${preset.name} character generated`);
341
348
  // 3. Generate map
@@ -420,7 +427,7 @@ program
420
427
  let enemyCount = 0;
421
428
  for (const enemyPreset of enemyPresets) {
422
429
  if (PRESETS[enemyPreset]) {
423
- const enemyBuffer = await composeCharacter(PRESETS[enemyPreset].spec, REPO_ROOT);
430
+ const enemyBuffer = await composeCharacter(PRESETS[enemyPreset].spec, assetRoot);
424
431
  await exportCharacterToGodot(enemyBuffer, outputDir, enemyPreset, { isPlayer: false });
425
432
  enemyCount++;
426
433
  }
@@ -432,7 +439,7 @@ program
432
439
  let npcCount = 0;
433
440
  for (const npcPreset of npcPresets) {
434
441
  if (PRESETS[npcPreset] && npcPreset !== 'guard') {
435
- const npcBuffer = await composeCharacter(PRESETS[npcPreset].spec, REPO_ROOT);
442
+ const npcBuffer = await composeCharacter(PRESETS[npcPreset].spec, assetRoot);
436
443
  await exportCharacterToGodot(npcBuffer, outputDir, `npc_${npcPreset}`, { isPlayer: false });
437
444
  npcCount++;
438
445
  }
@@ -934,5 +941,51 @@ program
934
941
  process.exit(1);
935
942
  }
936
943
  });
944
+ // === SETUP COMMAND ===
945
+ program
946
+ .command('setup')
947
+ .description('Download and install LPC sprite assets (~1.3 GB)')
948
+ .option('--minimal', 'Download only required assets (body + definitions, ~200 MB)')
949
+ .option('--check', 'Check if assets are installed and up to date')
950
+ .option('--clean', 'Remove cached assets')
951
+ .option('--path <dir>', 'Custom asset directory (also set via LPC_FORGE_ASSETS env var)')
952
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
953
+ .action(async (opts) => {
954
+ const chalk = (await import('chalk')).default;
955
+ const { isAssetsInstalled, getAssetDir, cleanAssets, downloadWithProgress, } = await import('./assets/manager.js');
956
+ if (opts.check) {
957
+ const status = await isAssetsInstalled();
958
+ if (status.installed) {
959
+ console.log(chalk.green('✅ Assets installed'));
960
+ console.log(chalk.gray(` Version: ${status.version}`));
961
+ console.log(chalk.gray(` Location: ${status.path}`));
962
+ if (status.chunks) {
963
+ console.log(chalk.gray(` Chunks: ${status.chunks.join(', ')}`));
964
+ }
965
+ }
966
+ else {
967
+ console.log(chalk.yellow('⚠️ Assets not installed'));
968
+ console.log(chalk.gray(` Expected at: ${status.path}`));
969
+ if (status.version) {
970
+ console.log(chalk.gray(` Found version: ${status.version} (may need update)`));
971
+ }
972
+ console.log(chalk.gray('\n Run: lpc-forge setup'));
973
+ }
974
+ return;
975
+ }
976
+ if (opts.clean) {
977
+ const dir = opts.path || getAssetDir();
978
+ const ora = (await import('ora')).default;
979
+ const spinner = ora('Removing cached assets...').start();
980
+ await cleanAssets(dir);
981
+ spinner.succeed(`Assets removed from ${dir}`);
982
+ return;
983
+ }
984
+ // Download
985
+ await downloadWithProgress({
986
+ minimal: opts.minimal,
987
+ path: opts.path,
988
+ });
989
+ });
937
990
  program.parse();
938
991
  //# sourceMappingURL=cli.js.map
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "lpc-forge",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Complete 2D RPG asset pipeline — character compositor, procedural map generator, and Godot 4.6 exporter. Built on Liberated Pixel Cup sprites.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "lpc-forge": "./dist/cli.js"
7
+ "lpc-forge": "dist/cli.js"
8
8
  },
9
9
  "files": [
10
10
  "dist/cli.js",
@@ -16,6 +16,7 @@
16
16
  "dist/utils/**",
17
17
  "dist/license.js",
18
18
  "dist/license.d.ts",
19
+ "dist/assets/**",
19
20
  "assets/**",
20
21
  "README.md",
21
22
  "LICENSE",