Appearance
GPT Image 2.5 Prompt Archive Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
进度(2026-09-24):Task 1–2 已完成——档案写入脚本、manifest 校验、dry-run 与
--help均已落地,node --test tests/gpt-image-prompt-archive.test.mjs6 项测试全部通过(提交 e0f6dde、3d45640)。Task 3–5(页面组件与导航、首次内容归档)待后续实施。
Goal: Build a VitePress visual archive that publishes only final reverse-engineered prompts, their Codex-generated images, and traceable ChatGPT shared-session sources.
Architecture: scripts/gpt-image-prompt-archive.mjs owns stable record IDs, validation, deduplication, and the static JSON update. Codex is the orchestration boundary: when the user asks for an update, it reads the shared session, uses the built-in image generation capability for each new prompt, copies images into docs/public/gpt-image-prompts/, then invokes the script to atomically register completed assets. The Vue component reads the JSON and supplies source filtering, full prompt expansion, and clipboard copy.
Tech Stack: Node.js built-in test runner and crypto/fs modules; VitePress 1.6; Vue 3 Composition API; Codex built-in image generation (no OPENAI_API_KEY).
File structure
| Path | Responsibility |
|---|---|
scripts/gpt-image-prompt-archive.mjs | Validate prompt-manifest input, calculate source-scoped IDs, skip existing records, and write the static archive JSON. |
tests/gpt-image-prompt-archive.test.mjs | Unit and CLI-level behavior for record construction, source-scoped deduplication, validation, and dry runs. |
docs/public/gpt-image-prompts.json | Published archive data; starts empty and is updated only after image files exist. |
docs/public/gpt-image-prompts/ | Generated png images named by stable record ID. |
docs/.vitepress/theme/components/GptImagePromptArchive.vue | Fetch, filter, render, expand, and copy archive records. |
docs/gpt-image-prompts/index.md | SEO-aware VitePress entry page. |
docs/.vitepress/theme/index.ts | Globally registers the archive component. |
docs/.vitepress/config.ts | Adds top navigation and the local archive sidebar. |
tests/gpt-image-prompts-page.test.mjs | Static page integration contract for registration, navigation, data path, and copy behavior. |
package.json | Adds the safe JSON-registration command. |
Task 1: Create the source-scoped archive writer
Files:
Create:
tests/gpt-image-prompt-archive.test.mjsCreate:
scripts/gpt-image-prompt-archive.mjsCreate:
docs/public/gpt-image-prompts.jsonModify:
package.json[x] Step 1: Write failing tests for stable record construction and source-scoped deduplication.
js
test("buildRecord uses a stable source-and-prompt id and published image path", () => {
const record = buildRecord({
prompt: "A sunlit paper sculpture of a fox.",
sourceUrl: "https://chatgpt.com/share/example",
sourceTitle: "图片逆向提示词-1",
image: "gpt-image-prompts/abc123.png",
createdAt: "2026-09-19T00:00:00.000Z",
});
assert.match(record.id, /^[a-f0-9]{16}$/);
assert.equal(record.image, "/gpt-image-prompts/abc123.png");
assert.equal(record.model, "gpt-image-2.5-sunburst");
assert.equal(record.size, "1536x1024");
assert.equal(record.quality, "high");
});
test("mergeRecords skips a matching source-and-prompt id but retains the same prompt from another source", () => {
const prompt = "A sunlit paper sculpture of a fox.";
const sourceA = "https://chatgpt.com/share/a";
const sourceB = "https://chatgpt.com/share/b";
const existing = [buildRecord({ prompt, sourceUrl: sourceA, sourceTitle: "A", image: "gpt-image-prompts/a.png", createdAt: "2026-09-19T00:00:00.000Z" })];
const { added, skipped } = mergeRecords(existing, [
{ prompt, sourceUrl: sourceA, sourceTitle: "A", image: "gpt-image-prompts/a.png", createdAt: "2026-09-19T00:00:00.000Z" },
{ prompt, sourceUrl: sourceB, sourceTitle: "B", image: "gpt-image-prompts/b.png", createdAt: "2026-09-19T00:00:00.000Z" },
]);
assert.equal(skipped.length, 1);
assert.equal(added.length, 1);
assert.equal(added[0].sourceUrl, sourceB);
});- [x] Step 2: Run the focused test and verify it fails because the module does not exist.
Run: node --test tests/gpt-image-prompt-archive.test.mjs
Expected: failure mentioning scripts/gpt-image-prompt-archive.mjs cannot be imported.
- [x] Step 3: Implement the minimal archive module.
js
export function recordId(sourceUrl, prompt) {
return createHash("sha256").update(`${sourceUrl.trim()}\n${prompt.trim()}`).digest("hex").slice(0, 16);
}
export function buildRecord(input) {
const id = recordId(input.sourceUrl, input.prompt);
return {
id,
prompt: input.prompt.trim(),
sourceUrl: input.sourceUrl.trim(),
sourceTitle: input.sourceTitle.trim(),
image: `/${input.image.replace(/^\/+/, "")}`,
model: "gpt-image-2.5-sunburst",
size: "1536x1024",
quality: "high",
createdAt: input.createdAt,
};
}
export function mergeRecords(existing, candidates) {
const existingIds = new Set(existing.map((record) => record.id));
const added = [];
const skipped = [];
for (const candidate of candidates) {
const record = buildRecord(candidate);
if (existingIds.has(record.id)) skipped.push(record);
else {
existingIds.add(record.id);
added.push(record);
}
}
return { added, skipped };
}Use readFile, writeFile, and mkdir from node:fs/promises for an exported updateArchive({ dataFile, candidates, dryRun }) function. Sort records newest-first by createdAt; write { generatedAt, records } with a trailing newline. Create docs/public/gpt-image-prompts.json as {"generatedAt":"","records":[]}\n and add "register-gpt-image-prompts": "node scripts/gpt-image-prompt-archive.mjs" to package.json.
- [x] Step 4: Run the focused test and verify it passes.
Run: node --test tests/gpt-image-prompt-archive.test.mjs
Expected: both tests pass.
- [x] Step 5: Commit the data writer.
bash
git add scripts/gpt-image-prompt-archive.mjs tests/gpt-image-prompt-archive.test.mjs docs/public/gpt-image-prompts.json package.json
git commit -m "feat: add GPT image prompt archive writer"Task 2: Enforce completed-image input and safe dry runs
Files:
Modify:
tests/gpt-image-prompt-archive.test.mjsModify:
scripts/gpt-image-prompt-archive.mjs[x] Step 1: Write failing tests for required metadata, an existing image, and dry-run immutability.
js
test("updateArchive rejects a candidate whose image does not exist", async () => {
await assert.rejects(
updateArchive({ dataFile, imageRoot, candidates: [{ prompt: "Prompt", sourceUrl: "https://chatgpt.com/share/a", sourceTitle: "A", image: "gpt-image-prompts/missing.png", createdAt }], dryRun: false }),
/生成图片不存在/,
);
});
test("updateArchive reports a dry run without writing the JSON file", async () => {
const before = await readFile(dataFile, "utf8");
const summary = await updateArchive({ dataFile, imageRoot, candidates: [readyCandidate], dryRun: true });
assert.equal(summary.added, 1);
assert.equal(await readFile(dataFile, "utf8"), before);
});- [x] Step 2: Run the focused test and verify it fails for the new behavior.
Run: node --test tests/gpt-image-prompt-archive.test.mjs
Expected: failing assertions for missing-image acceptance and dry-run writing.
- [x] Step 3: Implement validation and manifest-based CLI input.
Add validateCandidate(candidate, imageRoot) that rejects blank prompt, invalid https://chatgpt.com/share/ source URLs, blank sourceTitle, invalid image paths, and files absent beneath docs/public. Add --manifest <absolute-or-relative-json-file> and --dry-run; the manifest shape is { "records": [{ "prompt", "sourceUrl", "sourceTitle", "image", "createdAt" }] }. Print exactly added=<n> skipped=<n> dryRun=<true|false> on success, and let thrown validation errors terminate with a nonzero status.
- [x] Step 4: Run focused tests and the CLI dry run.
Run: node --test tests/gpt-image-prompt-archive.test.mjs && npm run register-gpt-image-prompts -- --help
Expected: tests pass and help documents --manifest and --dry-run without modifying archive JSON.
- [x] Step 5: Commit the safe registration boundary.
bash
git add scripts/gpt-image-prompt-archive.mjs tests/gpt-image-prompt-archive.test.mjs
git commit -m "feat: validate GPT image archive manifests"Task 3: Add the visual archive component and page contract
Files:
Create:
tests/gpt-image-prompts-page.test.mjsCreate:
docs/.vitepress/theme/components/GptImagePromptArchive.vueCreate:
docs/gpt-image-prompts/index.mdModify:
docs/.vitepress/theme/index.ts[ ] Step 1: Write the failing static integration test.
js
test("GPT image prompt archive has a registered page and data-backed component", () => {
assert.equal(existsSync(pagePath), true);
assert.equal(existsSync(componentPath), true);
assert.match(readFileSync(pagePath, "utf8"), /<GptImagePromptArchive \/>/);
assert.match(readFileSync(themeIndex, "utf8"), /app\.component\("GptImagePromptArchive", GptImagePromptArchive\)/);
const component = readFileSync(componentPath, "utf8");
assert.match(component, /fetch\(withBase\("\/gpt-image-prompts\.json"\)\)/);
assert.match(component, /navigator\.clipboard.*writeText/);
assert.match(component, /sourceUrl/);
});- [ ] Step 2: Run the focused test and verify it fails because the archive files are absent.
Run: node --test tests/gpt-image-prompts-page.test.mjs
Expected: failure that docs/gpt-image-prompts/index.md does not exist.
- [ ] Step 3: Implement the page and focused Vue component.
Create the page with:
md
---
layout: doc
title: GPT Image 2.5 提示词档案
description: 收录逆向生成后的最终提示词,并以 GPT Image 2.5 生成配图;每条记录可追溯到原始会话。
aside: false
pageClass: gpt-image-prompts-page
---
# GPT Image 2.5 提示词档案
<GptImagePromptArchive />In the component, declare ArchiveRecord and ArchiveData types matching Task 1. Fetch withBase("/gpt-image-prompts.json") in onMounted, derive distinct sources, filter with an activeSource ref, show an image card per record, and use a <details> block for the exact prompt plus a 复制提示词 button. Copy errors must set visible, nonfatal status text. External source links require target="_blank" rel="noopener". Keep all component styles scoped, responsive, and consistent with existing VitePress variables.
Register GptImagePromptArchive in theme/index.ts in the same enhanceApp block as GithubProjects.
- [ ] Step 4: Run the focused page contract test.
Run: node --test tests/gpt-image-prompts-page.test.mjs
Expected: pass.
- [ ] Step 5: Commit the reader experience.
bash
git add docs/.vitepress/theme/components/GptImagePromptArchive.vue docs/gpt-image-prompts/index.md docs/.vitepress/theme/index.ts tests/gpt-image-prompts-page.test.mjs
git commit -m "feat: add GPT image prompt archive page"Task 4: Wire discovery navigation and build verification
Files:
Modify:
tests/gpt-image-prompts-page.test.mjsModify:
docs/.vitepress/config.tsModify:
docs/.vitepress/theme/style.css[ ] Step 1: Extend the failing contract test for discoverability.
js
test("GPT image prompt archive is reachable from navigation and its own sidebar", () => {
const config = readFileSync(path.join(root, "docs", ".vitepress", "config.ts"), "utf8");
assert.match(config, /text: "GPT Image 2\.5", link: "\/gpt-image-prompts\/"/);
assert.match(config, /"\/gpt-image-prompts\/"/);
assert.match(config, /text: "提示词图鉴", link: "\/gpt-image-prompts\/"/);
});- [ ] Step 2: Run the focused test and verify it fails on missing navigation.
Run: node --test tests/gpt-image-prompts-page.test.mjs
Expected: failure matching the absent navigation text.
- [ ] Step 3: Add navigation, sidebar, and motion parity.
Add { text: "GPT Image 2.5", link: "/gpt-image-prompts/" } to the primary nav array. Add a "/gpt-image-prompts/" sidebar section titled GPT Image 2.5 提示词档案 whose sole item is 提示词图鉴. Add .vp-doc .GptImagePromptArchive to both the normal and reduced-motion selector groups in style.css, so the new component follows the existing entry-motion accessibility policy.
- [ ] Step 4: Run focused test and full static-site build.
Run: node --test tests/gpt-image-prompt-archive.test.mjs tests/gpt-image-prompts-page.test.mjs && npm run build
Expected: every listed test passes and VitePress exits with status 0.
- [ ] Step 5: Commit site integration.
bash
git add docs/.vitepress/config.ts docs/.vitepress/theme/style.css tests/gpt-image-prompts-page.test.mjs
git commit -m "feat: expose GPT image prompt archive"Task 5: Perform the first agent-mediated archive update
Files:
Create:
docs/public/gpt-image-prompts/<record-id>.png(one file per successfully generated prompt)Modify:
docs/public/gpt-image-prompts.jsonCreate:
tmp/gpt-image-prompts-initial-manifest.json(temporary; delete before commit)[ ] Step 1: Read the current source session and extract only final prompts.
Open https://chatgpt.com/share/6aaea0e7-faf4-83ea-8342-c4b131ac1713 in Codex browser tooling. Extract only the final reverse-engineered prompts; exclude reference images, analysis, conversational filler, and duplicates. If the public page does not return visible conversation text, stop this task without altering the archive and ask the user to reopen/re-share it or provide an exported text transcript.
- [ ] Step 2: Generate each new candidate with Codex’s built-in image generator.
For each extracted final prompt, invoke the built-in image generator once using the prompt verbatim. Request 1536x1024 at high quality. Inspect each result for a nonempty image asset, then copy the selected image into docs/public/gpt-image-prompts/<record-id>.png; do not overwrite an existing file.
- [ ] Step 3: Write and dry-run the temporary manifest.
json
{
"records": [
{
"prompt": "<verbatim extracted final prompt>",
"sourceUrl": "https://chatgpt.com/share/6aaea0e7-faf4-83ea-8342-c4b131ac1713",
"sourceTitle": "图片逆向提示词-1",
"image": "gpt-image-prompts/<record-id>.png",
"createdAt": "2026-09-19T00:00:00.000Z"
}
]
}Run: npm run register-gpt-image-prompts -- --manifest tmp/gpt-image-prompts-initial-manifest.json --dry-run
Expected: output reports the number of newly added and skipped records; docs/public/gpt-image-prompts.json is unchanged.
- [ ] Step 4: Register assets and verify record-to-image correspondence.
Run: npm run register-gpt-image-prompts -- --manifest tmp/gpt-image-prompts-initial-manifest.json && node --input-type=module -e 'import fs from "node:fs"; const data=JSON.parse(fs.readFileSync("docs/public/gpt-image-prompts.json","utf8")); for (const record of data.records) { if (!fs.existsSync(docs/public${record.image})) throw new Error(missing ${record.image}); } console.log(${data.records.length} records have images)'
Expected: writer succeeds and every published record resolves to a local image. Delete the temporary manifest after this verification.
- [ ] Step 5: Build, inspect, and commit the initial data.
Run: npm run build && git status --short
Expected: build exits with status 0; staged changes contain only the intended image assets and archive JSON, never tmp/, .superpowers/, credentials, or unrelated promo-video/ files.
bash
git add docs/public/gpt-image-prompts.json docs/public/gpt-image-prompts
git commit -m "content: add initial GPT image prompt archive"Plan self-review
- Spec coverage: Tasks 1–2 implement source-scoped IDs, no-key operation boundary, completed-image-only registration, failures, and dry runs. Tasks 3–4 implement the filterable visual catalog, full prompt view/copy, source links, navigation, sidebar, and build coverage. Task 5 defines the Codex-only extraction/generation workflow and preserves the no-reference-image boundary.
- Placeholder scan: No implementation task depends on undefined code names. The placeholder-looking fields in Task 5 are a manifest template deliberately replaced with the actual extracted prompt and stable ID only after the source is available; they are not production code or an unimplemented requirement.
- Type consistency:
ArchiveRecord,buildRecord,mergeRecords,updateArchive,sourceUrl,sourceTitle,image,model,size,quality, andcreatedAtuse the same spellings throughout.