lootforge 0.3.0

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 (243) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +764 -0
  3. package/bin/lootforge.js +28 -0
  4. package/dist/benchmarks/coarseToFineCost.d.ts +21 -0
  5. package/dist/benchmarks/coarseToFineCost.js +49 -0
  6. package/dist/benchmarks/coarseToFineCost.js.map +1 -0
  7. package/dist/checks/boundaryMetrics.d.ts +12 -0
  8. package/dist/checks/boundaryMetrics.js +102 -0
  9. package/dist/checks/boundaryMetrics.js.map +1 -0
  10. package/dist/checks/candidateScore.d.ts +11 -0
  11. package/dist/checks/candidateScore.js +462 -0
  12. package/dist/checks/candidateScore.js.map +1 -0
  13. package/dist/checks/commandParser.d.ts +5 -0
  14. package/dist/checks/commandParser.js +99 -0
  15. package/dist/checks/commandParser.js.map +1 -0
  16. package/dist/checks/consistencyOutliers.d.ts +42 -0
  17. package/dist/checks/consistencyOutliers.js +156 -0
  18. package/dist/checks/consistencyOutliers.js.map +1 -0
  19. package/dist/checks/imageAcceptance.d.ts +67 -0
  20. package/dist/checks/imageAcceptance.js +967 -0
  21. package/dist/checks/imageAcceptance.js.map +1 -0
  22. package/dist/checks/packInvariants.d.ts +56 -0
  23. package/dist/checks/packInvariants.js +1064 -0
  24. package/dist/checks/packInvariants.js.map +1 -0
  25. package/dist/checks/softAdapters.d.ts +25 -0
  26. package/dist/checks/softAdapters.js +275 -0
  27. package/dist/checks/softAdapters.js.map +1 -0
  28. package/dist/checks/vlmGate.d.ts +8 -0
  29. package/dist/checks/vlmGate.js +200 -0
  30. package/dist/checks/vlmGate.js.map +1 -0
  31. package/dist/cli/commands/atlas.d.ts +5 -0
  32. package/dist/cli/commands/atlas.js +18 -0
  33. package/dist/cli/commands/atlas.js.map +1 -0
  34. package/dist/cli/commands/eval.d.ts +6 -0
  35. package/dist/cli/commands/eval.js +23 -0
  36. package/dist/cli/commands/eval.js.map +1 -0
  37. package/dist/cli/commands/generate.d.ts +18 -0
  38. package/dist/cli/commands/generate.js +66 -0
  39. package/dist/cli/commands/generate.js.map +1 -0
  40. package/dist/cli/commands/init.d.ts +15 -0
  41. package/dist/cli/commands/init.js +146 -0
  42. package/dist/cli/commands/init.js.map +1 -0
  43. package/dist/cli/commands/package.d.ts +6 -0
  44. package/dist/cli/commands/package.js +27 -0
  45. package/dist/cli/commands/package.js.map +1 -0
  46. package/dist/cli/commands/plan.d.ts +16 -0
  47. package/dist/cli/commands/plan.js +49 -0
  48. package/dist/cli/commands/plan.js.map +1 -0
  49. package/dist/cli/commands/process.d.ts +14 -0
  50. package/dist/cli/commands/process.js +29 -0
  51. package/dist/cli/commands/process.js.map +1 -0
  52. package/dist/cli/commands/regenerate.d.ts +29 -0
  53. package/dist/cli/commands/regenerate.js +244 -0
  54. package/dist/cli/commands/regenerate.js.map +1 -0
  55. package/dist/cli/commands/review.d.ts +5 -0
  56. package/dist/cli/commands/review.js +18 -0
  57. package/dist/cli/commands/review.js.map +1 -0
  58. package/dist/cli/commands/select.d.ts +6 -0
  59. package/dist/cli/commands/select.js +21 -0
  60. package/dist/cli/commands/select.js.map +1 -0
  61. package/dist/cli/commands/serve.d.ts +16 -0
  62. package/dist/cli/commands/serve.js +100 -0
  63. package/dist/cli/commands/serve.js.map +1 -0
  64. package/dist/cli/commands/validate.d.ts +17 -0
  65. package/dist/cli/commands/validate.js +108 -0
  66. package/dist/cli/commands/validate.js.map +1 -0
  67. package/dist/cli/index.d.ts +1 -0
  68. package/dist/cli/index.js +157 -0
  69. package/dist/cli/index.js.map +1 -0
  70. package/dist/cli/parseArgs.d.ts +3 -0
  71. package/dist/cli/parseArgs.js +37 -0
  72. package/dist/cli/parseArgs.js.map +1 -0
  73. package/dist/contracts/stageArtifacts.d.ts +4031 -0
  74. package/dist/contracts/stageArtifacts.js +663 -0
  75. package/dist/contracts/stageArtifacts.js.map +1 -0
  76. package/dist/manifest/load.d.ts +3 -0
  77. package/dist/manifest/load.js +50 -0
  78. package/dist/manifest/load.js.map +1 -0
  79. package/dist/manifest/normalize-palette.d.ts +17 -0
  80. package/dist/manifest/normalize-palette.js +235 -0
  81. package/dist/manifest/normalize-palette.js.map +1 -0
  82. package/dist/manifest/normalize-policy.d.ts +48 -0
  83. package/dist/manifest/normalize-policy.js +239 -0
  84. package/dist/manifest/normalize-policy.js.map +1 -0
  85. package/dist/manifest/normalize-prompt.d.ts +14 -0
  86. package/dist/manifest/normalize-prompt.js +73 -0
  87. package/dist/manifest/normalize-prompt.js.map +1 -0
  88. package/dist/manifest/normalize-target.d.ts +49 -0
  89. package/dist/manifest/normalize-target.js +542 -0
  90. package/dist/manifest/normalize-target.js.map +1 -0
  91. package/dist/manifest/schema.d.ts +7570 -0
  92. package/dist/manifest/schema.js +373 -0
  93. package/dist/manifest/schema.js.map +1 -0
  94. package/dist/manifest/semantic-validation.d.ts +4 -0
  95. package/dist/manifest/semantic-validation.js +526 -0
  96. package/dist/manifest/semantic-validation.js.map +1 -0
  97. package/dist/manifest/types.d.ts +263 -0
  98. package/dist/manifest/types.js +2 -0
  99. package/dist/manifest/types.js.map +1 -0
  100. package/dist/manifest/validate.d.ts +12 -0
  101. package/dist/manifest/validate.js +221 -0
  102. package/dist/manifest/validate.js.map +1 -0
  103. package/dist/output/assetPackManifest.d.ts +19 -0
  104. package/dist/output/assetPackManifest.js +20 -0
  105. package/dist/output/assetPackManifest.js.map +1 -0
  106. package/dist/output/catalog.d.ts +60 -0
  107. package/dist/output/catalog.js +107 -0
  108. package/dist/output/catalog.js.map +1 -0
  109. package/dist/output/contactSheet.d.ts +13 -0
  110. package/dist/output/contactSheet.js +124 -0
  111. package/dist/output/contactSheet.js.map +1 -0
  112. package/dist/output/phaserManifest.d.ts +8 -0
  113. package/dist/output/phaserManifest.js +25 -0
  114. package/dist/output/phaserManifest.js.map +1 -0
  115. package/dist/output/pixiManifest.d.ts +8 -0
  116. package/dist/output/pixiManifest.js +37 -0
  117. package/dist/output/pixiManifest.js.map +1 -0
  118. package/dist/output/provenance.d.ts +121 -0
  119. package/dist/output/provenance.js +10 -0
  120. package/dist/output/provenance.js.map +1 -0
  121. package/dist/output/runtimeManifests.d.ts +21 -0
  122. package/dist/output/runtimeManifests.js +82 -0
  123. package/dist/output/runtimeManifests.js.map +1 -0
  124. package/dist/output/unityImportManifest.d.ts +10 -0
  125. package/dist/output/unityImportManifest.js +58 -0
  126. package/dist/output/unityImportManifest.js.map +1 -0
  127. package/dist/output/zip.d.ts +5 -0
  128. package/dist/output/zip.js +68 -0
  129. package/dist/output/zip.js.map +1 -0
  130. package/dist/pipeline/atlas.d.ts +33 -0
  131. package/dist/pipeline/atlas.js +286 -0
  132. package/dist/pipeline/atlas.js.map +1 -0
  133. package/dist/pipeline/eval.d.ts +104 -0
  134. package/dist/pipeline/eval.js +246 -0
  135. package/dist/pipeline/eval.js.map +1 -0
  136. package/dist/pipeline/generate.d.ts +44 -0
  137. package/dist/pipeline/generate.js +1088 -0
  138. package/dist/pipeline/generate.js.map +1 -0
  139. package/dist/pipeline/package.d.ts +18 -0
  140. package/dist/pipeline/package.js +218 -0
  141. package/dist/pipeline/package.js.map +1 -0
  142. package/dist/pipeline/process.d.ts +15 -0
  143. package/dist/pipeline/process.js +776 -0
  144. package/dist/pipeline/process.js.map +1 -0
  145. package/dist/pipeline/review.d.ts +10 -0
  146. package/dist/pipeline/review.js +341 -0
  147. package/dist/pipeline/review.js.map +1 -0
  148. package/dist/pipeline/seamHeal.d.ts +2 -0
  149. package/dist/pipeline/seamHeal.js +70 -0
  150. package/dist/pipeline/seamHeal.js.map +1 -0
  151. package/dist/pipeline/select.d.ts +39 -0
  152. package/dist/pipeline/select.js +79 -0
  153. package/dist/pipeline/select.js.map +1 -0
  154. package/dist/providers/job.d.ts +29 -0
  155. package/dist/providers/job.js +113 -0
  156. package/dist/providers/job.js.map +1 -0
  157. package/dist/providers/localDiffusion.d.ts +28 -0
  158. package/dist/providers/localDiffusion.js +235 -0
  159. package/dist/providers/localDiffusion.js.map +1 -0
  160. package/dist/providers/nano.d.ts +36 -0
  161. package/dist/providers/nano.js +402 -0
  162. package/dist/providers/nano.js.map +1 -0
  163. package/dist/providers/openai.d.ts +37 -0
  164. package/dist/providers/openai.js +378 -0
  165. package/dist/providers/openai.js.map +1 -0
  166. package/dist/providers/policy.d.ts +9 -0
  167. package/dist/providers/policy.js +192 -0
  168. package/dist/providers/policy.js.map +1 -0
  169. package/dist/providers/prompt.d.ts +3 -0
  170. package/dist/providers/prompt.js +63 -0
  171. package/dist/providers/prompt.js.map +1 -0
  172. package/dist/providers/registry.d.ts +24 -0
  173. package/dist/providers/registry.js +92 -0
  174. package/dist/providers/registry.js.map +1 -0
  175. package/dist/providers/runtime.d.ts +15 -0
  176. package/dist/providers/runtime.js +101 -0
  177. package/dist/providers/runtime.js.map +1 -0
  178. package/dist/providers/runtimeConfig.d.ts +20 -0
  179. package/dist/providers/runtimeConfig.js +146 -0
  180. package/dist/providers/runtimeConfig.js.map +1 -0
  181. package/dist/providers/types-core.d.ts +514 -0
  182. package/dist/providers/types-core.js +60 -0
  183. package/dist/providers/types-core.js.map +1 -0
  184. package/dist/providers/types.d.ts +4 -0
  185. package/dist/providers/types.js +5 -0
  186. package/dist/providers/types.js.map +1 -0
  187. package/dist/service/generationRequest.d.ts +58 -0
  188. package/dist/service/generationRequest.js +203 -0
  189. package/dist/service/generationRequest.js.map +1 -0
  190. package/dist/service/providerCapabilities.d.ts +40 -0
  191. package/dist/service/providerCapabilities.js +114 -0
  192. package/dist/service/providerCapabilities.js.map +1 -0
  193. package/dist/service/server.d.ts +31 -0
  194. package/dist/service/server.js +774 -0
  195. package/dist/service/server.js.map +1 -0
  196. package/dist/shared/errors.d.ts +13 -0
  197. package/dist/shared/errors.js +24 -0
  198. package/dist/shared/errors.js.map +1 -0
  199. package/dist/shared/fs.d.ts +6 -0
  200. package/dist/shared/fs.js +30 -0
  201. package/dist/shared/fs.js.map +1 -0
  202. package/dist/shared/image.d.ts +25 -0
  203. package/dist/shared/image.js +136 -0
  204. package/dist/shared/image.js.map +1 -0
  205. package/dist/shared/paths.d.ts +30 -0
  206. package/dist/shared/paths.js +103 -0
  207. package/dist/shared/paths.js.map +1 -0
  208. package/dist/shared/schemas.d.ts +209 -0
  209. package/dist/shared/schemas.js +93 -0
  210. package/dist/shared/schemas.js.map +1 -0
  211. package/dist/shared/typeGuards.d.ts +1 -0
  212. package/dist/shared/typeGuards.js +4 -0
  213. package/dist/shared/typeGuards.js.map +1 -0
  214. package/dist/shared/zod.d.ts +1 -0
  215. package/dist/shared/zod.js +14 -0
  216. package/dist/shared/zod.js.map +1 -0
  217. package/dist/showcase/format.d.ts +9 -0
  218. package/dist/showcase/format.js +61 -0
  219. package/dist/showcase/format.js.map +1 -0
  220. package/dist/showcase/panelRenderer.d.ts +59 -0
  221. package/dist/showcase/panelRenderer.js +294 -0
  222. package/dist/showcase/panelRenderer.js.map +1 -0
  223. package/dist/showcase/releaseConfig.d.ts +233 -0
  224. package/dist/showcase/releaseConfig.js +75 -0
  225. package/dist/showcase/releaseConfig.js.map +1 -0
  226. package/dist/showcase/releaseEvidence.d.ts +25 -0
  227. package/dist/showcase/releaseEvidence.js +540 -0
  228. package/dist/showcase/releaseEvidence.js.map +1 -0
  229. package/dist/showcase/releaseEvidenceSchema.d.ts +1611 -0
  230. package/dist/showcase/releaseEvidenceSchema.js +165 -0
  231. package/dist/showcase/releaseEvidenceSchema.js.map +1 -0
  232. package/dist/showcase/scenarioRenderer.d.ts +19 -0
  233. package/dist/showcase/scenarioRenderer.js +488 -0
  234. package/dist/showcase/scenarioRenderer.js.map +1 -0
  235. package/docs/ADAPTER_CONTRACT.md +141 -0
  236. package/docs/ENGINE_TARGETING.md +86 -0
  237. package/docs/MANIFEST_POLICY_COVERAGE.md +130 -0
  238. package/docs/RELEASE_WORKFLOW.md +117 -0
  239. package/docs/ROADMAP.md +411 -0
  240. package/docs/ROADMAP_ISSUES.md +244 -0
  241. package/docs/SERVICE_MODE.md +137 -0
  242. package/docs/manifest-schema.md +254 -0
  243. package/package.json +70 -0
