Releases: LivioGama/pixel
Releases · LivioGama/pixel
Release list
v0.5.2
Immutable
release. Only release title and notes can be modified.
This release makes pixel lighter on disk and faster on large repositories: the history index is about 14 times smaller and bounded, and prepare-repo stops rebuilding a graph that still matches the tree.
Highlights
- A bounded history index:
history.dbmoves to FTS5 trigram indexes (359 MB to 25 MB here), evicts old diffs past a 365-day window or a 256 MiB budget, and is only created by the first history command. - Faster graph builds: a full build writes in one SQLite transaction (49 s to 9 s on a 60 000-symbol repository), and
prepare-reporeuses or incrementally updates a fresh graph, so a CI-cached.pixel/costs seconds. prepare-repotimings now report where the time goes, index layer by layer and graph phase by phase.
Added
- prepare-repo:
--jsongains atimingsblock: how the index base was obtained (reused,shared_cache,built_from_git) with the cost of each index layer, and the wall time of every graph build phase; the human output gets atimings:line naming the slowest phase.status --jsoncarries the index layers asindex.open. (#309)
Changed
- history:
history.dbis about 14 times smaller and bounded: diff and path text sit in FTS5 trigram indexes instead of a row per trigram (this repository: 359 MB + 24 MB WAL to 25 MB), diffs older than 365 days or past 256 MiB are evicted newest-kept with metadata intact (PIXEL_HISTORY_WINDOW_DAYS,PIXEL_HISTORY_BUDGET_MB), andstatus,doctorand session start no longer create the db. (#301) - recall:
recall search --agentwalks the time index and stops at the first page when the agent's sessions hold over 20 000 turns, instead of sorting them all first (recall search . --agent claude --limit 50, 248k turns: 1.34 s to 11 ms). A smaller agent, or one whose turns sit behind more newer turns, keeps the sort. (#301) - recall: a semantic index built by an older revision of its model now rebuilds itself: the recall daemon empties it and re-embeds the corpus in 5 000-turn slices per pass, and
recall embeddoes the same, so the model2vec-rs 0.3 update (it moved 133 of 300 sampled turn vectors) needs norecall embed --rebuild. (#302) - install, cli: the bundled agent and sub-agent prompts send "does A reach B" to
pixel evaluate pathand say how to read itsestablished/absent_in_snapshot/unknownstatuses, andcall-pathoutput gains asuccessorfield with the ready-to-runevaluate pathcommand; its other fields are unchanged. (#304) - graph: a full graph build (
prepare-repo,rebuild-graph, a first graph query) writes its rows in one SQLite transaction instead of one commit per row: on a 60 000-symbol Rails repository the build drops from 49 s to 9 s, with the same rows. (#310) - prepare-repo: keeps a stored graph that still matches the tree, and updates it in place when few files changed, instead of rebuilding it every run: a
.pixel/restored from a CI cache now costs seconds (3 s on a 60 000-symbol repository, 8 s 40 commits apart).timings.graph.buildnames the mode (fresh,incremental,fulland why);--rebuild-graphforces the rebuild. (#311)
Fixed
- history: a broad diff or path search no longer comes back short or empty: candidates held any one trigram of the query and only the first 400 were checked (0.5.1 found no commit for
mainin this repository), and several hunks of one commit used up the page. Candidates now hold every trigram and the page counts distinct commits. (#301) - history:
lifecycleon a token of three characters or more no longer fails: its query named a table alias that did not exist, so every such token errored. (#301) - index: building an index no longer edits a tracked
.gitignore: when git does not already ignore.pixel/, the entry goes to the clone's owninfo/exclude, sowhat-changedand other read-only commands leavegit statusclean and the nextgit checkoutunblocked, and a repo without a.gitignoreno longer gets one. (#307) - index: a
.pixel/whose base shard was built at a commit this clone does not hold (a CI cache filled on another pull request's merge ref, an amended and pruned commit) is rebuilt at HEAD; a failedgit diff base..HEADused to read as "nothing changed", and the index served the other commit's text as HEAD's. (#312)
v0.5.1
Immutable
release. Only release title and notes can be modified.
This release sharpens how Pixel measures itself and how it resolves Rust imports, and gives the project a website at https://pixel-cli.dev/.
Highlights
pixel auditmeasures what an agent reads to learn a repository's largest files, andlist-signaturesreports its real saving on the spot.- Rust
usestatements resolve correctly: aliases, grouped paths and scope-limited imports each bind the right names. search-contenttakes ripgrep's-g,-t,-l,-Fand-n, anddoctor --fixruns each repair once and re-checks.
Added
- docs: Pixel has a website at https://liviogama.github.io/pixel/: a landing page that shows a full file read burning down to a Pixel answer, a real
pixel scope-taskrun and the six recordings, plus/docs/and a/benchmarks/page with every figure's source and the cases where Pixel loses. Built fromwebsite/by a new Pages workflow; the docs drift test checks both pages. (#239) - doctor:
pixel doctor --fixruns the repair commands the flagged checks name, each distinct command once and in catalogue order (the home install, the repo install, the daemon, the index), then re-runs the checks: every repair is reportedfixed,not convergedorfailedfrom the new report, and the exit code is the re-run's. Thepixel installfix now carries the--shelldoctor was given. (#251) - search:
pixel search-contenttakes the ripgrep flags agents pass by habit:-g/--glob(.gitignorerules,!excludes, repeatable),-t/--type(rust,py,ts,md…),-l/--files-with-matches,-F/--fixed-strings, and-nas a no-op. They used to be a usage error that cost an agent a turn; the filters apply to the index's matches. (#254) - docs: The website's section titles now assemble out of pixels the first time they scroll into view, each with its keyword in green that lights up last, like a LED sign; reduced motion keeps them static. (#272)
- docs: The website gains a
/savings/estimate: a team's large-file reads times the saving measured on the well-known files, as a range around the median labelled an estimate, with the math, a share link and a README badge, computed in the page./benchmarks/now opens on the commands to measure Pixel on your own code. (#280) - docs: The website gains a comparison page per alternative under
/vs/(GitNexus, shunt, Jev, semble, grep, a language server, an editor's index), each with a short answer, the figures side by side, how they were measured and where the other tool wins. The home's Alternatives cards and the pages read one file,data/alternatives.toml, andllms.txtlinks every page. (#281) - docs: The website has a page per agent under
/for/: whatpixel installwrites for it and where, the plugin or rules file when it writes nothing, thepixel doctorcheck and the removal. One data file feeds them, the home and the/docs/table, and a test holds it to whatpixel installreally writes. (#282) - docs: The website gains
/answers/: six questions developers ask about coding agents (whole-file reads, MCP or CLI context cost, Claude Code's tokens, grep or an index, tool adoption, blast radius), each answered with a figure from/benchmarks/, its section and where Pixel does not win. The build fails on a number that section does not carry;llms.txtlists them. (#285) - docs: the website can count visits with Cloudflare Web Analytics, cookieless, once its token is set in
hugo.toml; the footer then says so and that the binary measures nothing. (#287) - cli:
pixel auditmeasures what an agent reads to learn what a repository's largest source files contain: each whole file against itslist-signaturesoutline, in tokens, with the total, the per-file median and per-language coverage. Files changed since indexing or with no signatures are left out and counted. A first run on a fresh clone builds the graph; nothing leaves the machine. (#290)
Changed
- install: the SessionStart hook no longer pastes the 2 KB capability block (every command, internal ones included) into Claude's context next to the agent prompt; one line says which commit the index covers and whether the code graph and history index are there. About 860 fewer tokens per session; the block stays in the hook's JSON for tools that parse it, and is still the context when no prompt is deployed. (#253)
- install: the SessionStart index line says which history phase is still running (commits and changed paths, blob sizes, or diff text), so an agent knows whether
file-historyor a phrase search is incomplete, instead of "still building". (#258) - docs: The website reads better on a phone and shares better anywhere: commands wrap instead of clipping, Docs stays in the nav under 620px, the docs code blocks get a Copy button, a 404 page, Open Graph and JSON-LD tags, a font request trimmed to the axes in use (120 KB of font CSS down to 17 KB), the hero counter announced once to screen readers, and sections that fade up as they scroll in, off under reduced motion. (#269)
- docs: The website gets a splash on the home, an animated pixel backdrop, chapter dividers with a progress rail, brand-coloured agent logos, pixel icons, a clearer JevBench block, a lighter install section, an agent demo that waits for Play, and outbound links in new tabs. (#271)
- docs: The website's hero now says who Pixel is for (Claude Code, Codex, Pi, Cursor and the rest) and the problem it solves before its category, and the token wall labels its two sides, shows the saving and turns a token into lines of code. (#273)
- docs: The website shows its scoping example right after the hero and its measured numbers after it, so a visitor sees what Pixel does before the comparisons. (#274)
- docs: The website's token wall now reads Hugging Face's
trainer.pyinstead of one of Pixel's own files, and/benchmarks/gains a table of eight well-known files (79.7 to 97.2% saved, median 94.5%) measured byscripts/bench-read-savings.sh. (#275) - docs: The website gains an "Alternatives" chapter (Pixel against GitNexus, shunt and Jev, with the rows where they win) and a "Teams" chapter (what Pixel gives developers, lead developers, harness engineers and CTOs, each with its number), and
llms.txtgives assistants the same head-to-head results with their sources. (#276) - docs: The README gives the GitNexus head-to-head, the licence difference and the well-known-files range, and points assistants to
llms.txt. The README, docs, FAQ andSECURITY.mdname every network path, includingpixel web-searchfalling back to DuckDuckGo and Wikipedia even with SearXNG set; the home badge narrows to "Your index stays on your machine". (#277) - cli:
pixel web-searchwithPIXEL_WEB_SEARCH_URLset now queries that SearXNG instance alone: a thin, empty or failed answer is no longer topped up from DuckDuckGo and Wikipedia, so the query stays with your own instance. Without SearXNG the public chain is unchanged. (#278) - docs: The website now has a sitemap named in
robots.txt, "Updated" dates from Git on/docs/and/benchmarks/, a 1200x630 share card rendered from the token wall's data, and a JSON-LD graph that tells Pixel apart from Google Pixel and carries the "Fair questions" as a FAQ from the same source as the page. Every block shows before the first scroll, and the footer links Discussions and the releases feed. (#279) - cli:
pixel list-signatures <file>now measures its saving on the spot: its stderr report readsfull read N tok, pixel answer M tok (-X%)from the file's size and the outline printed, both bytes / 4 like the benchmarks, instead of a 4 KiB-per-file guess that showed 47% where the answer was 94% smaller. Other commands' estimates name their base. Records move toworkflow-v2. (#284) - docs: the website moves to https://pixel-cli.dev/; the old
liviogama.github.io/pixel/addresses redirect to it. (#286)
Fixed
- graph: a Rust
usenow records the names it binds (use crate::push::{PushOptions, push}bindsPushOptionsandpush), so a call to an imported function another file also defines resolvesexactinstead of staying unresolved:who-callsonpixel-ops'spushfound no caller and now findsship_with_lease. On this repository 245 more call edges are exact (+6.5 %). (#255) - graph: a graph built before Rust
usebindings were recorded is now rebuilt on the next query, instead of keeping the unresolved edges those bindings fix until a full rebuild. (#258) - search: with
-g/-t,--limitcounts the matching lines printed, not the index rows read before the filter (--limit 1 -g 'tests/*'could print nothing), and--offsetresumes past the rows the filter dropped. The last-gmat...
v0.5.0
Immutable
release. Only release title and notes can be modified.
pixel 0.5.0 makes the call graph answer questions with proof, reach beyond one repository, and say where its time goes. One change needs action from 0.4 users: pixel doctor now exits 1 on a red check instead of 0, so a script or CI step that ran it unconditionally may now fail.
Highlights
pixel evaluate pathanswers whether one symbol reaches another in the indexed call graph, with the edges that prove it, an absence bounded to an exhaustive walk, or a typed reason;what-changednow also lists the changes it could not map to a symbol.- Beyond one checkout:
pixel workspacefansimpactandwho-callsout over member repos,index-pack/index-unpackship a built index between machines, andpixel mcpserves the core queries over MCP stdio. - Lighter and more legible daemon: an idle daemon sleeps instead of polling, history and transcripts are indexed on demand, the freshness walk runs in parallel, and every
actions.jsonlline records how its request was served and where the time went.
Added
- release: a release section can now open on its own narrative. An optional
changelog.d/_highlights.md— a lead paragraph and a### Highlightslist — is folded above the change sections at cut time, and so leads the GitHub release body too, which keeps that narrative out of the individual entries. (#197) - graph: new
predicatemodule, the evaluator behindpixel evaluate: multi-source reachability over the stored call relation that answersestablished(with a re-read witness path or an identity witness),absent_in_snapshot(only after an exhaustive traversal) orunknownwhen the depth or time cap dropped a frontier node;--tiersselects the relation, never a threshold. (#198) - cli:
pixel web-search "<term>"— deterministic web retrieval for the plan-refinement gate, no LLM and no daemon. Provider chain: SearXNG (PIXEL_WEB_SEARCH_URL), then DuckDuckGo Instant Answer and Wikipedia OpenSearch fallbacks; bounded hits withcomplete/unresolvedmarkers,--limit,--json. (#201) - proto: added the
pixel evaluatewire contract (pixel_proto::evaluate): one JSON object per answer,evaluationorerror, whosestatus,answer, witness, typedreasonandnext_actionsare one enum on the Rust side, so a verdict without its witness or anunknownwithout a reason cannot be built and is refused on read; the first summary sentence always names snapshot, relation, tiers and traversal (#202). - evaluate:
pixel evaluate pathanswers whether one symbol reaches another in the indexed call graph, with the edges and call sites that prove it, an absence bounded to an exhaustive traversal, or a typed reason and a next action. It names the snapshot it answered from and refuses to answer when the working tree moved before or during the walk; an unreadable database is reported as a technical failure, not a missing symbol, and--innarrows the lookup to a directory (#206). - graph:
what-changednow reports what it could not map.uncovered_changeslists each changed range with no symbolic anchor and its motif (outside_symbol,unsupported_language,excluded_by_file_cap,excluded_by_size,not_indexed,non_text_change);unanchorednames the symbols a change deleted. Both sides of the diff are read, the base side re-extracted from its blob, and a binary or mode-only change now counts as changed (#207). - cli:
pixel workspaceregisters member repos in.pixel/workspace.json;impact/who-calls --workspacefans out with per-repo provenance. (#211) - cli:
pixel index-pack/index-unpackmove a checksummed index bundle between machines — CI builds once, teammates install. (#211) - cli:
pixel mcpserves search/resolve/impact/evaluate/context/status over MCP stdio — one integration for every MCP-capable agent. (#211) - cli:
pixel coveragereports files-on-disk vs indexed per language;pixel plantracks done state in.pixel/plan.jsonacross re-plans. (#211) - docs: new always-loaded rule
.agents/rules/measuring.md— what a number must carry before it counts as evidence: a run's identity checked before it is cited, the command quoted beside the count, one input per ablation, the baseline measured, and a merged explanation corrected when a later measurement contradicts it.test-campaigns.mdgains the two mutant-counting traps that cost a run each (#213). - repo:
just cleanreclaims the disk a checkout spends on what it can rebuild, across every worktree at once -- 38 G on the machine it was written on, 21 of them in a singletarget.just diskprices each scope first and removes nothing; the index, the shard cache and the bench scratch are recipes of their own, and the recall corpus is never touched.scripts/clean.shis the same tool withoutjust. (#216) - cli:
pixel classify— a zero-shot decision over a bounded label set, answered by an OpenAI-compatible chat completion:--remote-preset openrouter | ollama | local,--remote-model,PIXEL_REMOTE_*overrides, andpixel config remote-key <preset> -to store a key (0600, never logged).--context(or JSONL"context") adds shared framing,--jsonlserves one decision per stdin line, and the output disclosessnapshot.deterministic=falseandsnapshot.provider(#209, #222). - metrics: each
actions.jsonlline now says how its requests were served:servelists the route (daemon,daemon_started, orin_processwith its reason, e.g.auto_start_disabled,start_timed_out) and the phase timings (probe_ms,start_ms,request_ms,open_ms,handle_ms) of every repository command. A slow call can now be told apart as a busy daemon, a cold start or a slow query (#241). - recall:
recall context,askandsearchnow record theirservestep inactions.jsonl: answered by the recall daemon (request_ms) or in process (daemon_absent,daemon_error,not_routedforcontext), with the transcript catch-up and model load timed apart from the query (open_ms,handle_ms). A 20 srecallline now says which of the two it waited on (#242). - install: after an upgrade, ordinary commands print one stderr note (not
install,doctor,uninstall, nor the hook, statusline, rg-shim and MCP streams) when theagent-prompt.mdorsubagent-prompt.mdthatpixel installdeployed no longer matches the running binary. Agents read those files as they are, so they kept the old release's command map with nothing butpixel doctorsaying so. Runpixel installto clear it (#243).
Changed
- release:
prepare.shnow gates the shape of achangelog.d/entry, so a released section is scannable: an entry opens on its scope (**graph:** …) and is refused over 900 bytes, with a warning over 500 or when it references no pull request. The 0.4.0 cut ran 264 to 1265 bytes a bullet with no scope prefix. (#196) - prompt: the agent prompt now gates
pixel planrefinement instead of leaving it implicit. A deterministic plan stays the base; the agent resolves an unknown term (one web search or one clarifying question, then re-runspixel plan) only when findings areunresolved/empty or a named term misses bothfind-codeandsearch-meaning— otherwise the checklist is used as emitted (#200). - graph: the freshness walk behind every graph command read and hashed its files on one thread; it moved to rayon's pool, path-sorted so the signature stayed byte-identical. A paired run cut a 50 000-file tree from 6.9 s to 0.9 s, and this repository's 249 files from 9.7 ms to 7.1 ms (#203).
- index: history and transcript indexes are demand-driven — the daemon no longer ingests git history at startup, and
recallcommands run a bounded on-demand catch-up in-process (cold: last 7 days, capped at 30 days) instead of answering from an empty corpus.build-index --historyandrecall indexremain the explicit full builds. (#211) - cli:
pixel config metrics on|off [--global]is the persistent opt-out for the 🟩 stats footer, writing.pixel/config.jsonat the repo or~/.pixel/config.jsonmachine-wide; the nearest scope wins over the on-by-default baseline,--metrics=off/PIXEL_METRICS=0still veto per invocation, and hidden output still records to the action journal. (#218) - daemon: an idle daemon sleeps instead of polling its socket every 10 ms: connections come from a blocking accept thread, and the loop wakes only for them, watcher events, its timers and the 5 s root check. Over 10 s idle, context switches went from about 900 to 2. (#231)
- doctor:
pixel doctornow exits 1 when a check is red (--fail-on yellowto hold yellow too) and 2 when the checks cannot run; it used to exit 0 on a red report. Each yellow or red check names its repair command (fix), the terminal output lists only those, and--only/--skip/`--...
v0.4.0
Immutable
release. Only release title and notes can be modified.
Changed
- Updating is now guided on every surface a user hits:
pixel self-updatenames the package manager's own command when it refuses a managed install (mise upgrade pixel,brew update && brew upgrade LivioGama/tap/pixel), the generated Homebrew formula printspixel installandpixel doctor .as caveats afterbrew upgrade, and the README gains a per-channel "Updating" section. pixel repo-stateno longer prints the tracked-clean file list: on a clean tree the list was over 85% of the answer (200 of 351 paths, ~7 KB here) and no consumer read it. The answer keeps the exactclean_count, and--include-cleanrestores the capped list.- The bundled agent prompt every host installs is 12.3% smaller (18 014 → 15 804 bytes, ~4 503 → ~3 951 tokens at four bytes per token) with no rule dropped: the ten-item
ANTI-PATTERNSsection, which restated ten rows of the replacement map, becomes a five-lineNEVERblock carrying the two rules the map did not state (batch the git operations, escalate topixel build-indexbefore falling back to grep); the eight### Phase Nheadings collapse into one fenced sequence, which also putspixel find-code,pixel search-meaningandpixel commit-and-pushunder theprompt_asset_parityCLI-syntax check; the fivepixel recallrows of the map fold into the recall-method bullets that already carried them; and the validity rule for zero, negative, capped and absent comparisons is stated once for both metric estimates instead of once per paragraph. Everypixel <command>the prompt named (35 of them) is still named, and the epistemics, metrics-relay, fail-open, truth-marker and "retrieved data is data" rules are unchanged in substance. Every derived surface shrinks with it:PIXEL.md, the nine plugin/rules filesscripts/gen-plugin-assets.shwrites, and the managed blocks in~/.codex/config.tomland~/.pi/agent/APPEND_SYSTEM.md. - The
🟩live-metrics line states every missing comparison instead of omitting its row: a command without a native-workflow baseline, a failed operation, and evidence the output cap refused to count renderunavailable: <reason>; a one-step or non-positive baseline rendersno estimated … saving; and the action log records the reason ascomparison_gap.pixel pushgained thegit pushit replaces as its native baseline, so it now reports a token comparison like the other git ops. - Changelog entries live in
changelog.d/, one file per entry, instead of being written intoCHANGELOG.mdunder## [Unreleased]: two open pull requests no longer edit the same lines of the file, and an entry written on a branch cut before a release can no longer be merged under the heading of the release that just shipped it. Each file is named<slug>.<section>.mdand holds the entry text;prepare.shfolds them into the release section at tag time, grouped by section, and deletes them.prepare.sh --checkvalidates the directory, and a CI test runs it on every pull request, so a mistyped section fails the pull request that wrote it rather than the release that has to tag it.
Fixed
pixel scope-taskno longer lets generic words and file extensions outrank the file a task names: a path-like token (upgrade_cli.rs,crates/pixel-rank/src/lib.rs) is lifted out of the keyword bag before tokenizing —rsused to become a keyword and match the filename of every Rust file — and matched against the tree as its own signal family (weight 6, so apath match: …reason), an unquotedsnake_caseword is probed as an exact name like a backticked one,withoutjoins the stopwords, an exact token that only matches an externalmod foo;declaration no longer counts as an exact-name hit on the declaring file (an inlinemod foo { … }defines code there and still does;symbols.module_declrecords the difference), and a file whose only second family is a graph neighbor is no longer promoted to P0. The JSON answer now carries the liftedpath_tokens. On the pinned 41-commit corpus (crates/pixel/tests/cli/scope_task_precision.rs,--ignored, ~2 min), recall@P0 rises 0.7317 → 0.8049 and precision@1 0.5122 → 0.6098, with no probe regressing, three going 0.00 → 1.00 and one 0.33 → 1.00; P0 precision rises 0.2244 → 0.2390 rather than being bought with recall.- The bundled agent prompt no longer makes committing and pushing an unconditional part of every task: phase 8 is marked opt-in —
pixel commitneeds an explicit commit request,pixel commit-and-push(orpixel push) needs separate authorization, and both show--files <f>...so only the reviewed change set is staged instead of the whole working tree, with the replacement-map rows scoped the same way. The phase-3 discovery escalations (pixel find-code,pixel search-meaning) are markedonly whento match the replacement map, and FAIL-OPEN's "Pixel indexes source code only" is corrected to what the index actually holds — regular non-binary files up to 4 MiB that are not git-ignored — so a search that must cover ignored, binary or oversized files falls back to nativegrep. Every derived surface is regenerated. pixel impact,pixel who-callsandpixel call-pathnow see two classes of internal calls that were previously counted only as unresolved lower-bound noise. A receiver call whose callee name has exactly one callable definition — an inherent method in the caller's own file — links to it asprobable(w.push_call(),idx.decide()); and a receiver naming a type links to the sole candidate whose qualified name carries that type (pixel_git::GitRunner::new,VectorStore::open). The receiver-shadow veto still applies where a competing definition exists (graph::build::f()next to a localf), and trait-impl methods on a value receiver (path.clone()besideBox::clone), free functions with a receiver (path.exists()), chained receivers, two same-name methods in one file (A::walkbesideB::walk) and same-named types in two crates all stay unresolved, solower_boundstays honest. Existing graphs rebuild once (extractor version bump).pixel impactno longer ranks an upstream walk that resolved no caller: an empty caller set reportedLOW, which reads as "safe to change", while it is equally consistent with "unused" and with "reached only through a reference class the index does not record" (dynamic dispatch, a callback passed as an argument, property access). The verdict is nowUNKNOWN, carrying arisk_notethat names both readings and the text search that separates them; the note is omitted — not null — on every ranked verdict, so an absent field can only mean "no caveat". Downstream is unchanged: an empty callee set answers what the symbol calls, which is a fact about the code rather than a safety claim. A callback registration is not a caller, so a symbol with no caller but a non-emptyreferenced_byalso reportsUNKNOWN.- The
asklane no longer fails on a change to the code it measures: it gated every probe onndcg@10 > 0over the 16-filecrates/pixel-graph/srccorpus, where 10 is 62% of the files, so"imports dependency resolved graph edge"— whose labelled file sits 10th onmain— was evicted to 11th by 26 lines of code with no test and no comment in any file of the subtree. A probe belowkis the channel reporting the file at rank 11, not the "no relevant evidence" a score of 0 stands for: the per-probe guard is now a presence check at the lane's own result limit, the mean still gates on how high the channel ranks the labelled file, and every probe's rank is printed so a slide toward the boundary is visible before it gates. - The ranking gate no longer fails on a difference it cannot show:
validate_query_scorecompared the reranked score to its unranked baseline with a bare<, so therankedlane failed on a 7.9e-4 loss while reportingquery regressed: 0.571 vs baseline 0.571— two identical numbers a reader cannot act on. The comparison now allows a 1e-3 tolerance, two orders of magnitude below the ~7e-2 a single rank position is worth at the tail of NDCG@10, and the failure message names the delta and the tolerance so a real regression is never reported as a tie. pixel find-coderesolves an exact enum-variant identifier to its definition instead of returning unrelated string hits: Rust enum variants are now symbols in the code graph (qualifiedEnum::Variant), so an identifier-shaped query likefind-code "SelfUpdate"answers the variant's file and line through the exact-symbol tier, above the string concepts that merely mention the name.
v0.3.1
Immutable
release. Only release title and notes can be modified.
Changed
install.shis published as an asset of every release, and the installer URL is nowhttps://github.com/LivioGama/pixel/releases/latest/download/install.shinstead ofscripts/install.shon themainbranch:mainis now the only long-lived branch and can be ahead of the latest release, so the script is taken from the release it installs. The oldraw.githubusercontent.com/.../main/scripts/install.shURL keeps working, but serves the branch's copy. The releasesmokejob checks the new URL.
v0.3.0
Added
pixel rename <name> <new_name>— IDE-style symbol rename driven by the code graph. The graph nominates the sites (the declaration, every resolved call and referencesite_line, and the import bindings that pull the symbol out of its file), each touched file is re-parsed with its tree-sitter grammar, and only identifiers verified to sit in the position the graph asserted are rewritten — bytes in comments, strings, andasaliases are never touched.--fileor--uiddisambiguate a shared name (an ambiguous call lists the candidates); sites the graph could not resolve are reported inskippedinstead of being guessed, and occurrences of the old name the graph never claimed are counted inunclaimed_text.--dry-runreturns the same verified edit set without writing; a file that changed between planning and writing is refused rather than half-renamed. New daemon oprename(inSESSION_CAPABILITIES), served by the daemon and the in-process fallback alike.pixel installdeploys the Pixel plugin to Antigravity (~/.gemini/config/plugins/pixel/), enables it in~/.gemini/config/config.json, and installs thepixel-guardhooks in~/.gemini/config/hooks.jsonwhen the Antigravity config directory exists.pixel doctorchecks the install andpixel uninstallremoves it. The.agents/skills/pixel/SKILL.mdskill surface is now generated byscripts/gen-plugin-assets.sh.
Changed
pixel <command> --jsonanswers a failure with a parsable failure envelope on stdout (ok: falsepluserror.code, the same reason stderr carries) instead of leaving stdout empty; human mode is unchanged (reason on stderr, stdout empty, exit 1), and a command that already wrote its own document (check-release --json) still writes one. The daemon now classifies the codespixel-opsnames in the messages it types itself (BUSY_REPOSITORYfor a held lock,NON_FAST_FORWARD,STALE_STATE,UNSUPPORTED_STATE,REF_EXISTS,GIT_FAILED,NETWORK_AMBIGUITY) instead of falling back toINVALID_INPUT;pixel_proto::ErrorCodedocuments which variants no producer reaches.pixel search-content --jsonnow ends its NDJSON with a page-metadata line (truncated,next_offset,epistemics,warnings), so a page cut short is no longer byte-for-byte a complete answer, and the global stdout cap is enforced while assembling the page (--contexttext, added after the daemon's own byte cap, used to exceedPIXEL_OUTPUT_CAP_BYTES— 154 KB measured under an 8 KB cap).pixel scope-taskandpixel find-codererank by the coefficients their signal options carry: the rerank formula readsactivity_weight,session_weightandfan_in_weightfromSignalOptionsinstead of keeping its own copies of 0.15/0.35/0.20, so tuning one of them changes the order instead of being silently ignored (the defaults, and therefore today's ranking, are unchanged).
Fixed
pixel list-errors since --tsrejects a duration it cannot represent instead of panicking or answering a wrong window: a non-ASCII unit (--ts é) cut the last byte inside a multi-byte character and panicked withnot a char boundary(exit 101, empty stdout), and an overflowing value (--ts 9223372036854775807d) wrapped the multiplication to a negative window and reported "no errors". Both now print the existingbad duration "…" (use 30s, 5m, 2h, 1d)and exit 1.pixel installtreats~/.pi/agent/APPEND_SYSTEM.mdas a shared file: the prompt goes between pixel's managed markers, the previous bytes are backed up, a failed write fails the step instead of being swallowed, andpixel uninstallstrips the block instead of deleting the file, so the user's own pi instructions survive.pixel doctorgained theinstall.pi-promptcheck andinstall.agent-promptnow compares the deployed prompt byte for byte with the bundled asset, so a prompt that diverged from the binary's is no longer green. A shell profile whose# >>> pixel-managed >>>block has no end marker is refused (it used to lose every line after the marker) with a red step, and every profile change is backed up (*.pixel-bak.*) and written through a temp file and a rename.- A qualified call no longer resolves to a same-name symbol in the calling file (
pixel who-calls,pixel impact,pixel plan --query dead-code).pixel_graph::build::build_graphinside the daemon'sbridge(or anyType::method()) whose callee name also existed locally resolved to that local symbol (capped atprobablebecause the receiver is real), so the real callee had no edge and the bridge counted as dead:impactansweredlower_bound: falseandpixel plan --query dead-codelistedbuild_graph/tree_delta. A call with a real receiver and a same-name local definition is now left unresolved, so the envelope reports the lower bound instead. pixel-git's runner enforces its timeout while waiting for a git that closes its own stdout: the loop stopped at the reader's EOF and then blocked inchild.wait(), so a pager, a hook or a merge driver that closes fd 1 and keeps running froze the calling thread — a daemon request — for the process's whole lifetime. The wait now runs under the same deadline as the read (child killed,GitError::Timeout), a failed read surfaces as an error instead of a silently truncated output, andpixel plan-rollback --apply --merge'sgit merge-filegoes through the runner instead of a direct spawn, so it gets the timeout, the output cap and stderr redaction like every other call.pixel recall daemon statusandpixel recall daemon startprobe the daemon with aPingthat never starts one: the probe went through the auto-starting daemon client, which spawned a repositoryServiceon the corpus directory (~/.local/share/pixel/recall/.pixel/base.shard, a daemon holding the socket for 30 minutes and refusing everyRecall), sostatusnamed the wrong daemon andstartprinted "already running" without ever starting the recall service.pixel recall showandpixel recall contextcount the bytes they write,pixel recall show --jsonis capped like every other machine document (PIXEL_OUTPUT_CAP_BYTES,truncatedandcap_bytesonce cut) instead of printing a turn of any size, and a recall daemon that answers an op with an error is reported on stderr before the in-process fallback instead of being taken for no daemon.- A closed stdout pipe is no longer a failure:
pixel action-log . --limit 100000 | head -1(and every other command rendering through the CLI's countedprint!/print_errorpath) exited 101 with aBrokenPipepanic, whichset -o pipefailreported as a failed command; EPIPE now counts as success, the policy the capped JSON path already applied. - The code graph no longer resolves a Go import or a Java wildcard import (
import com.example.*) to the first file of the first directory whose path suffix matched. Two…/utilpackages, or twocom.examplepackages, made the import edge — the evidencepixel impactandpixel who-callsread — point at whichever package the index walked first, and the target moved when the tree was reindexed. A suffix shared by several directories now stays unresolved, so the calls below it fall back toProbableor to the epistemic envelope; the files of one directory are still one package and resolve as before. - The daemon no longer answers a request from an index older than the mutation before it: the debounced watcher batch is applied before a connection is handled instead of after the previous one, so a
pixel search-content,pixel planorpixel what-changedthat follows an edit (orpixel sync-branch) sees it. A watcher-driven graph update that fails (a locked or corruptgraph.db) and anotifybackend error are no longer dropped: both are logged on the first failure and every doubling after it and counted inpixel statusaswatcher.graph_update_failuresandwatcher.notify_errors. GraphStore::openpins SQLite'sbusy_timeoutat 5 s, so a graph update that overlaps a concurrent writer waits for it instead of failing withSQLITE_BUSY("database is locked"). The value was already what rusqlite sets by default; it is now explicit and does not depend on that default.pixel recallcounts the transcript records it cannot parse instead of dropping them silently: a line a crashed writer left truncated is reported in the ingest count, and the daemon logsrecall daemon: <agent> wrote no session, skipped N unreadable record(s)(at most once a minute per agent) even when the pass wrote no session, so a transcript that never indexes is visible.pixel replay-flow replay --executefills a step'svalue_varwith the flow's declareddefaultwhen no--varwas passed, instead of sending the literal{{var}}placeholder to agent-browser. Arequiredvariable carrying a default was already accepted without a--var, but the resolver never readdefault, so a flow declaringdefault: "west"typed{{account}}into the form.pixel commit,pixel commit-and-push,pixel push,pixel squash-branch,pixel new-branch,pixel fast-forwardandpixel sync-branchnow serialize on one lock per repository. The lock was keyed on the raw<repo>/.gitspelling while the journal key was the canonical path, so/repo,/./repo, a symlink to the repository,/tmpvs/private/tmpand a linked worktree each took their own lock and two mutations of one repository ran concurrently. Lock and journal now derive from one canonical, worktree-aware key — git's real common directory, so a worktree shares the main checkout's lock; crash-safety records written by an earlier version keep the old key and are not resumed.pixel commitrefuses an interrupted commit whose recovery record it cannot read (a write truncated by a full disk, permissions, a futureschema_version) instead of re-runninggit add/git commit: the retry returned a successful second commit withHEADmoved...
v0.2.6
Fixed
pixel search-meaning, and the semantic fallback ofpixel scope-taskandpixel find-code, no longer search nested checkouts: a directory holding a.gitfile or directory (a linked worktree such as.claude/worktrees/<name>, a submodule, a nested clone) is skipped, so copies of the repository's own files on another branch stop filling the hits. The coveragescopesays so.pixel scope-taskadds semantic hits as unverified P2 leads (semantic lead (similarity 0.39, unverified)) instead of P1 targets, names that caveat and a capped scan in its epistemics, and no longer downloads the embedding model inside the request. The similarity does not separate related from unrelated files: on this repository a nonsense task's best file scored 0.33, a relevant French one 0.33 to 0.39, so a nonsense task used to come back with P1 targets.pixel find-code's semantic fallback carries the same caps and offline rule.- The per-commit base-shard cache (
~/.cache/pixel/shards) no longer spreads an incomplete or foreign index to other worktrees. A shard built withPIXEL_INDEX_NO_DEFAULT_IGNORESshared its key with the default build; the pixel-only.gitignorerule read the worktree file instead of the commit; a blob git failed to read was dropped and the shard still published; a failedgit ls-treebuilt and published an empty shard; and a corrupt entry was never replaced. The key now carries the ignore variant, the rule reads the committed blob, only complete shards are published (submodules are not counted as missing), a listing failure is an error, and a rebuild replaces an entry that did not open. pixel plan's dead-code query no longer reports Rust trait implementation methods (fmtinimpl Display for X,from,default,drop): they are called through the trait, never by name. The graph records them (symbols.trait_impl, extractor version 3, so existing graphs rebuild once).pixel plan --query hotspots ../repoplans../repo. Both the prompt and the path are optional positionals, so the directory was read as the prompt and the current directory was planned; with a query other thanby-concept, a prompt that names a directory is now the path.- The session-start capability block lists the commands an agent can type (
scope-task,fast-forward,fetch, …, aspixel --helpshows them) instead of the daemon's op tags. An agent readingupdateorsyncthere ranpixel update(nowfast-forward) orpixel sync(nowfetch), andtargets,searchorhistory_oponly worked, if at all, through the pre-rename aliases. pixel who-wroteno longer fails on every file when git'sblame.ignoreRevsFilenames a file the repository lacks (a global.git-blame-ignore-revsdefault): git refused the blame with "could not open object name list". The blame is retried without the ignore list and the answer carries a warning saying so; an existing ignore-revs file is still honoured.
Removed
- The root
plugin.json({"name": "pixel"}only). A root manifest takes precedence over the tool directories: Copilot CLI reads it before.claude-plugin/plugin.jsonand so loaded a plugin without the skill or the hooks, and Codex's Agent Plugins loader ignores the hooks declared in.codex-plugin/plugin.jsonwhen one is present (openai/codex#39895). Grok'splugin.jsonis optional metadata; its marketplace entry keeps thepixelname.
v0.2.5
Added
pixel plan "<prompt>"(or--query dead-interactive|dead-code|hotspots|recent-changes|by-concept): a deterministic todo list from the code graph and git history, as markdown,--format compactor--json, each finding with file, line, severity and fan-in.- Native plugin manifests for Claude Code, Codex, Gemini CLI, OpenCode, pi, Devin, Qoder and Grok, plus generated rules for Cursor, Windsurf, Kiro and Cline, all from the agent prompt (
scripts/gen-plugin-assets.sh). A SessionStart/SubagentStart hook (hooks/pixel-context.sh) injects the protocol as context. pixel recallindexes pi sessions (~/.pi/agent/sessions, or$PI_CODING_AGENT_DIR/sessions).pixel impactlists the symbols that pass the target as a callback argument underreferenced_by, and graph answers carryepistemics.extraction_limits(callbacks, dynamic dispatch, macros, eval) next toclosed_world: false.pixel scope-taskexpands French task words to their English code terms, and falls back to semantic code search when the lexical pass finds no P0/P1 file.- Base shards are cached per commit under
$XDG_CACHE_HOME/pixel/shards(else~/.cache/pixel/shards), so a second worktree at the same commit links the shard instead of rebuilding it. .agents/skills/release/: the release procedure as a project skill (version choice, tag, thedevelop→mainPR, a release record for resuming after a context loss, verification of the published release from a fresh download with the binary'scommit:line matched to the tag, failure classification before any retry, hotfixes) plusprepare.sh x.y.z, which moves every workspace member tox.y.z, inserts## [x.y.z] - DATEunder an empty Unreleased, refreshesCargo.lock, lists the pull requests merged since the last tag for the changelog review and runscheck-releasefrom the tree, refusing before any write when the tag or heading exists or Unreleased is empty. Listed in AGENTS.md and CONTRIBUTING.md.
Fixed
- Plugin installs get updates and a protocol they can follow. The plugin manifests said 0.2.3 while the crates were at 0.2.4 and nothing bumped them; Claude Code and Codex only update a plugin when its version changes. They now carry the workspace version,
prepare.shbumps them and regenerates the prompt surfaces, andpixel check-releasefails on a manifest at another version (newplugin-versionscheck). The SessionStart/SubagentStart hook no longer needspython3, gives sub-agents the short sub-agent prompt (newPIXEL-SUBAGENT.md) instead of the 18 KB agent prompt, and injects a short notice instead of the protocol whenpixelis missing or predates the command names; the injected setup text no longer tells the agent to pipe an installer frommainintosh. The hook command resolved against the non-existentCODEX_PLUGIN_ROOTand fell back to the project directory; it now usesCLAUDE_PLUGIN_ROOTor Codex'sPLUGIN_ROOT. Manifest descriptions name the current commands. pixel recallindexes pi sessions as pi writes them. A forked session starts with a copy of its parent's entries, which were indexed a second time (twice in search results and embeddings, with the parent's first timestamp); the fork now keeps only the entries after its header and records its parent. A subagent run (<cwd>/<parent>/<hash>/run-<n>/session.jsonl) is marked as a subagent of its parent instead of a top-level session, sorecall sessionshides it by default and its orchestrator prompts are not embedded as human intent.!commandruns (bashExecutionmessages) are indexed, stringcontentis read as well as text parts, andPI_CODING_AGENT_DIRmoves the indexed and watched directory as it moves pi's. The guard's transcript-store advisory covers~/.pi/agent/sessions.pixel scope-taskno longer drops French function words from English tasks:comment,car,plus,sans,des,estwere stopwords for every task, so "fix comment parsing" searched forparsingalone. A task is French when two distinct French function words (or unambiguous French domain words such asconnexion,bouton) occur; only then are the French stopwords dropped and the French relation keys that are also English words (client,message,modifier,installer,vue, …) expanded to their translation.trèswas never dropped (the task is accent-folded first);tresis.pixel planruns in the daemon (a newplanop, daemon protocol 10) instead of rebuilding the whole graph from the CLI on every call. The CLI build deleted and re-inserted each file's edges while a running daemon could read them (animpactanswering 0 callers, orSQLITE_BUSY); the daemon now refreshes its graph incrementally and serves the queries.pixel plan's dead-code findings readNo callers found for functionx: confirm it is unused before removinginstead ofRemove unused function, and skipmain, Go'sinitand functions in test files (tests/,__tests__/,*.test.*,*.spec.*,*_test.go,test_*.py). Prompts classify by whole words ("unlinked" no longer asks for dead links).recent-changesranks the files the graph knows by commits in the last 30 days and caps after that filter (it used to keep the first paths alphabetically, README included, and returned nothing once a month ofgit logexceeded 1 MiB). Hotspots break fan-in ties by path, and fan-in lookups are deduplicated and chunked so a plan over more than 32 766 dead symbols no longer fails with "too many SQL variables".- The code graph no longer links a value passed to a call to a function of the same name:
consume(user.name)madename()look referenced (hiding it from dead-code findings and adding it topixel impact'sreferenced_by). A member argument is a callback reference only onthis/self/Self(this.onClick), and an argument no function in the graph is named after (g(x)) leaves no unresolved row, sopixel who-calls <fn> --role calleesno longer reportslower_boundfor every function that passes a variable on. An unresolved callback no longer counts as an unresolved outgoing call either. - Rendering a JSX component (
<Button />,<Menu.Item>) is a call edge from the renderer to the component, so a component that is only rendered has callers inpixel impact/who-callsand is not reported as dead code bypixel plan. - A graph built by an older extractor is rebuilt: the freshness signature only hashed file contents, so existing
graph.dbfiles never picked up extraction changes.graph.dbnow records an extractor version and a mismatch forces a full rebuild. - The follow-up commands pixel prints for the agent use the current command names:
pixel plan-rollback's revert option (pixel plan-rollback --apply …, waspixel rescue --apply),pixel dig-history's--showfollow-up (waspixel excavate --show), the duplicate-flow error (pixel replay-flow revise), the scope-task manifest note (pixel scope-task --clear) and the rebuild hints (pixel build-index). The old spellings only ran through the aliases that go away at 1.0; a test now fails when production code prints a pre-rename or unknown command.scripts/system_audit.pyexpected the guard's rewrite to namepixel search-compatand failed; it and the demo and bench scripts call the current names. pixel self-update(upgrade) no longer overwrites a binary a package manager installed. Without--install-path, its resolution (the running binary, else the firstpixelon PATH, else~/.local/bin/pixel) lands on the mise install dir or the Homebrew keg on any machine wherepixelcomes from them, so a bareself-updatefrom a checkout replaced mise's 0.2.4 with a localtarget/dev-releasebuild whilemise lsstill reported 0.2.4. A resolved path under mise'sinstalls/(~/.local/share/mise, or$MISE_DATA_DIR) or a Homebrew Cellar (/opt/homebrew/Cellar,/usr/local/Cellar,$HOMEBREW_CELLAR), symlinks followed, is now refused before the build runs, with an error that names the resolved path and its owner and proposes--dry-run,--install-pathand--dev.--dry-runstill prints the path, then exits 1 on a refused one. An explicit--install-pathwrites where it says, as before. The new--devflag installs to~/.local/bin/pixel-dev, a side build called aspixel-devthat never shadows or replacespixel.- The surfaces that read a command name back as text accept both spellings of a renamed command.
pixel uninstallrecognised the Codex composed-guard entry only ashook composed-guard, so therun-hook composed-guardentry every install since the rename writes was never restored from its snapshot. The prompt-submit hook no longer treated a successfulcommit-and-push(formerlyship) as a task completion. The guard's foreign-hook snapshot boundary refused to replaypixel run-hook …but letpixel hook …through, which recursed. The call-loop breaker counts history written under an old name (search) toward the current one (search-content).pixel doctor'srule.scenariosaccepts a deployed rule text that names the mandatory scenarios by their old names (pixel targets,pixel resolve,pixel rescue,pixel reconcile), as every agent prompt installed before the rename does.scripts/pixel-smoke-test.shexpects the current guard advisories (pixel plan-rollback,pixel commit,pixel search-like-rg) and checksready,changes,symbolandimpactunder both names. - The command rename had also renamed the wire layer:
Op::op_name,SESSION_CAPABILITIES, the daemon's op-name matches and its JSON output keys (targets,symbol,processes,clusters) carried the new CLI spellings while the serde tags stayedsearch,targets,symbol, sotargetsresponses lost their content evidence, theinspect/reviewdirty-list rule never matched, and the session-start capability block advertised names no op answers to. The wire layer is back on the serde...
v0.2.4
Added
pixel publishandpixel shipaccept-F/--message-file <path>(-for stdin) as an alternative to-m: a multi-paragraph commit message comes from a file verbatim, trailing whitespace stripped, instead of being squeezed through a shell-quoted-m "$(cat msg)". The two flags are mutually exclusive (clap usage error, exit 2, nothing touched), and a blank file is refused before git runs..agents/skills/rust-guidelines/: Microsoft's Pragmatic Rust Guidelines as a project skill (SKILL.mdchecklist keyed byM-*id, full MIT text inguidelines.txtto grep on demand), symlinked as.claude/skillsso Claude Code loads it next to.claude/rules; AGENTS.md and CONTRIBUTING.md list it.pixel sniper runparses Ruby tool output. A failingbundle exec rails test/bin/rails test(Minitest) orbundle exec rspecrun used to leave one genericrun-wrapperrecord whose only payload was the raw output tail; it now records one error per failing test on the newminitest/rspecsurfaces (kindfailureorerror, test class and name, file, line, message withexpected/actualwhen the assertion printed them, the backtrace filtered to project paths, and the runner's rerun command) plus asummaryrecord carrying the run counters and the failing tests. The runner is detected from the output, so Rails parallel workers andparallel_testsare handled. A green run records atest-passevent with the counters, sopixel sniper testnow reports Minitest and RSpec runs the way it reports vitest instead of "no test runs recorded".rubocopoutput gives onelintrecord per offense on therubocopsurface (file, line, column, severity, cop, correctable). The generic tail record remains for everything else;tscandvitestrecords are unchanged.pixel --versionnow reports where the binary came from: after thepixel x.y.zline it printscommit: <sha>(-dirtyappended when the tree had uncommitted tracked changes),target:,rustc:andbuilt:(UTC date;SOURCE_DATE_EPOCHhonoured).pixel -Vkeeps the one-line form. Values a build cannot determine (a tarball without.git, nogiton PATH) readunknowninstead of failing the build. This answers "which build is this?" from a bug report or a CI artifact, and lets a rebuild loop see that the installed binary lags the tree.pixel release-check <version|tag> [--repo <path>] [--json]: the release consistency gate. It checks thatcrates/pixel/Cargo.tomlcarries the tagged version, thatCargo.lockholds every workspace member at its manifest version (a stale lock used to failcargo build --lockedon the release runner, after the tests had passed), and thatCHANGELOG.mdhas the## [x.y.z]heading with nothing left under## [Unreleased]. One[ok ]/[FAIL]line per check with the fix in the failure text, exit 1 on any failure. The release workflow runs it before the tests;v1.2.3andrefs/tags/v1.2.3are accepted.
Changed
- The declared minimum Rust version is now
rust-version = "1.91"(was1.85, which had not built for some time: the lockedtree-sitterneeds 1.90 andpixel-factsusesstr::floor_char_boundary, stable since 1.91). A newMSRVjob in theCIworkflow runscargo check --workspace --all-targetson that exact toolchain so the field cannot drift again. Users of the Homebrew binaries are unaffected; only a from-source build needs the toolchain. scripts/follow the current CLI: every script findspixelthrough$PIXEL_BIN, thencommand -v pixel(the mise/Homebrew/cargo installpixel upgradewrites to), thentarget/dev-release/pixelandtarget/release/pixel, instead of a fixed~/.local/bin/pixel.pixel-smoke-test.shasserts the guard hook's actual contract (advisory JSON with a pixel alternative for destructive or substitutable git commands,updatedInputrewrite topixel search-compatfor a grep on one file, silent passthrough otherwise; exit 0 throughout) across Claude, Devin, Codex and Gemini tool names, reads the install surface throughpixel doctor --json(agent prompt, shell wrappers, Codex config;PIXEL_SHELL=<shell>forwarded as--shell) instead of the hook entriespixel installstopped writing, and checks--version'scommit:line and therelease-check/upgrade/uninstall --wrappers-onlyhelp.pixel-vs-manual.shpasses a symbol uid toimpact/contextand--phrasetoexcavate(the bare forms were rejected).pixel-demo.shandpixel-bench.shgive the pixel arm the deployed agent prompt through--append-system-prompt-file/--append-subagent-system-prompt-file, as theclaudeshell wrapper does (a script callsclaudeby path, so the fish/zsh function never applied and the "with pixel" arm ran without pixel's instructions);pixel-bench-isolated.shreads its doctrine from the same file instead of the retired~/.agent-config/rules/pixel.md. Ascripts/README.mdlists every script with its one-line invocation and prerequisites; CONTRIBUTING's install loop usespixel upgradeinstead of a handcpinto~/.local/bin.- The
CIworkflow no longer runs on a pull request that only changes Markdown,docs/,LICENSEorNOTICE; the bundled agent prompts undercrates/still trigger it, and pushes todevelop/mainare unfiltered. - README no longer carries the
agent-config managedbadge and the "Install pixel rules" button that pointed at the externalpixel-rulesrepository: that rule was a copy of the agent promptpixel installstopped writing as a managed block in 0.2.0, and it had already drifted from the bundledpixel-agent-prompt.md. The "Start here" section now lists which agentspixel installwires (Claude Code, Codex, Pi) and how, and states that any other agent gets the same bundled prompt through its own rules mechanism;docs/manual-setup.mdgains an "Any other agent" section. The prompt size figures are corrected to ~300 lines / ~4 000 tokens. - The agent prompt's "Session Recall" section now carries a retrieval method, not only a command map: a table from the shape of the question (an exact token, a topic in your own words, "did we try X", "was X fixed", the session that ran here) to the recall command and to what counts as sufficient evidence, and six rules: two reformulations then stop, a miss is not proof of absence, query terms are probes and not evidence, a fix described in prose is not a fix until a tool turn shows it, narrow with
--repo/--sincebefore widening, read at the turn with--turn N..Mrather than the whole session. Re-runpixel installto deploy it. - The
fastembedfeature no longer enables fastembed'simage-modelsdefault: pixel never embeds images, and that feature alone pulled theimagecrate with every codec (rav1e, exr, tiff, png…), 64 crates and 600 lock lines out of the CLI's dependency graph. Text embedding, model download and the TLS stack are unchanged. - Building from source on macOS or Windows no longer compiles a vendored OpenSSL that was never linked:
pixel-recalldeclares it only for the targets where native-tls actually uses it (cfg(not(any(target_os = "windows", target_vendor = "apple"))), native-tls's own gate). Linux builds with thefastembedfeature are unchanged; about 90 s off a cold macOS build. pixel upgradereads the built binary fromtarget/<profile>/pixel, with the profile taken from the--profile/--releaseflags of its--buildcommand, instead of alwaystarget/release/pixel(a--buildon another profile installed whatever stale binary sat there, without error). The workspace gains adev-releaseprofile (releasewithout thin LTO, 16 codegen units) for the local reinstall loop: an incremental rebuild after touching the CLI crate drops from 55 s to 9 s on an 8-core M-series; shipped binaries keep thereleaseprofile.- Codex now receives the agent prompt through
developer_instructionsin~/.codex/config.toml($CODEX_HOMEhonoured) instead of acodexshell function. A config key reaches every Codex front end — the desktop app's bundled binary, the VS Code extension, scripts calling the binary by path,spawn_agentsub-agents — where the function only fronted interactive shells that sourced the profile.pixel installembeds the prompt as a TOML literal multi-line string between<!-- pixel:managed:begin -->/endmarker lines, rewrites only that key (the rest of the file keeps its layout and comments), keeps text outside the markers, and reports the step red without writing when the file does not parse.pixel doctorgainsinstall.codex-config(red when the file, the key or the block is missing, or the block differs from the bundled prompt);pixel uninstallremoves the block, or the key when nothing else was in it. Theclaudeshell wrapper block no longer carries acodexline; re-runpixel installto update it.
Fixed
pixel reconcilerunsgit rebase --continue,git merge-tree --write-treeand thegit --versiongate through thepixel-gitrunner instead of a barestd::process::Command. Those were the last four production git calls with no timeout and no output cap: a hook or a pager that hung inside one of them parked the daemon's single request thread for good. The runner's 120 s timeout and byte cap now apply, while the exit code stays data where it is one (merge-treeexits 1 on conflicts and its stage lines are still parsed;rebase --continuestill runs withGIT_EDITOR=true).pixel-gitgainsGitRunner::run_output(args, env)andGitOutputfor that shape of call.pixel installandpixel doctornow target the account's login shell instead of$SHELLwhen--shellis not given. The wrappers are aclaudefunction a human runs from an interactive shell, and$SHELLis not that shell under a coding agent: Claude Code's command tool reports/bin/zshon a fish machine, so an agent running the install loop wrote the wrappers into~/.zshrc, ...
v0.2.3
Added
pixel installdeploys a second, short (under 2 KB) prompt to~/.local/share/pixel/subagent-prompt.mdand theclaudeshell wrapper passes it with--append-subagent-system-prompt-filewhenever-p/--printis among the arguments (bash, zsh and fish). Claude Code sub-agents receive neither the session's--append-system-prompt-filenor its history, and honour the sub-agent flag in print mode only. The flag is written only whenclaude --versionreports 2.1.261 or newer (the release that added it; older ones exit 1 on the unknown option): below that,installwrites the plain wrapper and reports the step yellow with the version found; with no usableclaude(not on PATH, no answer within 5 s, unparseable version) it keeps the flag decision of the existing block, or writes the plain wrapper on a first install, and reports yellow. A short-flag cluster containingp(-pc,-cp) counts as print mode.pixel doctorgains aninstall.subagent-promptcheck, re-derives the expected wrapper from the Claude Code found at check time (red in both directions: flag in front of an old Claude Code, or missing in front of a new one; yellow whenclaudecannot be probed), andpixel uninstallremoves the file. The task-worker command builder accepts a matchingsubagent_prompt_fileoption; nothing sets it yet.
Fixed
- The bundled agent prompt (
agent-prompt.md) no longer documents command lines the CLI rejects:pixel uses "X" --callers|--calleesis--role callers|callees,publish/ship/branch/updatetake--request-id(there is no-r),updateneeds--target-oidand--expected-head,lifecycletakes--file <path>,syncneeds a remote. The "installed at~/.local/bin/pixel" sentence, wrong for mise/Homebrew/CI installs, now points atcommand -v pixel. pixel publish --files <path>(andship) no longer fails withgit add: pathspec '<path>' did not match any fileswhen<path>is a deletion already staged withgit rm. Paths absent from both the worktree and the index skip thegit addstep and are committed by the pathspec-scopedgit commit; a path git has never known is still rejected, and no commit is created.