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.
- package/CHANGELOG.md +87 -0
- package/README.md +764 -0
- package/bin/lootforge.js +28 -0
- package/dist/benchmarks/coarseToFineCost.d.ts +21 -0
- package/dist/benchmarks/coarseToFineCost.js +49 -0
- package/dist/benchmarks/coarseToFineCost.js.map +1 -0
- package/dist/checks/boundaryMetrics.d.ts +12 -0
- package/dist/checks/boundaryMetrics.js +102 -0
- package/dist/checks/boundaryMetrics.js.map +1 -0
- package/dist/checks/candidateScore.d.ts +11 -0
- package/dist/checks/candidateScore.js +462 -0
- package/dist/checks/candidateScore.js.map +1 -0
- package/dist/checks/commandParser.d.ts +5 -0
- package/dist/checks/commandParser.js +99 -0
- package/dist/checks/commandParser.js.map +1 -0
- package/dist/checks/consistencyOutliers.d.ts +42 -0
- package/dist/checks/consistencyOutliers.js +156 -0
- package/dist/checks/consistencyOutliers.js.map +1 -0
- package/dist/checks/imageAcceptance.d.ts +67 -0
- package/dist/checks/imageAcceptance.js +967 -0
- package/dist/checks/imageAcceptance.js.map +1 -0
- package/dist/checks/packInvariants.d.ts +56 -0
- package/dist/checks/packInvariants.js +1064 -0
- package/dist/checks/packInvariants.js.map +1 -0
- package/dist/checks/softAdapters.d.ts +25 -0
- package/dist/checks/softAdapters.js +275 -0
- package/dist/checks/softAdapters.js.map +1 -0
- package/dist/checks/vlmGate.d.ts +8 -0
- package/dist/checks/vlmGate.js +200 -0
- package/dist/checks/vlmGate.js.map +1 -0
- package/dist/cli/commands/atlas.d.ts +5 -0
- package/dist/cli/commands/atlas.js +18 -0
- package/dist/cli/commands/atlas.js.map +1 -0
- package/dist/cli/commands/eval.d.ts +6 -0
- package/dist/cli/commands/eval.js +23 -0
- package/dist/cli/commands/eval.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +18 -0
- package/dist/cli/commands/generate.js +66 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +15 -0
- package/dist/cli/commands/init.js +146 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/package.d.ts +6 -0
- package/dist/cli/commands/package.js +27 -0
- package/dist/cli/commands/package.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +16 -0
- package/dist/cli/commands/plan.js +49 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/process.d.ts +14 -0
- package/dist/cli/commands/process.js +29 -0
- package/dist/cli/commands/process.js.map +1 -0
- package/dist/cli/commands/regenerate.d.ts +29 -0
- package/dist/cli/commands/regenerate.js +244 -0
- package/dist/cli/commands/regenerate.js.map +1 -0
- package/dist/cli/commands/review.d.ts +5 -0
- package/dist/cli/commands/review.js +18 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/select.d.ts +6 -0
- package/dist/cli/commands/select.js +21 -0
- package/dist/cli/commands/select.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +16 -0
- package/dist/cli/commands/serve.js +100 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +17 -0
- package/dist/cli/commands/validate.js +108 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +157 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +3 -0
- package/dist/cli/parseArgs.js +37 -0
- package/dist/cli/parseArgs.js.map +1 -0
- package/dist/contracts/stageArtifacts.d.ts +4031 -0
- package/dist/contracts/stageArtifacts.js +663 -0
- package/dist/contracts/stageArtifacts.js.map +1 -0
- package/dist/manifest/load.d.ts +3 -0
- package/dist/manifest/load.js +50 -0
- package/dist/manifest/load.js.map +1 -0
- package/dist/manifest/normalize-palette.d.ts +17 -0
- package/dist/manifest/normalize-palette.js +235 -0
- package/dist/manifest/normalize-palette.js.map +1 -0
- package/dist/manifest/normalize-policy.d.ts +48 -0
- package/dist/manifest/normalize-policy.js +239 -0
- package/dist/manifest/normalize-policy.js.map +1 -0
- package/dist/manifest/normalize-prompt.d.ts +14 -0
- package/dist/manifest/normalize-prompt.js +73 -0
- package/dist/manifest/normalize-prompt.js.map +1 -0
- package/dist/manifest/normalize-target.d.ts +49 -0
- package/dist/manifest/normalize-target.js +542 -0
- package/dist/manifest/normalize-target.js.map +1 -0
- package/dist/manifest/schema.d.ts +7570 -0
- package/dist/manifest/schema.js +373 -0
- package/dist/manifest/schema.js.map +1 -0
- package/dist/manifest/semantic-validation.d.ts +4 -0
- package/dist/manifest/semantic-validation.js +526 -0
- package/dist/manifest/semantic-validation.js.map +1 -0
- package/dist/manifest/types.d.ts +263 -0
- package/dist/manifest/types.js +2 -0
- package/dist/manifest/types.js.map +1 -0
- package/dist/manifest/validate.d.ts +12 -0
- package/dist/manifest/validate.js +221 -0
- package/dist/manifest/validate.js.map +1 -0
- package/dist/output/assetPackManifest.d.ts +19 -0
- package/dist/output/assetPackManifest.js +20 -0
- package/dist/output/assetPackManifest.js.map +1 -0
- package/dist/output/catalog.d.ts +60 -0
- package/dist/output/catalog.js +107 -0
- package/dist/output/catalog.js.map +1 -0
- package/dist/output/contactSheet.d.ts +13 -0
- package/dist/output/contactSheet.js +124 -0
- package/dist/output/contactSheet.js.map +1 -0
- package/dist/output/phaserManifest.d.ts +8 -0
- package/dist/output/phaserManifest.js +25 -0
- package/dist/output/phaserManifest.js.map +1 -0
- package/dist/output/pixiManifest.d.ts +8 -0
- package/dist/output/pixiManifest.js +37 -0
- package/dist/output/pixiManifest.js.map +1 -0
- package/dist/output/provenance.d.ts +121 -0
- package/dist/output/provenance.js +10 -0
- package/dist/output/provenance.js.map +1 -0
- package/dist/output/runtimeManifests.d.ts +21 -0
- package/dist/output/runtimeManifests.js +82 -0
- package/dist/output/runtimeManifests.js.map +1 -0
- package/dist/output/unityImportManifest.d.ts +10 -0
- package/dist/output/unityImportManifest.js +58 -0
- package/dist/output/unityImportManifest.js.map +1 -0
- package/dist/output/zip.d.ts +5 -0
- package/dist/output/zip.js +68 -0
- package/dist/output/zip.js.map +1 -0
- package/dist/pipeline/atlas.d.ts +33 -0
- package/dist/pipeline/atlas.js +286 -0
- package/dist/pipeline/atlas.js.map +1 -0
- package/dist/pipeline/eval.d.ts +104 -0
- package/dist/pipeline/eval.js +246 -0
- package/dist/pipeline/eval.js.map +1 -0
- package/dist/pipeline/generate.d.ts +44 -0
- package/dist/pipeline/generate.js +1088 -0
- package/dist/pipeline/generate.js.map +1 -0
- package/dist/pipeline/package.d.ts +18 -0
- package/dist/pipeline/package.js +218 -0
- package/dist/pipeline/package.js.map +1 -0
- package/dist/pipeline/process.d.ts +15 -0
- package/dist/pipeline/process.js +776 -0
- package/dist/pipeline/process.js.map +1 -0
- package/dist/pipeline/review.d.ts +10 -0
- package/dist/pipeline/review.js +341 -0
- package/dist/pipeline/review.js.map +1 -0
- package/dist/pipeline/seamHeal.d.ts +2 -0
- package/dist/pipeline/seamHeal.js +70 -0
- package/dist/pipeline/seamHeal.js.map +1 -0
- package/dist/pipeline/select.d.ts +39 -0
- package/dist/pipeline/select.js +79 -0
- package/dist/pipeline/select.js.map +1 -0
- package/dist/providers/job.d.ts +29 -0
- package/dist/providers/job.js +113 -0
- package/dist/providers/job.js.map +1 -0
- package/dist/providers/localDiffusion.d.ts +28 -0
- package/dist/providers/localDiffusion.js +235 -0
- package/dist/providers/localDiffusion.js.map +1 -0
- package/dist/providers/nano.d.ts +36 -0
- package/dist/providers/nano.js +402 -0
- package/dist/providers/nano.js.map +1 -0
- package/dist/providers/openai.d.ts +37 -0
- package/dist/providers/openai.js +378 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/policy.d.ts +9 -0
- package/dist/providers/policy.js +192 -0
- package/dist/providers/policy.js.map +1 -0
- package/dist/providers/prompt.d.ts +3 -0
- package/dist/providers/prompt.js +63 -0
- package/dist/providers/prompt.js.map +1 -0
- package/dist/providers/registry.d.ts +24 -0
- package/dist/providers/registry.js +92 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/runtime.d.ts +15 -0
- package/dist/providers/runtime.js +101 -0
- package/dist/providers/runtime.js.map +1 -0
- package/dist/providers/runtimeConfig.d.ts +20 -0
- package/dist/providers/runtimeConfig.js +146 -0
- package/dist/providers/runtimeConfig.js.map +1 -0
- package/dist/providers/types-core.d.ts +514 -0
- package/dist/providers/types-core.js +60 -0
- package/dist/providers/types-core.js.map +1 -0
- package/dist/providers/types.d.ts +4 -0
- package/dist/providers/types.js +5 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/service/generationRequest.d.ts +58 -0
- package/dist/service/generationRequest.js +203 -0
- package/dist/service/generationRequest.js.map +1 -0
- package/dist/service/providerCapabilities.d.ts +40 -0
- package/dist/service/providerCapabilities.js +114 -0
- package/dist/service/providerCapabilities.js.map +1 -0
- package/dist/service/server.d.ts +31 -0
- package/dist/service/server.js +774 -0
- package/dist/service/server.js.map +1 -0
- package/dist/shared/errors.d.ts +13 -0
- package/dist/shared/errors.js +24 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/fs.d.ts +6 -0
- package/dist/shared/fs.js +30 -0
- package/dist/shared/fs.js.map +1 -0
- package/dist/shared/image.d.ts +25 -0
- package/dist/shared/image.js +136 -0
- package/dist/shared/image.js.map +1 -0
- package/dist/shared/paths.d.ts +30 -0
- package/dist/shared/paths.js +103 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/schemas.d.ts +209 -0
- package/dist/shared/schemas.js +93 -0
- package/dist/shared/schemas.js.map +1 -0
- package/dist/shared/typeGuards.d.ts +1 -0
- package/dist/shared/typeGuards.js +4 -0
- package/dist/shared/typeGuards.js.map +1 -0
- package/dist/shared/zod.d.ts +1 -0
- package/dist/shared/zod.js +14 -0
- package/dist/shared/zod.js.map +1 -0
- package/dist/showcase/format.d.ts +9 -0
- package/dist/showcase/format.js +61 -0
- package/dist/showcase/format.js.map +1 -0
- package/dist/showcase/panelRenderer.d.ts +59 -0
- package/dist/showcase/panelRenderer.js +294 -0
- package/dist/showcase/panelRenderer.js.map +1 -0
- package/dist/showcase/releaseConfig.d.ts +233 -0
- package/dist/showcase/releaseConfig.js +75 -0
- package/dist/showcase/releaseConfig.js.map +1 -0
- package/dist/showcase/releaseEvidence.d.ts +25 -0
- package/dist/showcase/releaseEvidence.js +540 -0
- package/dist/showcase/releaseEvidence.js.map +1 -0
- package/dist/showcase/releaseEvidenceSchema.d.ts +1611 -0
- package/dist/showcase/releaseEvidenceSchema.js +165 -0
- package/dist/showcase/releaseEvidenceSchema.js.map +1 -0
- package/dist/showcase/scenarioRenderer.d.ts +19 -0
- package/dist/showcase/scenarioRenderer.js +488 -0
- package/dist/showcase/scenarioRenderer.js.map +1 -0
- package/docs/ADAPTER_CONTRACT.md +141 -0
- package/docs/ENGINE_TARGETING.md +86 -0
- package/docs/MANIFEST_POLICY_COVERAGE.md +130 -0
- package/docs/RELEASE_WORKFLOW.md +117 -0
- package/docs/ROADMAP.md +411 -0
- package/docs/ROADMAP_ISSUES.md +244 -0
- package/docs/SERVICE_MODE.md +137 -0
- package/docs/manifest-schema.md +254 -0
- 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
|
+
}
|