@@ -0,0 +1,137 @@
1
+ # Service Mode (`lootforge serve`)
2
+
3
+ `lootforge serve` runs a local HTTP server for command execution without adding auth/credit controls in core.
4
+
5
+ ## Start
6
+
7
+ ```bash
8
+ lootforge serve --host 127.0.0.1 --port 8744
9
+ ```
10
+
11
+ Optional defaults:
12
+
13
+ - `--out <dir>`: default out directory injected when command payload omits `out`
14
+ - `--max-active-jobs <number>`: max concurrent active tool executions before returning `429 service_busy` (default `2`)
15
+ - `LOOTFORGE_SERVICE_HOST`
16
+ - `LOOTFORGE_SERVICE_PORT`
17
+ - `LOOTFORGE_SERVICE_OUT`
18
+ - `LOOTFORGE_SERVICE_MAX_ACTIVE_JOBS`
19
+
20
+ ## Endpoints (`v1`)
21
+
22
+ - `GET /v1/health`
23
+ - `GET /v1/tools`
24
+ - `GET /v1/contracts/generation-request`
25
+ - `GET /v1/contracts/provider-capabilities`
26
+ - `GET /v1/providers/capabilities`
27
+ - `POST /v1/tools/:name`
28
+ - `POST /v1/:name` (alias for tools endpoint)
29
+ - `POST /v1/generation/requests` (canonical generation request contract)
30
+
31
+ Root helper endpoint:
32
+
33
+ - `GET /`
34
+
35
+ Provider capabilities query options:
36
+
37
+ - `provider` (optional): `openai|nano|local`
38
+ - `model` (optional): model override for introspection; requires `provider`
39
+
40
+ ## Request Shape
41
+
42
+ Tool execution accepts one of:
43
+
44
+ 1. `params` object (recommended stable interface)
45
+
46
+ ```json
47
+ {
48
+ "requestId": "req-123",
49
+ "params": {
50
+ "out": "/abs/path/to/workdir",
51
+ "strict": true
52
+ }
53
+ }
54
+ ```
55
+
56
+ 2. `args` override (raw CLI flag compatibility)
57
+
58
+ ```json
59
+ {
60
+ "args": ["--out", "/abs/path/to/workdir", "--strict", "true"]
61
+ }
62
+ ```
63
+
64
+ `ids` and `runtimes` support string arrays in `params` and are converted to CSV flags.
65
+
66
+ Canonical generation request contract:
67
+
68
+ ```json
69
+ {
70
+ "requestId": "req-gen-001",
71
+ "request": {
72
+ "manifestPath": "/abs/path/to/manifest.json",
73
+ "outDir": "/abs/path/to/out",
74
+ "provider": "auto",
75
+ "targetIds": ["hero", "enemy_01"],
76
+ "skipLocked": true,
77
+ "selectionLockPath": "/abs/path/to/locks/selection-lock.json"
78
+ }
79
+ }
80
+ ```
81
+
82
+ Notes:
83
+
84
+ - `request.manifest` can be provided inline instead of `manifestPath`; service mode will materialize it before planning.
85
+ - Canonical request execution maps to `plan -> generate` and returns both plan metadata and generation run metadata.
86
+
87
+ ## Response Shape
88
+
89
+ Success:
90
+
91
+ ```json
92
+ {
93
+ "ok": true,
94
+ "apiVersion": "v1",
95
+ "tool": "validate",
96
+ "requestId": "req-123",
97
+ "result": {}
98
+ }
99
+ ```
100
+
101
+ Failure:
102
+
103
+ ```json
104
+ {
105
+ "ok": false,
106
+ "apiVersion": "v1",
107
+ "tool": "validate",
108
+ "error": {
109
+ "code": "manifest_validation_failed",
110
+ "message": "Manifest validation failed with 1 error(s).",
111
+ "exitCode": 1
112
+ }
113
+ }
114
+ ```
115
+
116
+ Busy (`429`):
117
+
118
+ ```json
119
+ {
120
+ "ok": false,
121
+ "apiVersion": "v1",
122
+ "tool": "generate",
123
+ "error": {
124
+ "code": "service_busy",
125
+ "message": "Service is busy (2/2 active jobs). Retry later."
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## MCP Wrapper Compatibility Notes
131
+
132
+ - `GET /v1/tools` exposes stable tool metadata and parameter keys.
133
+ - `POST /v1/tools/:name` gives deterministic JSON envelopes for tool-call wrappers.
134
+ - `POST /v1/generation/requests` provides a canonical mapping layer from service request payloads to manifest planning + generation targets.
135
+ - `GET /v1/providers/capabilities` provides provider/model capability introspection for wrapper-side feature gating (`pixel`, `highRes`, `references`).
136
+ - `GET /v1/contracts/provider-capabilities` exposes a machine-readable contract for the capabilities endpoint.
137
+ - CORS headers are permissive (`*`) for local tool hosts.
@@ -0,0 +1,254 @@
1
+ # Manifest Schema (`version: "next"`)
2
+
3
+ `next` is the only supported manifest contract in this rewrite.
4
+
5
+ Manifest policy coverage gate:
6
+
7
+ - `docs/MANIFEST_POLICY_COVERAGE.md` is the machine-checkable index of documented policy fields.
8
+ - `npm run check:manifest-policy` validates coverage status (`implemented|reserved`) and required test evidence, then emits `coverage/manifest-policy-coverage.json`.
9
+
10
+ ## Top-level fields
11
+
12
+ - `version`: must be `"next"`
13
+ - `pack`: `{ id, version, license?, author? }`
14
+ - `providers`: `{ default, openai?, nano?, local? }`
15
+ - Provider configs support runtime fields:
16
+ - `model?`
17
+ - `endpoint?` (OpenAI generation endpoint, Nano API base)
18
+ - `timeoutMs?` (request timeout per provider call)
19
+ - `maxRetries?` (default retries when target policy omits `generationPolicy.maxRetries`)
20
+ - `minDelayMs?` (provider-level minimum spacing between jobs)
21
+ - `defaultConcurrency?` (provider-level worker count)
22
+ - `providers.local` also supports `baseUrl?` (alias for local endpoint)
23
+ - `styleKits[]` (required, at least one)
24
+ - `id`
25
+ - `rulesPath`
26
+ - `palettePath?`
27
+ - `referenceImages[]`
28
+ - `styleReferenceImages[]?` (directed style-image scaffold inputs; provider support varies)
29
+ - `lightingModel`
30
+ - `negativeRulesPath?`
31
+ - `loraPath?`
32
+ - `loraStrength?` (`0..2`, requires `loraPath`)
33
+ - `visualPolicy?`: `{ lineContrastMin?, shadingBandCountMax?, uiRectilinearityMin? }`
34
+ - machine-checkable style-bible constraints applied during acceptance/eval
35
+ - `consistencyGroups[]` (optional)
36
+ - `id`
37
+ - `description?`
38
+ - `styleKitId?`
39
+ - `referenceImages[]`
40
+ - `targetTemplates[]` (optional)
41
+ - `id`
42
+ - `dependsOn[]?` (target-id dependency policy)
43
+ - `styleReferenceFrom[]?` (target-id style-reference chain policy)
44
+ - `evaluationProfiles[]` (required, at least one)
45
+ - `id`
46
+ - `hardGates?`: `{ requireAlpha?, maxFileSizeKB?, seamThreshold?, seamStripPx?, paletteComplianceMin?, alphaHaloRiskMax?, alphaStrayNoiseMax?, alphaEdgeSharpnessMin?, mattingHiddenRgbLeakMax?, mattingMaskConsistencyMin?, mattingSemiTransparencyRatioMax?, packTextureBudgetMB?, spritesheetSilhouetteDriftMax?, spritesheetAnchorDriftMax?, spritesheetIdentityDriftMax?, spritesheetPoseDriftMax? }`
47
+ - `consistencyGroupScoring?`: `{ warningThreshold?, penaltyThreshold?, penaltyWeight? }`
48
+ - `warningThreshold`: normalized drift score threshold for group-level warning signals
49
+ - `penaltyThreshold`: normalized drift score threshold where ranking penalties activate
50
+ - `penaltyWeight`: deterministic multiplier used for final-score penalty (`round(score * weight)`)
51
+ - `scoreWeights?`: `{ readability?, fileSize?, consistency?, clip?, lpips?, ssim? }`
52
+ - `scoringProfiles[]` (optional)
53
+ - `id`
54
+ - `scoreWeights?`: global score-weight overrides for all target kinds
55
+ - `kindScoreWeights?`: optional per-kind overrides for `sprite|tile|background|effect|spritesheet`
56
+ - `atlas?`: atlas defaults + optional per-group overrides
57
+ - `targets[]` (required)
58
+
59
+ Style kit palette defaults:
60
+
61
+ - If a target omits `palette`, LootForge will attempt to load `styleKits[].palettePath` and apply it as an exact palette policy.
62
+ - If `targets[].palette` is provided, it overrides any style-kit palette default.
63
+
64
+ ## Target contract
65
+
66
+ Required on every target:
67
+
68
+ - `id`
69
+ - `kind`
70
+ - `out`
71
+ - `styleKitId`
72
+ - `consistencyGroup`
73
+ - `evaluationProfileId`
74
+ - `generationMode` (`text|edit-first`) recommended
75
+
76
+ Optional quality controls:
77
+
78
+ - `templateId?`: references `targetTemplates[].id` for reusable orchestration policy
79
+ - `dependsOn?`: target-id dependencies used for deterministic execution staging
80
+ - `styleReferenceFrom?`: target-id style-reference lineage for chaining generated assets
81
+ - when omitted, style-reference lineage defaults to `dependsOn`
82
+ - `palette`: `{ mode: exact|max-colors, colors?, maxColors?, dither?, strict? }`
83
+ - `strict` is supported only in `mode: "exact"` and enforces 100% visible-pixel palette compliance.
84
+ - `scoringProfile?`: profile id from `scoringProfiles[]` (falls back to `evaluationProfileId` lookup)
85
+ - `tileable`, `seamThreshold`, `seamStripPx`
86
+ - `seamHeal?`: `{ enabled?, stripPx?, strength? }` (optional edge blending pass for tileable targets)
87
+ - `wrapGrid?`: `{ columns, rows, seamThreshold?, seamStripPx?, topology? }` (per-cell wrap validation gates)
88
+ - `topology?`: `{ mode, maxMismatchRatio?, colorTolerance? }`
89
+ - `mode`: `self|one-to-one|many-to-many` adjacency validation, evaluated independently from seam score
90
+
91
+ Generation + processing:
92
+
93
+ - `prompt` or `promptSpec` (required for non-`spritesheet` targets)
94
+ - `generationPolicy`
95
+ - `generationPolicy.highQuality?` (directed-synthesis scaffold flag)
96
+ - `generationPolicy.hiresFix?`: `{ enabled?, upscale?, denoiseStrength? }`
97
+ - `generationPolicy.vlmGate?`: `{ threshold?, rubric? }`
98
+ - `threshold` defaults to `4` (scored on `0..5`) when gate is configured
99
+ - `generationPolicy.coarseToFine?`: `{ enabled?, promoteTopK?, minDraftScore?, requireDraftAcceptance? }`
100
+ - `generationPolicy.agenticRetry?`: `{ enabled?, maxRetries? }`
101
+ - when enabled, failed VLM/edge-boundary candidates can trigger bounded edit-first self-healing retries
102
+ - optional quality split: `draftQuality` (coarse pass) and `finalQuality` (refinement pass)
103
+ - `postProcess`
104
+ - `postProcess.operations.smartCrop?`: `{ enabled?, mode?, padding? }`
105
+ - `mode`: `alpha-bounds|center`
106
+ - `postProcess.operations.pixelPerfect?`: `{ enabled?, scale? }`
107
+ - favors nearest-neighbor semantics during resize when enabled
108
+ - `postProcess.operations.emitVariants?`: `{ raw?, pixel?, styleRef? }`
109
+ - writes explicit `__raw`, `__pixel`, and `__style_ref` processed artifacts when enabled
110
+ - layered export toggles: `layerColor?`, `layerMatte?` write `__layer_color` + `__layer_matte` artifacts
111
+ - `acceptance`
112
+ - `runtimeSpec`
113
+ - `provider`, `model`, `edit`, `auxiliaryMaps`
114
+ - `controlImage?` + `controlMode?` (`canny|depth|openpose`) must be provided together
115
+ - `generationMode: "edit-first"` requires an edit-capable provider (`openai`, `local`, or `nano` with an image-edit-capable Gemini model)
116
+ - `edit.inputs[].path` must resolve inside the active `--out` root at runtime
117
+ - `generationPolicy.background: "transparent"` requires a provider that supports transparent outputs (unsupported providers now fail validation)
118
+ - `generationPolicy.vlmGate` requires runtime evaluator transport via `LOOTFORGE_VLM_GATE_CMD` or `LOOTFORGE_VLM_GATE_URL`
119
+ - edge-aware hard gates can be configured in `evaluationProfiles[].hardGates`:
120
+ - `alphaHaloRiskMax` (`0..1`, lower is stricter)
121
+ - `alphaStrayNoiseMax` (`0..1`, lower is stricter)
122
+ - `alphaEdgeSharpnessMin` (`0..1`, higher is stricter)
123
+ - matting-assisted alpha QA hard gates can be configured in `evaluationProfiles[].hardGates`:
124
+ - `mattingHiddenRgbLeakMax` (`0..1`, lower is stricter)
125
+ - `mattingMaskConsistencyMin` (`0..1`, higher is stricter)
126
+ - `mattingSemiTransparencyRatioMax` (`0..1`, lower is stricter)
127
+ - score weighting defaults and overrides:
128
+ - LootForge applies deterministic built-in score presets by target kind.
129
+ - If a matching `scoringProfiles[]` entry is found (by `targets[].scoringProfile` or `evaluationProfileId`), profile weights override the built-in kind preset.
130
+ - If no matching `scoringProfiles[]` entry exists, `evaluationProfiles[].scoreWeights` acts as a compatibility fallback override.
131
+ - pack-level gates can be configured in `evaluationProfiles[].hardGates` and are normalized onto each planned target:
132
+ - `packTextureBudgetMB` (`>0`, optional profile-level uncompressed texture budget)
133
+ - `spritesheetSilhouetteDriftMax` (`0..1`, optional max adjacent-frame silhouette drift)
134
+ - `spritesheetAnchorDriftMax` (`0..1`, optional max adjacent-frame anchor drift)
135
+ - `spritesheetIdentityDriftMax` (`0..1`, optional max adjacent-frame visual identity drift)
136
+ - `spritesheetPoseDriftMax` (`0..1`, optional max adjacent-frame pose/orientation drift)
137
+ - consistency-group drift controls can be configured in `evaluationProfiles[].consistencyGroupScoring` and are normalized onto each planned target:
138
+ - `warningThreshold` (`>0`, optional warning trigger for aggregate group diagnostics)
139
+ - `penaltyThreshold` (`>0`, optional threshold for deterministic ranking penalty)
140
+ - `penaltyWeight` (`>=0`, optional multiplier for ranking influence)
141
+
142
+ Provider runtime precedence for generate/regenerate:
143
+
144
+ - target-level `generationPolicy` overrides provider defaults for retries/concurrency settings
145
+ - provider runtime fields resolve from manifest and can be overridden by environment variables (`LOOTFORGE_*` / provider-specific env aliases)
146
+ - capability parity is enforced at runtime (`supports(...)` must match provider capability flags)
147
+
148
+ ## Spritesheet targets
149
+
150
+ For `kind: "spritesheet"`, define:
151
+
152
+ - `animations`: record keyed by animation name
153
+ - `count` (required)
154
+ - `prompt` (required)
155
+ - `fps?`, `loop?`, `pivot?`
156
+
157
+ Planner behavior:
158
+
159
+ - expands each animation frame into internal frame targets under `__frames/...`
160
+ - emits a generation-disabled sheet target that process stage assembles
161
+
162
+ Pack invariants enforced during acceptance/eval:
163
+
164
+ - runtime output uniqueness across non-catalog targets (case-insensitive normalized path)
165
+ - spritesheet sheet/frame family and atlas-group integrity checks
166
+ - continuity metrics per animation (`maxSilhouetteDrift`, `maxAnchorDrift`, `maxIdentityDrift`, `maxPoseDrift`) with optional hard-gate thresholds
167
+ - optional profile texture budget gate using estimated uncompressed bytes (`width * height * 4`)
168
+
169
+ Style-bible visual policy checks (from `styleKits[].visualPolicy`) in acceptance/eval:
170
+
171
+ - `lineContrastMin`: rejects low-contrast outputs when measured line contrast is below threshold
172
+ - `shadingBandCountMax`: rejects outputs whose visible-pixel luma bands exceed threshold
173
+ - `uiRectilinearityMin`: rejects non-rectilinear silhouettes when bounding-box fill ratio is too low
174
+
175
+ ## Example (minimal sprite)
176
+
177
+ ```json
178
+ {
179
+ "version": "next",
180
+ "pack": {
181
+ "id": "my-pack",
182
+ "version": "0.1.0"
183
+ },
184
+ "providers": {
185
+ "default": "openai",
186
+ "openai": { "model": "gpt-image-1" }
187
+ },
188
+ "styleKits": [
189
+ {
190
+ "id": "fantasy-topdown",
191
+ "rulesPath": "style/fantasy/style.md",
192
+ "palettePath": "style/fantasy/palette.txt",
193
+ "referenceImages": [],
194
+ "lightingModel": "top-left key with warm fill"
195
+ }
196
+ ],
197
+ "consistencyGroups": [
198
+ {
199
+ "id": "player-family",
200
+ "description": "Shared protagonist style and silhouette constraints.",
201
+ "styleKitId": "fantasy-topdown",
202
+ "referenceImages": []
203
+ }
204
+ ],
205
+ "evaluationProfiles": [
206
+ {
207
+ "id": "sprite-quality",
208
+ "hardGates": {
209
+ "requireAlpha": true,
210
+ "maxFileSizeKB": 512,
211
+ "alphaHaloRiskMax": 0.08,
212
+ "alphaStrayNoiseMax": 0.01,
213
+ "alphaEdgeSharpnessMin": 0.8,
214
+ "packTextureBudgetMB": 48,
215
+ "spritesheetSilhouetteDriftMax": 0.2,
216
+ "spritesheetAnchorDriftMax": 0.15
217
+ }
218
+ }
219
+ ],
220
+ "targets": [
221
+ {
222
+ "id": "player.hero",
223
+ "kind": "sprite",
224
+ "out": "player_hero.png",
225
+ "styleKitId": "fantasy-topdown",
226
+ "consistencyGroup": "player-family",
227
+ "evaluationProfileId": "sprite-quality",
228
+ "generationMode": "text",
229
+ "prompt": "Top-down hero sprite with clear readable silhouette.",
230
+ "generationPolicy": {
231
+ "size": "1024x1024",
232
+ "background": "transparent",
233
+ "outputFormat": "png",
234
+ "quality": "high",
235
+ "candidates": 4,
236
+ "vlmGate": {
237
+ "threshold": 4,
238
+ "rubric": "Score silhouette clarity and framing quality from 0 to 5."
239
+ }
240
+ },
241
+ "postProcess": {
242
+ "resizeTo": "512x512",
243
+ "algorithm": "lanczos3",
244
+ "stripMetadata": true
245
+ },
246
+ "acceptance": {
247
+ "size": "512x512",
248
+ "alpha": true,
249
+ "maxFileSizeKB": 512
250
+ }
251
+ }
252
+ ]
253
+ }
254
+ ```
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "lootforge",
3
+ "private": false,
4
+ "version": "0.3.0",
5
+ "type": "module",
6
+ "description": "LootForge CLI for manifest-driven game asset packs",
7
+ "engines": {
8
+ "node": ">=22.0.0"
9
+ },
10
+ "bin": {
11
+ "lootforge": "./bin/lootforge.js"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "dist",
16
+ "README.md",
17
+ "CHANGELOG.md",
18
+ "docs/*.md"
19
+ ],
20
+ "scripts": {
21
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
22
+ "build": "npm run clean && tsc -p tsconfig.json",
23
+ "dev": "tsx src/cli/index.ts",
24
+ "test": "vitest run",
25
+ "test:unit": "vitest run test/unit",
26
+ "test:integration": "vitest run test/integration",
27
+ "test:coverage:full": "vitest run --coverage --coverage.include=\"src/**/*.ts\" --coverage.reporter=text --coverage.reporter=json-summary",
28
+ "test:coverage:critical": "npm run test:coverage:full",
29
+ "lint": "eslint src/ test/",
30
+ "lint:fix": "eslint src/ test/ --fix",
31
+ "format": "prettier --write .",
32
+ "format:check": "prettier --check .",
33
+ "typecheck": "tsc --noEmit -p tsconfig.json",
34
+ "check": "npm run typecheck && npm run lint && npm run format:check",
35
+ "check:manifest-policy": "node scripts/check-manifest-policy-coverage.mjs --report coverage/manifest-policy-coverage.json",
36
+ "check:release:metadata": "tsx scripts/check-release-metadata.ts --release 0.3.0",
37
+ "benchmark:coarse-to-fine": "tsx scripts/benchmark-coarse-to-fine.ts",
38
+ "release:showcase": "tsx scripts/release-showcase.ts",
39
+ "check:release-showcase": "tsx scripts/check-release-showcase.ts --release 0.3.0",
40
+ "pack:dry-run": "npm pack --dry-run --cache .npm-cache",
41
+ "check:release": "npm run check && npm test && npm run test:coverage:full && npm run build && npm run check:manifest-policy && npm run check:release:metadata && npm run check:release-showcase && npm audit --audit-level=high && npm run pack:dry-run",
42
+ "prepack": "npm run build"
43
+ },
44
+ "dependencies": {
45
+ "sharp": "^0.34.5",
46
+ "yazl": "^3.3.1",
47
+ "zod": "^3.25.76"
48
+ },
49
+ "devDependencies": {
50
+ "@eslint/js": "^9.39.3",
51
+ "@types/node": "^22.13.1",
52
+ "@types/yauzl": "^2.10.3",
53
+ "@types/yazl": "^2.4.5",
54
+ "@vitest/coverage-v8": "^4.0.18",
55
+ "eslint": "^9.39.3",
56
+ "eslint-config-prettier": "^10.1.8",
57
+ "eslint-import-resolver-typescript": "^4.4.4",
58
+ "eslint-plugin-import-x": "^4.16.1",
59
+ "eslint-plugin-n": "^17.24.0",
60
+ "prettier": "^3.8.1",
61
+ "tsx": "^4.19.2",
62
+ "typescript": "^5.7.3",
63
+ "typescript-eslint": "^8.56.0",
64
+ "vitest": "^4.0.18",
65
+ "yauzl": "^3.2.0"
66
+ },
67
+ "overrides": {
68
+ "minimatch": ">=10.2.1"
69
+ }
70
+ }