pixelkiln 0.2.0 → 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/CONTRIBUTING.md +48 -0
- package/NAMING.md +15 -15
- package/PROVIDERS.md +13 -13
- package/README.md +22 -9
- package/dist/cli.d.ts +18 -1
- package/dist/cli.js +588 -142
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +291 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -1
- package/dist/index.d.ts +168 -1
- package/dist/index.js +280 -21
- package/dist/index.js.map +1 -1
- package/docs/AGENTS.md +1 -1
- package/docs/ARCHITECTURE.md +4 -2
- package/docs/CLI.md +47 -0
- package/docs/ENDPOINTS.md +39 -38
- package/docs/GENERATORS.md +1 -1
- package/docs/RECOVERY.md +44 -0
- package/docs/TILES.md +1 -1
- package/examples/minimal/README.md +2 -2
- package/package.json +1 -1
- package/schema/workspace.schema.json +54 -0
package/CONTRIBUTING.md
CHANGED
|
@@ -53,6 +53,14 @@ for pipeline behavior and mocked HTTP responses for PixelLab wire contracts.
|
|
|
53
53
|
- Start from `main` and keep one coherent change per pull request.
|
|
54
54
|
- Use conventional commit subjects (`feat:`, `fix:`, `docs:`, `refactor:`,
|
|
55
55
|
`test:`, `chore:`). Semantic Release derives versions from them.
|
|
56
|
+
- **Scope website-only work as `chore(website):`.** `website/` is not in the
|
|
57
|
+
package `files` allowlist, so nothing under it can reach the published
|
|
58
|
+
tarball. Semantic Release cannot see that, and a `feat(website):`
|
|
59
|
+
subject cuts a minor release whose contents are byte-identical to the one
|
|
60
|
+
before it. That happened once already: 0.2.0 is a favicon and an Open Graph
|
|
61
|
+
image. Any `website` scope is also refused a release by `releaseRules` in
|
|
62
|
+
`.releaserc.json`, so a slip is caught rather than published, but the right
|
|
63
|
+
subject keeps the changelog honest.
|
|
56
64
|
- Add regression coverage for bug fixes and behavior coverage for new public
|
|
57
65
|
options or exports.
|
|
58
66
|
- Update README/help text and focused docs in the same change as user-facing
|
|
@@ -90,3 +98,43 @@ full check results in the PR description.
|
|
|
90
98
|
|
|
91
99
|
Security-sensitive findings should follow [SECURITY.md](./SECURITY.md), not a
|
|
92
100
|
public issue with exploit details.
|
|
101
|
+
|
|
102
|
+
## Releases
|
|
103
|
+
|
|
104
|
+
Merging to `main` is the release. Semantic Release derives the version from the
|
|
105
|
+
conventional commit subjects in the range, publishes to npm, tags the commit,
|
|
106
|
+
writes the GitHub release, prepends to `CHANGELOG.md`, and commits the changelog
|
|
107
|
+
and version back to `main` with `[skip ci]`. Contributors do not run anything.
|
|
108
|
+
|
|
109
|
+
**There is no npm token.** Publishing authenticates over OIDC trusted
|
|
110
|
+
publishing: the workflow grants `id-token: write`, npm exchanges that for a
|
|
111
|
+
short-lived credential, and the npm CLI performs the exchange itself during
|
|
112
|
+
`npm publish`. A side effect worth keeping is that every release carries a
|
|
113
|
+
signed provenance attestation linking the tarball to its source commit and
|
|
114
|
+
workflow run.
|
|
115
|
+
|
|
116
|
+
Two conditions have to hold on the npm side, and neither lives in this
|
|
117
|
+
repository:
|
|
118
|
+
|
|
119
|
+
- the package must exist on the registry, and
|
|
120
|
+
- it must have a Trusted Publisher entry naming this repository and the
|
|
121
|
+
`release.yml` workflow.
|
|
122
|
+
|
|
123
|
+
When one is missing, the token exchange reports `404 OIDC token exchange error
|
|
124
|
+
- package not found`, and Semantic Release then falls through to token auth and
|
|
125
|
+
fails with `EINVALIDNPMTOKEN`. **That 404 does not necessarily mean the package
|
|
126
|
+
is absent.** It reads identically when the package is published but has no
|
|
127
|
+
Trusted Publisher entry, which is the more likely cause once a release has ever
|
|
128
|
+
succeeded. Check the entry before doubting the publish.
|
|
129
|
+
|
|
130
|
+
Because the exchange cannot authenticate against a package that does not exist
|
|
131
|
+
yet, `0.1.0` was published by hand to bootstrap that trust, and its changelog
|
|
132
|
+
section was written by hand for the same reason. Every release from `0.2.0`
|
|
133
|
+
onward is automated.
|
|
134
|
+
|
|
135
|
+
A failed release opens an issue labelled `semantic-release`, which the next
|
|
136
|
+
successful run closes. That label must exist in the repository or the reporting
|
|
137
|
+
step itself fails with a validation error and hides the original failure.
|
|
138
|
+
|
|
139
|
+
See the website scoping rule under [change guidelines](#change-guidelines) for
|
|
140
|
+
the one commit convention that changes whether a release happens at all.
|
package/NAMING.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Naming
|
|
1
|
+
# Naming resolved: `pixelkiln`
|
|
2
2
|
|
|
3
3
|
**The name is settled.** The project ships as `pixelkiln`. `package.json` is
|
|
4
4
|
no longer `private`.
|
|
@@ -20,19 +20,19 @@ GET https://registry.npmjs.org/pixelsmith → 200, latest 2.6.0, "Node based
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
`pixelkiln` was never published. The "v3.5.1 CSS spritesheet builder" is
|
|
23
|
-
**`spritesmith`**, a real and long-established package
|
|
23
|
+
**`spritesmith`**, a real and long-established package. `pixelsmith` really
|
|
24
24
|
is its engine, exactly as claimed, but neither of them is `pixelkiln`. The
|
|
25
25
|
original research conflated the two real packages into a collision that
|
|
26
26
|
didn't exist. (`pixelsmith` also appears, separately and correctly, in the
|
|
27
|
-
rejected-candidates list below
|
|
28
|
-
project
|
|
27
|
+
rejected-candidates list below. It is a real name in a different, adjacent
|
|
28
|
+
project, which should have been the tell.)
|
|
29
29
|
|
|
30
30
|
A GitHub search for `pixelkiln` returns nothing but this repository, and a
|
|
31
31
|
general web search turns up no product, tool, or project by that name
|
|
32
32
|
anywhere. Nothing else needs to change: `bin/pixelkiln.js`, `package.json`'s
|
|
33
33
|
`name`/`bin`, the README, `scripts/gen-schema.ts`'s schema title, and
|
|
34
|
-
heybud-admin's `package.json` badge scripts already all say `pixelkiln
|
|
35
|
-
|
|
34
|
+
heybud-admin's `package.json` badge scripts already all say `pixelkiln`.
|
|
35
|
+
There is no rename to do, only the `private: true` flag to drop, which is
|
|
36
36
|
done.
|
|
37
37
|
|
|
38
38
|
## What the space already looks like
|
|
@@ -44,13 +44,13 @@ The "manifest-driven AI asset generation CLI" idea is not novel. Prior art found
|
|
|
44
44
|
| [pickbitsai/sprite-generator](https://github.com/pickbitsai/sprite-generator) | Closest. JSON manifest, `assets[]` with id/category/description, `defaultStyle`, `--dry-run`, `--category`, `--concurrency` | OpenAI `gpt-image-1` |
|
|
45
45
|
| [mcp-tool-shop-org/sprite-foundry](https://github.com/mcp-tool-shop-org/sprite-foundry) | SQLite lifecycle tracking, review/accept/reject, provenance, deterministic export with manifest + checksums | ComfyUI (local GPU) |
|
|
46
46
|
| [lx-0/restyle-sprites](https://github.com/lx-0/restyle-sprites) | Restyle a whole pack from source + style reference | Gemini / OpenAI |
|
|
47
|
-
| [trebeljahr/sprite-tools](https://github.com/trebeljahr/sprite-tools) | Post-processing toolkit
|
|
47
|
+
| [trebeljahr/sprite-tools](https://github.com/trebeljahr/sprite-tools) | Post-processing toolkit for atlas, collision, and palette | n/a |
|
|
48
48
|
| [dt-pirate/openrender](https://github.com/dt-pirate/openrender) | Installs generated assets into engines, manifests, rollback | n/a |
|
|
49
49
|
| [freema/pixelforge-mcp](https://github.com/freema/pixelforge-mcp) | MCP server for pixel art | Gemini |
|
|
50
50
|
| [ralphy](https://ralphy.mintlify.app/advanced/asset-manifest) | `asset-manifest.json` slot pointers + `generations.jsonl` audit log | multi |
|
|
51
51
|
|
|
52
|
-
**Implication for naming:** the `sprite-*` namespace is crowded
|
|
53
|
-
`sprite-foundry`, `sprite-tools`, `restyle-sprites` all exist. Avoid it.
|
|
52
|
+
**Implication for naming:** the `sprite-*` namespace is crowded. `sprite-generator`,
|
|
53
|
+
`sprite-foundry`, `sprite-tools`, and `restyle-sprites` all exist. Avoid it.
|
|
54
54
|
|
|
55
55
|
## What is actually differentiated here
|
|
56
56
|
|
|
@@ -67,7 +67,7 @@ Worth knowing, because the name should point at it:
|
|
|
67
67
|
table plus a log. A committed, sorted, hash-on-both-sides lockfile in the npm
|
|
68
68
|
sense was not found.
|
|
69
69
|
|
|
70
|
-
## Shortlist (historical
|
|
70
|
+
## Shortlist (historical, kept for context)
|
|
71
71
|
|
|
72
72
|
All verified free on npm, zero GitHub repos by that name, no web presence
|
|
73
73
|
(checked 2026-07-29 for the alternates; `pixelkiln` itself re-verified
|
|
@@ -75,13 +75,13 @@ All verified free on npm, zero GitHub repos by that name, no web presence
|
|
|
75
75
|
|
|
76
76
|
| Candidate | Note |
|
|
77
77
|
|---|---|
|
|
78
|
-
| **pixelkiln** ✅ **chosen** | Firing/baking metaphor fits pixel art, avoids the crowded `sprite-*` space. The npm collision this was shelved for turned out not to exist
|
|
79
|
-
| pixelquarry | Extraction metaphor
|
|
78
|
+
| **pixelkiln** ✅ **chosen** | Firing/baking metaphor fits pixel art, avoids the crowded `sprite-*` space. The npm collision this was shelved for turned out not to exist. See Resolution. |
|
|
79
|
+
| pixelquarry | Extraction metaphor, since you quarry many candidates and keep the good ones. Still free, not needed. |
|
|
80
80
|
| pixelcrate | Packaging/inventory feel; leans toward the lockfile idea. Still free, not needed. |
|
|
81
81
|
| dithermill | Most pixel-art-native word available; "mill" carries batch production. Still free, not needed. |
|
|
82
82
|
| spriteledger | Points hardest at provenance, but inherits the crowded `sprite-` prefix. Still free, not needed. |
|
|
83
83
|
|
|
84
|
-
Rejected after checking: `pixelwright` (three live businesses use it
|
|
85
|
-
builder, an iOS audit firm, a UI developer's brand), `spritefoundry` (taken by a
|
|
86
|
-
real project), `pixelsmith` (real package, `spritesmith`'s engine
|
|
84
|
+
Rejected after checking: `pixelwright` (three live businesses use it: an AI app
|
|
85
|
+
builder, an iOS audit firm, and a UI developer's brand), `spritefoundry` (taken by a
|
|
86
|
+
real project), `pixelsmith` (real package, `spritesmith`'s engine, see
|
|
87
87
|
Resolution), `spritemill`, `pixelloom`, `bitforge`, `spritelab`.
|
package/PROVIDERS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Multi-provider
|
|
1
|
+
# Multi-provider status and notes
|
|
2
2
|
|
|
3
3
|
**The seam is built.** `src/provider.ts` defines the interface;
|
|
4
4
|
`src/providers/pixellab.ts` is the reference implementation and
|
|
@@ -42,9 +42,9 @@ and the natural second provider:
|
|
|
42
42
|
|
|
43
43
|
- A real, documented developer API, with
|
|
44
44
|
[published examples](https://github.com/Retro-Diffusion/api-examples).
|
|
45
|
-
- Purpose-built for pixel art
|
|
46
|
-
so it shares this tool's domain model rather than needing a
|
|
47
|
-
quantize pass bolted on.
|
|
45
|
+
- Purpose-built for pixel art, with grid-aligned output and no blur or
|
|
46
|
+
anti-aliasing, so it shares this tool's domain model rather than needing a
|
|
47
|
+
downscale and quantize pass bolted on.
|
|
48
48
|
- Supports seamless tiles, sprite-sheet animation, and free cost estimates,
|
|
49
49
|
which map onto `plan` almost directly.
|
|
50
50
|
|
|
@@ -65,13 +65,13 @@ Both were global assumptions baked into `plan`; both are now provider-owned.
|
|
|
65
65
|
Kept here as the rationale, since a second adapter has to honour them.
|
|
66
66
|
|
|
67
67
|
1. **Cost is not universally "generations".** `Plan.cost` used to be a bare
|
|
68
|
-
number meaning PixelLab subscription generations. It now carries a unit
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
number meaning PixelLab subscription generations. It now carries a unit: USD
|
|
69
|
+
for OpenAI, generations for PixelLab, free for local. `plan` prints an honest
|
|
70
|
+
figure and `--budget` means something in every backend.
|
|
71
71
|
|
|
72
|
-
2. **Free candidates were a PixelLab quirk.** The core loop
|
|
73
|
-
get 16 candidates for one fixed price, pick the best
|
|
74
|
-
PixelLab charges per *call* and scales candidates inversely with canvas
|
|
72
|
+
2. **Free candidates were a PixelLab quirk.** The core loop is to generate
|
|
73
|
+
small, get 16 candidates for one fixed price, and pick the best. It works
|
|
74
|
+
because PixelLab charges per *call* and scales candidates inversely with canvas
|
|
75
75
|
size. OpenAI charges per *image*, so 16 candidates costs 16×. The picker
|
|
76
76
|
still works either way, but the strategy advice in the README does not
|
|
77
77
|
generalise, which is why `candidateCount()` moved behind the interface.
|
|
@@ -86,9 +86,9 @@ asynchronously.
|
|
|
86
86
|
## Done: the integration tests it unblocked
|
|
87
87
|
|
|
88
88
|
`FakeProvider` turned out to be exactly the better test double predicted here.
|
|
89
|
-
17 integration tests now cover `submit → poll → fetch`, `pushTags` and `adopt
|
|
90
|
-
|
|
91
|
-
the five real bugs in this project lived. Verified non-vacuous by mutation:
|
|
89
|
+
17 integration tests now cover `submit → poll → fetch`, `pushTags` and `adopt`.
|
|
90
|
+
Those are the stages that spend money and previously had zero coverage, where
|
|
91
|
+
four of the five real bugs in this project lived. Verified non-vacuous by mutation:
|
|
92
92
|
breaking the output hashes, the budget check, or the v1 lock rejection each
|
|
93
93
|
fails tests.
|
|
94
94
|
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ PixelKiln supplies the missing project model:
|
|
|
38
38
|
output hashes;
|
|
39
39
|
- planning distinguishes missing, stale, recoverable, in-flight, untracked, and
|
|
40
40
|
manually changed files before money is spent;
|
|
41
|
-
- local review keeps human judgment where it matters
|
|
41
|
+
- local review keeps human judgment where it matters, choosing artwork;
|
|
42
42
|
- content-addressed recovery prevents a transient URL failure from buying the
|
|
43
43
|
same image twice;
|
|
44
44
|
- derived artifact bundles retain source provenance and recover across ordinary
|
|
@@ -52,7 +52,7 @@ PixelKiln supplies the missing project model:
|
|
|
52
52
|
| Generate and review | Resumable submit/poll/pick/fetch pipeline with a fast local candidate sheet. |
|
|
53
53
|
| Existing-art onboarding | Manifest scaffolding, exact-hash account adoption, and prompt recovery. |
|
|
54
54
|
| Recovery | Validated local content cache, provider URL restore, account object-hash cache, and resumable jobs. |
|
|
55
|
-
| Shared-account safety | Cross-project claim files, sibling-style exclusion, reviewed salvage, keep/discard tags, separate confirmed purge. |
|
|
55
|
+
| Shared-account safety | Cross-project claim files or a registered workspace catalog, sibling-style exclusion, reviewed salvage, keep/discard tags, separate confirmed purge. |
|
|
56
56
|
| Quality control | Palette distance, transparency, color-count, relative outlier, cache-integrity, and doctor gates. |
|
|
57
57
|
| Sprite packaging | Deterministic RGBA packing, stable-cell mounting, explicit external input lists, structural output roles. |
|
|
58
58
|
| Engine export | Lossless generic tile contract, Tiled Wang sets, and Godot 4 terrain sets. |
|
|
@@ -253,9 +253,20 @@ pixelkiln purge
|
|
|
253
253
|
```
|
|
254
254
|
|
|
255
255
|
Salvage imports, keeps, or tags discard; it never deletes. On shared accounts,
|
|
256
|
-
pass every other project lockfile via `--claims
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
pass every other project lockfile via `--claims`, or register siblings once in
|
|
257
|
+
a workspace catalog and pass `--workspace`, so shipped art cannot appear
|
|
258
|
+
unowned:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
pixelkiln workspace add ../other-game/pixelkiln.manifest.json
|
|
262
|
+
pixelkiln workspace status
|
|
263
|
+
pixelkiln salvage --workspace pixelkiln.workspace.json
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
A registered project's missing or unreadable lockfile is a hard error for
|
|
267
|
+
`workspace claims` and `salvage --workspace` — never a silent skip. Purge only
|
|
268
|
+
targets objects already tagged discard and requires an explicit confirmation.
|
|
269
|
+
See [Recovery and account safety](./docs/RECOVERY.md).
|
|
259
270
|
|
|
260
271
|
## Automation
|
|
261
272
|
|
|
@@ -323,10 +334,12 @@ Project policies: [Contributing](./CONTRIBUTING.md),
|
|
|
323
334
|
|
|
324
335
|
## Scope
|
|
325
336
|
|
|
326
|
-
Animated eight-direction characters and their ZIP/engine-resource export are
|
|
327
|
-
currently implemented.
|
|
328
|
-
|
|
329
|
-
workspace
|
|
337
|
+
Animated eight-direction characters and their ZIP/engine-resource export are
|
|
338
|
+
not currently implemented. Cross-project content-cache reuse and
|
|
339
|
+
`workspace find <hash|asset-id>` are deferred beyond the current read-only
|
|
340
|
+
workspace catalog. See the open
|
|
341
|
+
[roadmap issues](https://github.com/gfargo/pixelkiln/issues) for additional
|
|
342
|
+
provider adapters and this remaining workspace work.
|
|
330
343
|
|
|
331
344
|
## License
|
|
332
345
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -34,8 +34,25 @@ interface Args {
|
|
|
34
34
|
minTransparency?: number;
|
|
35
35
|
maxColors?: number;
|
|
36
36
|
sigma?: number;
|
|
37
|
+
/** `workspace`: add/remove/list/status/claims. */
|
|
38
|
+
subcommand?: string;
|
|
39
|
+
/** Path to a workspace catalog. Defaults to `pixelkiln.workspace.json` in cwd. */
|
|
40
|
+
workspace?: string;
|
|
41
|
+
/** `workspace add`: manifest path. `workspace remove`: project id or manifest path. */
|
|
42
|
+
target?: string;
|
|
43
|
+
/** `workspace add`: provider id to register the project under. Defaults to "pixellab". */
|
|
44
|
+
provider?: string;
|
|
45
|
+
/** `workspace add`: free-form account label, e.g. distinguishing sandboxes. */
|
|
46
|
+
account?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Raw `--lock` value with no manifest-relative default applied. `workspace
|
|
49
|
+
* add` needs to know whether the user actually passed `--lock`, since the
|
|
50
|
+
* ambient default (beside `--manifest`, which usually names an unrelated
|
|
51
|
+
* project) is meaningless for the manifest being registered.
|
|
52
|
+
*/
|
|
53
|
+
explicitLock?: string;
|
|
37
54
|
}
|
|
38
|
-
declare const COMMANDS: readonly ["init", "plan", "doctor", "gen", "submit", "poll", "pick", "fetch", "restore", "adopt", "accept", "salvage", "purge", "prune", "audit", "cache", "pack", "mount", "export", "tag", "balance", "status", "help", "--help", "-h", "--version", "-v"];
|
|
55
|
+
declare const COMMANDS: readonly ["init", "plan", "doctor", "gen", "submit", "poll", "pick", "fetch", "restore", "adopt", "accept", "salvage", "purge", "prune", "audit", "cache", "pack", "mount", "export", "tag", "balance", "status", "workspace", "help", "--help", "-h", "--version", "-v"];
|
|
39
56
|
/**
|
|
40
57
|
* Strict parsing. Unknown flags are a hard error rather than being ignored,
|
|
41
58
|
* because a silently-dropped filter is expensive here: `--styles neon` (plural,
|