pixelmuse 0.2.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/LICENSE ADDED
@@ -0,0 +1,88 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: StarMorph LLC
6
+ Licensed Work: Pixelmuse CLI 0.2.0
7
+ The Licensed Work is (c) 2025 StarMorph LLC.
8
+ Additional Use Grant: You may make production use of the Licensed Work,
9
+ provided that you do not use the Licensed Work to
10
+ offer a commercial image generation service that
11
+ competes with or substitutes for the Licensor's
12
+ image generation products and services.
13
+ Change Date: 2029-03-01
14
+ Change License: GNU General Public License v2.0 or later
15
+
16
+ Terms
17
+
18
+ The Licensor hereby grants you the right to copy, modify, create
19
+ derivative works, redistribute, and make non-production use of the
20
+ Licensed Work. The Licensor may make an Additional Use Grant, above,
21
+ permitting limited production use.
22
+
23
+ Effective on the Change Date, or the fourth anniversary of the first
24
+ publicly available distribution of a specific version of the Licensed
25
+ Work under this License, whichever comes first, the Licensor hereby
26
+ grants you rights under the terms of the Change License, and the rights
27
+ granted in the paragraph above terminate.
28
+
29
+ If your use of the Licensed Work does not comply with the requirements
30
+ currently in effect as described in this License, you must purchase a
31
+ commercial license from the Licensor, its affiliated entities, or
32
+ authorized resellers, or you must refrain from using the Licensed Work.
33
+
34
+ All copies of the original and modified Licensed Work, and derivative
35
+ works of the Licensed Work, are subject to this License. This License
36
+ applies separately for each version of the Licensed Work and the Change
37
+ Date may vary for each version of the Licensed Work released by
38
+ Licensor.
39
+
40
+ You must conspicuously display this License on each original or modified
41
+ copy of the Licensed Work. If you receive the Licensed Work in original
42
+ or modified form from a third party, the terms and conditions set forth
43
+ in this License apply to your use of that work.
44
+
45
+ Any use of the Licensed Work in violation of this License will
46
+ automatically terminate your rights under this License for the current
47
+ and all other versions of the Licensed Work.
48
+
49
+ This License does not grant you any right in any trademark or logo of
50
+ Licensor or its affiliates (provided that you may use a trademark or
51
+ logo of Licensor as expressly required by this License).
52
+
53
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS
54
+ PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES
55
+ AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION)
56
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
57
+ NON-INFRINGEMENT, AND TITLE.
58
+
59
+ MariaDB hereby grants you permission to use this License's text to
60
+ license your works, and to refer to it using the trademark "Business
61
+ Source License", as long as you comply with the Covenants of Licensor
62
+ below.
63
+
64
+ Covenants of Licensor
65
+
66
+ In consideration of the right to use this License's text and the
67
+ "Business Source License" name and trademark, Licensor covenants to
68
+ MariaDB, and to all other recipients of the licensed work to be provided
69
+ by Licensor:
70
+
71
+ 1. To specify as the Change License the GPL Version 2.0 or any later
72
+ version, or a license that is compatible with GPL Version 2.0 or a
73
+ later version, where "compatible" means that software provided under
74
+ the Change License can be included in a program with software
75
+ provided under GPL Version 2.0 or a later version. Licensor may
76
+ specify additional Change Licenses without limitation.
77
+
78
+ 2. To either: (a) specify an additional grant of rights to use that does
79
+ not impose any additional restriction on the right granted in this
80
+ License, as the Additional Use Grant; or (b) insert the text "None".
81
+
82
+ 3. To specify a Change Date.
83
+
84
+ 4. Not to modify this License in any other way.
85
+
86
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights
87
+ Reserved. "Business Source License" is a trademark of MariaDB
88
+ Corporation Ab.
package/README.md ADDED
@@ -0,0 +1,274 @@
1
+ <p align="center">
2
+ <img src="./assets/banner.png" alt="pixelmuse" width="700" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>AI image generation from the terminal.</strong><br/>
7
+ CLI, interactive TUI, and MCP server — powered by the <a href="https://pixelmuse.studio">Pixelmuse</a> API.
8
+ </p>
9
+
10
+ <p align="center">
11
+ <a href="https://www.npmjs.com/package/pixelmuse"><img src="https://img.shields.io/npm/v/pixelmuse.svg" alt="npm version" /></a>
12
+ <a href="https://www.npmjs.com/package/pixelmuse"><img src="https://img.shields.io/npm/dm/pixelmuse.svg" alt="npm downloads" /></a>
13
+ <a href="https://github.com/starmorph/pixelmuse-cli/actions/workflows/ci.yml"><img src="https://github.com/starmorph/pixelmuse-cli/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
14
+ <a href="https://github.com/starmorph/pixelmuse-cli/actions/workflows/security.yml"><img src="https://github.com/starmorph/pixelmuse-cli/actions/workflows/security.yml/badge.svg" alt="Security" /></a>
15
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/License-BSL%201.1-blue.svg" alt="License: BSL 1.1" /></a>
16
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg" alt="Node.js" /></a>
17
+ </p>
18
+
19
+ ---
20
+
21
+ <!-- GIF: Hero — basic generation. Record: pixelmuse "a cyberpunk city at sunset, neon lights reflecting on wet streets" -->
22
+ <!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Pixelmuse CLI demo" width="600" /></p> -->
23
+
24
+ ---
25
+
26
+ ## Why Pixelmuse?
27
+
28
+ - **One command, any model.** Flux, Imagen 3, Recraft V4, and more — switch models with a flag, no separate accounts or API keys.
29
+ - **Built for developer workflows.** Pipe from stdin, JSON output for scripting, watch mode for prompt iteration, MCP server for AI agents.
30
+ - **Predictable credit pricing.** 1-3 credits per generation, no surprises. Free credits on signup.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ npm install -g pixelmuse
36
+ ```
37
+
38
+ Then authenticate:
39
+
40
+ ```bash
41
+ pixelmuse login
42
+ ```
43
+
44
+ Sign up at [pixelmuse.studio/sign-up](https://www.pixelmuse.studio/sign-up) — new accounts include free credits.
45
+
46
+ > Requires Node.js 20+. For terminal image previews, install [chafa](https://hpjansson.org/chafa/) (`brew install chafa` / `sudo apt install chafa`).
47
+
48
+ ## Quick Start
49
+
50
+ ```bash
51
+ # Generate an image
52
+ pixelmuse "a cat floating through space"
53
+
54
+ # Choose model and aspect ratio
55
+ pixelmuse "neon cityscape at night" -m recraft-v4 -a 16:9
56
+
57
+ # Apply a style
58
+ pixelmuse "mountain landscape" -s anime -a 21:9
59
+
60
+ # Save to specific path
61
+ pixelmuse "app icon, minimal" -o icon.png
62
+ ```
63
+
64
+ <!-- GIF: Model selection. Record: pixelmuse "a cat astronaut" -m recraft-v4 --open -->
65
+ <!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Model selection" width="600" /></p> -->
66
+
67
+ ## Prompt Templates
68
+
69
+ Save reusable prompts as YAML files with variables and default settings.
70
+
71
+ ```bash
72
+ # Scaffold a new template
73
+ pixelmuse template init product-shot
74
+
75
+ # Generate with a template
76
+ pixelmuse template use blog-thumbnail --var subject="React hooks guide"
77
+
78
+ # List all templates
79
+ pixelmuse template list
80
+ ```
81
+
82
+ <!-- GIF: Template system. Record: pixelmuse template use blog-thumbnail --var subject="TypeScript generics" -->
83
+ <!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Template system" width="600" /></p> -->
84
+
85
+ Templates are stored at `~/.config/pixelmuse-cli/prompts/`:
86
+
87
+ ```yaml
88
+ # blog-thumbnail.yaml
89
+ name: Blog Thumbnail
90
+ description: Dark-themed blog post thumbnail
91
+ prompt: >
92
+ A cinematic {{subject}} on a dark gradient background,
93
+ dramatic lighting, 8K resolution
94
+ defaults:
95
+ model: nano-banana-2
96
+ aspect_ratio: "16:9"
97
+ variables:
98
+ subject: "code editor with syntax highlighting"
99
+ tags: [blog, thumbnail, dark]
100
+ ```
101
+
102
+ ## Interactive TUI
103
+
104
+ A full terminal UI for visual browsing, generation wizards, gallery, and account management:
105
+
106
+ ```bash
107
+ pixelmuse ui
108
+ ```
109
+
110
+ <!-- GIF: TUI. Record: pixelmuse ui → navigate Generate → pick model → type prompt → generate -->
111
+ <!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Interactive TUI" width="600" /></p> -->
112
+
113
+ ## Scripting & Pipes
114
+
115
+ ```bash
116
+ # Pipe prompt from stdin
117
+ echo "hero banner for SaaS landing page" | pixelmuse -o hero.png
118
+ cat prompt.txt | pixelmuse -m recraft-v4
119
+
120
+ # JSON output for scripting
121
+ pixelmuse --json "logo concept" | jq .output_path
122
+
123
+ # Watch mode — regenerates when prompt file changes
124
+ pixelmuse --watch prompt.txt -o output.png
125
+
126
+ # Skip preview, copy to clipboard
127
+ pixelmuse "avatar" --no-preview --clipboard
128
+ ```
129
+
130
+ <!-- GIF: Pipe/scripting. Record: echo "minimalist logo for a coffee shop" | pixelmuse --json -m flux-schnell -->
131
+ <!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Scripting and pipes" width="600" /></p> -->
132
+
133
+ ## MCP Server (Claude Code, Cursor, Windsurf)
134
+
135
+ The MCP server lets AI agents generate images, list models, and check your balance — no manual CLI steps needed.
136
+
137
+ **Claude Code** — add to `~/.claude/.mcp.json`:
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "pixelmuse": {
143
+ "command": "npx",
144
+ "args": ["-y", "pixelmuse-mcp"],
145
+ "env": {
146
+ "PIXELMUSE_API_KEY": "pm_live_your_key_here"
147
+ }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ **Cursor / Windsurf** — add to your MCP settings:
154
+
155
+ ```json
156
+ {
157
+ "pixelmuse": {
158
+ "command": "npx",
159
+ "args": ["-y", "pixelmuse-mcp"],
160
+ "env": {
161
+ "PIXELMUSE_API_KEY": "pm_live_your_key_here"
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ The agent gets three tools:
168
+
169
+ | Tool | What it does |
170
+ |------|-------------|
171
+ | `generate_image` | Generate an image from a prompt with model, aspect ratio, style, and output path. |
172
+ | `list_models` | List all available models with credit costs. |
173
+ | `check_balance` | Check your credit balance and plan info. |
174
+
175
+ **Example prompts for your AI agent:**
176
+
177
+ - "Generate a hero image for my landing page, 16:9, save to `./public/hero.png`"
178
+ - "Create a blog thumbnail about React hooks using the anime style"
179
+ - "What Pixelmuse models are available?"
180
+
181
+ ## Which Interface?
182
+
183
+ Pixelmuse ships four interfaces. Pick the one that fits your workflow — they all use the same API and credentials.
184
+
185
+ | | **CLI** | **TUI** | **MCP Server** | **Claude Skill** |
186
+ |---|---|---|---|---|
187
+ | **What it is** | Command-line tool | Interactive terminal UI | AI agent tool server | Claude Code prompt template |
188
+ | **Launch** | `pixelmuse "prompt"` | `pixelmuse ui` | Auto-starts with Claude/Cursor | Auto-triggers on keywords |
189
+ | **Best for** | Scripting, automation, CI/CD | Visual browsing, exploring models | Letting AI agents generate images | Generating images mid-conversation |
190
+ | **Input** | Flags, stdin, pipe, watch mode | Guided wizard with menus | AI decides params from natural language | Natural language to Claude |
191
+
192
+ **Rule of thumb:**
193
+ - **You type the prompt** → CLI or TUI
194
+ - **AI types the prompt** → MCP Server or Claude Skill
195
+ - **Quick one-off** → CLI
196
+ - **Browsing/exploring** → TUI
197
+
198
+ ## Models
199
+
200
+ | Model | Credits | Best For |
201
+ |-------|---------|----------|
202
+ | **Nano Banana 2** (default) | 1 | Speed, text rendering, world knowledge |
203
+ | Nano Banana Pro | 4 | Text rendering, real-time info, multi-image editing |
204
+ | Flux Schnell | 1 | Quick mockups, ideation |
205
+ | Google Imagen 3 | 1 | Realistic photos, complex compositions |
206
+ | Recraft V4 | 1 | Typography, design, composition |
207
+ | Recraft V4 Pro | 7 | High-res design, art direction |
208
+
209
+ ## CLI Reference
210
+
211
+ ### Commands
212
+
213
+ | Command | Description |
214
+ |---------|-------------|
215
+ | `pixelmuse "prompt"` | Generate an image (default command) |
216
+ | `pixelmuse models` | List available models with costs |
217
+ | `pixelmuse account` | Account balance and usage stats |
218
+ | `pixelmuse history` | Recent generations table |
219
+ | `pixelmuse open <id>` | Open a generation in system viewer |
220
+ | `pixelmuse login` | Authenticate with API key |
221
+ | `pixelmuse logout` | Remove stored credentials |
222
+ | `pixelmuse template <cmd>` | Manage prompt templates |
223
+ | `pixelmuse ui` | Launch interactive TUI |
224
+
225
+ ### Flags
226
+
227
+ | Flag | Description |
228
+ |------|-------------|
229
+ | `-m, --model` | Model ID (default: `nano-banana-2`) |
230
+ | `-a, --aspect-ratio` | Aspect ratio (default: `1:1`) |
231
+ | `-s, --style` | `realistic`, `anime`, `artistic`, `none` |
232
+ | `-o, --output` | Output file path |
233
+ | `--json` | Machine-readable JSON output |
234
+ | `--no-preview` | Skip terminal image preview |
235
+ | `--open` | Open result in system viewer |
236
+ | `--clipboard` | Copy image to clipboard |
237
+ | `--watch <file>` | Watch prompt file, regenerate on save |
238
+ | `--no-save` | Don't save image to disk |
239
+
240
+ ## Configuration
241
+
242
+ Settings at `~/.config/pixelmuse-cli/config.yaml`:
243
+
244
+ ```yaml
245
+ defaultModel: nano-banana-2
246
+ defaultAspectRatio: "1:1"
247
+ defaultStyle: none
248
+ autoPreview: true
249
+ autoSave: true
250
+ ```
251
+
252
+ | Path | Contents |
253
+ |------|----------|
254
+ | `~/.config/pixelmuse-cli/config.yaml` | User settings |
255
+ | `~/.config/pixelmuse-cli/auth.json` | API key (fallback if keychain unavailable) |
256
+ | `~/.config/pixelmuse-cli/prompts/` | Prompt template YAML files |
257
+ | `~/.local/share/pixelmuse-cli/generations/` | Auto-saved generation images |
258
+
259
+ ## Links
260
+
261
+ - [Pixelmuse](https://www.pixelmuse.studio) — Platform home
262
+ - [Sign up](https://www.pixelmuse.studio/sign-up) — Create an account (free credits included)
263
+ - [API keys](https://www.pixelmuse.studio/settings/api-keys) — Manage API keys
264
+ - [API docs](https://www.pixelmuse.studio/developers) — Full API reference (Scalar)
265
+ - [Get credits](https://www.pixelmuse.studio/get-credits) — Buy credit packs
266
+ <!-- - [YouTube demo](YOUR_YOUTUBE_URL) — Full walkthrough video -->
267
+
268
+ ## License
269
+
270
+ Business Source License 1.1 (BSL 1.1). See [LICENSE](./LICENSE) for details.
271
+
272
+ Free for any use except offering a competing image generation API or platform. Converts to GPL-2.0 on 2029-03-01.
273
+
274
+ Copyright 2025 StarMorph LLC.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ pollGeneration,
4
+ slugify
5
+ } from "./chunk-ZVJQFWUI.js";
6
+ import {
7
+ autoSave,
8
+ imageToBuffer,
9
+ saveImage
10
+ } from "./chunk-MZZY4JXW.js";
11
+
12
+ // src/core/generate.ts
13
+ import { existsSync } from "fs";
14
+ import { join, resolve } from "path";
15
+ function resolveOutputPath(prompt, output) {
16
+ if (output) return resolve(output);
17
+ const base = slugify(prompt).slice(0, 60) || "generation";
18
+ let filename = `${base}.png`;
19
+ let counter = 2;
20
+ while (existsSync(join(process.cwd(), filename))) {
21
+ filename = `${base}-${counter}.png`;
22
+ counter++;
23
+ }
24
+ return join(process.cwd(), filename);
25
+ }
26
+ async function generateImage(client, options) {
27
+ const {
28
+ prompt,
29
+ model = "nano-banana-2",
30
+ aspectRatio = "1:1",
31
+ style,
32
+ output,
33
+ noSave = false,
34
+ onProgress
35
+ } = options;
36
+ const start = Date.now();
37
+ let gen = await client.generate({
38
+ prompt,
39
+ model,
40
+ aspect_ratio: aspectRatio,
41
+ style: style === "none" ? void 0 : style
42
+ });
43
+ if (gen.status === "processing" || gen.status === "pending") {
44
+ gen = await pollGeneration(client, gen.id, { onProgress });
45
+ }
46
+ const elapsed = (Date.now() - start) / 1e3;
47
+ let imagePath = null;
48
+ if (gen.output?.[0] && !noSave) {
49
+ const buf = await imageToBuffer(gen.output[0]);
50
+ const outPath = resolveOutputPath(prompt, output);
51
+ saveImage(buf, outPath);
52
+ imagePath = outPath;
53
+ autoSave(gen.id, buf);
54
+ }
55
+ return { generation: gen, imagePath, elapsed };
56
+ }
57
+
58
+ export {
59
+ generateImage
60
+ };
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/config.ts
4
+ import envPaths from "env-paths";
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6
+ import { join } from "path";
7
+ import YAML from "yaml";
8
+ var paths = envPaths("pixelmuse-cli");
9
+ var PATHS = {
10
+ config: paths.config,
11
+ data: paths.data,
12
+ auth: join(paths.config, "auth.json"),
13
+ settings: join(paths.config, "config.yaml"),
14
+ prompts: join(paths.config, "prompts"),
15
+ generations: join(paths.data, "generations")
16
+ };
17
+ var DEFAULT_SETTINGS = {
18
+ defaultModel: "nano-banana-2",
19
+ defaultAspectRatio: "1:1",
20
+ defaultStyle: "none",
21
+ autoPreview: true,
22
+ autoSave: true
23
+ };
24
+ function ensureDirs() {
25
+ for (const dir of [PATHS.config, PATHS.data, PATHS.prompts, PATHS.generations]) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ }
29
+ function readSettings() {
30
+ ensureDirs();
31
+ if (!existsSync(PATHS.settings)) {
32
+ writeSettings(DEFAULT_SETTINGS);
33
+ return DEFAULT_SETTINGS;
34
+ }
35
+ try {
36
+ const raw = readFileSync(PATHS.settings, "utf-8");
37
+ return { ...DEFAULT_SETTINGS, ...YAML.parse(raw) };
38
+ } catch {
39
+ return DEFAULT_SETTINGS;
40
+ }
41
+ }
42
+ function writeSettings(settings) {
43
+ ensureDirs();
44
+ writeFileSync(PATHS.settings, YAML.stringify(settings), "utf-8");
45
+ }
46
+
47
+ export {
48
+ PATHS,
49
+ ensureDirs,
50
+ readSettings,
51
+ writeSettings
52
+ };
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PATHS
4
+ } from "./chunk-7ARYEFAH.js";
5
+
6
+ // src/core/image.ts
7
+ import { execSync } from "child_process";
8
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
9
+ import { dirname, join } from "path";
10
+ var _chafaAvailable = null;
11
+ function hasChafa() {
12
+ if (_chafaAvailable !== null) return _chafaAvailable;
13
+ try {
14
+ execSync("chafa --version", { stdio: "ignore" });
15
+ _chafaAvailable = true;
16
+ } catch {
17
+ _chafaAvailable = false;
18
+ }
19
+ return _chafaAvailable;
20
+ }
21
+ async function imageToBuffer(output) {
22
+ if (output.startsWith("data:")) {
23
+ const base64 = output.split(",", 2)[1];
24
+ if (!base64) throw new Error("Invalid data URI: missing base64 content");
25
+ return Buffer.from(base64, "base64");
26
+ }
27
+ const res = await fetch(output);
28
+ if (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`);
29
+ return Buffer.from(await res.arrayBuffer());
30
+ }
31
+ function saveImage(buffer, path) {
32
+ const dir = dirname(path);
33
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
34
+ writeFileSync(path, buffer);
35
+ return path;
36
+ }
37
+ function autoSave(id, buffer) {
38
+ const dir = PATHS.generations;
39
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
40
+ const path = join(dir, `${id}.png`);
41
+ writeFileSync(path, buffer);
42
+ return path;
43
+ }
44
+ function renderImageDirect(path) {
45
+ if (!hasChafa()) return false;
46
+ try {
47
+ execSync(`chafa --animate off "${path}"`, {
48
+ stdio: "inherit",
49
+ maxBuffer: 10 * 1024 * 1024
50
+ });
51
+ return true;
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ export {
58
+ hasChafa,
59
+ imageToBuffer,
60
+ saveImage,
61
+ autoSave,
62
+ renderImageDirect
63
+ };