Logo
Back to writing
July 6, 20266 min read

Bespoke Command Line

The CLI I built for the agency I work at, and how a script written to avoid one boring job became the foundation for automating most of our project setup.

One of the biggest hidden costs of agency life is context switching.

Every new client project starts the same way. Create the repository. Clone and strip back the default theme. Set up a project in whatever tool the client uses. Turn the brief into actionable tickets. Configure everything. Then, finally, start building.

None of that is engineering, and none of it is what developers want to be doing. But it happens on every single project, and it's easy to overlook how many hours quietly disappear into it.

How can we make starting a new client project simple?


The Problem

The worst offender was turning a project specification into a backlog. Slow, repetitive, entirely manual, and worth nothing to the client the moment it's done.

That's true of a lot of our setup work. Every agency ends up maintaining its own collection of improvements on top of whatever base theme it uses — performance optimisations, SEO enhancements, accessibility fixes, preferred tooling, analytics integrations, and the handful of features almost every client asks for. Adding those by hand to each new project costs developer time and client budget twice over, and provides very little value after the first implementation.

The obvious fix is to build a bespoke theme from scratch, and we seriously considered it. But that comes with its own bill:

  • New Shopify features have to be implemented manually.
  • Someone has to build and maintain it, which takes resource away from client work.
  • It's a much longer timescale project.
  • It needs dedicated QA and UAT time.
  • You have to convince the client it's worth paying for.

So the most cost-effective route was to keep using Dawn as the base and build a shared foundation of reusable components and tooling on top of it.


What I Built

I started with the backlog, not because I had a clever technical solution, but because I didn't want to keep doing it by hand.

Project specifications in an agency are usually written in a fairly consistent format (I hope). So the first version was a small Node.js script that took a brief, created a GitHub repository via the GitHub CLI, and generated a set of tickets from the specification.

Something along the lines of:

node create-project.js \
  --project "Project Name" \
  --client "Client Name" \
  --specs "path/to/specs"

Creating 100+ tickets went from a few hours of a developer's time to about five minutes. I honestly can't remember exactly how that first version worked or what flags it had, but it solved enough of the problem that people started asking:

"Liam, can you set up this new client project for me?"

That's the point I stopped treating it as a personal utility. The local script became a proper CLI, published via npm so anyone on the team could install it. Once it was a shared tool rather than my script, it opened the door to far more than backlog creation — new commands and workflows could live in the same place, and the rest of the team could contribute instead of waiting on one developer to maintain it.


How It Works

Scaffolding instead of stripping back Dawn

At the time, almost every Shopify build in the agency started from Dawn. Clone the latest version, remove everything the client doesn't need, add our tooling, install dependencies, configure the project, and only then start on features. Same fixes, every time — and increasingly awkward once Dawn stopped receiving regular updates.

The CLI now handles that whole sequence, so a new project arrives already configured against our conventions rather than being dragged there by hand.

A section library that ships unstyled

If you work with premium or luxury brands, there's another recurring pattern.

Designers love sections. Clients love sections. Developers... tolerate sections.

Luxury brands want complete control over merchandising and storytelling, so projects routinely contain dozens of highly configurable content sections. They're genuinely valuable to the client and almost entirely uninteresting to build — most are combinations of content, layout options and Shopify schema. Spending hours rebuilding another image-and-text section isn't moving the needle on revenue.

Why are we rebuilding the same foundation and components over and over again?

So the library doesn't contain finished components. Every section ships with the absolute minimum styling, but with the functionality, schema and accessibility already complete. Each project installs the section and gives it a fresh lick of paint. Everything in there has been used in production multiple times, which means far fewer surprises in QA and UAT.

Standardised tooling and schemas

Every project now uses the same CSS utility library and the same conventions. It sounds minor until you've worked across dozens of client codebases and had to ask, every single time: where is this component, what's this class called, does this utility already exist, am I about to duplicate something?

Schema ordering turned out to be a smaller win that mattered more than expected. One developer puts typography and spacing controls at the top of the schema, another puts them at the bottom. Neither is wrong, but the inconsistency compounds until every section is organised differently and you're hunting for a setting you know exists. Keeping every schema predictable makes the theme noticeably easier to use — and the client is the one adopting the system, so the less cognitive effort it takes them to find a setting, the more polished the whole thing feels.


What It Changed

The old process:

  1. Clone Dawn.
  2. Remove everything the client doesn't need.
  3. Add agency tooling and dependencies.
  4. Configure the project.
  5. Build every section from scratch.
  6. Finally start building the ecommerce features.

The new process:

  1. Run the project setup CLI.
  2. Select the reusable sections included in the spec.
  3. Configure CSS variables from the design system or Figma.

My rough estimate is that it removes 50–70% of the setup and repetitive work, though I should be honest that it's an estimate rather than something we've measured properly. Every project still has bespoke components and unique experiences — the point was never to automate those. It's that if you automate everything which isn't engineering, developers get significantly more time on the features that actually differentiate the project.

The unexpected benefit was collaboration. Because the CLI is a shared package rather than one person's script, other developers add commands to it, and the conventions get argued about openly instead of being rediscovered per project.


What I'd Do Differently

Ticket estimates are still the outstanding piece. Labels are automated, and GitHub Actions handle milestones, but estimating remains manual — and it's the part that would save project managers the most time.

I'd also have measured properly from day one. The clearest evidence I have that it works is that people started asking me to run it for them, which is a good signal but still an anecdote. A real before-and-after number would have made the case for investing in the tool much earlier than I managed to.