senzu: v0.11.1
What's Changed release: v0.11.1 by @github-actions[bot] in https://github.com/aliou/senzu/pull/15 Full Changelog: https://github.com/aliou/senzu/compare/v0.11.0...v0.11.1
What's Changed release: v0.11.1 by @github-actions[bot] in https://github.com/aliou/senzu/pull/15 Full Changelog: https://github.com/aliou/senzu/compare/v0.11.0...v0.11.1
Release v0.3.3
Minor Changes e7b6b5d: Add a provider.api setting (/neuralwatt:settings → API, default openai-completions) that serves the catalog on Neuralwatt's Anthropic-compatible POST /v1/messages endpoint (vLLM-backed) — native tool use and thinking streams with the same model ids. Applie…
Patch Changes 23fae71: Sync the static model catalog with the Synthetic API: remove hf:zai-org/GLM-5.2 (no longer upstream), add hf:deepseek-ai/DeepSeek-V4.1-Flash (524K context, vision, reasoning efforts none/low/high/xhigh/max), and update syn:large:text for its new GLM-5.3-Fl…
Patch Changes 45373d6: Take flex model pricing from the API's already-discounted values instead of applying a local 0.65 multiplier at catalog build time. 48b331e: Sync the offline fallback model table with the live Neuralwatt API: remove GLM-5.2 and its variants (gone upstream)…
Minor Changes 92efa3d: Upgrade @aliou/sh to 0.3.1 and adapt the AST walker to its compound-redirect model. Fixed Heredoc command evasion (permission gate): a dangerous command placed after a heredoc body (cat <<EOF\n…\nEOF\nrm -rf …) was folded into the heredoc command's words b…
Minor Changes 418c780: Make provenance headers configurable and follow Pi's telemetry configuration. The Referer: https://pi.dev and x-session-id headers injected on every provider request can now be turned off with the new shouldSendProvenanceHeaders config option (default true…
Minor Changes 65e8787: Add Fetch Pro mode and structured extraction to linkup_web_fetch. The tool gains mode ("standard"/"pro") and schema/instructions (typed JSON returned in data alongside the markdown) parameters, and documents PDF support up to 100 MB. Updates the balance co…
Patch Changes 7780a12: Fix proxy auth registration so immediate spawn completions use Aperture placeholder credentials before gateway reconciliation. 6704355: Proxy mode: strip the transport-qualified provider prefix from the persisted assistant message model id in the message_e…
Patch Changes 51f9dbf: fix: exclude embeddings model from catalog and harden resolveMaxTokens d15f9c8: Make the model-store cache scaffold-aware. A fresh entry persisted without a key only carries the public scope, so logged-in users never saw preview, grant-gated, or private mo…
Release v0.3.2
Patch Changes f64c101: ci: upgrade @changesets/cli to v3 and migrate changesets/action to v2 in the publish workflow 02d7c29: Derive the provider catalog from /v1/models instead of /api/providers, which requires the admin role. Provider discovery, dedicated model refresh, proxy…
Release v0.3.1
Release v0.14.0
Minor Changes c405645: Add a header field type to SettingsDetailEditor: a dim, non-interactive row skipped by navigation and Enter, with an optional dim value in the value column. Use it as a section divider inside a long field list or as an inert placeholder row (e.g. entries t…
Minor Changes b3caf85: Build the model catalog from the live /v1/models response instead of local visibility buckets. Model refresh now fetches anonymously when no API key is configured, builds the catalog with overrides for Flex pricing, Kimi K3 context, Qwen chat-template comp…
Minor Changes 860d98e: Add an adopt API so other extensions can hand an already-running child process over to the manager. ProcessManager.adopt(name, command, cwd, child, { initialStdout, initialStderr, startTime }) registers an externally spawned child (detached process group,…
Minor Changes b163e5e: Add an optional per-provider api override for dedicated and proxy modes (extensions/shared/config/types.ts, editable per provider in /aperture:settings). A gateway provider that serves more than one compatibility surface (e.g. openai_chat + anthropic_messa…
Minor Changes 37d6620: Add per-provider keepGatewayModelsOnly in proxy.upstreamProviders (default false). When enabled on a proxied provider, its registered models are filtered down to the ones the Aperture gateway actually serves (same /api/providers + /v1/models cross-referenc…
Minor Changes 02d0e5d: Add Qwen3.8-27B to the Synthetic model fallback catalog.
Release v0.3.0
Minor Changes 2eff528: The /ps:logs overlay now has an opt-in soft-wrap mode (w key toggle). When enabled, long log lines expand into multiple display rows instead of being hard-clipped, making full content visible. Truncation remains the default for the /ps overview, dock, and…
Minor Changes 05bdf78: SectionedSettings' contentHeight is now a true flex layout instead of pad-only: the item list window shrinks to make room for the selected item's description, which is wrapped in full (never truncated, no ellipsis) and bottom-anchored just above the hint l…
Release v0.13.0
What's Changed release: v0.11.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/14 Full Changelog: https://github.com/aliou/senzu/compare/v0.10.0...v0.11.0
Full Changelog: https://github.com/aliou/senzu/compare/v0.9.0...v0.10.0
Minor Changes 7e76634: Native pi-ai provider registration for both modes, and a proxy-mode routing fix. Proxy mode: requests for extension-native providers (synthetic, neuralwatt) now go through the Aperture gateway. Sync previously re-registered providers via the name-plus-conf…
Minor Changes 36ece2d: Register the Neuralwatt provider as a complete pi-ai Provider via pi.registerProvider(provider) instead of the name-plus-config form, with auth resolution that falls back to an anonymous credential so the model catalog refreshes without an API key.
Minor Changes d58a55f: Derive reasoning levels from the endpoint's supported_efforts. thinkingLevelMap is now built by identity from each model's metadata.reasoning.supported_efforts (plus mandatory) via a single buildThinkingLevelMap helper, instead of hand-tuned per-family con…
Minor Changes fbd1b1c: Adopt @aliou/pi-utils-settings 0.19.1 versioned migrations. Bumps the dependency and adds semver version strings to all config migrations, leaving their content-based shouldRun gates intact. Switches schema generation to pi-settings-schema. 32ba87a: Add an…
Minor Changes ffc7ca7: path-access: filter non-path bash arguments by shape and filesystem plausibility instead of per-command allow-lists. Arguments that look like paths but are not (Context7 library IDs such as /websites/apisix, Go package patterns like ./..., URLs, user@host:…
What's Changed release: v0.9.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/13 Full Changelog: https://github.com/aliou/senzu/compare/v0.8.0...v0.9.0
Release @aliou/sesame-cli@0.12.0
What's Changed release: v0.8.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/12 Full Changelog: https://github.com/aliou/senzu/compare/v0.7.1...v0.8.0
What's Changed release: v0.13.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/16 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.12.0...v0.13.0
What's Changed release: v0.12.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/15 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.11.0...v0.12.0
What's Changed release: v0.11.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/14 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.10.0...v0.11.0
What's Changed release: v0.7.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/10 Full Changelog: https://github.com/aliou/senzu/compare/v0.6.0...v0.7.0
Minor Changes 09a5603: - Versioned migrations: Migration.version (monotonic int) auto-stamps the config file; shouldRun defaults to a version comparison; run/shouldRun/message factory receive a MigrationContext with applied-migration history. ConfigLoader.getVersion() reads the…
Minor Changes a146230: Bound in-memory process output by both line count and a new configurable byte budget. /ps, /ps:logs, and the dock now append live output without repeatedly reading full log files, and show a marker when a burst drops lines. 530dd99: Create the per-session…
Minor Changes eee1395: Dedicated mode: model capability metadata and Pi models-store caching. Dedicated models are now enriched at refresh time instead of registering with flat safe defaults. Capability metadata (context window, output limit, vision input, reasoning, thinking le…
Minor Changes 46fd304: Dedicated mode now infers the gateway base URL per model from Pi's native model registry, fixing non-/v1 upstreams like Z.ai in dedicated mode. Previously dedicated mode registered every OpenAI-compat model at gateway/v1, so Z.ai (/api/coding/paas/v4) prod…
Minor Changes 4d10d48: Add a separate alias model ID setting for active creator-scoped Neuralwatt model IDs.
What's Changed release: v0.6.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/9 Full Changelog: https://github.com/aliou/senzu/compare/v0.5.0...v0.6.0
Minor Changes 650b4c1: Apply Synthetic's 80% cache-read discount in the model catalog instead of the streamSimple wrapper. The wrapper is removed and Pi's built-in cost calculation now uses the discounted cacheRead rate directly. a6f10fa: Add Kimi-K3, drop Kimi-K2.7-Code, and st…
Guardrails approval prompts now show up as blocked in Herdr, via one shared event
Minor Changes fff80de: Add correlated prompt lifecycle events and report active approval prompts to Herdr.
What's Changed release: v0.5.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/8 Full Changelog: https://github.com/aliou/senzu/compare/v0.4.0...v0.5.0
What's Changed release: v0.10.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/13 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.9.0...v0.10.0
What's Changed style(readme): use blog mono stack for previews by @aliou in https://github.com/aliou/senzu/pull/6 release: v0.4.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/7 Full Changelog: https://github.com/aliou/senzu/compare/v0.3.1...v0.4.0
Release v0.2.0
Okay, but what can I actually do with that token?
Even after building OIDC into orbs, it wasn’t immediately obvious to me how far we could push an OIDC-based dev env. So here are a few ideas to get the juices flowing, drawn from both internal usage and feedback from our early partners.
- Configure workload identity federation in GCP and grant access to your production logs and metrics from all orbs in your workspace.
I’ve been banging my head around how to give the agent the credentials necessary for whichever task it needs to do in the sandboxI had decided without much conviction to inject encrypted secrets and have them be decrypted on sandbox boot with the caveats that I would spend more time researching a better solution later. .
To optimize for scale, we decided that everything taking O(sandboxes) or O(nodes) load must be horizontally scalable by default, the sandbox creation path should be as simple as possible, and everything else should be secondary. The solution we came to is notably different than existing systems. We completely dispensed with any sort of central coordination, and traded global consistency for scalability and performance everywhere on the critical path for running and creating sandboxes.
I have nowhere near the same requirements for my personal sandbox setup but it still super interesting to see how what is needed to reach massive scale.
Release v0.12.0
Minor Changes a86f124: Expose Pi's native max thinking level on hf:zai-org/GLM-5.2 (and its syn:large:text alias). GLM-5.2 now maps max -> "max" (literal, accepted by Synthetic's OpenAI shim) instead of the previous xhigh -> "medium" fallthrough. xhigh is hidden; the two effecti…
Minor Changes f74cc65: Synchronize Synthetic models from the provider API Provider models are now fetched asynchronously from https://api.synthetic.new/openai/v1/models using Pi 0.80.8's ProviderConfig.refreshModels API. Discovered models are persisted through context.store with…
Minor Changes ad515e8: Update to Pi 0.80.8 provider authentication and refreshable model catalogs. Cache the complete Neuralwatt catalog through Pi's model store and preserve stale hidden models when refresh fails.
Minor Changes e5e562a: Migrate Neuralwatt settings to nested per-feature configuration. Old flat config is migrated automatically, and a backup file is written next to the migrated config. Feature behavior is unchanged.
What's Changed release: v0.9.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/11 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.8.0...v0.9.0
Minor Changes 8dba5d1: Bump Node engine requirement to >=26.0.0 and migrate binary builds to @tsdown/exe cross-platform SEA (replaces per-platform CI matrix with a single build job; drops useCodeCache which is incompatible with cross-platform SEAs). Repos using the flake devShel…
What's Changed release: v0.3.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/3 Full Changelog: https://github.com/aliou/senzu/compare/v0.2.0...v0.3.0
What's Changed release: v0.2.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/2 Full Changelog: https://github.com/aliou/senzu/compare/v0.1.0...v0.2.0
Release v0.7.0
What's Changed release: v0.1.0 by @github-actions[bot] in https://github.com/aliou/senzu/pull/1 New Contributors @github-actions[bot] made their first contribution in https://github.com/aliou/senzu/pull/1 Full Changelog: https://github.com/aliou/senzu/commits/v0.1.0
What's Changed release: v0.8.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/10 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.7.0...v0.8.0
Release @aliou/sesame-cli@0.11.0
Release v0.11.0
I think that’s a great summary of the things we’ve done to make agents more productive in our codebase: don’t make them guess.
I’ve been trying to articulate to friends and colleagues why I never seem to have my agent screw up that much and it boils down to this: not letting them guess. Whether it’s via skills or instructions, but also toolinge.g. plugins for your favorite linter that explain why something is prohibited. and scripts, having everything in front of your agent decreases its chances of doing things wrong.
Of course it’s hard work to keep all of this up to date, but I find it way more rewarding than running a random npx skills addNo shade! and crossing finger that it will solve all my problems.
My initial instinct was to go full near-airgap: remove the default route, run an SNI-based TLS proxy on the host, and only allow traffic to
api.telegram.organdopenrouter.ai.I built this with unbound returning fake DNS records pointing at the host bridge IP, nginx stream with
ssl_prereaddoing SNI-based forwarding, nftables dropping everything in the FORWARD chain.It worked, technically. But it was brittle. The proxy needed careful handling of dynamic IPs, and Openclaw’s error messages when DNS returned unexpected results were… unhelpful.
For this demo, I just said screw it and allowed everything (with a log). The VM has full internet access, but every DNS query and every new connection is visible from the host.
Continuing on my research around microvm, I discovered microvm.nix and yet another way of monitoring and/or locking down networking for vms that will be used by agents.
Minor Changes b4f5b84: Update Synthetic model catalog with Kimi K2.7 Code and remove delisted models.
Agents naturally gravitate toward the smallest stable abstraction. So, in an agent-first world, the most important question facing product teams is not what features and why, but instead how do I design the right primitive ?
Building great developer tools (e.g. primitives) has always been an exercise in exquisite taste and deep domain expertise because it requires balancing a set of competing, and often contradictory, forces: namely the tension between expressivity and simplicity. — The primitive is the product
Will be super interesting to see how companies adopt this and it will truly be a differentiator in adoption.
/tree and /spawn to investigate bugs Simple context management for when you don’t know what you’re looking for.
A VM that can reach the host’s local network is a VM that can reach things it has no business reaching. So QEMU doesn’t run in the host’s network namespace at all. We
unshareinto fresh user, net and mount namespaces first. Inside that namespace a small wrapper bind-mounts a resolv.conf pointing at127.0.0.1so that QEMU’s built-in slirp DNS isn’t used, then installs blackhole routes for every special-use IP range (RFC 6890, so private networks, link-local, loopback, etc.) before it execs QEMU.slirp4netnsthen provides the namespace’s outbound internet connection, with--disable-host-loopback, sandbox and seccomp all on. QEMU runs inside that namespace, and the guest’s network card is attached to QEMU’s own built-in user-mode networking. So every packet from the guest takes two hops: guest → QEMU’s slirp → the namespace’sslirp4netns→ the internet. The guest never sees the host’s network and the host’s network never sees the guest. All of this is done without needing any privileges!
I’ve been reading a lot about microVMs and Firecracker lately as I work on async agent side projectMore on that soon hopefully. and I find it super interesting how people tackel the same UX problems (in this case: locking down the network).
Release v0.2.0
Minor Changes 61e66dc: Filter disabled gateway providers out of proxy and dedicated flows. /api/providers lists every provider regardless of its disabled flag, so disabled providers and their models leaked into the proxy settings/onboarding checklist, the dedicated provider list…
this is a huge reason why i use pi.
i absolutely do not want my harness regularly changing behavior out from under me, including system prompt changes, on top of an already stochastic llm
Imagine I have /skill-1 and /skill-2, both with disable-model-invocation: true I tell Claude Code: “/skill-1, /skill-2 do XYZ”
Which of the skills will be invoked?
I’ll give you a clue - the behaviour was one thing 3 weeks ago, another thing 1 week ago, and back to the original today.
I’ve been playing around making my own harnessPi remains my main harness, but trying to think about how I work in this agent-looping world. recently, and also noticed how the behaviour of many Claude features seemingly seem to change every time I spin it up to test something. For my harness, I just decided on a simple principle: trying to keep the primitives (i.e. skills and mid-turn messages affect the agentMost of the time i want immediate steering, but I realized I do use follow-up quite a lot when leaving agents do their thing, especially when running in sandboxes and/or building prototypes where the result itself doesn’t matter much for me. ) consistent with how I expect agent to react.
introducing tau τ — an educational agent harness that teaches you how to build agent harnesses
This is so cool. I truly think we’re moving towards a world where writing/making a great agent harness ends up becoming accessible to most people because the knowledge is just readily available.
Minor Changes e86681a: Add a pythonToPython3 rule that rewrites bare python command invocations to python3 when enabled.
Minor Changes 69b7dba: Initial Cohere provider extension for Pi. Patch Changes 4a44d57: Align repository scaffolding and release workflow with pi-edgee.
Application scope and user experience: Sun Microsystems co-founder Bill Joy famously said: “No matter who you are, most of the smartest people work for someone else.” That applies with particular force to AI. The range of what agents can do — and the variety of industries, workflows, and real-world contexts they touch — is so vast that no single company can build the best solution for all of them. We see a near-infinite opportunity to rewrite the world using agentic AI as foundational infrastructure, and it will require many companies with focused effort.
Thinking a lot about this lately, especially around my own use. I want from using Anthropic models and tools exclusively a year ago to barely using them and instead using different models in different harnesses depending on the task at hand.
It’s very likely that I’m part of the minority of AI users in this case currently but I wonder how long this will become more common ground amongst the majority of users.
/aperture/config endpoint so non-admin Aperture grants can use the extensionus jurisdiction for Durable Objects Durable Objects now supports a new
usjurisdiction. Create namespaces restricted to the US to keep compute and storage local.
i feel like i have a aluminium hatit’s called a tinfoil hat dummass , but it seems like everyone is preparing for us/non-us tiered system around llm access.
@earendil-works/pi-coding-agent and @earendil-works/pi-tui from >=0.74.0 <1 to * to match the canonical Pi package convention in docs/packages.md. No runtime behavior changes; no post-0.74 APIs are usedRelease v0.18.0
pathAccess.allowedPaths from a flat string[] (trailing-slash convention) to an explicit { kind, path } discriminated array@aliou/pi-utils-settings to ^0.17.0 and switch migration warnings to its built-in Migration.message fieldWhat's Changed release: v0.7.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/9 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.6.1...v0.7.0
Release v0.5.0
Release v0.8.0
This article will sound like a schizo ramble, so prepare. To start off I want to state my true beliefs on the matter:
- LLMs are not conscious and won’t ever be.
- LLMs are inaccurate statistical machines.
- Open Source AI is good but must get better.
- I have no hatred, or malice for any closed source lab
- I respect what I see as my peers at OpenAI, Anthropic, xAI etc.
You might think, well if that’s the case what’s the problem? These models aren’t intelligent, they can’t really replace people, can they?
Yeah that’s my shit.
Release v0.10.0
been playing with exe.dev this weekend and was curious how they made ssh [vm-name].exe.dev just work, and it’s pretty cool: https://blog.exe.dev/ssh-host-header
we landed on a pretty good workflow for doing parallel work in OpenCode
this demo is with git worktrees but i also preview an alternative we’re working on at the end
this will be in 1.6.0
— @thdxr
Rift looks interesting.
Take a look, I really like how this is explained.
buildSchemaUrl callers to customize schema hosting URLs with baseUrl or template optionsonBeforeClose to intercept settings UI close requests before discarding draftsRelease v0.1.0
I like the idea of Claude’s workflow thingy but since it’s writing code, it keeps failing with these kind of errors:

In addition, the UI of Claude feels a bit overwhelming to me.

There’s also some strong validations around making the workflow deterministic and I wonder if it could still be handled? I’m also curious on why they’re not giving guidance to the model about this and/or linting workflow code to have this be caught before the harness runs the workflow.

What's Changed release: v0.2.0 by @github-actions[bot] in https://github.com/aliou/granola-cli/pull/2 Full Changelog: https://github.com/aliou/granola-cli/compare/v0.1.0...v0.2.0
What's Changed release: v0.1.0 by @github-actions[bot] in https://github.com/aliou/granola-cli/pull/1 New Contributors @github-actions[bot] made their first contribution in https://github.com/aliou/granola-cli/pull/1 Full Changelog: https://github.com/aliou/granola-cli/commits/v…
Missed that claude/opus has a fast mode so I had Pi make a quick extensionDon’t look too closely at that horrendous code that Kimi with reasoning disabled produced. to play with it. Below, a comparison with the vanilla Opus.
requiresReasoningContentOnAssistantMessages compat flag for reasoning modelsguardrails:action:prompted event that fires when guardrails shows an interactive prompt to the user, before the user has respondedMinor Changes ea5dab6: Catch await expressions buried directly inside call arguments. Patch Changes a53d66e: Fix pi-no-node-exec to flag multi-name child_process spawning imports while allowing non-spawning imports.
What's Changed release: v0.6.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/7 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.5.0...v0.6.0
Release @aliou/vite-plugin-obsidian@0.2.0
Closing my open tabs and finally read Mikker’s post. Borrowed his review workflow but wanted to have it in a Ghostty split (thanks to AppleScript).
I then had Kimi and GLM figure it out and make me an extension.
Release v0.1.0
Been using variations of this prompt every time I notice a gap in my knowledge. Choosing a starting point that I broadly understand and then throwing stuff I literally don’t know about works super well for me.

I want to learn more and understand what’s fine tuning for llm models, what’s lora, how does https://thinkingmachines.ai/ ‘s products fit in this etc. to do so, i want to use coding agent traces as training data, from hugging face (see https://huggingface.co/changelog/agent-trace-viewer ) , in particular traces from pi.
let’s assume i know nothing about what is finetuning and how it works. let’s go through this blog post https://leoniemonigatti.com/blog/fine-tuning-lfm2-5-1-2b-instruct-with-grpo.html and quizz me on it to see what is my current understanding. use the current directory as scratchpad / notes. download the markdown of the article here so I/we can annotate it . curl it using this url
https://markdown.new/https://leoniemonigatti.com/blog/fine-tuning-lfm2-5-1-2b-instruct-with-grpo.html
New TIL: I figured out how to use my LLM CLI tool in a shebang line, which means you can write executable scripts in English, or hook up more complex scripts with a snippet of YAML template
— @simonw
It’s becoming a meme at this point, but here’s a Pi extension to do the same:
Also watch GLM-5.1 completely overthink while trying to speed up the video and add a fast-forward overlay.
Current status.
Currently cleaning up a backlog of issues and PRs in a client’s repo and was tired of alt-tabbing between Pi and my browser, so made a quick Pi extension to autocomplete GitHub PRs and issues.
Never going to use this ever again, but might be useful to other people so sharing it as a gist.
@aliou/pi-utils-ui panels while preserving the existing pi-utils-settings component APIsWas about to make a Pi extension to communicate between sessions, but actually only needed Amp’s read thread tool.

reasoningEffortMap to thinkingLevelMap per Pi 0.72.0 API. Replace session_switch event with session_start. Swap @sinclair/typebox for typebox. Add Kimi K2.5 model. Set reasoning: false for Llama 3.3 70B/v1/models on session start and re-registers the provider with live data (including pricing, capabilities, and limits from the new API metadata). A notification is shown when live models differ from the cache. Falls back to the hardcoded cache if the fetch failssrc/lib/, introduces ApertureRuntime class with dependency injection, replaces lifecycle hooks with session_start + onSync callback pattern, and adds provider unregistration with user notificationstreamSimple wrapper that sends x-session-id header with the Pi session ID. This groups all requests from the same Pi session together in the Aperture dashboardmodels from registerProvider call. Rely on the baseUrl-override path instead, which preserves built-in model definitions (reasoning, compat, thinking levels) and only updates the endpoint URLCan’t help but re-read this every time I write “just” somewhere.
ctx.ui.custom() returns undefined, with fallback to ctx.ui.select()Frame to extend Container from pi-tui, inheriting addChild, removeChild, and clear. borderColor is now required. Children are no longer accepted via constructor options — use addChild insteadMinor Changes eb09413: Add no-empty-catch plugin: disallows empty catch blocks Patch Changes 526fb68: Bump Biome peer dependency to >=2.5.0 ef2d41d: Add biome-plugins-setup skill for configuring plugins in consumer projects
r key binding to the quotas command to refetch and refresh quota data without closing the panel~-based patterns match correctly and expand to the current user’s home directory during blocking and existence checksdisplayToStorageValue helper. The default change handler now stores raw strings as-is instead of coercing “on”/“off”/“enabled”/“disabled” to booleans. Use onSettingChange to convert display values to the correct storage typesMinor Changes c72c019: Add the no-buried-await plugin to disallow burying await inside parentheses. This catches patterns like return (await foo()) || "" and Boolean(await foo()) and encourages awaiting the value first before using it in a separate expression.
Banger.
Two ways to connect Pi to a custom model endpoint, using Synthetic as an example
linkup_web_fetch outputs in tool results and save the full fetched content to a temp filelimit parameter to linkup_web_search so agents can cap returned result countbash.sourceMode with deterministic override-bash and composed-bash routing for rewrite featuresWhat's Changed release: v0.5.0 by @github-actions[bot] in https://github.com/aliou/linear-cli/pull/7 Full Changelog: https://github.com/aliou/linear-cli/compare/v0.4.0...v0.5.0
Minor Changes 4f2e6c0: Add pi-no-node-exec plugin: disallows importing from child_process in pi extensions. Use pi.exec() instead.
FuzzySelector via searchThreshold (default 7). When item count is at or below the threshold, it now renders a simple Up/Down/Enter list without a search input while keeping callbacks and currentValue pre-selection behavior consistentWhat's Changed release: v0.4.0 by @github-actions[bot] in https://github.com/aliou/linear-cli/pull/6 Full Changelog: https://github.com/aliou/linear-cli/compare/v0.3.0...v0.4.0
Needed to scaffold a project this morning with an AGENTS.md and liked how Claude Code’s new /init looked, so had Pi figure out how it works and make its own version.
Another one-time use extension.
@aliou/pi-extension-dev to @aliou/pi-dev-kitWhat's Changed release: v0.3.0 by @github-actions[bot] in https://github.com/aliou/linear-cli/pull/5 Full Changelog: https://github.com/aliou/linear-cli/compare/v0.2.2...v0.3.0
pi config can enable or disable them individually.pi config loading, shared command registration, and dynamic Linkup system prompt guidance. Closes #17.The models are getting better and better which makes it easy to forget to not delegate your thinking.
What's Changed release: v0.5.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/6 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.4.1...v0.5.0
Finally got around to reading Linear’s Agent interaction guidelines and did a quick implementation with Pi. It was pretty simple to set up and I kinda like the UX!
Take a look at the code on GitHub.
Minor Changes 9231b94: Add a new no-ts-import-extension plugin that disallows .ts extensions in import and re-export paths.
What's Changed release: v0.2.0 by @github-actions[bot] in https://github.com/aliou/fastmail-cli/pull/3 Full Changelog: https://github.com/aliou/fastmail-cli/compare/v0.1.1...v0.2.0
Had to update Ghostty to fix that annoying auto-scroll bug but didn’t want to lose my open tabs. Asked Pi/GPT-5.4 to dump and restore my tabs and splits. It mostly worked and I’m never going to reuse this.
Full Changelog: https://github.com/aliou/linear-cli/commits/v0.1.0
buildSchemaUrl helper and schemaUrl option for ConfigLoader. When set, save() injects $schema as the first key and load() strips it from parsed configenvFiles to a new policies system with per-rule protection levels (noAccess, readOnly, none), add migration from old config fields, and replace the old env hook with a general policies hook@aliou/pi-utils-settings to latest 0.5.x, clarify fallback behavior in README/AGENTS, ignore .pi/settings.json, and ensure migrated configs write the current schema version without lexicographic version comparisons/ps:dock, /ps:focus, /ps:logs commands. Add deprecated /process:* commands. Replace status widget with log dock. Preserve ANSI colors. Fix duplicate notifications. Use proper ThemeColor typewrite action to write to process stdinextraTabs support to registerSettingsCommand so extensions can render non-scope top-level tabs (for example, an Examples tab) after scope tabsSettingsTheme that works as both SettingsListTheme and full pi `ThemeSettingsDetailEditor component for focused second-level settings editingWhat's Changed release: v0.4.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/3 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.3.0...v0.4.0
Minor Changes 0a00dc1: Add a new no-inner-types plugin that disallows type and interface declarations inside function bodies.
Got absolutely screwed by Opus thinking it was running on Linux instead of macOS while debugging some CLI stuff with tmux.
But the session of Opus figuring out what happened is pretty fascinating:
Also created a small tool that uses BM25 to find content in previous sessions. It’s super useful to just say “I was running a Pi session at the same time, could it be related?” This is completely borrowed from Amp.
What made me switch coding agents and why I stayed
deep boolean with depth enum on web-answer tool for consistency with web-searchallowImportingTsExtensions and setting noEmit: falseWhat's Changed Updating @aliou/sesame to version 0.3.0 by @github-actions[bot] in https://github.com/aliou/sesame/pull/6 Full Changelog: https://github.com/aliou/sesame/compare/v0.2.0...v0.3.0
What's Changed release: v0.3.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/2 Full Changelog: https://github.com/aliou/preview-markdown/compare/v0.2.0...v0.3.0
What's Changed release: v0.1.0 by @github-actions[bot] in https://github.com/aliou/sesame/pull/1 New Contributors @github-actions[bot] made their first contribution in https://github.com/aliou/sesame/pull/1 Full Changelog: https://github.com/aliou/sesame/commits/v0.1.0
Minor Changes fcd49ce: Correct version bump: new plugins are minor releases, not patches. This bump acknowledges that the no-emojis plugin added in 0.2.1 was a feature addition.
Minor Changes 04b0993: Add no-js-import-extension plugin that disallows .js extensions in import and re-export paths Patch Changes b900cf9: Bundle biome-gritql-plugins skill for agents working with this package
Maintenance matters more than setup: keeping context fresh as codebases evolve
Very meta: wanted to use Kimi K2.5 via Synthetic, so I had Pi set up a one-off extension that creates the provider and defines its model. Then reloaded Pi and asked Kimi to create the Synthetic provider.
Release pi-linkup@0.1.0
Another one, but this one probably more for me than other people: guardrails!
Fun extension I made for Pi: processes. Let your agent handle running processes in the background and be notified when they finish / die etc.
Ported by beloved jellybeans-mono for Pi, both dark and light!


What's Changed release: v0.1.0 by @github-actions[bot] in https://github.com/aliou/fastmail-cli/pull/1 New Contributors @github-actions[bot] made their first contribution in https://github.com/aliou/fastmail-cli/pull/1 Full Changelog: https://github.com/aliou/fastmail-cli/commit…
Your AGENTS.md is not enough. Leverage deterministic tools to enforce rules
What's Changed release: v0.2.0 by @github-actions[bot] in https://github.com/aliou/preview-markdown/pull/1 New Contributors @github-actions[bot] made their first contribution in https://github.com/aliou/preview-markdown/pull/1 Full Changelog: https://github.com/aliou/preview-mar…
Progressive disclosure for coding agents: load context only when needed
Full Changelog: https://github.com/aliou/preview-markdown/commits/v0.1.0
The new wave of coding models needs less hand-holding than you think
How coding agents changed the way I worked
Fixing nvim-treesitter after the master-to-main rewrite
I needed to quickly investigate some data and used the following pattern to do so. Using the model of an e-commerce store as example, here’s what I did.
First, setup a cube.js defining the dbType:
module.exports = {
dbType: 'duckdb',
}
Then, define the driverFactory, and configure the access to the GCS bucket:
const driverFactory = async () => {
const { DuckDBDriver } = require('@cubejs-backend/duckdb-driver');
return new DuckDBDriver({
database: ':memory:',
initSql: `
INSTALL httpfs;
LOAD httpfs;
CREATE SECRET (
TYPE gcs,
KEY_ID 'YOUR_KEY_ID',
SECRET 'YOUR_SECRET_ID'
);
`,
});
}
Finally, add to initSql the commands to setup our views and access our data:
CREATE VIEW orders AS
SELECT * FROM read_parquet('gs://tmp-bucket/orders/*.parquet');
CREATE VIEW products AS
SELECT * FROM read_parquet('gs://tmp-bucket/products/*.parquet');
CREATE VIEW line_items AS
SELECT * FROM read_parquet('gs://tmp-bucket/line_items/*.parquet');
CREATE VIEW users AS
SELECT * FROM read_parquet('gs://tmp-bucket/users/*.parquet');
CREATE VIEW inventory_transactions AS
SELECT * FROM read_parquet('gs://tmp-bucket/inventory/*.parquet');
Views, Not Tables: I specifically used CREATE VIEW instead of CREATE TABLE AS to avoid loading all the data into memory. Views just store the query definition - DuckDB streams the Parquet data chunk by chunk when you actually query it. With tables, DuckDB would copy everything into its internal format, eating up memory or disk space.
Zero Infrastructure: The :memory: database means no provisioning. Each Cube process gets a fresh DuckDB instance that reads directly from existing Parquet files.
Direct Access: No ETL pipeline, no data loading. The read_parquet() function streams data directly from GCS, and DuckDB’s columnar engine only fetches the columns needed for each query.
Wildcard Patterns: The /*.parquet pattern lets you treat multiple files as a single table without any preprocessing. Perfect for data that’s already partitioned by date or other dimensions.
In this example, you could run complex queries across orders, products, line items, and inventory - joining across millions of rows within seconds. What would normally require hours of data pipeline setup can be done immediately.
DuckDB’s aggressive caching means the first query might take a few seconds to fetch metadata, but subsequent queries run pretty much instantly even on larger datasets.
Benefits:
Drawbacks:
:memory: means state vanishes on restartThis pattern works for any quick data investigation. Got Parquet files? Throw them in GCS, point Cube at them with this config, and you’re analyzing data in minutes. For more permanent setups, swap :memory: for a file path to persist the DuckDB catalog between restarts.
Your data lake is already an OLAP database. You just need DuckDB to unlock it.
Continuing my work on cube-records, I wanted to support joined field names like orders.total or users.email - matching exactly how the underlying Cube names them. Instead of manually defining these combinations, TypeScript’s template literal types generate them automatically.
Here’s a simplified version of the Cube schema:
// User defines their schema
interface Schema {
users: {
fields: { id: string; email: string };
joins: ['orders'];
};
orders: {
fields: { total: number; status: string };
joins: [];
};
}
Then, we use a template literal type to defined our joined fields:
// Template literal magic
type JoinedFields<T extends keyof Schema> =
Schema[T]['joins'][number] extends infer Join
? Join extends keyof Schema
? `${Join}.${keyof Schema[Join]['fields'] & string}`
: never
: never;
Finally, we can also use the type to create our own custom type.
// TypeScript now knows these fields exist:
type UserJoinedFields = JoinedFields<'users'>;
// Result: 'orders.total' | 'orders.status'
The infer keyword extracts each join name, then template literals compose the dot-notation paths.
With the schema defined above and our new type, we can define a Query type like the following:
type Query<T extends keyof Schema> = {
model: T;
fields: (Schema[T]['fields'] & JoinedFields<T>)[];
}
And in our code, we can declare a query serenely thanks to the type system preventing us from writing a query with invalid fields:
const query: Query = {
model: 'users',
fields: [
'email', // ✓ users field
'orders.total', // ✓ joined field
'orders.status', // ✓ joined field
'invalid.field' // ✗ Type error
]
};
The real implementation has more conditionals for safety, but the core idea remains the same: extract the join names, then use template literals to compose the field paths.
Benefits:
Drawbacks:
This pattern shines when you control the schema and need to generate predictable string patterns. It’s less suitable for user-defined schemas or when you need runtime validation anyway. The sweet spot is internal APIs where you want compiler-enforced consistency between your types and runtime behavior.
What you don’t write is as important as what you do. No manual string unions. No keeping field lists in sync. Add a new join relationship, and TypeScript immediately knows about every possible field combination.
When Ghostty came out, I wrote this blog post which contained a few screenshots. I forgot how I managed to have both light and dark mode work seamlessly in Eleventy, so I figured I’d write it down for future me or anyone that sees this.
I use an Eleventy shortcode that conditionally renders different images based on theme preference. The basic version wraps images in figure tags with captions:
config.addShortcode('image', (src: string, alt: string, withDarkMode = false) => {
const lastDotIndex = src.lastIndexOf('.');
const basePath = src.slice(0, lastDotIndex);
const extension = src.slice(lastDotIndex);
const lightPath = `${basePath}-light${extension}`;
const darkPath = `${basePath}-dark${extension}`;
return `<figure>${
withDarkMode
? `<img class="not-dark:hidden" src="${darkPath}" alt="${alt}">` +
`<img class="dark:hidden" src="${lightPath}" alt="${alt}">`
: `<img src="${src}" alt="${alt}">`
}<figcaption>${alt}</figcaption></figure>`;
});
The images would swap instantly when toggling themes, which felt jarring. Adding Tailwind’s transition utilities creates a cross-fade effect:
withDarkMode
? `<img class="not-dark:hidden not-dark:opacity-0 dark:opacity-100 starting:dark:opacity-0 transition-discrete transition-opacity duration-1000" src="${darkPath}" alt="${alt}">` +
`<img class="dark:hidden dark:opacity-0 not-dark:opacity-100 starting:not-dark:opacity-0 transition-discrete transition-opacity duration-1000" src="${lightPath}" alt="${alt}">`
: `<img src="${src}" alt="${alt}">`
The starting: modifiers prevent the fade animation on initial page load, which I borrowed from how Tailwind’s own docs handle theme switching.
Now {% raw %}{% image "ghostty-screenshot.png" "Terminal with ligatures" true %}{% endraw %} automatically looks for ghostty-screenshot-light.png and ghostty-screenshot-dark.png, then smoothly transitions between them when you toggle themes.
Benefits:
Drawbacks:
This approach works well for static sites where you control every image. For dynamic content or high-traffic sites, you’d want lazy loading for the hidden variant or CSS filters for automatic adjustments.
Figured how to create custom shortcode after reading this article by Anh.
While working on @general-dexterity/cube-records, I needed a way to defineOr use codegen with another library. my domain-specific Cube models in a way that would provide type-safe autocompletion. The solution was ended up pretty simple: global interface augmentation.
The lib defines an empty global interface that users can augment with their own cube definitions. However, after publishing, I discovered that tsup was optimizing the empty interface into a type alias during the build:
// What we write
export interface CubeRecordMap {}
// What tsup outputs with dts: true
export type CubeRecordMap = {}
// Now augmentation fails
declare global {
interface CubeRecordMap { // Error: can't augment a type alias
users: { /* ... */ }
}
}
Adding a dummy property prevents this optimization:
export interface CubeRecordMap {
__empty: {
measures: {};
dimensions: {};
joins: [];
};
}
That __empty property isn’t arbitrary - it ensures the interface remains augmentable through the entire build pipelineThis is specifically a build tool optimization when generating declaration files, not a TypeScript language limitation. .
Users can now extend the interface in their projects:
declare global {
interface CubeRecordMap {
users: {
measures: { count: { type: number } };
dimensions: {
id: { type: string };
email: { type: string };
};
joins: readonly ['orders'];
};
// ... other models and views
}
}
The library extracts type-safe cube names and fields:
type CubeRecordName = keyof CubeRecordMap;
type CubeRecordMeasure<T extends CubeRecordName> =
keyof CubeRecordMap[T]['measures'] & string;
Benefits:
Drawbacks:
This isn’t just for my personal benefitsEven though it was pretty fun to try to figure this out. , this pattern provides domain-specific autocompletion without runtime cost. Instead of searching through Cube models for field names, TypeScript provides instant feedback as you type.
While writing my Ghostty blog post, I wanted syntax highlighting for the configuration snippets. Since Shiki doesn’t support Ghostty’s config format, I added a custom language definition.
const ghostty = {
name: 'ghostty',
scopeName: 'source.ghostty',
fileTypes: ['ghostty'],
repository: {},
patterns: [
{
include: '#strings',
},
{
name: 'comment.line.number-sign.ghostty',
match: '^\\s*#.*$',
},
{
match: '\\b(font-family|font-feature)\\b',
name: 'keyword.other.ghostty',
},
],
};
I only defined patterns for comments (#) and the font keywords I was actually using. The grammar follows TextMate rules but doesn’t try to implement the full Ghostty spec - just enough for my examples.
highlighter = await createHighlighter({
themes: ['github-dark', 'github-light'],
langs: ['ruby', 'javascript', 'json', 'elixir', 'typescript', ghostty],
});
Enabling Monaspace’s stylistic sets for code ligatures in Ghostty
Parsing structured strings with Ruby’s named regex captures instead of split
Making Ruby value objects look like constants by overriding inspect
Serializing value objects into ActiveRecord models with the Attributes API
.gitignore, .git/info/exclude, and core.excludesfile
Building a custom Ecto type for Postgres timestamp ranges
A Rails source spelunking find: custom validators can be namespaced with ::
Setting up a global .gitignore to keep editor and OS files out of every repo