Skip to content

Releases: LivioGama/pixel

v0.5.2

Choose a tag to compare

@github-actions github-actions released this 26 Sep 19:41
Immutable release. Only release title and notes can be modified.
d34bb6f

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.db moves 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-repo reuses or incrementally updates a fresh graph, so a CI-cached .pixel/ costs seconds.
  • prepare-repo timings now report where the time goes, index layer by layer and graph phase by phase.

Added

  • prepare-repo: --json gains a timings block: 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 a timings: line naming the slowest phase. status --json carries the index layers as index.open. (#309)

Changed

  • history: history.db is 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), and status, doctor and session start no longer create the db. (#301)
  • recall: recall search --agent walks 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 embed does the same, so the model2vec-rs 0.3 update (it moved 133 of 300 sampled turn vectors) needs no recall embed --rebuild. (#302)
  • install, cli: the bundled agent and sub-agent prompts send "does A reach B" to pixel evaluate path and say how to read its established / absent_in_snapshot / unknown statuses, and call-path output gains a successor field with the ready-to-run evaluate path command; 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.build names the mode (fresh, incremental, full and why); --rebuild-graph forces 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 main in 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: lifecycle on 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 own info/exclude, so what-changed and other read-only commands leave git status clean and the next git checkout unblocked, and a repo without a .gitignore no 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 failed git diff base..HEAD used to read as "nothing changed", and the index served the other commit's text as HEAD's. (#312)

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 25 Sep 18:32
Immutable release. Only release title and notes can be modified.
1d41882

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 audit measures what an agent reads to learn a repository's largest files, and list-signatures reports its real saving on the spot.
  • Rust use statements resolve correctly: aliases, grouped paths and scope-limited imports each bind the right names.
  • search-content takes ripgrep's -g, -t, -l, -F and -n, and doctor --fix runs 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-task run and the six recordings, plus /docs/ and a /benchmarks/ page with every figure's source and the cases where Pixel loses. Built from website/ by a new Pages workflow; the docs drift test checks both pages. (#239)
  • doctor: pixel doctor --fix runs 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 reported fixed, not converged or failed from the new report, and the exit code is the re-run's. The pixel install fix now carries the --shell doctor was given. (#251)
  • search: pixel search-content takes the ripgrep flags agents pass by habit: -g/--glob (.gitignore rules, ! excludes, repeatable), -t/--type (rust, py, ts, md…), -l/--files-with-matches, -F/--fixed-strings, and -n as 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, and llms.txt links every page. (#281)
  • docs: The website has a page per agent under /for/: what pixel install writes for it and where, the plugin or rules file when it writes nothing, the pixel doctor check and the removal. One data file feeds them, the home and the /docs/ table, and a test holds it to what pixel install really 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.txt lists 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 audit measures what an agent reads to learn what a repository's largest source files contain: each whole file against its list-signatures outline, 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-history or 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.py instead 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 by scripts/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.txt gives 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 and SECURITY.md name every network path, including pixel web-search falling back to DuckDuckGo and Wikipedia even with SearXNG set; the home badge narrows to "Your index stays on your machine". (#277)
  • cli: pixel web-search with PIXEL_WEB_SEARCH_URL set 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 reads full 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 to workflow-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 use now records the names it binds (use crate::push::{PushOptions, push} binds PushOptions and push), so a call to an imported function another file also defines resolves exact instead of staying unresolved: who-calls on pixel-ops's push found no caller and now finds ship_with_lease. On this repository 245 more call edges are exact (+6.5 %). (#255)
  • graph: a graph built before Rust use bindings 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, --limit counts the matching lines printed, not the index rows read before the filter (--limit 1 -g 'tests/*' could print nothing), and --offset resumes past the rows the filter dropped. The last -g mat...
Read more

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 23 Sep 21:43
Immutable release. Only release title and notes can be modified.
aaa1a3b

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 path answers 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-changed now also lists the changes it could not map to a symbol.
  • Beyond one checkout: pixel workspace fans impact and who-calls out over member repos, index-pack/index-unpack ship a built index between machines, and pixel mcp serves 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.jsonl line 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 ### Highlights list — 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 predicate module, the evaluator behind pixel evaluate: multi-source reachability over the stored call relation that answers established (with a re-read witness path or an identity witness), absent_in_snapshot (only after an exhaustive traversal) or unknown when the depth or time cap dropped a frontier node; --tiers selects 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 with complete/unresolved markers, --limit, --json. (#201)
  • proto: added the pixel evaluate wire contract (pixel_proto::evaluate): one JSON object per answer, evaluation or error, whose status, answer, witness, typed reason and next_actions are one enum on the Rust side, so a verdict without its witness or an unknown without 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 path answers 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 --in narrows the lookup to a directory (#206).
  • graph: what-changed now reports what it could not map. uncovered_changes lists 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); unanchored names 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 workspace registers member repos in .pixel/workspace.json; impact/who-calls --workspace fans out with per-repo provenance. (#211)
  • cli: pixel index-pack/index-unpack move a checksummed index bundle between machines — CI builds once, teammates install. (#211)
  • cli: pixel mcp serves search/resolve/impact/evaluate/context/status over MCP stdio — one integration for every MCP-capable agent. (#211)
  • cli: pixel coverage reports files-on-disk vs indexed per language; pixel plan tracks done state in .pixel/plan.json across 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.md gains the two mutant-counting traps that cost a run each (#213).
  • repo: just clean reclaims 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 single target. just disk prices 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.sh is the same tool without just. (#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, and pixel config remote-key <preset> - to store a key (0600, never logged). --context (or JSONL "context") adds shared framing, --jsonl serves one decision per stdin line, and the output discloses snapshot.deterministic=false and snapshot.provider (#209, #222).
  • metrics: each actions.jsonl line now says how its requests were served: serve lists the route (daemon, daemon_started, or in_process with 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, ask and search now record their serve step in actions.jsonl: answered by the recall daemon (request_ms) or in process (daemon_absent, daemon_error, not_routed for context), with the transcript catch-up and model load timed apart from the query (open_ms, handle_ms). A 20 s recall line 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 the agent-prompt.md or subagent-prompt.md that pixel install deployed no longer matches the running binary. Agents read those files as they are, so they kept the old release's command map with nothing but pixel doctor saying so. Run pixel install to clear it (#243).

Changed

  • release: prepare.sh now gates the shape of a changelog.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 plan refinement 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-runs pixel plan) only when findings are unresolved/empty or a named term misses both find-code and search-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 recall commands 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 --history and recall index remain the explicit full builds. (#211)
  • cli: pixel config metrics on|off [--global] is the persistent opt-out for the 🟩 stats footer, writing .pixel/config.json at the repo or ~/.pixel/config.json machine-wide; the nearest scope wins over the on-by-default baseline, --metrics=off/PIXEL_METRICS=0 still 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 doctor now exits 1 when a check is red (--fail-on yellow to 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/`--...
Read more

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 21 Sep 10:44
Immutable release. Only release title and notes can be modified.
5c9b4ad

Changed

  • Updating is now guided on every surface a user hits: pixel self-update names 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 prints pixel install and pixel doctor . as caveats after brew upgrade, and the README gains a per-channel "Updating" section.
  • pixel repo-state no 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 exact clean_count, and --include-clean restores 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-PATTERNS section, which restated ten rows of the replacement map, becomes a five-line NEVER block carrying the two rules the map did not state (batch the git operations, escalate to pixel build-index before falling back to grep); the eight ### Phase N headings collapse into one fenced sequence, which also puts pixel find-code, pixel search-meaning and pixel commit-and-push under the prompt_asset_parity CLI-syntax check; the five pixel recall rows 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. Every pixel <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 files scripts/gen-plugin-assets.sh writes, and the managed blocks in ~/.codex/config.toml and ~/.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 render unavailable: <reason>; a one-step or non-positive baseline renders no estimated … saving; and the action log records the reason as comparison_gap. pixel push gained the git push it 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 into CHANGELOG.md under ## [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>.md and holds the entry text; prepare.sh folds them into the release section at tag time, grouped by section, and deletes them. prepare.sh --check validates 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-task no 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 — rs used 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 a path match: … reason), an unquoted snake_case word is probed as an exact name like a backticked one, without joins the stopwords, an exact token that only matches an external mod foo; declaration no longer counts as an exact-name hit on the declaring file (an inline mod foo { … } defines code there and still does; symbols.module_decl records 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 lifted path_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 commit needs an explicit commit request, pixel commit-and-push (or pixel 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 marked only when to 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 native grep. Every derived surface is regenerated.
  • pixel impact, pixel who-calls and pixel call-path now 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 as probable (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 local f), and trait-impl methods on a value receiver (path.clone() beside Box::clone), free functions with a receiver (path.exists()), chained receivers, two same-name methods in one file (A::walk beside B::walk) and same-named types in two crates all stay unresolved, so lower_bound stays honest. Existing graphs rebuild once (extractor version bump).
  • pixel impact no longer ranks an upstream walk that resolved no caller: an empty caller set reported LOW, 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 now UNKNOWN, carrying a risk_note that 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-empty referenced_by also reports UNKNOWN.
  • The ask lane no longer fails on a change to the code it measures: it gated every probe on ndcg@10 > 0 over the 16-file crates/pixel-graph/src corpus, where 10 is 62% of the files, so "imports dependency resolved graph edge" — whose labelled file sits 10th on main — was evicted to 11th by 26 lines of code with no test and no comment in any file of the subtree. A probe below k is 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_score compared the reranked score to its unranked baseline with a bare <, so the ranked lane failed on a 7.9e-4 loss while reporting query 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-code resolves an exact enum-variant identifier to its definition instead of returning unrelated string hits: Rust enum variants are now symbols in the code graph (qualified Enum::Variant), so an identifier-shaped query like find-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

Choose a tag to compare

@github-actions github-actions released this 15 Sep 21:35
Immutable release. Only release title and notes can be modified.
9cf6f0b

Changed

  • install.sh is published as an asset of every release, and the installer URL is now https://github.com/LivioGama/pixel/releases/latest/download/install.sh instead of scripts/install.sh on the main branch: main is 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 old raw.githubusercontent.com/.../main/scripts/install.sh URL keeps working, but serves the branch's copy. The release smoke job checks the new URL.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 15 Sep 18:27
abf6b3e

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 reference site_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, and as aliases are never touched. --file or --uid disambiguate a shared name (an ambiguous call lists the candidates); sites the graph could not resolve are reported in skipped instead of being guessed, and occurrences of the old name the graph never claimed are counted in unclaimed_text. --dry-run returns the same verified edit set without writing; a file that changed between planning and writing is refused rather than half-renamed. New daemon op rename (in SESSION_CAPABILITIES), served by the daemon and the in-process fallback alike.
  • pixel install deploys the Pixel plugin to Antigravity (~/.gemini/config/plugins/pixel/), enables it in ~/.gemini/config/config.json, and installs the pixel-guard hooks in ~/.gemini/config/hooks.json when the Antigravity config directory exists. pixel doctor checks the install and pixel uninstall removes it. The .agents/skills/pixel/SKILL.md skill surface is now generated by scripts/gen-plugin-assets.sh.

Changed

  • pixel <command> --json answers a failure with a parsable failure envelope on stdout (ok: false plus error.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 codes pixel-ops names in the messages it types itself (BUSY_REPOSITORY for a held lock, NON_FAST_FORWARD, STALE_STATE, UNSUPPORTED_STATE, REF_EXISTS, GIT_FAILED, NETWORK_AMBIGUITY) instead of falling back to INVALID_INPUT; pixel_proto::ErrorCode documents which variants no producer reaches.
  • pixel search-content --json now 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 (--context text, added after the daemon's own byte cap, used to exceed PIXEL_OUTPUT_CAP_BYTES — 154 KB measured under an 8 KB cap).
  • pixel scope-task and pixel find-code rerank by the coefficients their signal options carry: the rerank formula reads activity_weight, session_weight and fan_in_weight from SignalOptions instead 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 --ts rejects 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 with not 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 existing bad duration "…" (use 30s, 5m, 2h, 1d) and exit 1.
  • pixel install treats ~/.pi/agent/APPEND_SYSTEM.md as 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, and pixel uninstall strips the block instead of deleting the file, so the user's own pi instructions survive. pixel doctor gained the install.pi-prompt check and install.agent-prompt now 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_graph inside the daemon's bridge (or any Type::method()) whose callee name also existed locally resolved to that local symbol (capped at probable because the receiver is real), so the real callee had no edge and the bridge counted as dead: impact answered lower_bound: false and pixel plan --query dead-code listed build_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 in child.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, and pixel plan-rollback --apply --merge's git merge-file goes 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 status and pixel recall daemon start probe the daemon with a Ping that never starts one: the probe went through the auto-starting daemon client, which spawned a repository Service on the corpus directory (~/.local/share/pixel/recall/.pixel/base.shard, a daemon holding the socket for 30 minutes and refusing every Recall), so status named the wrong daemon and start printed "already running" without ever starting the recall service.
  • pixel recall show and pixel recall context count the bytes they write, pixel recall show --json is capped like every other machine document (PIXEL_OUTPUT_CAP_BYTES, truncated and cap_bytes once 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 counted print!/print_error path) exited 101 with a BrokenPipe panic, which set -o pipefail reported 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 …/util packages, or two com.example packages, made the import edge — the evidence pixel impact and pixel who-calls read — 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 to Probable or 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 plan or pixel what-changed that follows an edit (or pixel sync-branch) sees it. A watcher-driven graph update that fails (a locked or corrupt graph.db) and a notify backend error are no longer dropped: both are logged on the first failure and every doubling after it and counted in pixel status as watcher.graph_update_failures and watcher.notify_errors.
  • GraphStore::open pins SQLite's busy_timeout at 5 s, so a graph update that overlaps a concurrent writer waits for it instead of failing with SQLITE_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 recall counts 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 logs recall 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 --execute fills a step's value_var with the flow's declared default when no --var was passed, instead of sending the literal {{var}} placeholder to agent-browser. A required variable carrying a default was already accepted without a --var, but the resolver never read default, so a flow declaring default: "west" typed {{account}} into the form.
  • pixel commit, pixel commit-and-push, pixel push, pixel squash-branch, pixel new-branch, pixel fast-forward and pixel sync-branch now serialize on one lock per repository. The lock was keyed on the raw <repo>/.git spelling while the journal key was the canonical path, so /repo, /./repo, a symlink to the repository, /tmp vs /private/tmp and 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 commit refuses an interrupted commit whose recovery record it cannot read (a write truncated by a full disk, permissions, a future schema_version) instead of re-running git add/git commit: the retry returned a successful second commit with HEAD moved...
Read more

v0.2.6

Choose a tag to compare

@github-actions github-actions released this 14 Sep 18:37
2fde9f2

Fixed

  • pixel search-meaning, and the semantic fallback of pixel scope-task and pixel find-code, no longer search nested checkouts: a directory holding a .git file 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 coverage scope says so.
  • pixel scope-task adds 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 with PIXEL_INDEX_NO_DEFAULT_IGNORES shared its key with the default build; the pixel-only .gitignore rule read the worktree file instead of the commit; a blob git failed to read was dropped and the shard still published; a failed git ls-tree built 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 (fmt in impl 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 ../repo plans ../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 than by-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, …, as pixel --help shows them) instead of the daemon's op tags. An agent reading update or sync there ran pixel update (now fast-forward) or pixel sync (now fetch), and targets, search or history_op only worked, if at all, through the pre-rename aliases.
  • pixel who-wrote no longer fails on every file when git's blame.ignoreRevsFile names a file the repository lacks (a global .git-blame-ignore-revs default): 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.json and so loaded a plugin without the skill or the hooks, and Codex's Agent Plugins loader ignores the hooks declared in .codex-plugin/plugin.json when one is present (openai/codex#39895). Grok's plugin.json is optional metadata; its marketplace entry keeps the pixel name.

v0.2.5

Choose a tag to compare

@github-actions github-actions released this 14 Sep 16:52
7685e24

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 compact or --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 recall indexes pi sessions (~/.pi/agent/sessions, or $PI_CODING_AGENT_DIR/sessions).
  • pixel impact lists the symbols that pass the target as a callback argument under referenced_by, and graph answers carry epistemics.extraction_limits (callbacks, dynamic dispatch, macros, eval) next to closed_world: false.
  • pixel scope-task expands 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, the develop → main PR, a release record for resuming after a context loss, verification of the published release from a fresh download with the binary's commit: line matched to the tag, failure classification before any retry, hotfixes) plus prepare.sh x.y.z, which moves every workspace member to x.y.z, inserts ## [x.y.z] - DATE under an empty Unreleased, refreshes Cargo.lock, lists the pull requests merged since the last tag for the changelog review and runs check-release from 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.sh bumps them and regenerates the prompt surfaces, and pixel check-release fails on a manifest at another version (new plugin-versions check). The SessionStart/SubagentStart hook no longer needs python3, gives sub-agents the short sub-agent prompt (new PIXEL-SUBAGENT.md) instead of the 18 KB agent prompt, and injects a short notice instead of the protocol when pixel is missing or predates the command names; the injected setup text no longer tells the agent to pipe an installer from main into sh. The hook command resolved against the non-existent CODEX_PLUGIN_ROOT and fell back to the project directory; it now uses CLAUDE_PLUGIN_ROOT or Codex's PLUGIN_ROOT. Manifest descriptions name the current commands.
  • pixel recall indexes 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, so recall sessions hides it by default and its orchestrator prompts are not embedded as human intent. !command runs (bashExecution messages) are indexed, string content is read as well as text parts, and PI_CODING_AGENT_DIR moves the indexed and watched directory as it moves pi's. The guard's transcript-store advisory covers ~/.pi/agent/sessions.
  • pixel scope-task no longer drops French function words from English tasks: comment, car, plus, sans, des, est were stopwords for every task, so "fix comment parsing" searched for parsing alone. A task is French when two distinct French function words (or unambiguous French domain words such as connexion, 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ès was never dropped (the task is accent-folded first); tres is.
  • pixel plan runs in the daemon (a new plan op, 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 (an impact answering 0 callers, or SQLITE_BUSY); the daemon now refreshes its graph incrementally and serves the queries.
  • pixel plan's dead-code findings read No callers found for function x: confirm it is unused before removing instead of Remove unused function, and skip main, Go's init and 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-changes ranks 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 of git log exceeded 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) made name() look referenced (hiding it from dead-code findings and adding it to pixel impact's referenced_by). A member argument is a callback reference only on this/self/Self (this.onClick), and an argument no function in the graph is named after (g(x)) leaves no unresolved row, so pixel who-calls <fn> --role callees no longer reports lower_bound for 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 in pixel impact/who-calls and is not reported as dead code by pixel plan.
  • A graph built by an older extractor is rebuilt: the freshness signature only hashed file contents, so existing graph.db files never picked up extraction changes. graph.db now 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 …, was pixel rescue --apply), pixel dig-history's --show follow-up (was pixel 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.py expected the guard's rewrite to name pixel search-compat and 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 first pixel on PATH, else ~/.local/bin/pixel) lands on the mise install dir or the Homebrew keg on any machine where pixel comes from them, so a bare self-update from a checkout replaced mise's 0.2.4 with a local target/dev-release build while mise ls still reported 0.2.4. A resolved path under mise's installs/ (~/.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-path and --dev. --dry-run still prints the path, then exits 1 on a refused one. An explicit --install-path writes where it says, as before. The new --dev flag installs to ~/.local/bin/pixel-dev, a side build called as pixel-dev that never shadows or replaces pixel.
  • The surfaces that read a command name back as text accept both spellings of a renamed command. pixel uninstall recognised the Codex composed-guard entry only as hook composed-guard, so the run-hook composed-guard entry every install since the rename writes was never restored from its snapshot. The prompt-submit hook no longer treated a successful commit-and-push (formerly ship) as a task completion. The guard's foreign-hook snapshot boundary refused to replay pixel run-hook … but let pixel hook … through, which recursed. The call-loop breaker counts history written under an old name (search) toward the current one (search-content). pixel doctor's rule.scenarios accepts 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.sh expects the current guard advisories (pixel plan-rollback, pixel commit, pixel search-like-rg) and checks ready, changes, symbol and impact under 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 stayed search, targets, symbol, so targets responses lost their content evidence, the inspect/review dirty-list rule never matched, and the session-start capability block advertised names no op answers to. The wire layer is back on the serde...
Read more

v0.2.4

Choose a tag to compare

@github-actions github-actions released this 13 Sep 23:44
ad03f8f

Added

  • pixel publish and pixel ship accept -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.md checklist keyed by M-* id, full MIT text in guidelines.txt to grep on demand), symlinked as .claude/skills so Claude Code loads it next to .claude/rules; AGENTS.md and CONTRIBUTING.md list it.
  • pixel sniper run parses Ruby tool output. A failing bundle exec rails test / bin/rails test (Minitest) or bundle exec rspec run used to leave one generic run-wrapper record whose only payload was the raw output tail; it now records one error per failing test on the new minitest/rspec surfaces (kind failure or error, test class and name, file, line, message with expected/actual when the assertion printed them, the backtrace filtered to project paths, and the runner's rerun command) plus a summary record carrying the run counters and the failing tests. The runner is detected from the output, so Rails parallel workers and parallel_tests are handled. A green run records a test-pass event with the counters, so pixel sniper test now reports Minitest and RSpec runs the way it reports vitest instead of "no test runs recorded". rubocop output gives one lint record per offense on the rubocop surface (file, line, column, severity, cop, correctable). The generic tail record remains for everything else; tsc and vitest records are unchanged.
  • pixel --version now reports where the binary came from: after the pixel x.y.z line it prints commit: <sha> (-dirty appended when the tree had uncommitted tracked changes), target:, rustc: and built: (UTC date; SOURCE_DATE_EPOCH honoured). pixel -V keeps the one-line form. Values a build cannot determine (a tarball without .git, no git on PATH) read unknown instead 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 that crates/pixel/Cargo.toml carries the tagged version, that Cargo.lock holds every workspace member at its manifest version (a stale lock used to fail cargo build --locked on the release runner, after the tests had passed), and that CHANGELOG.md has 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.3 and refs/tags/v1.2.3 are accepted.

Changed

  • The declared minimum Rust version is now rust-version = "1.91" (was 1.85, which had not built for some time: the locked tree-sitter needs 1.90 and pixel-facts uses str::floor_char_boundary, stable since 1.91). A new MSRV job in the CI workflow runs cargo check --workspace --all-targets on 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 finds pixel through $PIXEL_BIN, then command -v pixel (the mise/Homebrew/cargo install pixel upgrade writes to), then target/dev-release/pixel and target/release/pixel, instead of a fixed ~/.local/bin/pixel. pixel-smoke-test.sh asserts the guard hook's actual contract (advisory JSON with a pixel alternative for destructive or substitutable git commands, updatedInput rewrite to pixel search-compat for a grep on one file, silent passthrough otherwise; exit 0 throughout) across Claude, Devin, Codex and Gemini tool names, reads the install surface through pixel doctor --json (agent prompt, shell wrappers, Codex config; PIXEL_SHELL=<shell> forwarded as --shell) instead of the hook entries pixel install stopped writing, and checks --version's commit: line and the release-check/upgrade/uninstall --wrappers-only help. pixel-vs-manual.sh passes a symbol uid to impact/context and --phrase to excavate (the bare forms were rejected). pixel-demo.sh and pixel-bench.sh give the pixel arm the deployed agent prompt through --append-system-prompt-file/--append-subagent-system-prompt-file, as the claude shell wrapper does (a script calls claude by path, so the fish/zsh function never applied and the "with pixel" arm ran without pixel's instructions); pixel-bench-isolated.sh reads its doctrine from the same file instead of the retired ~/.agent-config/rules/pixel.md. A scripts/README.md lists every script with its one-line invocation and prerequisites; CONTRIBUTING's install loop uses pixel upgrade instead of a hand cp into ~/.local/bin.
  • The CI workflow no longer runs on a pull request that only changes Markdown, docs/, LICENSE or NOTICE; the bundled agent prompts under crates/ still trigger it, and pushes to develop/main are unfiltered.
  • README no longer carries the agent-config managed badge and the "Install pixel rules" button that pointed at the external pixel-rules repository: that rule was a copy of the agent prompt pixel install stopped writing as a managed block in 0.2.0, and it had already drifted from the bundled pixel-agent-prompt.md. The "Start here" section now lists which agents pixel install wires (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.md gains 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/--since before widening, read at the turn with --turn N..M rather than the whole session. Re-run pixel install to deploy it.
  • The fastembed feature no longer enables fastembed's image-models default: pixel never embeds images, and that feature alone pulled the image crate 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-recall declares 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 the fastembed feature are unchanged; about 90 s off a cold macOS build.
  • pixel upgrade reads the built binary from target/<profile>/pixel, with the profile taken from the --profile/--release flags of its --build command, instead of always target/release/pixel (a --build on another profile installed whatever stale binary sat there, without error). The workspace gains a dev-release profile (release without 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 the release profile.
  • Codex now receives the agent prompt through developer_instructions in ~/.codex/config.toml ($CODEX_HOME honoured) instead of a codex shell 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_agent sub-agents — where the function only fronted interactive shells that sourced the profile. pixel install embeds the prompt as a TOML literal multi-line string between <!-- pixel:managed:begin -->/end marker 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 doctor gains install.codex-config (red when the file, the key or the block is missing, or the block differs from the bundled prompt); pixel uninstall removes the block, or the key when nothing else was in it. The claude shell wrapper block no longer carries a codex line; re-run pixel install to update it.

Fixed

  • pixel reconcile runs git rebase --continue, git merge-tree --write-tree and the git --version gate through the pixel-git runner instead of a bare std::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-tree exits 1 on conflicts and its stage lines are still parsed; rebase --continue still runs with GIT_EDITOR=true). pixel-git gains GitRunner::run_output(args, env) and GitOutput for that shape of call.
  • pixel install and pixel doctor now target the account's login shell instead of $SHELL when --shell is not given. The wrappers are a claude function a human runs from an interactive shell, and $SHELL is not that shell under a coding agent: Claude Code's command tool reports /bin/zsh on a fish machine, so an agent running the install loop wrote the wrappers into ~/.zshrc, ...
Read more

v0.2.3

Choose a tag to compare

@github-actions github-actions released this 12 Sep 18:46

Added

  • pixel install deploys a second, short (under 2 KB) prompt to ~/.local/share/pixel/subagent-prompt.md and the claude shell wrapper passes it with --append-subagent-system-prompt-file whenever -p/--print is among the arguments (bash, zsh and fish). Claude Code sub-agents receive neither the session's --append-system-prompt-file nor its history, and honour the sub-agent flag in print mode only. The flag is written only when claude --version reports 2.1.261 or newer (the release that added it; older ones exit 1 on the unknown option): below that, install writes the plain wrapper and reports the step yellow with the version found; with no usable claude (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 containing p (-pc, -cp) counts as print mode. pixel doctor gains an install.subagent-prompt check, 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 when claude cannot be probed), and pixel uninstall removes the file. The task-worker command builder accepts a matching subagent_prompt_file option; nothing sets it yet.

Fixed

  • The bundled agent prompt (agent-prompt.md) no longer documents command lines the CLI rejects: pixel uses "X" --callers|--callees is --role callers|callees, publish/ship/branch/update take --request-id (there is no -r), update needs --target-oid and --expected-head, lifecycle takes --file <path>, sync needs a remote. The "installed at ~/.local/bin/pixel" sentence, wrong for mise/Homebrew/CI installs, now points at command -v pixel.
  • pixel publish --files <path> (and ship) no longer fails with git add: pathspec '<path>' did not match any files when <path> is a deletion already staged with git rm. Paths absent from both the worktree and the index skip the git add step and are committed by the pathspec-scoped git commit; a path git has never known is still rejected, and no commit is created.