May 25, 2026

A Bash Ability, and a Leaner Build

Chalie can now run shell commands safely, and the whole project got faster to install and cheaper to maintain.

Today had two threads running in parallel: giving Chalie a genuinely new capability, and paying down the accumulated weight of how the project installs and builds itself. Neither is glamorous alone, but together they say something about where the product is headed — more capable, and lighter to run.

The headline feature is a bash ability. Chalie can now execute shell commands directly, which sounds simple until you think about what “safely” has to mean for an agent that runs unsupervised. The design classifies every command into one of seven risk categories before it runs, using the model’s own judgment as a first pass. But self-classification from an LLM isn’t something you trust blindly, so I added a pre_dispatch hook to the ability base class — a checkpoint that lets an ability inspect and correct its own parameters before the policy engine ever sees them. Bash uses this to run a heuristic shell-syntax check that can escalate a command’s risk category upward if the model under-classified it, closing the gap between “what the model thinks this does” and “what this actually does.” Destructive commands are blocked outright, and anything pulled from the environment gets secrets stripped before it’s exposed. Policy defaults reflect the caution this deserves: read-level bash access is allowed in normal chat, but every other sub-action — and all of them for anything running as an external agent — requires explicit permission or is denied by default.

The second thread was infrastructure hygiene, and it’s the kind of work that doesn’t show up in a feature list but shows up every time someone tries to get the project running. Dependency management moved from requirements.txt to pyproject.toml, with optional groups for voice and GPU variants instead of a scatter of separate requirement files. The install and run scripts now lean on uv, which resolves dependencies in tens of milliseconds when nothing’s changed, so I ripped out the stamp-file logic and conditional checks that used to exist purely to avoid slow reinstalls — 128 lines of install script became 82. Pip still works as a fallback; this isn’t a bet against compatibility, it’s dropping dead weight now that a faster tool makes the old workaround pointless.

CI got the same treatment. Nine workflows became five: two release-announcement workflows tied to infrastructure the project no longer needs were deleted outright, the docs-sync workflow went because the website no longer pulls docs that way, and CodeQL was scaled back to a weekly cadence plus manual dispatch instead of running on every push. Dev tooling like linters moved out of a dedicated requirements file and into the CI job directly, since they’re utilities, not project dependencies. Closing the loop, I fixed everything CodeQL and the linter had been flagging: a DNS resolution check that catches cloud-metadata-IP addresses hiding behind DNS rebinding, containment guards against path traversal in the file and skill-writing code, and sanitization of response bodies before they hit logs, so a malicious payload can’t inject fake log lines.

None of this is the kind of thing that goes in a highlight reel, but it’s the difference between a project that’s pleasant to contribute to and one that accumulates friction until people stop bothering. A faster, honest build pipeline and a real shell capability, shipped the same day, is a decent proxy for how I want Chalie to keep growing: more powerful, and easier to trust.

Next up is watching how the bash ability behaves under real use — that’s where the classification heuristics either hold up or need tightening.

  • Shipped a bash ability with 7-category risk classification, a pre_dispatch hook for heuristic escalation, destructive-command blocking, and secret stripping from the environment.

  • Migrated dependency management to pyproject.toml with optional voice/GPU groups, replacing scattered requirements files.

  • Cut install script logic from 128 to 82 lines by dropping stamp-file checks now that uv resolves dependencies almost instantly.

  • Reduced CI from 9 workflows to 5, retiring unused automation and scaling CodeQL back to a weekly cadence.

  • Closed out a round of security findings: DNS-rebinding protection, path-traversal guards, and log-injection sanitization.