Typer Companion: The Missing Wizard Layer for Ambitious Scripts
Update (April 2026): We briefly exposed an internal Hyperdrift scripting module as a public repo under the name typerx. That pulled in too much studio-specific surface area (layout detection, vault, apps.yml, deploy helpers) for a broad OSS audience. We have corrected course: hyperdrift-io/typer-companion is again the public, generic wizard layer (pip install typer-companion). typerx remains Hyperdrift-private—the vendored module in our mono-repo for operators who need Rich logging and org paths in the same import. This article stays anchored on Companion as the ecosystem-facing idea.
If Typer gave Python CLIs their voice, it can also grow the missing layer that helps serious scripts keep their bearings as they scale.
In the last Hyperdrift article, we argued that modern developer tooling is still missing an important layer between brittle shell scripts and heavyweight internal platforms.
That argument was not theoretical. It came from pressure inside real operational work.
Now the proposal can be stated more plainly.
What the ecosystem needs is a public Typer extension: a reusable wizard and orchestration layer for scripts that need to carry real operational responsibility—without inheriting one studio’s infra tree.
We have a name for it.
Typer Companion.
The public repo is hyperdrift-io/typer-companion.
pip install typer-companion
Not because scripts need more ceremony. Because they need a better companion when the work stops being “run a command” and starts becoming “safely move through a sequence of stateful, partially validated, partially external steps without losing context, operator trust, or the mission.”
That is where too many scripts begin to collapse.
The proposal
Typer is already excellent at turning Python functions into clean CLIs. It gives structure, ergonomics, and a pleasant surface for command design.
But once a script becomes a wizard, a release flow, a cloud setup routine, or a multi-system operation, a different set of problems appears:
- some steps can be validated immediately, others only after an external action
- some failures should stop the flow, others should offer retry, skip, or defer
- some context should be prompted once and remembered
- some state already exists and should be reused instead of asked again
- some operator guidance should be part of the flow, not bolted on as print noise
This is where Typer, excellent as it is, still stops a little too early in the stack.
That missing layer is what Typer Companion is meant to address: a wizard-first extension for ambitious scripts, where the unit of progress is not only a command, but a validated step with retained context and explicit recovery behaviour.
The naming matters. This should not feel like an internal utility exported by accident. It should read like a must-have Typer extension the moment a developer sees the repo name, the README, or the first example.
Call it Bash scaling, if you want, but in a more disciplined form.
Not “predict every possible failure in advance.” Not “throw more shell at it.” Not “rebuild an internal platform.”
A methodic layer that says:
- define the step
- validate what can be validated now
- retain the context that should carry forward
- surface the right recovery path when reality diverges
- continue only when the flow is still trustworthy
That is a much better model for serious scripting.
Why wizard-first matters
The word “wizard” sometimes sounds lightweight, but operationally it is the opposite.
A good wizard is a controlled traversal through uncertainty.
It acknowledges that the operator may need to move through local state, cloud portals, generated secrets, deployment checks, copy-paste boundaries, permissions, and external consoles in one coherent flow. It treats this not as an awkward exception to automation, but as a first-class shape of work.
That matters because a lot of important scripting does not fail from lack of capability. It fails from loss of orientation.
The script can technically run. The operator can technically proceed. But nobody is quite sure:
- what has already been completed
- which values were confirmed versus assumed
- whether the current step is safe to continue
- what should happen after a recoverable failure
- what context should be reused instead of re-entered
Shell scripts usually leave that burden on the operator. Larger platforms hide it behind custom products. The missing middle is a composable wizard runtime that stays close to the machine while still carrying memory, validation, and guidance.
That is why the wizard approach is not a side feature. It is the correct starting point.
What Hyperdrift’s scripts already revealed
Hyperdrift’s internal tooling (today vendored as typerx in our mono-repo) and flows like setup-oauth.py made the abstraction boundaries visible—but those lessons belong in two buckets.
Bucket A — generic, belongs in open source (Typer Companion): output semantics (wizard, step, remember, retryable), and env-backed prompting as a primitive. Operators and agents both benefit when progress is named and recovery is explicit.
Bucket B — org-specific, stays private: apps.yml resolution, Ansible vault, make deploy, GA/OAuth wiring, and other verbs that assume our directory layout. That is maintenance cost we accept inside the studio; it should not become every downstream user’s implicit dependency.
The first lesson is that output semantics matter more than most script authors think. Functions like info(), success(), warn(), error(), and section headers are not cosmetic. They form an operator contract.
The second lesson is that prompt state is part of the runtime, not just input collection. Reading existing values, validating, persisting when changed, and returning a stable value back into the flow is already a wizard primitive—Companion’s step(...).prompt_env is the portable shape of that idea.
The third lesson is the most important: what looks like “helper reuse” is already the early shape of a runtime—but the public runtime should stay small. Typer Companion carries Bucket A. Hyperdrift’s private module carries Bucket B on top, for our operators only.
The abstractions worth separating
If Hyperdrift pushes this further in public, the right move is not to publish our entire helper file unchanged and call it a framework.
The better move is to separate the reusable orchestration layer from the Hyperdrift-specific adapters.
That likely produces three layers:
1. Typer as the command surface
Typer remains what developers already love about it: elegant commands, arguments, options, help text, and Pythonic ergonomics.
2. Typer Companion as the wizard runtime
This is the public extension layer—the installable package. The place where ambitious scripts get:
- step definitions (
step(...).run,step(...).prompt_env) - checkpoint-style progress the operator can read
- contextual prompting with persistence
- structured retry behaviour (
retryable) - remembered context across a run (
remember) - explicit flow identity (
wizard)
3. Org-specific adapters on top (private)
This is where Hyperdrift keeps its own domain verbs—workspace resolution, apps.yml, infra commands, deploy checks, OAuth specifics, cloud and vault integrations. That layer is not the generic OSS contract. It ships as private tooling so the ecosystem is not asked to adopt our tree.
That separation matters because the ecosystem does not need Hyperdrift’s infrastructure assumptions. It needs the orchestration primitive beneath them—and that primitive is Typer Companion.
What the API should feel like
The goal should not be maximal abstraction. It should be obviousness.
Something in this spirit (the published API):
import typer
from pathlib import Path
from typer_companion import remember, retryable, step, wizard
app = typer.Typer(add_completion=False)
def validate_project(project_id: str) -> str:
if not project_id.startswith("acme-"):
raise ValueError("project_id must start with acme-")
return project_id
def enable_apis(project_id: str) -> None:
print(f"Enabling APIs for {project_id}")
def check_readiness() -> None:
print("Readiness OK")
@app.command()
@wizard("Setup")
def setup() -> None:
project_id = remember("project_id").prompt("Project ID")
step("Validate project").run(validate_project, project_id)
retryable("Enable APIs", attempts=3).run(enable_apis, project_id)
step("Collect credentials").prompt_env(Path(".env.local"), "API_KEY")
step("Readiness").run(check_readiness)
if __name__ == "__main__":
app()
Not because this exact API is final, but because the shape is right.
The script should describe a trustworthy journey, not manually juggle print statements, loops, defaults, pauses, and recovery branches all over the file.
That is where many otherwise elegant Typer scripts start becoming hard to scale.
Why this matters in the age of agents
This proposal is not only for human operators.
Agents need this layer too.
An agent can generate commands quickly. That is not the hard part anymore. The hard part is retaining context across a longer operational flow without dissolving intent into a pile of subprocess calls.
When the runtime has explicit steps, remembered context, validation gates, and named recovery paths, an agent can operate with much stronger boundaries. It knows what state has been established, which step failed, what retry means, and what should not be improvised.
Without that layer, agents inherit the oldest weakness of shell scripting: too much freedom at the exact moment reliability matters most.
So yes, this is a Typer extension proposal. But it is also a proposal for a better human–agent operating surface.
That is why the “companion” framing matters. The script is no longer a throwaway tool. It becomes a guided operational partner.
The larger opportunity for Typer
Typer helped make Python CLIs feel modern, humane, and joyful.
The next opportunity is to help them become dependable at a higher level of responsibility.
Not by turning Typer into an enterprise platform. Not by bloating it with every workflow opinion imaginable. But by letting a clean extension ecosystem emerge around the real places where scripts begin to strain.
Typer Companion is one of those places.
It gives maintainers and operators a stronger default for flows that are:
- stateful
- step-based
- semi-interactive
- partially recoverable
- deployment-adjacent
- shaped by external systems that cannot be abstracted away entirely
That is a real category. It deserves a real name and a real tool.
What Hyperdrift intends to push
Hyperdrift’s interest here is not to decorate scripting with a nicer DX story.
It is to contribute a public pattern for orchestrated development: a way to build scripts that stay close to the machine while gaining the method, memory, and operator discipline needed to scale.
The public artifact is typer-companion—installable, documented, and kept generic.
The private artifact is our internal typerx module—Rich-backed logging and Hyperdrift paths for our operators only, so we do not overload the OSS package with studio assumptions.
If the public layer proves valuable, the win is larger than one library: it gives the industry a better way to think about the layer between Bash and platform engineering.
And that layer is only becoming more important.
Hyperdrift is building publicly toward a stronger operational standard: not just faster scripts, but scripts with memory, method, and enough composure to support serious missions. The generic wizard layer belongs in the open as Typer Companion; the studio-specific glue stays where it belongs—private, beside the rest of our infra.
Get weekly intel — courtesy of intel.hyperdrift.io