Compare commits

...
Author SHA1 Message Date
Paul Bakaus 71937d33f1 Remove generated harness copies
Keep the feature PR source-first; main will regenerate provider output after merge.\n\nAI-assisted: implemented and validated by Codex under maintainer direction.
2026-08-31 16:57:57 -07:00
Abdul WahabandCursor a037b19ecc Sync generated generate-image copies for the spaced-path embed fix (#670)
Plugin and harness installs read these tracked copies, not skill/scripts. Keep them on fileURLToPath so embed still works under a path with spaces.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 20:07:54 +05:00
Abdul WahabandCursor c8a9d9e38a Fix: embed prompts when generate-image lives under a path with spaces (#670)
URL.pathname leaves spaces percent-encoded, so the embed helper never ran and the generator still claimed success. Convert with fileURLToPath, check the spawn result, and warn instead of lying.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 01:34:05 +05:00
3 changed files with 91 additions and 3 deletions
+2 -1
View File
@@ -26,7 +26,7 @@ export const SUITES = {
triggers: [
...COMMON_INFRA_PATTERNS,
/^scripts\/(?!benchmark-detector|build-browser-detector|build-extension)/,
/^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|comp-diff|comp-spec|build-phase|font-match|data\/font-index|concept-seed|context|context-signals|critique-storage|design-parser|doctor|hook|impeccable-paths|is-generated|lib\/(artifact-schema|png|raster|image-metrics|font-fingerprint|font-index|hero-checks|composition-catalog|concept-catalog|provider|staleness|staleness-deep|staleness-notice|surface-briefs|target-slug|template-extensions)|pin|surface-brief))/,
/^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|comp-diff|comp-spec|build-phase|font-match|data\/font-index|concept-seed|generate-image|context|context-signals|critique-storage|design-parser|doctor|hook|impeccable-paths|is-generated|lib\/(artifact-schema|png|raster|image-metrics|font-fingerprint|font-index|hero-checks|composition-catalog|concept-catalog|provider|staleness|staleness-deep|staleness-notice|surface-briefs|target-slug|template-extensions)|pin|surface-brief))/,
/^README(\.npm)?\.md$/,
/^cli\/bin\//,
],
@@ -54,6 +54,7 @@ export const SUITES = {
'tests/ci-test-plan.test.mjs',
'tests/cli-args.test.mjs',
'tests/concept-seed.test.mjs',
'tests/generate-image-embed.test.mjs',
'tests/comp-diff.test.mjs',
'tests/build-phase.test.mjs',
'tests/font-match.test.mjs',
+5 -2
View File
@@ -431,12 +431,15 @@ fs.writeFileSync(out, Buffer.from(b64, 'base64'));
// The prompt travels with the asset: embedded in the file itself (EXIF-class
// metadata via embed-prompt.mjs) so intent survives copies across harnesses,
// plus a sidecar for anything that indexes rather than opens the image.
let embedded = false;
try {
const { spawnSync } = await import('node:child_process');
spawnSync(process.execPath, [new URL('./embed-prompt.mjs', import.meta.url).pathname, out, '--prompt', prompt], { stdio: 'ignore' });
const result = spawnSync(process.execPath, [fileURLToPath(new URL('./embed-prompt.mjs', import.meta.url)), out, '--prompt', prompt], { stdio: 'ignore' });
embedded = !result.error && result.status === 0;
if (!embedded) console.warn('generate-image: failed to embed prompt in the image');
fs.writeFileSync(`${out}.json`, JSON.stringify({ prompt, createdAt: new Date().toISOString(), tool: 'generate-image.mjs', model: 'gpt-image-2', ...(refs.length ? { refs } : {}) }, null, 2));
} catch { /* embedding is best-effort */ }
console.log(`IMAGE: ${out} (${size}, ${quality}, gpt-image-2, billed to your OpenAI key); prompt embedded + sidecar at ${out}.json`);
console.log(`IMAGE: ${out} (${size}, ${quality}, gpt-image-2, billed to your OpenAI key); ${embedded ? 'prompt embedded + sidecar' : 'sidecar'} at ${out}.json`);
if (plateCtx && plateCtx.chroma) {
const frac = await keyChroma(out, plateCtx.chroma);
console.log(`PLATE-CHROMA keyed ${(frac * 100).toFixed(0)}% of pixels to alpha (${plateCtx.chroma}); place with a plain <img> over the page's own ground, no background on the plate. If the keyed fraction is under 20% the generator ignored the key: regenerate with --no-chroma and use mix-blend-mode: multiply instead.`);
+84
View File
@@ -0,0 +1,84 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const SOURCE_SCRIPT = path.join(ROOT, 'skill', 'scripts', 'generate-image.mjs');
const EMBED_SCRIPT = path.join(ROOT, 'skill', 'scripts', 'embed-prompt.mjs');
const PNG_B64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
function makeSpacedInstall({ withEmbed = true } = {}) {
const parent = mkdtempSync(path.join(tmpdir(), 'gen-img-parent-'));
const installDir = path.join(parent, 'impeccable space');
mkdirSync(installDir, { recursive: true });
copyFileSync(SOURCE_SCRIPT, path.join(installDir, 'generate-image.mjs'));
if (withEmbed) copyFileSync(EMBED_SCRIPT, path.join(installDir, 'embed-prompt.mjs'));
const preload = path.join(parent, 'fetch-mock.mjs');
writeFileSync(preload, `globalThis.fetch = async () => ({ ok: true, json: async () => ({ data: [{ b64_json: '${PNG_B64}' }] }) });\n`);
const cwd = mkdtempSync(path.join(tmpdir(), 'gen-img-cwd-'));
return { parent, installDir, cwd, script: path.join(installDir, 'generate-image.mjs'), preload };
}
function runGenerate(install, prompt) {
const out = path.join(install.cwd, 'out.png');
const env = { ...process.env, OPENAI_API_KEY: 'test-key' };
delete env.IMPECCABLE_IMAGE_GEN_FAKE;
const result = spawnSync(process.execPath, [
'--import', pathToFileURL(install.preload).href,
install.script,
'--prompt', prompt,
'--out', out,
], {
cwd: install.cwd,
encoding: 'utf-8',
env,
});
return { ...result, out, sidecar: `${out}.json` };
}
function cleanup(install) {
rmSync(install.parent, { recursive: true, force: true });
rmSync(install.cwd, { recursive: true, force: true });
}
describe('generate-image embed', () => {
it('embeds prompt when install path contains a space', () => {
const install = makeSpacedInstall({ withEmbed: true });
try {
const prompt = 'space-path embed test';
const result = runGenerate(install, prompt);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /prompt embedded/);
assert.ok(existsSync(result.out));
assert.ok(existsSync(result.sidecar));
assert.equal(JSON.parse(readFileSync(result.sidecar, 'utf8')).prompt, prompt);
unlinkSync(result.sidecar);
const readBack = spawnSync(process.execPath, [path.join(install.installDir, 'embed-prompt.mjs'), result.out, '--read'], {
encoding: 'utf-8',
});
assert.equal(readBack.status, 0, readBack.stderr);
assert.equal(readBack.stdout.trim(), prompt);
} finally {
cleanup(install);
}
});
it('warns when embed helper is missing but keeps image and sidecar', () => {
const install = makeSpacedInstall({ withEmbed: false });
try {
const prompt = 'missing helper test';
const result = runGenerate(install, prompt);
assert.equal(result.status, 0, result.stderr);
assert.doesNotMatch(result.stdout, /prompt embedded/);
assert.match(result.stderr, /failed to embed prompt/);
assert.ok(existsSync(result.out));
assert.ok(existsSync(result.sidecar));
} finally {
cleanup(install);
}
});
});