[{"data":1,"prerenderedAt":2982},["ShallowReactive",2],{"search-projects":3,"search-posts":85},[4,16,27,37,48,58,67,75],{"path":5,"title":6,"description":7,"tech":8,"tags":11,"image":14,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Fcodex-img","codex-img","Generate and edit images from the command line with your ChatGPT\u002FCodex subscription. No API key, and no agent in between.",[9,10],"rust","python",[12,13],"ai","cli","\u002Fimg\u002Fproducts\u002Fcodex-img.webp",null,{"path":17,"title":18,"description":19,"tech":20,"tags":24,"image":26,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Fusage-window-starter","Usage Window Starter","A macOS menu bar app that starts your next Claude Code and Codex 5-hour session the moment the old one resets.",[21,22,23],"go","typescript","electrobun",[12,25],"macos","\u002Fimg\u002Fproducts\u002Fusage-window-starter.webp",{"path":28,"title":29,"description":30,"tech":31,"tags":34,"image":36,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Fcamera-video-converter","Camera Video Converter","Free, offline Android app that turns hard-to-share camera footage into standard 1080p MP4s, right on the phone.",[32,33],"kotlin","android",[35],"offline-first","\u002Fimg\u002Fproducts\u002Fcamera-video-converter.webp",{"path":38,"title":39,"description":40,"tech":41,"tags":44,"image":45,"imageFit":46,"coverLabel":47},"\u002Fprojects\u002Fopen-claude-agent-sdk","open-claude-agent-sdk","Drop-in, open-source replacement for @anthropic-ai\u002Fclaude-agent-sdk. Readable TypeScript that runs on the Claude CLI you already have installed.",[22,42,43],"bun","mcp",[12],"\u002Fimg\u002Fproducts\u002Fopen-claude-agent-sdk-home.webp","contain","",{"path":49,"title":50,"description":51,"tech":52,"tags":55,"image":57,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Fyoutube-shorts-blocker","YouTube Shorts Blocker","A lightweight Chrome extension that hides YouTube Shorts everywhere and can redirect Shorts links.",[53,54],"javascript","chrome-extension",[56,35],"privacy","\u002Fimg\u002Fproducts\u002Fyoutube-shorts-blocker.webp",{"path":59,"title":60,"description":61,"tech":62,"tags":65,"image":66,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Fsub2utf","sub2utf","Converts subtitle files (.srt) from legacy encodings to UTF-8 so Plex shows č, ć, š, đ and ž correctly.",[63,64],"svelte","tauri",[],"\u002Fimg\u002Fproducts\u002Fsub2utf.webp",{"path":68,"title":69,"description":70,"tech":71,"tags":74,"image":15,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Fdocument-iteration-skill","document-iteration-skill","A lightweight markdown syntax for editing documents with Claude: leave feedback inline instead of asking it to regenerate the whole document.",[72,73],"markdown","claude",[12],{"path":76,"title":77,"description":78,"tech":79,"tags":82,"image":84,"imageFit":15,"coverLabel":15},"\u002Fprojects\u002Flitrpgvault","LitRPG Vault","A database of LitRPG and Progression Fantasy books: 250+ series with ratings, reading orders and audiobook links.",[80,81],"vue","nuxt",[83],"static","\u002Fimg\u002Fproducts\u002Flitrpgvault.webp",[86,1125,1681,2178,2411,2654,2919],{"path":87,"title":88,"description":89,"date":90,"tags":91,"body":94},"\u002Fblog\u002Fcodex-img-0-8-0","codex-img 0.7 and 0.8 add camera angles, exact palettes and a way to review images","Frame shapes that the backend actually follows, camera presets for game art, named styles and characters, palettes snapped to exact colours, then projects with a history of every run, comments that become edits, reruns, and batch versions that are never lost.","2026-10-04",[12,92,73,93],"codex","open source",{"type":95,"value":96,"toc":1106},"minimark",[97,108,121,130,140,173,179,191,195,202,207,239,248,253,259,263,274,398,476,479,483,494,544,548,562,567,576,580,587,592,599,603,606,613,661,666,669,672,677,683,704,709,713,719,727,734,738,753,757,773,786,790,793,819,832,836,839,887,894,916,921,924,928,931,1008,1027,1033,1037,1051,1055,1080,1084,1102],[98,99,100,107],"p",{},[101,102,106],"a",{"href":103,"rel":104},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img\u002Freleases\u002Ftag\u002Fv0.7.0",[105],"nofollow","codex-img 0.7"," is about the parts of a prompt that kept being written again for every image: the shape, the camera angle, the art style, what a character looks like, and which colours to use. Each one now has an option, and most of them have presets that a project defines once. Claude Code built it, and tested every option on real requests, about 70 images in total, before writing down what worked. Every claim about 0.7 below comes from those tests.",[98,109,110,115,116,120],{},[101,111,114],{"href":112,"rel":113},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img\u002Freleases\u002Ftag\u002Fv0.8.0",[105],"0.8"," is about what happens after an image is made: looking at it, saying what's wrong, and getting a better version without losing the one you had. It's ",[101,117,119],{"href":118},"#projects-keep-a-record-of-every-run","further down",".",[122,123,125,126],"h2",{"id":124},"the-prompt-sets-the-shape-not-size","The prompt sets the shape, not ",[127,128,129],"code",{},"--size",[98,131,132,133,135,136,139],{},"Earlier versions passed ",[127,134,129],{}," to the backend and documented it as a hint. In tests it wasn't even that: a portrait and a landscape request came back at the same 1312x1199. What does set the shape is a sentence at the start of the prompt, which ",[127,137,138],{},"-a"," adds:",[141,142,146],"pre",{"className":143,"code":144,"language":145,"meta":47,"style":47},"language-sh shiki shiki-themes github-light github-dark","codex-img \"a red apple on a wooden table\" -a 16:9 -o apple.png\n","sh",[127,147,148],{"__ignoreMap":47},[149,150,153,156,160,164,167,170],"span",{"class":151,"line":152},"line",1,[149,154,6],{"class":155},"sScJk",[149,157,159],{"class":158},"sZZnC"," \"a red apple on a wooden table\"",[149,161,163],{"class":162},"sj4cs"," -a",[149,165,166],{"class":158}," 16:9",[149,168,169],{"class":162}," -o",[149,171,172],{"class":158}," apple.png\n",[174,175],"figure-image",{"alt":176,"small":177,"src":178},"Four photos of the same red apple on a wooden table, each drawn at its real proportions. The first two, requested with -s 1024x1536 and -s 1536x1024, both came back almost square at 1312x1199. The third, requested with -a 2:3, is a tall 1024x1536, and the fourth, with -a 16:9, a wide 1672x941","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Faspect-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Faspect.webp",[98,180,181,182,185,186,120],{},"The backend keeps about 1.57 megapixels whatever the ratio, so 2:3 lands on exactly 1024x1536. codex-img warns when an image comes back more than 2% off the ratio, and refuses ratios past 3:1, which the backend clamps. It works on edits too: a 2:3 photo edited with ",[127,183,184],{},"-a 16:9"," came back 16:9, with the scene widened around the subject. The idea and the pixel counts came from ",[101,187,190],{"href":188,"rel":189},"https:\u002F\u002Fgithub.com\u002Fjdmnk\u002Fcodex-imagegen-cli\u002Fpull\u002F21",[105],"a pull request to another tool",[122,192,194],{"id":193},"camera-angles","Camera angles",[98,196,197,198,201],{},"The racing game's prompts had the camera angle written into every one, in a dozen slightly different ways. Some said \"front view at a slight angle\", which shows a little of the top surface, and in a game with a low camera that looks like the ground sloping up behind the building. ",[127,199,200],{},"--view"," replaces all of that with five built-in angles:",[174,203],{"alt":204,"small":205,"src":206},"A car, a cottage and an oak tree, each drawn in five views. Side shows the car's profile and a flat elevation of the cottage. Front shows the car's nose and the cottage's front wall. Top-down shows the car's roof from directly above and the cottage almost entirely as roof, with a sliver of its front wall. Three-quarter looks down at the front of each, with the roof filling the top half. Isometric turns the car and the cottage 45 degrees, showing the top and two sides","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fviews-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fviews.webp",[208,209,210,221,227,233],"ul",{},[211,212,213,216,217,220],"li",{},[127,214,215],{},"side"," and ",[127,218,219],{},"front"," are flat elevations with no top surface and a straight bottom edge, for pseudo-3D roadsides and side-scrollers.",[211,222,223,226],{},[127,224,225],{},"top-down"," looks straight down. Cars and trees come out as clean plan views, but buildings keep a sliver of their front wall.",[211,228,229,232],{},[127,230,231],{},"three-quarter"," looks down at about 45° with the front square to the camera, like a classic RPG.",[211,234,235,238],{},[127,236,237],{},"isometric"," turns the subject 45° and shows the top and two sides.",[98,240,241,242,244,245,247],{},"Two of them needed rewording before they worked. The first ",[127,243,231],{}," turned the cottage 45°, which is isometric, and the first ",[127,246,225],{}," gave a front view under a big roof:",[174,249],{"alt":250,"small":251,"src":252},"The same cottage generated with two wordings of each view. Under three-quarter, the first wording turned the cottage 45 degrees like an isometric drawing, and the built-in wording keeps its front facing the camera with the roof above. Under top-down, the first wording showed mostly the front wall under the roof, and the built-in wording shows mostly roof","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fview-wording-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fview-wording.webp",[98,254,255,256,258],{},"One more change came from making the picture above. The ",[127,257,231],{}," wording said \"as in 16-bit RPGs\", and twice in a row that turned the car into pixel art: a camera preset was also setting the style. 0.7.1 drops the phrase, and the car in that column was made with the new wording.",[122,260,262],{"id":261},"presets-a-project-defines-once","Presets a project defines once",[98,264,265,266,269,270,273],{},"Views, styles, characters and palettes are all presets. A project keeps its own in ",[127,267,268],{},"codex-img.json",", and ",[127,271,272],{},"codex-img presets"," lists everything available and where each one is defined:",[141,275,279],{"className":276,"code":277,"language":278,"meta":47,"style":47},"language-json shiki shiki-themes github-light github-dark","{\n  \"views\": {\"roadside\": \"seen straight on from the side at eye level, its bottom edge a straight line\"},\n  \"styles\": {\"harbour\": {\"text\": \"bright cartoon harbour game art, bold colours\", \"refs\": [\"refs\u002Fboat.png\"]}},\n  \"characters\": {\"captain\": {\"text\": \"a stocky walrus sea captain with big tusks, in a yellow raincoat\", \"refs\": [\"art\u002Fcaptain.png\"]}},\n  \"palettes\": {\"harbour\": \"#2B1D14 #6B3E26 #C7743A #F2C14E #F7EBD0 #3B6E5A\"}\n}\n","json",[127,280,281,287,308,344,375,393],{"__ignoreMap":47},[149,282,283],{"class":151,"line":152},[149,284,286],{"class":285},"sVt8B","{\n",[149,288,290,293,296,299,302,305],{"class":151,"line":289},2,[149,291,292],{"class":162},"  \"views\"",[149,294,295],{"class":285},": {",[149,297,298],{"class":162},"\"roadside\"",[149,300,301],{"class":285},": ",[149,303,304],{"class":158},"\"seen straight on from the side at eye level, its bottom edge a straight line\"",[149,306,307],{"class":285},"},\n",[149,309,311,314,316,319,321,324,326,329,332,335,338,341],{"class":151,"line":310},3,[149,312,313],{"class":162},"  \"styles\"",[149,315,295],{"class":285},[149,317,318],{"class":162},"\"harbour\"",[149,320,295],{"class":285},[149,322,323],{"class":162},"\"text\"",[149,325,301],{"class":285},[149,327,328],{"class":158},"\"bright cartoon harbour game art, bold colours\"",[149,330,331],{"class":285},", ",[149,333,334],{"class":162},"\"refs\"",[149,336,337],{"class":285},": [",[149,339,340],{"class":158},"\"refs\u002Fboat.png\"",[149,342,343],{"class":285},"]}},\n",[149,345,347,350,352,355,357,359,361,364,366,368,370,373],{"class":151,"line":346},4,[149,348,349],{"class":162},"  \"characters\"",[149,351,295],{"class":285},[149,353,354],{"class":162},"\"captain\"",[149,356,295],{"class":285},[149,358,323],{"class":162},[149,360,301],{"class":285},[149,362,363],{"class":158},"\"a stocky walrus sea captain with big tusks, in a yellow raincoat\"",[149,365,331],{"class":285},[149,367,334],{"class":162},[149,369,337],{"class":285},[149,371,372],{"class":158},"\"art\u002Fcaptain.png\"",[149,374,343],{"class":285},[149,376,378,381,383,385,387,390],{"class":151,"line":377},5,[149,379,380],{"class":162},"  \"palettes\"",[149,382,295],{"class":285},[149,384,318],{"class":162},[149,386,301],{"class":285},[149,388,389],{"class":158},"\"#2B1D14 #6B3E26 #C7743A #F2C14E #F7EBD0 #3B6E5A\"",[149,391,392],{"class":285},"}\n",[149,394,396],{"class":151,"line":395},6,[149,397,392],{"class":285},[141,399,401],{"className":143,"code":400,"language":145,"meta":47,"style":47},"codex-img presets add character captain --ref art\u002Fcaptain.png --text \"a stocky walrus sea captain ...\"\ncodex-img presets promote character captain     # copy it to the global presets, for every project\ncodex-img \"the captain waving from a pier\" --character captain --view side -a 3:2 -o wave.png\n",[127,402,403,431,448],{"__ignoreMap":47},[149,404,405,407,410,413,416,419,422,425,428],{"class":151,"line":152},[149,406,6],{"class":155},[149,408,409],{"class":158}," presets",[149,411,412],{"class":158}," add",[149,414,415],{"class":158}," character",[149,417,418],{"class":158}," captain",[149,420,421],{"class":162}," --ref",[149,423,424],{"class":158}," art\u002Fcaptain.png",[149,426,427],{"class":162}," --text",[149,429,430],{"class":158}," \"a stocky walrus sea captain ...\"\n",[149,432,433,435,437,440,442,444],{"class":151,"line":289},[149,434,6],{"class":155},[149,436,409],{"class":158},[149,438,439],{"class":158}," promote",[149,441,415],{"class":158},[149,443,418],{"class":158},[149,445,447],{"class":446},"sJ8bj","     # copy it to the global presets, for every project\n",[149,449,450,452,455,458,460,463,466,468,471,473],{"class":151,"line":310},[149,451,6],{"class":155},[149,453,454],{"class":158}," \"the captain waving from a pier\"",[149,456,457],{"class":162}," --character",[149,459,418],{"class":158},[149,461,462],{"class":162}," --view",[149,464,465],{"class":158}," side",[149,467,163],{"class":162},[149,469,470],{"class":158}," 3:2",[149,472,169],{"class":162},[149,474,475],{"class":158}," wave.png\n",[98,477,478],{},"A batch spec can define its own too, and then the spec wins over the project, the project over global presets, and those over the built-ins. Defining a preset with a built-in name replaces the built-in one.",[122,480,482],{"id":481},"what-actually-gets-sent","What actually gets sent",[98,484,485,486,489,490,493],{},"Every option that adds to the prompt adds a sentence in a fixed place, and ",[127,487,488],{},"--json"," shows the whole result as ",[127,491,492],{},"submittedPrompt",". In order:",[495,496,497,503,509,524,529,532,538],"ol",{},[211,498,499,502],{},[127,500,501],{},"--aspect 3:2",": “The frame must be in 3:2 landscape format, wider than it is tall.”",[211,504,505,508],{},[127,506,507],{},"--view side",": “Camera: seen perfectly straight on from the side at eye level: a flat side elevation…”",[211,510,511,512,515,516,519,520,523],{},"One line per reference image, numbered after any ",[127,513,514],{},"-i"," images. ",[127,517,518],{},"--character captain"," adds “Image 1: character reference for \"captain\": keep the same character (face, proportions, outfit, colours) in a new pose and scene.”, and ",[127,521,522],{},"--composition-ref"," adds “Image 2: composition reference only: follow its layout and framing, not its subject or style.”",[211,525,526,528],{},[127,527,518],{},": “The character \"captain\": a stocky walrus sea captain with big tusks…”",[211,530,531],{},"Your prompt: “The captain waving from the end of a pier.”",[211,533,534,537],{},[127,535,536],{},"--style harbour",": the style's text, “Bright cartoon harbour game art, bold colours.”",[211,539,540,543],{},[127,541,542],{},"--palette harbour",": “Use only these 6 colours, exactly, and no others: #2B1D14, #6B3E26…”",[122,545,547],{"id":546},"reference-images-with-a-role","Reference images with a role",[98,549,550,552,553,331,556,216,558,561],{},[127,551,514],{}," sends an image without saying what it's for, so the prompt had to explain it. Three new options say it for you: ",[127,554,555],{},"--style-ref",[127,557,522],{},[127,559,560],{},"--character-ref"," each add a line telling the model how to use that image.",[174,563],{"alt":564,"small":565,"src":566},"Three rows, each a reference image, an arrow labelled with the option and the prompt, and the result. A street lamp drawn with flat colours and dark outlines, used with --style-ref and \"a red fox sitting\", gave a fox in the same flat outlined style. An isometric cottage used with --composition-ref and \"a glass greenhouse\" gave a greenhouse at the same isometric angle, even with a chimney and front steps. A photo of an apple on a wooden table used with --style-ref gave a photo of a fox on that same table","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Freferences-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Freferences.webp",[98,568,569,570,572,573,575],{},"The last row is the catch. Any input image makes the request an edit, so the result takes the reference's frame and tends to keep its setting: the fox ended up on the apple's table. Describe the new background, and pass ",[127,571,138],{}," for another shape. In two A\u002FB tests, the labels made no visible difference compared with a plain ",[127,574,514],{},". They're there so that several images can't be confused.",[122,577,579],{"id":578},"the-same-character-in-every-scene","The same character in every scene",[98,581,582,583,586],{},"A character preset is an image of the character and a text describing them. The text should describe only their looks. The first version of ",[127,584,585],{},"presets add --from"," took the anchor image's whole prompt as the text, and that prompt said \"isolated on a transparent background\". Every scene with the captain then faded to transparent at the edges:",[174,588],{"alt":589,"small":590,"src":591},"Three images of the same cartoon walrus captain in a yellow raincoat and a white captain's hat. On the left, the anchor image on a transparent background. In the middle, the captain steering a ship's wheel in a storm, with the scene fading out at the edges. On the right, the same storm scene filling the frame","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fcharacter-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fcharacter.webp",[98,593,594,595,598],{},"With only his looks as text, the scene fills the frame and the captain keeps his face, coat and hat. ",[127,596,597],{},"--from"," now takes the image only.",[122,600,602],{"id":601},"palettes-with-exact-colours","Palettes with exact colours",[98,604,605],{},"A game with a fixed palette needs every pixel in it. In tests, asking for that in words didn't work: \"a limited palette of 8 colours\" changed nothing, and naming a palette without its colours (\"PICO-8\") put 8% of pixels near it. Listing the hex codes worked much better, with 81–88% of pixels within a small distance of a palette colour. Still, every image had 9,000 to 14,000 distinct colours.",[98,607,608,609,612],{},"So ",[127,610,611],{},"--palette"," does both. It adds the hex codes to the prompt, and then it snaps every colour to the nearest palette colour and writes a palette PNG with exactly those colours:",[141,614,616],{"className":143,"code":615,"language":145,"meta":47,"style":47},"codex-img \"a treasure chest sprite\" --palette '#2B1D14,#6B3E26,#C7743A,#F2C14E,#F7EBD0,#3B6E5A' -b transparent -o chest.png\ncodex-img \"a treasure chest sprite\" --palette pico-8 -b transparent -o chest.png\n",[127,617,618,642],{"__ignoreMap":47},[149,619,620,622,625,628,631,634,637,639],{"class":151,"line":152},[149,621,6],{"class":155},[149,623,624],{"class":158}," \"a treasure chest sprite\"",[149,626,627],{"class":162}," --palette",[149,629,630],{"class":158}," '#2B1D14,#6B3E26,#C7743A,#F2C14E,#F7EBD0,#3B6E5A'",[149,632,633],{"class":162}," -b",[149,635,636],{"class":158}," transparent",[149,638,169],{"class":162},[149,640,641],{"class":158}," chest.png\n",[149,643,644,646,648,650,653,655,657,659],{"class":151,"line":289},[149,645,6],{"class":155},[149,647,624],{"class":158},[149,649,627],{"class":162},[149,651,652],{"class":158}," pico-8",[149,654,633],{"class":162},[149,656,636],{"class":158},[149,658,169],{"class":162},[149,660,641],{"class":158},[174,662],{"alt":663,"small":664,"src":665},"The same treasure chest prompt with four palettes, each with its colours shown below it: six warm browns and golds with a teal, the four Game Boy greens, the sixteen PICO-8 colours, which turned the chest into pixel art, and the sixty-four colours of Resurrect 64","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fpalettes-chest-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fpalettes-chest.webp",[98,667,668],{},"The model designs around the colours it gets: the Game Boy chest is all greens, and the PICO-8 one became pixel art without being asked. After the snap, these images look almost the same as before it, because the model already kept close to the palette. With 64 colours, the prompt mattered less (33–49% of pixels close), but a 64-colour palette covers most shades, so the snap still looks right.",[98,670,671],{},"Art that wasn't made with the palette is harder. Its shading falls between palette colours, and PICO-8 has no dark brown:",[174,673],{"alt":674,"small":675,"src":676},"Three close-ups of the corner of a wooden treasure chest. As generated, the wood is smooth brown. Snapped with --palette pico-8, the wood turns into purple streaks with dark blue speckles. With --palette-clean added, the same wood is a flat neutral grey with clean edges","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fpalette-clean-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fpalette-clean.webp",[98,678,679,682],{},[127,680,681],{},"--palette-clean"," first reduces the image to twice the palette's colours, then matches hue before lightness and removes stray pixels. The brown becomes the palette's neutral grey instead of purple. It's off by default, because on art generated with the palette it changes more than it needs to.",[98,684,685,686,691,692,695,696,699,700,703],{},"There are 14 built-in palettes, taken from ",[101,687,690],{"href":688,"rel":689},"https:\u002F\u002Flospec.com\u002Fpalette-list",[105],"Lospec",". ",[127,693,694],{},"presets add palette"," reads your own from hex codes, a GIMP ",[127,697,698],{},".gpl"," or ",[127,701,702],{},".hex"," file, or a swatch image:",[174,705],{"alt":706,"small":707,"src":708},"The fourteen built-in palettes as rows of colour swatches: pico-8 with 16 colours, game-boy with 4, nes with 55, c64 with 16, zx-spectrum with 15, cga with 4, ega with 16, ega-64 with 64, sweetie-16 with 16, dawnbringer-16 and dawnbringer-32, endesga-32 with 32, and resurrect-64 and aap-64 with 64 each","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fpalettes-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-7-0\u002Fpalettes.webp",[122,710,712],{"id":711},"a-record-of-how-each-image-was-made","A record of how each image was made",[98,714,715,718],{},[127,716,717],{},"batch"," now writes a manifest beside every raw image, with the prompt that was sent, the presets and where they came from, every input image with a fingerprint, and what the backend reported. When the spec changes later, the asset's line says so:",[141,720,725],{"className":721,"code":723,"language":724},[722],"language-text","skip   generate crane (raw image exists)\nskip   generate captain-wave (raw image exists; note: changed since its raw image was generated, delete it to re-roll)\n","text",[127,726,723],{"__ignoreMap":47},[98,728,729,730,733],{},"It never generates the image again by itself, because that spends quota. The same record is written for single images with ",[127,731,732],{},"--manifest",". There's no seed, so it can't recreate the exact pixels, but it shows what to change.",[122,735,737],{"id":736},"also-in-07","Also in 0.7",[208,739,740,743,746],{},[211,741,742],{},"The Agent Skill is half its old size. It keeps what every image request needs, and points to separate references for game art, converting images and the Python fallback, so an agent making an icon doesn't read about sprite pipelines.",[211,744,745],{},"The prompt guide took in what applies from the image skill that ships with Codex: a labelled-line format for longer prompts, how much detail to add to a vague request, and recipes for slides, wireframes and character consistency.",[211,747,748,749,752],{},"A review of the release found four problems, all fixed before it shipped. A batch could overwrite a reference image with its own output, two presets could share a file that removing one of them deleted, the Python fallback sent ",[127,750,751],{},"presets"," as a prompt, and changing an asset's background didn't count as a change.",[122,754,756],{"id":755},"projects-keep-a-record-of-every-run","Projects keep a record of every run",[98,758,759,760,762,763,766,767,770,771,120],{},"A project is a folder with a ",[127,761,268],{},", the file that already holds its presets, and ",[127,764,765],{},"codex-img init"," creates an empty one. Every run inside it gets its own file in ",[127,768,769],{},".codex-img\u002Fruns\u002F",", one line per event: what was asked for, the backend's progress, where the image was saved, and why a job failed. Paths in it are relative to the project, so the folder can be moved or committed. Every image generated in a project also gets the manifest from 0.7, without ",[127,772,732],{},[98,774,775,776,779,780,699,782,785],{},"Each run also records whether it called the backend, so the images that used quota can be counted from these files, separately from local work like conversion and contact sheets. ",[127,777,778],{},"\"events\": false"," in ",[127,781,268],{},[127,783,784],{},"CODEX_IMG_EVENTS=off"," turns the run files off, and manifests are still written, because the commands below read them.",[122,787,789],{"id":788},"run-the-same-request-again","Run the same request again",[98,791,792],{},"The backend has no seed, so an image can't be made again exactly. The next best thing is sending the same request:",[141,794,796],{"className":143,"code":795,"language":145,"meta":47,"style":47},"codex-img rerun art\u002Ffox.png -n 3 -o art\u002Fvariations\u002F\n",[127,797,798],{"__ignoreMap":47},[149,799,800,802,805,808,811,814,816],{"class":151,"line":152},[149,801,6],{"class":155},[149,803,804],{"class":158}," rerun",[149,806,807],{"class":158}," art\u002Ffox.png",[149,809,810],{"class":162}," -n",[149,812,813],{"class":162}," 3",[149,815,169],{"class":162},[149,817,818],{"class":158}," art\u002Fvariations\u002F\n",[98,820,821,824,825,828,829,120],{},[127,822,823],{},"rerun"," reads the image's manifest and sends the prompt that was actually submitted, with its references and settings, then converts the result the way the original was converted. It uses the old preset text even if the preset has changed since. A reference image that has changed since stops it before any quota is spent, and ",[127,826,827],{},"--anyway"," sends it anyway. The new images record the original as their ",[127,830,831],{},"parent",[122,833,835],{"id":834},"comments-that-become-edits","Comments that become edits",[98,837,838],{},"Reviewing a set of images used to mean keeping notes somewhere else. Now a comment or a star goes into the image's manifest, or onto the asset in a batch spec:",[141,840,842],{"className":143,"code":841,"language":145,"meta":47,"style":47},"codex-img comments art\u002Fshroom.png --set \"Give it a small green leaf on top of the cap\"\ncodex-img stars art\u002Fhero.png --set true\ncodex-img comments --json        # every comment in the project\n",[127,843,844,860,875],{"__ignoreMap":47},[149,845,846,848,851,854,857],{"class":151,"line":152},[149,847,6],{"class":155},[149,849,850],{"class":158}," comments",[149,852,853],{"class":158}," art\u002Fshroom.png",[149,855,856],{"class":162}," --set",[149,858,859],{"class":158}," \"Give it a small green leaf on top of the cap\"\n",[149,861,862,864,867,870,872],{"class":151,"line":289},[149,863,6],{"class":155},[149,865,866],{"class":158}," stars",[149,868,869],{"class":158}," art\u002Fhero.png",[149,871,856],{"class":162},[149,873,874],{"class":162}," true\n",[149,876,877,879,881,884],{"class":151,"line":310},[149,878,6],{"class":155},[149,880,850],{"class":158},[149,882,883],{"class":162}," --json",[149,885,886],{"class":446},"        # every comment in the project\n",[98,888,889,890,893],{},"Only that field is changed, and the rest of the file keeps its layout, so in a hand-written spec under git the change is one line. ",[127,891,892],{},"refine"," turns the comment into an edit of the saved image:",[141,895,897],{"className":143,"code":896,"language":145,"meta":47,"style":47},"codex-img refine art\u002Fshroom.png --from-comment -o art\u002Fshroom-leaf.png\n",[127,898,899],{"__ignoreMap":47},[149,900,901,903,906,908,911,913],{"class":151,"line":152},[149,902,6],{"class":155},[149,904,905],{"class":158}," refine",[149,907,853],{"class":158},[149,909,910],{"class":162}," --from-comment",[149,912,169],{"class":162},[149,914,915],{"class":158}," art\u002Fshroom-leaf.png\n",[174,917],{"alt":918,"small":919,"src":920},"Two pixel-art mushroom creatures with a red cap with white spots and big black eyes. The first is the original, with the comment \"Give it a small green leaf on top of the cap\". The second, made with codex-img refine --from-comment, is the same mushroom with a small green leaf on top of its cap","\u002Fimg\u002Fblog\u002Fcodex-img-0-8-0\u002Frefine-small.webp","\u002Fimg\u002Fblog\u002Fcodex-img-0-8-0\u002Frefine.webp",[98,922,923],{},"It sends \"Change only: … Keep everything else exactly as it is.\" with the image, and keeps the image's presets and settings. If a preset has changed since the image was made, it warns and names it, because the edit will use the new text. The comment is cleared only after the new image and its manifest are saved, so a failed edit keeps it.",[122,925,927],{"id":926},"batch-versions-that-arent-lost","Batch versions that aren't lost",[98,929,930],{},"In 0.7, re-rolling a batch asset meant deleting its raw image. Now there's an option for it, and the old image is kept:",[141,932,934],{"className":143,"code":933,"language":145,"meta":47,"style":47},"codex-img batch art\u002Fassets.json --reroll hero --dry-run   # what it would cost\ncodex-img batch art\u002Fassets.json --reroll hero --expect-images 1\ncodex-img batch art\u002Fassets.json --restore hero .codex-img\u002Fhistory\u002Fhero\u002F\u003Cversion>.png\n",[127,935,936,958,976],{"__ignoreMap":47},[149,937,938,940,943,946,949,952,955],{"class":151,"line":152},[149,939,6],{"class":155},[149,941,942],{"class":158}," batch",[149,944,945],{"class":158}," art\u002Fassets.json",[149,947,948],{"class":162}," --reroll",[149,950,951],{"class":158}," hero",[149,953,954],{"class":162}," --dry-run",[149,956,957],{"class":446},"   # what it would cost\n",[149,959,960,962,964,966,968,970,973],{"class":151,"line":289},[149,961,6],{"class":155},[149,963,942],{"class":158},[149,965,945],{"class":158},[149,967,948],{"class":162},[149,969,951],{"class":158},[149,971,972],{"class":162}," --expect-images",[149,974,975],{"class":162}," 1\n",[149,977,978,980,982,984,987,989,992,996,999,1002,1005],{"class":151,"line":310},[149,979,6],{"class":155},[149,981,942],{"class":158},[149,983,945],{"class":158},[149,985,986],{"class":162}," --restore",[149,988,951],{"class":158},[149,990,991],{"class":158}," .codex-img\u002Fhistory\u002Fhero\u002F",[149,993,995],{"class":994},"szBVR","\u003C",[149,997,998],{"class":158},"versio",[149,1000,1001],{"class":285},"n",[149,1003,1004],{"class":994},">",[149,1006,1007],{"class":158},".png\n",[98,1009,1010,1011,1014,1015,1018,1019,1022,1023,1026],{},"The current raw image stays in place until the new one is saved, and then moves to ",[127,1012,1013],{},".codex-img\u002Fhistory\u002Fhero\u002F",". If generating fails, nothing moves. ",[127,1016,1017],{},"--restore"," puts any version back, or any other image, and costs nothing. ",[127,1020,1021],{},"--expect-images"," stops before spending quota if the run would generate a different number of images than the dry run said, because a reference it depends on went missing in between, for example. Two batch runs on the same spec wait for each other, and ",[127,1024,1025],{},"--no-wait"," fails instead.",[98,1028,1029,1032],{},[127,1030,1031],{},"batch --inspect --json"," lists every asset with its comment, star, whether its spec has changed since it was generated, and its earlier versions. It needs no login.",[122,1034,1036],{"id":1035},"checking-a-spec-before-it-costs-anything","Checking a spec before it costs anything",[98,1038,1039,1040,1043,1044,1046,1047,1050],{},"A typo in a batch spec used to show up when the batch ran. ",[127,1041,1042],{},"codex-img check"," reads a spec or ",[127,1045,268],{}," with the same code that runs it, and lists every problem it finds: unknown fields, presets that don't exist, references that point at each other. It needs no login. The repository also has JSON Schemas for both files, and pointing ",[127,1048,1049],{},"$schema"," at them gives autocomplete in an editor.",[122,1052,1054],{"id":1053},"also-in-08","Also in 0.8",[208,1056,1057,1063,1073],{},[211,1058,1059,1062],{},[127,1060,1061],{},"--nearest"," resizes by copying pixels, for scaling pixel art up by a whole number, a 32×32 sprite to 96×96 for example.",[211,1064,1065,1068,1069,1072],{},[127,1066,1067],{},"convert --mask-out"," writes a black and white image of which pixels the background removal took out, and ",[127,1070,1071],{},"convert --json"," reports how many pixels hard alpha and background removal changed.",[211,1074,1075,1076,1079],{},"Local image processing (conversion, palettes, contact sheets and tiles) moved into a separate library with no network or login code, and ",[127,1077,1078],{},"convert","'s output is checked byte for byte against saved files.",[122,1081,1083],{"id":1082},"try-it","Try it",[98,1085,1086,1087,1091,1092,1095,1096,1098,1099,1101],{},"Download the binary for macOS, Linux or Windows from the ",[101,1088,1090],{"href":112,"rel":1089},[105],"release page",", and log in once with ",[127,1093,1094],{},"codex login"," using your ChatGPT account. Run ",[127,1097,272],{}," to see the views and palettes, and ",[127,1100,765],{}," in a game's folder to keep a record of its art. The Agent Skill in the repository tells Claude Code and Codex when to use each of them.",[1103,1104,1105],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":47,"searchDepth":289,"depth":289,"links":1107},[1108,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124],{"id":124,"depth":289,"text":1109},"The prompt sets the shape, not --size",{"id":193,"depth":289,"text":194},{"id":261,"depth":289,"text":262},{"id":481,"depth":289,"text":482},{"id":546,"depth":289,"text":547},{"id":578,"depth":289,"text":579},{"id":601,"depth":289,"text":602},{"id":711,"depth":289,"text":712},{"id":736,"depth":289,"text":737},{"id":755,"depth":289,"text":756},{"id":788,"depth":289,"text":789},{"id":834,"depth":289,"text":835},{"id":926,"depth":289,"text":927},{"id":1035,"depth":289,"text":1036},{"id":1053,"depth":289,"text":1054},{"id":1082,"depth":289,"text":1083},{"path":1126,"title":1127,"description":1128,"date":1129,"tags":1130,"body":1131},"\u002Fblog\u002Fcodex-img-0-6-0","codex-img 0.5 and 0.6 turn generated images into a game's finished art","Sprite trimming and resizing in the same command that generates the image, clean alpha edges, batch builds from a JSON spec, removal of painted-in backgrounds, seamless panoramas, contact sheets, output that no longer changes between runs, and Windows builds.","2026-09-30",[12,92,73,93],{"type":95,"value":1132,"toc":1670},[1133,1146,1150,1180,1183,1190,1233,1240,1244,1247,1253,1256,1260,1267,1512,1532,1542,1546,1557,1561,1564,1573,1579,1597,1601,1604,1610,1616,1620,1626,1630,1636,1653,1655,1667],[98,1134,1135,1139,1140,1145],{},[101,1136,6],{"href":1137,"rel":1138},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img",[105]," went from 0.4.0 to ",[101,1141,1144],{"href":1142,"rel":1143},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img\u002Freleases\u002Ftag\u002Fv0.6.4",[105],"0.6.4"," while I made the art for a small racing game, with Claude Code writing the prompts and running the tool. Generating went well, and images were usable on the first try. The work came after generating, in Python scripts that cropped, resized and cleaned up every image. Claude Code wrote down everything it had to build around codex-img, and these releases move it into the tool.",[122,1147,1149],{"id":1148},"from-prompt-to-sprite-in-one-command","From prompt to sprite in one command",[141,1151,1153],{"className":143,"code":1152,"language":145,"meta":47,"style":47},"codex-img - -b transparent --hard-alpha --trim=4 --resize 400x300 --no-enlarge -c 160 -o kart.png \u003C\u003C'EOF'\nGame asset sprite for a retro racing game: a small red go-kart with a driver\nin a white helmet and blue suit, seen from the side... 16-bit arcade pixel art...\nIsolated on a transparent background, no ground, no shadow, no text.\nEOF\n",[127,1154,1155,1160,1165,1170,1175],{"__ignoreMap":47},[149,1156,1157],{"class":151,"line":152},[149,1158,1159],{},"codex-img - -b transparent --hard-alpha --trim=4 --resize 400x300 --no-enlarge -c 160 -o kart.png \u003C\u003C'EOF'\n",[149,1161,1162],{"class":151,"line":289},[149,1163,1164],{},"Game asset sprite for a retro racing game: a small red go-kart with a driver\n",[149,1166,1167],{"class":151,"line":310},[149,1168,1169],{},"in a white helmet and blue suit, seen from the side... 16-bit arcade pixel art...\n",[149,1171,1172],{"class":151,"line":346},[149,1173,1174],{},"Isolated on a transparent background, no ground, no shadow, no text.\n",[149,1176,1177],{"class":151,"line":377},[149,1178,1179],{},"EOF\n",[98,1181,1182],{},"The backend returned a 1536x1024 PNG of 1.1 MB, and codex-img saved this 400x215 sprite of 13.7 KB. It also kept the untouched original, because there's no seed to generate it again.",[98,1184,1185],{},[1186,1187],"img",{"alt":1188,"src":1189},"A pixel-art sprite of a red go-kart with a driver in a white and red helmet and a blue suit, puffing smoke from its exhaust, on a transparent background","\u002Fimg\u002Fblog\u002Fcodex-img-0-5-0\u002Fkart.png",[208,1191,1192,1198,1221,1227],{},[211,1193,1194,1197],{},[127,1195,1196],{},"--trim"," crops to the visible pixels, ignoring the faint specks generated images scatter over the transparent background.",[211,1199,1200,1203,1204,331,1207,699,1210,269,1213,1216,1217,1220],{},[127,1201,1202],{},"--resize"," takes ",[127,1205,1206],{},"400x",[127,1208,1209],{},"x300",[127,1211,1212],{},"400x300",[127,1214,1215],{},"--fit"," decides what happens when the aspect ratio differs. ",[127,1218,1219],{},"--no-enlarge"," makes the size a maximum.",[211,1222,1223,1226],{},[127,1224,1225],{},"--hard-alpha"," makes every pixel fully solid or fully transparent. The backend never returns a fully solid pixel, which doesn't suit pixel art.",[211,1228,1229,1232],{},[127,1230,1231],{},"-c 160"," reduces the sprite to a 160-colour palette.",[98,1234,1235,1236,1239],{},"All of it also works in ",[127,1237,1238],{},"codex-img convert",", for images you already have, with no login and no quota.",[122,1241,1243],{"id":1242},"the-colour-under-transparent-pixels","The colour under transparent pixels",[98,1245,1246],{},"A fully transparent pixel still stores a colour, and generated images store a dark vignette with coloured glows there. On the right is the same kart with its alpha channel ignored:",[98,1248,1249],{},[1186,1250],{"alt":1251,"src":1252},"Two versions of the raw go-kart image. On the left it sits on a checkerboard, as an image viewer shows transparency. On the right the transparency is ignored, revealing a black background with red and blue-white glows around the kart and its smoke","\u002Fimg\u002Fblog\u002Fcodex-img-0-5-0\u002Fhidden.webp",[98,1254,1255],{},"When a game engine scales a texture without taking alpha into account, that colour bleeds into the edges as a dark outline. codex-img resizes with premultiplied alpha, and writes the nearest visible colour under every fully transparent pixel, as texture tools do.",[122,1257,1259],{"id":1258},"a-games-art-from-one-file","A game's art from one file",[98,1261,1262,1263,1266],{},"The game lists its art in a JSON file, and ",[127,1264,1265],{},"codex-img batch"," reads it directly:",[141,1268,1270],{"className":276,"code":1269,"language":278,"meta":47,"style":47},"{\n  \"raw_dir\": \"raw\",\n  \"out_dir\": \"..\u002Fpublic\u002Fassets\",\n  \"style\": \"16-bit arcade racing game pixel art, bold saturated colours, clean dark outlines, no text.\",\n  \"defaults\": {\"background\": \"transparent\", \"hard_alpha\": true, \"colors\": 160, \"trim\": true},\n  \"assets\": {\n    \"shared\u002Ftrees\u002Foak\": {\"prompt\": \"Game asset sprite: a single big old oak tree...\", \"size\": \"1536x1024\", \"max\": [420, 380]},\n    \"tracks\u002Fautumn\u002Fwindmill-full\": {\"prompt\": \"A wooden windmill...\", \"size\": \"1024x1536\", \"publish\": false},\n    \"tracks\u002Fautumn\u002Fwindmill-sails\": {\"prompt\": \"Edit this image: only the four lattice sails...\",\n                                     \"size\": \"1024x1024\", \"reference\": \"tracks\u002Fautumn\u002Fwindmill-full\", \"max\": [400, 400]}\n  }\n}\n",[127,1271,1272,1276,1289,1301,1313,1359,1367,1411,1447,1464,1501,1507],{"__ignoreMap":47},[149,1273,1274],{"class":151,"line":152},[149,1275,286],{"class":285},[149,1277,1278,1281,1283,1286],{"class":151,"line":289},[149,1279,1280],{"class":162},"  \"raw_dir\"",[149,1282,301],{"class":285},[149,1284,1285],{"class":158},"\"raw\"",[149,1287,1288],{"class":285},",\n",[149,1290,1291,1294,1296,1299],{"class":151,"line":310},[149,1292,1293],{"class":162},"  \"out_dir\"",[149,1295,301],{"class":285},[149,1297,1298],{"class":158},"\"..\u002Fpublic\u002Fassets\"",[149,1300,1288],{"class":285},[149,1302,1303,1306,1308,1311],{"class":151,"line":346},[149,1304,1305],{"class":162},"  \"style\"",[149,1307,301],{"class":285},[149,1309,1310],{"class":158},"\"16-bit arcade racing game pixel art, bold saturated colours, clean dark outlines, no text.\"",[149,1312,1288],{"class":285},[149,1314,1315,1318,1320,1323,1325,1328,1330,1333,1335,1338,1340,1343,1345,1348,1350,1353,1355,1357],{"class":151,"line":377},[149,1316,1317],{"class":162},"  \"defaults\"",[149,1319,295],{"class":285},[149,1321,1322],{"class":162},"\"background\"",[149,1324,301],{"class":285},[149,1326,1327],{"class":158},"\"transparent\"",[149,1329,331],{"class":285},[149,1331,1332],{"class":162},"\"hard_alpha\"",[149,1334,301],{"class":285},[149,1336,1337],{"class":162},"true",[149,1339,331],{"class":285},[149,1341,1342],{"class":162},"\"colors\"",[149,1344,301],{"class":285},[149,1346,1347],{"class":162},"160",[149,1349,331],{"class":285},[149,1351,1352],{"class":162},"\"trim\"",[149,1354,301],{"class":285},[149,1356,1337],{"class":162},[149,1358,307],{"class":285},[149,1360,1361,1364],{"class":151,"line":395},[149,1362,1363],{"class":162},"  \"assets\"",[149,1365,1366],{"class":285},": {\n",[149,1368,1370,1373,1375,1378,1380,1383,1385,1388,1390,1393,1395,1398,1400,1403,1405,1408],{"class":151,"line":1369},7,[149,1371,1372],{"class":162},"    \"shared\u002Ftrees\u002Foak\"",[149,1374,295],{"class":285},[149,1376,1377],{"class":162},"\"prompt\"",[149,1379,301],{"class":285},[149,1381,1382],{"class":158},"\"Game asset sprite: a single big old oak tree...\"",[149,1384,331],{"class":285},[149,1386,1387],{"class":162},"\"size\"",[149,1389,301],{"class":285},[149,1391,1392],{"class":158},"\"1536x1024\"",[149,1394,331],{"class":285},[149,1396,1397],{"class":162},"\"max\"",[149,1399,337],{"class":285},[149,1401,1402],{"class":162},"420",[149,1404,331],{"class":285},[149,1406,1407],{"class":162},"380",[149,1409,1410],{"class":285},"]},\n",[149,1412,1414,1417,1419,1421,1423,1426,1428,1430,1432,1435,1437,1440,1442,1445],{"class":151,"line":1413},8,[149,1415,1416],{"class":162},"    \"tracks\u002Fautumn\u002Fwindmill-full\"",[149,1418,295],{"class":285},[149,1420,1377],{"class":162},[149,1422,301],{"class":285},[149,1424,1425],{"class":158},"\"A wooden windmill...\"",[149,1427,331],{"class":285},[149,1429,1387],{"class":162},[149,1431,301],{"class":285},[149,1433,1434],{"class":158},"\"1024x1536\"",[149,1436,331],{"class":285},[149,1438,1439],{"class":162},"\"publish\"",[149,1441,301],{"class":285},[149,1443,1444],{"class":162},"false",[149,1446,307],{"class":285},[149,1448,1450,1453,1455,1457,1459,1462],{"class":151,"line":1449},9,[149,1451,1452],{"class":162},"    \"tracks\u002Fautumn\u002Fwindmill-sails\"",[149,1454,295],{"class":285},[149,1456,1377],{"class":162},[149,1458,301],{"class":285},[149,1460,1461],{"class":158},"\"Edit this image: only the four lattice sails...\"",[149,1463,1288],{"class":285},[149,1465,1467,1470,1472,1475,1477,1480,1482,1485,1487,1489,1491,1494,1496,1498],{"class":151,"line":1466},10,[149,1468,1469],{"class":162},"                                     \"size\"",[149,1471,301],{"class":285},[149,1473,1474],{"class":158},"\"1024x1024\"",[149,1476,331],{"class":285},[149,1478,1479],{"class":162},"\"reference\"",[149,1481,301],{"class":285},[149,1483,1484],{"class":158},"\"tracks\u002Fautumn\u002Fwindmill-full\"",[149,1486,331],{"class":285},[149,1488,1397],{"class":162},[149,1490,337],{"class":285},[149,1492,1493],{"class":162},"400",[149,1495,331],{"class":285},[149,1497,1493],{"class":162},[149,1499,1500],{"class":285},"]}\n",[149,1502,1504],{"class":151,"line":1503},11,[149,1505,1506],{"class":285},"  }\n",[149,1508,1510],{"class":151,"line":1509},12,[149,1511,392],{"class":285},[141,1513,1515],{"className":143,"code":1514,"language":145,"meta":47,"style":47},"codex-img batch art\u002Fassets.json                # generate what's missing, then convert everything\ncodex-img batch art\u002Fassets.json tracks\u002Fautumn  # one folder of keys, or a single key\ncodex-img batch art\u002Fassets.json --dry-run      # what would be generated, without spending quota\n",[127,1516,1517,1522,1527],{"__ignoreMap":47},[149,1518,1519],{"class":151,"line":152},[149,1520,1521],{},"codex-img batch art\u002Fassets.json                # generate what's missing, then convert everything\n",[149,1523,1524],{"class":151,"line":289},[149,1525,1526],{},"codex-img batch art\u002Fassets.json tracks\u002Fautumn  # one folder of keys, or a single key\n",[149,1528,1529],{"class":151,"line":310},[149,1530,1531],{},"codex-img batch art\u002Fassets.json --dry-run      # what would be generated, without spending quota\n",[98,1533,1534,1535,1537,1538,1541],{},"Each key is a path, for the generated image and the finished one. ",[127,1536,1103],{}," is added to every prompt, which keeps the art looking like one game. ",[127,1539,1540],{},"reference"," makes an asset an edit of another, so the windmill's sails match the windmill. A raw image is never generated twice, so deleting one is how you re-roll it, and the whole file is checked before any request. The game's 107 published assets convert in about 3 seconds.",[122,1543,1545],{"id":1544},"the-same-input-gives-the-same-file","The same input gives the same file",[98,1547,1548,1549,1552,1553,1556],{},"Running the processing again made git show unchanged sprites as modified. The palette library, exoquant, counts colours in a Rust ",[127,1550,1551],{},"HashMap",", which is seeded randomly in every process, so the palette came out different each time. codex-img now includes a copy of exoquant that keeps the colours sorted, and the same input and options always give the same bytes. ",[127,1554,1555],{},"convert --force"," leaves a file untouched when its bytes would be the same.",[122,1558,1560],{"id":1559},"removing-painted-in-sea-and-sky","Removing painted-in sea and sky",[98,1562,1563],{},"Every boat prompt said \"no ground\", and the model painted sea under every boat anyway. The game draws its own water, so each boat had a lighter patch at its base, and the foam left after removing the water made it float. codex-img removes it with options that don't know about water:",[141,1565,1567],{"className":143,"code":1566,"language":145,"meta":47,"style":47},"codex-img convert boat.png --hard-alpha --key auto --key-region bottom:30% --key-cut --trim --trim-density 0.15\n",[127,1568,1569],{"__ignoreMap":47},[149,1570,1571],{"class":151,"line":152},[149,1572,1566],{},[98,1574,1575],{},[1186,1576],{"alt":1577,"src":1578},"Two rows of five pixel-art harbor sprites on a dark background: a red and white buoy, a blue fishing boat, wooden pilings with seagulls, a sailboat with a striped sail and a red tugboat. In the top row each one sits in a patch of painted blue sea with white foam. In the bottom row the sea is gone and each stands directly on the line under it","\u002Fimg\u002Fblog\u002Fcodex-img-0-6-0\u002Fground.webp",[98,1580,1581,1584,1585,1588,1589,1592,1593,1596],{},[127,1582,1583],{},"--key auto"," samples the colours at the bottom edge and removes them, starting from the transparent background, so the fishing boat's blue hull survives inside its outline. ",[127,1586,1587],{},"--key-cut"," removes the rows below the waterline, and ",[127,1590,1591],{},"--trim-density"," stops leftover specks from becoming the bottom edge. From the top, with ",[127,1594,1595],{},"--key-spread"," to follow a gradient, the same options remove a sky behind a building.",[122,1598,1600],{"id":1599},"panoramas-without-a-seam","Panoramas without a seam",[98,1602,1603],{},"The game repeats each sky sideways. Mirroring hid the seam but put every landmark in twice. An inpainting edit with a mask would fix the seam, but the Codex image endpoint accepts a mask and ignores it.",[98,1605,1606,1609],{},[127,1607,1608],{},"codex-img tile"," works without one. It moves the seam to the middle, asks for an edit that joins it, and takes only a band around the seam from the result, so every pixel outside it stays as it was. Two skies, each repeated twice, before and after:",[98,1611,1612],{},[1186,1613],{"alt":1614,"src":1615},"Four wide pixel-art panoramas, each repeated twice side by side. The first shows a Japanese landscape where Mount Fuji is cut in half at the right edge and the next copy starts abruptly. The second is the same sky after tile, with one whole Fuji where the copies meet. The third is a coastline where a town on a cliff ends in a hard vertical edge between copies, and the fourth is the same coastline continuing smoothly","\u002Fimg\u002Fblog\u002Fcodex-img-0-6-0\u002Ftile.webp",[122,1617,1619],{"id":1618},"a-contact-sheet-for-review","A contact sheet for review",[98,1621,1622,1625],{},[127,1623,1624],{},"codex-img sheet"," lays a set of images out in one grid, standing on a common line, so a sprite with empty rows under it shows a gap. The harbor picture above is one. For an agent, one image is also far cheaper to look at than twenty.",[122,1627,1629],{"id":1628},"windows-and-fixes","Windows and fixes",[98,1631,1632,1633,1635],{},"0.6.1 added Windows builds, and 0.6.2 tidied the paths ",[127,1634,717],{}," prints. 0.6.3 and 0.6.4 come from two reviews of the code by Codex:",[208,1637,1638,1641,1647,1650],{},[211,1639,1640],{},"A taken output name is now refused before the request. Before, the image was generated and then thrown away, because codex-img never overwrites a file.",[211,1642,1643,1644,1646],{},"A new file only appears once it's completely written, so an interrupted save can't leave a broken image that ",[127,1645,717],{}," then treats as done.",[211,1648,1649],{},"The declared minimum Rust is now 1.88, the version the dependencies need, and CI builds with it.",[211,1651,1652],{},"The README now separates what codex-img controls from what has only been observed about the backend, such as which model it uses and how requests count against your plan.",[122,1654,1083],{"id":1082},[98,1656,1086,1657,1091,1660,1662,1663,1666],{},[101,1658,1090],{"href":1142,"rel":1659},[105],[127,1661,1094],{}," using your ChatGPT account. The Agent Skill in the repository tells Claude Code and Codex when to use these options, including to run ",[127,1664,1665],{},"batch --dry-run"," before spending quota.",[1103,1668,1669],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":47,"searchDepth":289,"depth":289,"links":1671},[1672,1673,1674,1675,1676,1677,1678,1679,1680],{"id":1148,"depth":289,"text":1149},{"id":1242,"depth":289,"text":1243},{"id":1258,"depth":289,"text":1259},{"id":1544,"depth":289,"text":1545},{"id":1559,"depth":289,"text":1560},{"id":1599,"depth":289,"text":1600},{"id":1618,"depth":289,"text":1619},{"id":1628,"depth":289,"text":1629},{"id":1082,"depth":289,"text":1083},{"path":1682,"title":1683,"description":1684,"date":1685,"tags":1686,"body":1687},"\u002Fblog\u002Fcodex-img","codex-img generates images from the terminal with your ChatGPT subscription","A small Rust CLI that sends your prompt straight to the Codex image endpoint, using the login Codex already saved. No API key, no agent in between, and an Agent Skill so Claude Code and Codex can use it too.","2026-09-28",[12,92,73,93],{"type":95,"value":1688,"toc":2170},[1689,1698,1733,1736,1742,1748,1754,1758,1761,1764,1768,1781,1838,1845,1849,1855,1862,1866,1869,1958,1965,2005,2011,2014,2020,2023,2058,2062,2065,2125,2132,2134,2164,2167],[98,1690,1691,1694,1695,1697],{},[101,1692,6],{"href":1137,"rel":1693},[105]," generates and edits images from the command line with a ChatGPT subscription. It sends your prompt to the same image endpoint the Codex CLI uses, with the login ",[127,1696,1094],{}," already saved, and writes the result to disk.",[141,1699,1701],{"className":143,"code":1700,"language":145,"meta":47,"style":47},"codex-img \"a red fox in snow, flat vector\" -o fox.png\ncodex-img \"make it night with aurora\" -i fox.png -o fox-night.png\n",[127,1702,1703,1715],{"__ignoreMap":47},[149,1704,1705,1707,1710,1712],{"class":151,"line":152},[149,1706,6],{"class":155},[149,1708,1709],{"class":158}," \"a red fox in snow, flat vector\"",[149,1711,169],{"class":162},[149,1713,1714],{"class":158}," fox.png\n",[149,1716,1717,1719,1722,1725,1728,1730],{"class":151,"line":289},[149,1718,6],{"class":155},[149,1720,1721],{"class":158}," \"make it night with aurora\"",[149,1723,1724],{"class":162}," -i",[149,1726,1727],{"class":158}," fox.png",[149,1729,169],{"class":162},[149,1731,1732],{"class":158}," fox-night.png\n",[98,1734,1735],{},"These are the images those two commands produced, with the prompts exactly as written, converted to lossy WebP for this page (1.2 MB and 1.5 MB became 41 KB and 78 KB). The first took 33 seconds:",[98,1737,1738],{},[1186,1739],{"alt":1740,"src":1741},"A flat vector illustration of a red fox standing in snow, with snowy pines and mountains behind it","\u002Fimg\u002Fblog\u002Fcodex-img\u002Ffox.webp",[98,1743,1744,1745,1747],{},"The second passes the first as a reference with ",[127,1746,514],{},". The edit kept the fox, its pose and the whole composition, and changed only the lighting and the sky:",[98,1749,1750],{},[1186,1751],{"alt":1752,"src":1753},"The same fox and landscape at night, under green and purple northern lights","\u002Fimg\u002Fblog\u002Fcodex-img\u002Ffox-night.webp",[122,1755,1757],{"id":1756},"why","Why",[98,1759,1760],{},"My subscription includes image generation, but in the terminal the usual way to use it is to ask the Codex agent to draw something. That puts a chat model between me and the image model, and it may rewrite the prompt on the way. I wanted one command, one request, and my prompt reaching the image model exactly as written.",[98,1762,1763],{},"The other reason is coding agents. When Claude Code needs an icon or a cover for something it's building, it should be able to call a CLI and get a file back, like any other tool.",[122,1765,1767],{"id":1766},"what-i-learned-about-the-endpoint","What I learned about the endpoint",[98,1769,1770,1771,331,1774,331,1777,1780],{},"The endpoint accepts a prompt, ",[127,1772,1773],{},"size",[127,1775,1776],{},"quality",[127,1778,1779],{},"background"," and up to five reference images. It follows some of them more loosely than you'd expect:",[208,1782,1783,1799,1805,1811,1832],{},[211,1784,1785,1790,1791,1794,1795,1798],{},[1786,1787,1788],"strong",{},[127,1789,1773],{}," ",[1786,1792,1793],{},"is a hint."," A ",[127,1796,1797],{},"1536x1024"," request has come back at 1536x1024 and also at 1370x1148. The shape you describe in the prompt seems to count for at least as much: the cover of this post asked for \"a very wide 2.3:1 banner\" and came back at 1902x827.",[211,1800,1801,1804],{},[1786,1802,1803],{},"The model field is ignored."," The backend picks the image model itself, and the Codex CLI hardcodes the name anyway.",[211,1806,1807,1810],{},[1786,1808,1809],{},"Quality is capped at medium"," on the subscription.",[211,1812,1813,1790,1817,1820,1821,1823,1824,1827,1828,1831],{},[1786,1814,1815],{},[127,1816,1779],{},[1786,1818,1819],{},"defaults to the backend's choice, and that can be transparent."," I didn't ask for transparency in the fox above, but ",[127,1822,488],{}," reported ",[127,1825,1826],{},"\"background\":\"transparent\"",": the sky came back partly see-through, which looks like black smudges in most viewers. Flattened onto white (which codex-img does when it writes JPEG), it's the clean image you see here. Pass ",[127,1829,1830],{},"-b opaque"," when you need a solid background.",[211,1833,1834,1837],{},[1786,1835,1836],{},"It doesn't validate anything."," Unknown values are silently ignored, so codex-img checks every option before sending, instead of spending quota on a request that quietly did something else.",[98,1839,1840,1841,1844],{},"codex-img only reads ",[127,1842,1843],{},"~\u002F.codex\u002Fauth.json",", and it never refreshes the token. Refresh tokens rotate, so refreshing from a second program could log Codex itself out. If the login has expired, codex-img exits with code 2 and asks you to open Codex, which renews it.",[122,1846,1848],{"id":1847},"built-for-agents","Built for agents",[98,1850,1851,1852,1854],{},"Paths go to stdout, progress to stderr, and ",[127,1853,488],{}," prints one line per image with its path, size and timing. Every kind of failure has its own exit code: 2 for login, 3 for quota, 4 for moderation. The repository includes an Agent Skill that tells Claude Code and Codex how to use all this. It covers when to spend quota (only when you asked for an image), not to retry a quota or login error, to look at the result before reporting back, and how to write prompts for the image model.",[98,1856,1857,1858,1861],{},"The skill also works without the binary. It comes with a small Python script that uses only the standard library and speaks the same flags, exit codes and JSON, but writes PNG only. Copying the skill folder into ",[127,1859,1860],{},"~\u002F.claude\u002Fskills"," is enough to get started.",[122,1863,1865],{"id":1864},"smaller-files","Smaller files",[98,1867,1868],{},"The endpoint always returns PNG, and a 1536x1024 PNG is usually close to 1 MB. So I added local conversion to the binary. Here's one generated image, a flat red paper plane, in each output:",[1870,1871,1872,1885],"table",{},[1873,1874,1875],"thead",{},[1876,1877,1878,1882],"tr",{},[1879,1880,1881],"th",{},"Output",[1879,1883,1884],{},"Size",[1886,1887,1888,1897,1905,1913,1925,1936,1947],"tbody",{},[1876,1889,1890,1894],{},[1891,1892,1893],"td",{},"PNG from the backend",[1891,1895,1896],{},"946 KB",[1876,1898,1899,1902],{},[1891,1900,1901],{},"PNG, recompressed losslessly (every PNG gets this now)",[1891,1903,1904],{},"462 KB",[1876,1906,1907,1910],{},[1891,1908,1909],{},"JPEG, quality 90",[1891,1911,1912],{},"82 KB",[1876,1914,1915,1922],{},[1891,1916,1917,1918,1921],{},"PNG, 256 colours (",[127,1919,1920],{},"-c 256",")",[1891,1923,1924],{},"137 KB",[1876,1926,1927,1933],{},[1891,1928,1929,1930,1921],{},"PNG, 64 colours (",[127,1931,1932],{},"-c 64",[1891,1934,1935],{},"8 KB",[1876,1937,1938,1944],{},[1891,1939,1940,1941,1921],{},"WebP, lossy at quality 80 (the default for ",[127,1942,1943],{},"-f webp",[1891,1945,1946],{},"7.5 KB",[1876,1948,1949,1955],{},[1891,1950,1951,1952,1921],{},"WebP, lossless (",[127,1953,1954],{},"--lossless",[1891,1956,1957],{},"469 KB",[98,1959,1960,1961,1964],{},"The palette option works like pngquant and keeps transparency, so it's a good fit for icons and stickers. This sticker was generated with ",[127,1962,1963],{},"-b transparent"," and reduced to 64 colours, from 898 KB to 128 KB. It sits directly on the page, with no background of its own:",[141,1966,1968],{"className":143,"code":1967,"language":145,"meta":47,"style":47},"codex-img \"sticker of a sleepy orange cat curled up, thick white border, flat vector\" -b transparent -o cat.png\ncodex-img convert cat.png -c 64          # -> cat.min.png\n",[127,1969,1970,1986],{"__ignoreMap":47},[149,1971,1972,1974,1977,1979,1981,1983],{"class":151,"line":152},[149,1973,6],{"class":155},[149,1975,1976],{"class":158}," \"sticker of a sleepy orange cat curled up, thick white border, flat vector\"",[149,1978,633],{"class":162},[149,1980,636],{"class":158},[149,1982,169],{"class":162},[149,1984,1985],{"class":158}," cat.png\n",[149,1987,1988,1990,1993,1996,1999,2002],{"class":151,"line":289},[149,1989,6],{"class":155},[149,1991,1992],{"class":158}," convert",[149,1994,1995],{"class":158}," cat.png",[149,1997,1998],{"class":162}," -c",[149,2000,2001],{"class":162}," 64",[149,2003,2004],{"class":446},"          # -> cat.min.png\n",[98,2006,2007],{},[1186,2008],{"alt":2009,"src":2010},"A sticker of a sleepy orange tabby cat curled up, with a thick white border and a transparent background","\u002Fimg\u002Fblog\u002Fcodex-img\u002Fcat-sticker.png",[98,2012,2013],{},"The transparency wasn't perfectly clean: faint, almost invisible specks floated outside the white border, and they showed up on this site's dark background. I cleared pixels under 35% opacity with ImageMagick before reducing the colours.",[98,2015,2016,2017,120],{},"One thing surprised me: dithering, which is normally on in tools like this, made the paper plane's 64-colour version 109 KB instead of 13 KB (both before recompression). The dither noise spreads across the flat background and compresses badly, so in codex-img dithering is off unless you ask for it with ",[127,2018,2019],{},"--dither",[98,2021,2022],{},"All of this also works on images you already have, with no login and no quota:",[141,2024,2026],{"className":143,"code":2025,"language":145,"meta":47,"style":47},"codex-img convert hero.png -o hero.webp\ncodex-img convert icon.png -c 64        # -> icon.min.png\n",[127,2027,2028,2042],{"__ignoreMap":47},[149,2029,2030,2032,2034,2037,2039],{"class":151,"line":152},[149,2031,6],{"class":155},[149,2033,1992],{"class":158},[149,2035,2036],{"class":158}," hero.png",[149,2038,169],{"class":162},[149,2040,2041],{"class":158}," hero.webp\n",[149,2043,2044,2046,2048,2051,2053,2055],{"class":151,"line":289},[149,2045,6],{"class":155},[149,2047,1992],{"class":158},[149,2049,2050],{"class":158}," icon.png",[149,2052,1998],{"class":162},[149,2054,2001],{"class":162},[149,2056,2057],{"class":446},"        # -> icon.min.png\n",[122,2059,2061],{"id":2060},"the-cover","The cover",[98,2063,2064],{},"The cover of this post came from codex-img on the first try:",[141,2066,2068],{"className":143,"code":2067,"language":145,"meta":47,"style":47},"codex-img - -s 1536x1024 -o cover.png --json \u003C\u003C'EOF'\nWide banner cover image for a developer tool's project page. Deep\ncharcoal-to-plum background with a soft radial glow in the center...\nIn the exact center, a single glossy macOS-style app icon: a rounded\nsquircle with a warm coral-to-orange gradient, with a terminal prompt\nchevron \">\" and a small picture-frame symbol...\nEOF\n",[127,2069,2070,2096,2101,2106,2111,2116,2121],{"__ignoreMap":47},[149,2071,2072,2074,2077,2080,2083,2085,2088,2090,2093],{"class":151,"line":152},[149,2073,6],{"class":155},[149,2075,2076],{"class":158}," -",[149,2078,2079],{"class":162}," -s",[149,2081,2082],{"class":158}," 1536x1024",[149,2084,169],{"class":162},[149,2086,2087],{"class":158}," cover.png",[149,2089,883],{"class":162},[149,2091,2092],{"class":994}," \u003C\u003C",[149,2094,2095],{"class":158},"'EOF'\n",[149,2097,2098],{"class":151,"line":289},[149,2099,2100],{"class":158},"Wide banner cover image for a developer tool's project page. Deep\n",[149,2102,2103],{"class":151,"line":310},[149,2104,2105],{"class":158},"charcoal-to-plum background with a soft radial glow in the center...\n",[149,2107,2108],{"class":151,"line":346},[149,2109,2110],{"class":158},"In the exact center, a single glossy macOS-style app icon: a rounded\n",[149,2112,2113],{"class":151,"line":377},[149,2114,2115],{"class":158},"squircle with a warm coral-to-orange gradient, with a terminal prompt\n",[149,2117,2118],{"class":151,"line":395},[149,2119,2120],{"class":158},"chevron \">\" and a small picture-frame symbol...\n",[149,2122,2123],{"class":151,"line":1369},[149,2124,1179],{"class":158},[98,2126,2127,2128,2131],{},"It also showed me a gap. At first codex-img could only write lossless WebP, and lossless formats struggle with smooth gradients: the cover came out at 623 KB, slightly bigger than the PNG. So I added lossy WebP, using libwebp. The cover on this page is now ",[127,2129,2130],{},"codex-img convert cover.png -f webp",", and it's 7.3 KB. For flat art the palette PNG is about as small and keeps exact colours; for gradients and photos, lossy WebP wins.",[122,2133,1083],{"id":1082},[98,2135,2136,2137,2142,2143,1091,2146,2148,2149,2152,2153,2155,2156,2159,2160,2163],{},"Download the binary for macOS or Linux from the ",[101,2138,2141],{"href":2139,"rel":2140},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img\u002Freleases\u002Flatest",[105],"latest release",", put it on your ",[127,2144,2145],{},"PATH",[127,2147,1094],{}," using your ChatGPT account. ",[127,2150,2151],{},"codex-img status"," checks the login without using any quota. Format conversion, lossy WebP, palette PNGs and ",[127,2154,1078],{}," are on ",[127,2157,2158],{},"main"," and will be in the next release. Until then, ",[127,2161,2162],{},".\u002Fscripts\u002Finstall.sh"," builds from source and links the skill into Claude Code and Codex.",[98,2165,2166],{},"This isn't an official API. It's the backend the Codex CLI uses, so it can break when OpenAI changes it. It's Apache-2.0 licensed.",[1103,2168,2169],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":47,"searchDepth":289,"depth":289,"links":2171},[2172,2173,2174,2175,2176,2177],{"id":1756,"depth":289,"text":1757},{"id":1766,"depth":289,"text":1767},{"id":1847,"depth":289,"text":1848},{"id":1864,"depth":289,"text":1865},{"id":2060,"depth":289,"text":2061},{"id":1082,"depth":289,"text":1083},{"path":2179,"title":2180,"description":2181,"date":2182,"tags":2183,"body":2184},"\u002Fblog\u002Fusage-window-starter","Usage Window Starter activates your next Claude and Codex session the minute the old one resets","A small macOS menu bar app that watches the 5-hour usage limits of Claude Code and Codex and sends one short message to start the next session as soon as the old one resets.","2026-09-24",[12,73,92,93],{"type":95,"value":2185,"toc":2403},[2186,2193,2200,2202,2205,2209,2212,2215,2265,2271,2275,2284,2290,2294,2303,2309,2313,2330,2336,2339,2345,2358,2361,2363,2371,2390,2397,2400],[98,2187,2188,2192],{},[101,2189,18],{"href":2190,"rel":2191},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fusage-window-starter",[105]," is a macOS menu bar app for people who use Claude Code or Codex on a subscription. Both have a 5-hour usage limit, and the 5 hours only start counting when you send your first message. Usage Window Starter watches both limits and, as soon as a session resets, sends one short message to start the next one.",[98,2194,2195,2199],{},[2196,2197,2198],"em",{},"Update, 26 September:"," version 0.3 moved the app from Bun to Go, which took the download from 23 MB to 4.4 MB. It also added a panel under the menu bar icon and automatic updates, and you can now download it instead of building it yourself.",[122,2201,1757],{"id":1756},[98,2203,2204],{},"If your session resets at 14:47 and you only get back to work at 16:30, your new session runs until 21:30. The time in between isn't used, so a long day ends up with fewer sessions than it could have had. Starting the next session right away fixes that. It costs one \"Reply with just: ok\" message per session.",[122,2206,2208],{"id":2207},"no-api-calls","No API calls",[98,2210,2211],{},"The easiest way to read usage limits is to call the endpoints the official apps use, with the OAuth token from your keychain. I didn't want to do that. Those endpoints aren't meant for third-party tools, and I'd rather not risk my subscription account on it.",[98,2213,2214],{},"So the app only talks to the official CLIs, headless, with your normal login:",[1870,2216,2217,2229],{},[1873,2218,2219],{},[1876,2220,2221,2223,2226],{},[1879,2222],{},[1879,2224,2225],{},"Limit check (no model call)",[1879,2227,2228],{},"Starting a session",[1886,2230,2231,2246],{},[1876,2232,2233,2236,2241],{},[1891,2234,2235],{},"Claude",[1891,2237,2238],{},[127,2239,2240],{},"claude -p \"\u002Fusage\"",[1891,2242,2243],{},[127,2244,2245],{},"claude -p … --model haiku",[1876,2247,2248,2251,2260],{},[1891,2249,2250],{},"Codex",[1891,2252,2253,2256,2257],{},[127,2254,2255],{},"codex app-server"," → ",[127,2258,2259],{},"account\u002FrateLimits\u002Fread",[1891,2261,2262],{},[127,2263,2264],{},"codex exec -m \u003Cmodel>",[98,2266,2267,2270],{},[127,2268,2269],{},"\u002Fusage"," in print mode is a local command that doesn't query a model, and the Codex app server has a proper JSON-RPC method for rate limits. Neither touches your Claude or Codex configuration.",[122,2272,2274],{"id":2273},"two-things-i-learned-about-the-limits","Two things I learned about the limits",[98,2276,2277,2280,2281,2283],{},[1786,2278,2279],{},"Codex reports a session even when none is running."," When no Codex session is running, ",[127,2282,2259],{}," doesn't say \"none\". It reports 0% used with a reset exactly five hours from the moment you ask, and that time moves forward on every read. My first version took that at face value, decided a session was running, and never started one. Now 0% with a reset exactly five hours away counts as idle.",[98,2285,2286,2289],{},[1786,2287,2288],{},"Claude seems to round session start times down to the hour."," A session I started at 17:07 reset at 22:00, not 22:07. So for Claude, a few minutes of delay doesn't cost anything, as long as the new session starts within the same hour. Codex doesn't round: a session started at 15:06:19 reset at exactly 20:06:19.",[122,2291,2293],{"id":2292},"checking-at-the-right-time","Checking at the right time",[98,2295,2296,2297,2299,2300,2302],{},"The app checks every 10 minutes, which is modest: each check is one request, the same one you make by typing ",[127,2298,2269],{},". It also checks once more right after each session's reported reset time: 30 seconds after for Codex, and 90 seconds for Claude, because ",[127,2301,2269],{}," only shows minutes. With that, today's Codex reset looked like this in the log:",[141,2304,2307],{"className":2305,"code":2306,"language":724,"meta":47},[722],"20:06:19  old session resets\n20:06:49  reset check → idle\n20:06:53  start message sent\n",[127,2308,2306],{"__ignoreMap":47},[122,2310,2312],{"id":2311},"the-app","The app",[98,2314,2315,2316,2321,2322,2325,2326,2329],{},"It's built on ",[101,2317,2320],{"href":2318,"rel":2319},"https:\u002F\u002Fframework.blackboard.sh\u002Felectrobun\u002F",[105],"Electrobun"," with a Go backend, and the panel and settings window are plain HTML and CSS in the system WebKit. The menu bar shows a countdown for each session, like ",[127,2323,2324],{},"C 4:30 · X 2:18",". Clicking it opens a panel with each session's usage and time left, your weekly usage, and a ",[1786,2327,2328],{},"Start"," button when a session is idle.",[98,2331,2332],{},[1186,2333],{"alt":2334,"src":2335},"The Usage Window Starter panel","\u002Fimg\u002Fscreens\u002Fusage-window-starter-panel.webp",[98,2337,2338],{},"In Settings you can pick the models, pause auto-start, limit it to certain hours, and send a test message to check that a model still works.",[98,2340,2341],{},[1186,2342],{"alt":2343,"src":2344},"The Usage Window Starter settings window","\u002Fimg\u002Fscreens\u002Fusage-window-starter-settings.webp",[98,2346,2347,2348,331,2351,216,2354,2357],{},"The Codex model list comes from your installed Codex, so when a model is retired, Settings flags it and you get a notification on the first failed start. For Claude, the ",[127,2349,2350],{},"haiku",[127,2352,2353],{},"sonnet",[127,2355,2356],{},"opus"," aliases always point at the newest model.",[98,2359,2360],{},"The first version was a 23 MB download, almost all of it the Bun runtime. Electrobun can also run a Go main process, but its Go SDK has no menus for menu bar icons, so I waited until the panel replaced the menu. Then I ported the backend to Go. The download is now 4.4 MB, and with the panel closed the app uses about 30 MB of memory.",[122,2362,1083],{"id":1082},[98,2364,2365,2366,2370],{},"Download the DMG from the ",[101,2367,2141],{"href":2368,"rel":2369},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fusage-window-starter\u002Freleases\u002Flatest",[105]," and drag the app to Applications. It isn't signed or notarized yet, so macOS will probably say it's damaged the first time. Run this once in Terminal to allow it:",[141,2372,2374],{"className":143,"code":2373,"language":145,"meta":47,"style":47},"xattr -dr com.apple.quarantine \"\u002FApplications\u002FUsage Window Starter.app\"\n",[127,2375,2376],{"__ignoreMap":47},[149,2377,2378,2381,2384,2387],{"class":151,"line":152},[149,2379,2380],{"class":155},"xattr",[149,2382,2383],{"class":162}," -dr",[149,2385,2386],{"class":158}," com.apple.quarantine",[149,2388,2389],{"class":158}," \"\u002FApplications\u002FUsage Window Starter.app\"\n",[98,2391,2392,2393,2396],{},"Then open it and turn on ",[1786,2394,2395],{},"Launch at login"," in Settings. New versions install from the panel.",[98,2398,2399],{},"It's MIT licensed.",[1103,2401,2402],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":47,"searchDepth":289,"depth":289,"links":2404},[2405,2406,2407,2408,2409,2410],{"id":1756,"depth":289,"text":1757},{"id":2207,"depth":289,"text":2208},{"id":2273,"depth":289,"text":2274},{"id":2292,"depth":289,"text":2293},{"id":2311,"depth":289,"text":2312},{"id":1082,"depth":289,"text":1083},{"path":2412,"title":2413,"description":2414,"date":2415,"tags":2416,"body":2417},"\u002Fblog\u002Fdocument-iteration-skill-6-0","document-iteration-skill 6.0 rewrites the skill in positive terms","The skill now says what Claude should do instead of listing what it shouldn't, the slash commands became plain requests, and the repo went from about 7,700 lines to 850.","2026-09-23",[12,73,93],{"type":95,"value":2418,"toc":2647},[2419,2435,2438,2442,2453,2456,2459,2474,2480,2484,2506,2512,2515,2519,2522,2531,2535,2565,2569,2580,2632,2644],[98,2420,2421,2426,2427,2430,2431,2434],{},[101,2422,2425],{"href":2423,"rel":2424},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fdocument-iteration-skill",[105],"document-iteration-skill 6.0"," is out. It's a Claude skill for editing documents with inline feedback. You write ",[127,2428,2429],{},"%% comments %%"," next to the text, and Claude edits the document and replies next to them with ",[127,2432,2433],{},"•%%> responses \u003C%%•",". The whole discussion stays in the file and in git.",[98,2436,2437],{},"I stopped working on it in January, but I kept using it, and it kept being useful. In the meantime, the way skills are written has changed quite a bit, so this release is mostly a rewrite.",[122,2439,2441],{"id":2440},"say-what-to-do-not-what-to-avoid","Say what to do, not what to avoid",[98,2443,2444,2445,2448,2449,2452],{},"The old ",[127,2446,2447],{},"SKILL.md"," was full of rules like \"NEVER remove user comments\" and \"NEVER use ",[127,2450,2451],{},"%% %%"," yourself\", under headings like \"MANDATORY RULES (NEVER SKIP)\" and \"NEVER DO THIS\". It also showed examples of wrong output next to the right ones.",[98,2454,2455],{},"One thing I've learned since is that negations like these don't hold up well. As the context grows, the \"never\" fades and what's left is the thing you said not to do. The same goes for examples of wrong output: models tend to copy what they see.",[98,2457,2458],{},"So every rule now describes the behavior I want, with a short reason:",[208,2460,2461,2467],{},[211,2462,2463,2464,2466],{},"Before: \"NEVER use ",[127,2465,2451],{}," for your own content.\"",[211,2468,2469,2470,2473],{},"Now: \"Every piece of text you add as a remark goes inside ",[127,2471,2472],{},"•%%> … \u003C%%•",", including when you draft a brand-new document. Keeping the two voices distinct is what lets anyone reading the file see who said what.\"",[98,2475,2476,2477,2479],{},"The reason matters as much as the rule, because it lets Claude handle cases the rule doesn't mention. The examples now show only correct output. ",[127,2478,2447],{}," is shorter too, and the details moved into reference files that Claude reads only when it needs them.",[122,2481,2483],{"id":2482},"no-more-slash-commands","No more slash commands",[98,2485,2486,2487,331,2490,331,2493,331,2496,331,2499,216,2502,2505],{},"Version 5 came with six slash commands: ",[127,2488,2489],{},"\u002Fiterate",[127,2491,2492],{},"\u002Fiterate-ask",[127,2494,2495],{},"\u002Ffeedback",[127,2497,2498],{},"\u002Fcleanup",[127,2500,2501],{},"\u002Finit-vscode",[127,2503,2504],{},"\u002Finit-obsidian",". They're gone. The skill now picks the mode from a plain request:",[141,2507,2510],{"className":2508,"code":2509,"language":724},[722],"> respond to the comments in docs\u002Fplan.md\n> review docs\u002Fspec.md and add your feedback\n> draft a proposal for dark mode\n> clean up docs\u002Fplan.md\n> set up Obsidian highlighting for the vault in docs\u002F\n",[127,2511,2509],{"__ignoreMap":47},[98,2513,2514],{},"The editor configs for VSCode and Obsidian now ship inside the skill, so Claude can set them up without a separate download.",[122,2516,2518],{"id":2517},"a-smaller-repo","A smaller repo",[98,2520,2521],{},"The repo had grown to about 7,700 lines of text for what is essentially one markdown file. There were five cleanup scripts doing the same thing in Python, JavaScript, Bash, PowerShell and Batch, plus four setup prompts, a devcontainer, and around 45 documentation pages in a separate repo.",[98,2523,2524,2525,2530],{},"Now it's about 850 lines: the skill, one Python cleanup script with tests, a four-page ",[101,2526,2529],{"href":2527,"rel":2528},"https:\u002F\u002Ffoksa.github.io\u002Fdocument-iteration-skill\u002F",[105],"docs site",", and one real example.",[122,2532,2534],{"id":2533},"other-changes","Other changes",[208,2536,2537,2555],{},[211,2538,2539,2542,2543,2546,2547,2550,2551,2554],{},[1786,2540,2541],{},"Tokens update in place."," When Claude rewrites highlighted text, it keeps the token: ",[127,2544,2545],{},"==PostgreSQL(DB)=="," becomes ",[127,2548,2549],{},"==SQLite(DB)==",", so your ",[127,2552,2553],{},"%%(DB)"," comment still points at the right place.",[211,2556,2557,2560,2561,2564],{},[1786,2558,2559],{},"Claude.ai support."," The release now contains only the skill folder as ",[127,2562,2563],{},"document-iteration-skill.zip",", ready to upload under Settings → Skills.",[122,2566,2568],{"id":2567},"upgrading","Upgrading",[98,2570,2571,2572,2575,2576,2579],{},"Delete ",[127,2573,2574],{},".claude\u002Fskills\u002Fdocument-iteration-skill",", along with any of the old command files you copied into ",[127,2577,2578],{},".claude\u002Fcommands\u002F",". Then install again:",[141,2581,2585],{"className":2582,"code":2583,"language":2584,"meta":47,"style":47},"language-bash shiki shiki-themes github-light github-dark","git clone --depth 1 https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fdocument-iteration-skill.git \u002Ftmp\u002Fdis\ncp -r \u002Ftmp\u002Fdis\u002Fdocument-iteration-skill .claude\u002Fskills\u002F && rm -rf \u002Ftmp\u002Fdis\n","bash",[127,2586,2587,2607],{"__ignoreMap":47},[149,2588,2589,2592,2595,2598,2601,2604],{"class":151,"line":152},[149,2590,2591],{"class":155},"git",[149,2593,2594],{"class":158}," clone",[149,2596,2597],{"class":162}," --depth",[149,2599,2600],{"class":162}," 1",[149,2602,2603],{"class":158}," https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fdocument-iteration-skill.git",[149,2605,2606],{"class":158}," \u002Ftmp\u002Fdis\n",[149,2608,2609,2612,2615,2618,2621,2624,2627,2630],{"class":151,"line":289},[149,2610,2611],{"class":155},"cp",[149,2613,2614],{"class":162}," -r",[149,2616,2617],{"class":158}," \u002Ftmp\u002Fdis\u002Fdocument-iteration-skill",[149,2619,2620],{"class":158}," .claude\u002Fskills\u002F",[149,2622,2623],{"class":285}," && ",[149,2625,2626],{"class":155},"rm",[149,2628,2629],{"class":162}," -rf",[149,2631,2606],{"class":158},[98,2633,2444,2634,2637,2638,120],{},[127,2635,2636],{},"starter-kit.zip"," download is gone. If you linked to it, switch to ",[101,2639,2642],{"href":2640,"rel":2641},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fdocument-iteration-skill\u002Freleases\u002Fdownload\u002Fstarter-kit\u002Fdocument-iteration-skill.zip",[105],[127,2643,2563],{},[1103,2645,2646],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":47,"searchDepth":289,"depth":289,"links":2648},[2649,2650,2651,2652,2653],{"id":2440,"depth":289,"text":2441},{"id":2482,"depth":289,"text":2483},{"id":2517,"depth":289,"text":2518},{"id":2533,"depth":289,"text":2534},{"id":2567,"depth":289,"text":2568},{"path":2655,"title":2656,"description":2657,"date":2658,"tags":2659,"body":2660},"\u002Fblog\u002Fopen-claude-agent-sdk-0-50-0","open-claude-agent-sdk 0.50.0 fixes broken clean installs","Since v0.25.0 the package failed to import unless the official SDK was also installed. 0.50.0 fixes that and catches up with official SDK 0.3.276.","2026-09-22",[22,12,43,93],{"type":95,"value":2661,"toc":2911},[2662,2670,2699,2706,2710,2726,2733,2753,2757,2798,2802,2808,2824,2827,2831,2834,2837,2840,2844,2879,2883,2897,2900,2908],[98,2663,2664,2669],{},[101,2665,2668],{"href":2666,"rel":2667},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fopen-claude-agent-sdk\u002Freleases\u002Ftag\u002Fv0.50.0",[105],"open-claude-agent-sdk v0.50.0"," came out on September 19. The headline is a bug fix: from v0.25.0 on, importing the package on a clean install failed with this error:",[141,2671,2675],{"className":2672,"code":2673,"language":2674,"meta":47,"style":47},"language-ts shiki shiki-themes github-light github-dark","import { query } from 'open-claude-agent-sdk';\n\u002F\u002F Error: Cannot find package '@anthropic-ai\u002Fclaude-agent-sdk'\n","ts",[127,2676,2677,2694],{"__ignoreMap":47},[149,2678,2679,2682,2685,2688,2691],{"class":151,"line":152},[149,2680,2681],{"class":994},"import",[149,2683,2684],{"class":285}," { query } ",[149,2686,2687],{"class":994},"from",[149,2689,2690],{"class":158}," 'open-claude-agent-sdk'",[149,2692,2693],{"class":285},";\n",[149,2695,2696],{"class":151,"line":289},[149,2697,2698],{"class":446},"\u002F\u002F Error: Cannot find package '@anthropic-ai\u002Fclaude-agent-sdk'\n",[98,2700,2701,2702,2705],{},"If you only installed this package, without the official SDK next to it, you were affected. The release also brings the API in line with the official ",[127,2703,2704],{},"@anthropic-ai\u002Fclaude-agent-sdk"," 0.3.276 and fixes several bugs found in a full review of the code.",[122,2707,2709],{"id":2708},"how-to-upgrade","How to upgrade",[141,2711,2713],{"className":2582,"code":2712,"language":2584,"meta":47,"style":47},"npm install open-claude-agent-sdk@0.50.0\n",[127,2714,2715],{"__ignoreMap":47},[149,2716,2717,2720,2723],{"class":151,"line":152},[149,2718,2719],{"class":155},"npm",[149,2721,2722],{"class":158}," install",[149,2724,2725],{"class":158}," open-claude-agent-sdk@0.50.0\n",[98,2727,2728,2729,2732],{},"If you use TypeScript, also add the official SDK as a dev dependency. This package reuses its types but never loads it at runtime, and ",[127,2730,2731],{},"--omit=optional"," skips its bundled CLI binary:",[141,2734,2736],{"className":2582,"code":2735,"language":2584,"meta":47,"style":47},"npm install -D @anthropic-ai\u002Fclaude-agent-sdk --omit=optional\n",[127,2737,2738],{"__ignoreMap":47},[149,2739,2740,2742,2744,2747,2750],{"class":151,"line":152},[149,2741,2719],{"class":155},[149,2743,2722],{"class":158},[149,2745,2746],{"class":162}," -D",[149,2748,2749],{"class":158}," @anthropic-ai\u002Fclaude-agent-sdk",[149,2751,2752],{"class":162}," --omit=optional\n",[122,2754,2756],{"id":2755},"breaking-changes","Breaking changes",[208,2758,2759,2775],{},[211,2760,2761,2767,2768,2771,2772,120],{},[1786,2762,2763,2766],{},[127,2764,2765],{},"abort()"," now stops for real."," It rejects the iteration with ",[127,2769,2770],{},"AbortError"," and kills the CLI, the same as the official SDK. Before, it only sent an interrupt, and you still got a final result. If you want a graceful stop, use ",[127,2773,2774],{},"q.interrupt()",[211,2776,2777,1790,2780,331,2783,331,2786,2789,2790,2793,2794,2797],{},[1786,2778,2779],{},"Some unsupported helpers are removed:",[127,2781,2782],{},"startup",[127,2784,2785],{},"resolveSettings",[127,2787,2788],{},"filterEscalatingDefaultMode"," and the ",[127,2791,2792],{},"sessionStore"," helpers. ",[127,2795,2796],{},"query()"," never supported them.",[122,2799,2801],{"id":2800},"what-was-wrong","What was wrong",[98,2803,2804,2805,2807],{},"The package's entry point re-exported a few runtime values, such as ",[127,2806,2770],{}," and some constants, straight from the official SDK. The official SDK was only a dev dependency, so it wasn't there on a clean install, and the import failed.",[98,2809,2810,2811,331,2814,331,2817,2820,2821,2823],{},"Those values are now defined in this package. The session helpers (",[127,2812,2813],{},"listSessions",[127,2815,2816],{},"getSessionInfo",[127,2818,2819],{},"forkSession"," and others) are also its own code now instead of re-exports. That fixed two more bugs: ",[127,2822,2813],{}," returned nothing under Node, and sessions in long project paths were never found. The official SDK is now an optional peer dependency, used for types only.",[98,2825,2826],{},"To keep this from happening again, CI now packs the package, installs it in an empty project without the official SDK, and imports every entry point under Node.",[122,2828,2830],{"id":2829},"how-it-slipped-through","How it slipped through",[98,2832,2833],{},"Honestly, I stopped paying close attention. At one point Anthropic decided the Claude Agent SDK could only be used through the API, and after that I lost a lot of my motivation to work on this project.",[98,2835,2836],{},"It kept going anyway. I have a Claude Code skill that updates the SDK to match each new official release, and for the most part it did the job well. But there was a stretch, around the later Opus 4.6 days and into Opus 4.7, when the models felt noticeably worse to me. They cut corners everywhere. The bug arrived in v0.25.0 on April 24, about a week after Opus 4.7 came out, so my guess is it came from that period: a re-export that looked fine, passed the tests on my machine (where the official SDK was always installed), and nobody double-checked.",[98,2838,2839],{},"What finally caught it was a full review of the code against the official SDK. That's also why this release has more fixes than usual.",[122,2841,2843],{"id":2842},"other-fixes","Other fixes",[208,2845,2846,2860,2869],{},[211,2847,2848,2851,2852,2855,2856,2859],{},[1786,2849,2850],{},"Permissions:"," without a ",[127,2853,2854],{},"canUseTool"," callback, permission requests were quietly ",[1786,2857,2858],{},"allowed",". They now fail, as they do in the official SDK.",[211,2861,2862,2865,2866,2868],{},[1786,2863,2864],{},"Hangs:"," a ",[127,2867,2854],{}," or hook callback that called a query method waited forever. It doesn't anymore.",[211,2870,2871,2874,2875,2878],{},[1786,2872,2873],{},"MCP:"," servers added mid-session with ",[127,2876,2877],{},"setMcpServers()"," were never registered. Now they are.",[122,2880,2882],{"id":2881},"catching-up-with-the-official-sdk","Catching up with the official SDK",[98,2884,2885,2886,2889,2890,2892,2893,2896],{},"This release matches official SDK 0.3.276. It adds the ",[127,2887,2888],{},"projectConfigRoot"," option, passes more detail to ",[127,2891,2854],{}," callbacks (which MCP server a tool came from, its title and the matching ask rule), and improves how ",[127,2894,2895],{},"getSessionMessages"," handles messages queued during a tool call and parallel tool calls.",[98,2898,2899],{},"Each behavior change comes with a test that runs against both this package and the official SDK and compares the results.",[98,2901,2902,2903,120],{},"The full list of changes is in the ",[101,2904,2907],{"href":2905,"rel":2906},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fopen-claude-agent-sdk\u002Fcompare\u002Fv0.49.0...v0.50.0",[105],"v0.49.0...v0.50.0 comparison",[1103,2909,2910],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":47,"searchDepth":289,"depth":289,"links":2912},[2913,2914,2915,2916,2917,2918],{"id":2708,"depth":289,"text":2709},{"id":2755,"depth":289,"text":2756},{"id":2800,"depth":289,"text":2801},{"id":2829,"depth":289,"text":2830},{"id":2842,"depth":289,"text":2843},{"id":2881,"depth":289,"text":2882},{"path":2920,"title":2921,"description":2922,"date":2923,"tags":2924,"body":2925},"\u002Fblog\u002Fcamera-video-converter-is-on-play-store","Camera Video Converter is on the Play Store","My free, offline Android app for making camera footage shareable is now on Google Play.","2026-09-02",[33,32,35],{"type":95,"value":2926,"toc":2979},[2927,2935,2939,2942,2973],[98,2928,2929,2930,2934],{},"Man, that took a while, but finally: ",[101,2931,29],{"href":2932,"rel":2933},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=dev.foksa.cameravideoconverter",[105]," is live on the Google Play Store.",[122,2936,2938],{"id":2937},"what-it-is","What it is",[98,2940,2941],{},"Footage from mirrorless cameras often won't play or share properly on a phone. Camera Video Converter turns those clips into ordinary MP4 files, up to 1080p, right on the device. It was built around Sony A6700 footage, but it accepts any unencrypted SDR H.264 or H.265 MP4 or MOV file.",[208,2943,2944,2950,2956,2962],{},[211,2945,2946,2949],{},[1786,2947,2948],{},"Fully offline",": no internet permission, no ads, no analytics, no accounts. Your videos never leave the phone.",[211,2951,2952,2955],{},[1786,2953,2954],{},"Works from the share menu",": send a video to the app, convert it, and pass it on.",[211,2957,2958,2961],{},[1786,2959,2960],{},"Doesn't mess up colors",": HDR, Dolby Vision and Log footage are rejected rather than converted with the wrong colors.",[211,2963,2964,2967,2968,120],{},[1786,2965,2966],{},"Free and open source",": the code is on ",[101,2969,2972],{"href":2970,"rel":2971},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcamera-video-converter",[105],"GitHub",[98,2974,2975,2976,120],{},"More details and screenshots are on the ",[101,2977,2978],{"href":28},"project page",{"title":47,"searchDepth":289,"depth":289,"links":2980},[2981],{"id":2937,"depth":289,"text":2938},1791126066981]