{"slug": "terminal-velocity-the-shell-tools-that-make-claude-code-fly", "title": "Terminal velocity: the shell tools that make Claude Code fly", "summary": "Nearform teams use Claude Code daily and rely on command-line tools such as jq, yq, and the GitHub CLI (gh) to keep the AI coding agent's context lean, according to a Nearform article on terminal tooling for Claude Code. The article notes Claude Code offers models with a 1 million token context window, and that extracting only needed data via utilities like cut and tail preserves accuracy and avoids context degradation and lost-in-the-middle effects. On macOS, the recommended tools install via brew install, and the article suggests documenting JSON and YAML extraction preferences in a CLAUDE.md file.", "body_md": "### Douglas McIlroy, one of the Unix Godfathers, once wrote: \"This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.\"\n\nOne of the biggest intuitions Claude Code got right from the start is its \"form factor\": running in the terminal. The Unix philosophy is about manipulating text streams and files via composable tools (even if we sort of lost the \"small\" part along the way), and this is exactly the ecosystem Claude Code gets to play in. It can stitch tools together on the fly with pipes and redirects, and you can hand it new capabilities by simply adding a command to the shell.\n\nAnother advantage of the shell composability is the ability to surgically query and extract just the data you need; Claude Code is perfectly able to extract just the small part of the payload it needs via the use of well-known utilities like `cut` or `tail` and so on. This is of great benefit: even if Claude Code offers models with a 1 million token context window, keeping the context lean is, in general, a great idea. Accuracy stays high, we avoid context degradation and lost-in-the-middle effects, and the harness and the model perform at their best.\n\nAt Nearform, many teams use Claude Code every day, and a lot of us are reaching for new CLI tooling (or dusting off our old Unix hacker tricks) to get even more out of AI-native engineering.\n\nSo let's take a look at some of our battle-tested favourites! If you’re on macOS, all these tools are just a `brew install toolname` away.\n\n## A tour of CLI tools for Claude Code\n\n### [*jq*](https://jqlang.org/) and [*yq*](https://github.com/mikefarah/yq)\n\n*jq*\n\n*yq*\n\nThese two tools are a very important building block for your CLI adventures: many of the programs we’ll see on this list can emit structured output, and structured output in general is everywhere in today's landscape, usually JSON, sometimes YAML in the DevOps world.\n\n`jq` is the universal adapter able to reshape JSON payloads precisely as you (or Claude Code) need. You can filter, reformat, extract, and build new objects. As we mentioned, this technique is essential for preserving context. You do not want to blow up your context with massive objects, so being able to extract only what is needed is extremely useful.\n\n`yq` is the same idea for YAML, and it can also convert YAML to JSON and vice versa. Essential if you work with things like Kubernetes manifests, CI configs, `docker compose` files, and so on.\n\n```\ngh pr list --json number,title,author | jq '.[] | select(.author.login==\"giorgiovilardo\") | .title'\nacli jira workitem view FOO-1234 --json | jq '.fields.summary'\nyq -o=json '.services | keys' docker-compose.yml\n```\n\nClaude Code usually uses both on its own, but if you want to give a gentle nudge:\n\n```\n- When working with JSON data, just extract the data you need using `jq`\n- When working with YAML data, just extract the data you need using `yq`\n  - Use the `-o=json` flag on `yq` if you want JSON to pass to `jq`\n```\n\n*CLAUDE.md*\n\n`jq` is a tiny programming language of its own; we strongly advise taking a look at the documentation to understand its mental model.\n\nTransforming data via pipes to use it in a different way is something very fundamental that we'll build upon during the course of the article.\n\n### [*gh*](https://cli.github.com/) - the GitHub CLI\n\n*gh*\n\nMany of our projects are hosted on GitHub, so having programmatic access to issues, pull requests, actions, etc., is critical to keep the inner dev loop tight; we don't want to tab out to a browser, and not having to copy-paste issue context or PR descriptions is fantastic.\n\n`gh` is very easy to use: install it, set it up with `gh auth`, and you're ready to go.\n\n```\ngh repo clone owner/repository\ngh issue view issue_number\ngh pr create\n```\n\nWe suggest setting up a Pull Request template and referencing it in your `CLAUDE.md` or in your PR-writing skill; this will point Claude Code in the right direction when compiling a PR.\n\n```\n- Use the `gh` CLI to interact with GitHub\n- Segment your work into atomic and logical commits, so a PR can be reviewed commit by commit\n- Use @.github/pull_request_template.md when writing PR text\n```\n\n*CLAUDE.md*\n\nFor folks on GitLab, [the `glab` CLI](https://docs.gitlab.com/cli/) is also available.\n\n### [*acli*](https://developer.atlassian.com/cloud/acli/guides/introduction/) - the Atlassian CLI\n\n*acli*\n\nLove it or hate it, the Atlassian suite is a powerhouse among project management tools; Jira and Confluence are everywhere, and you're bound to use them at some point. With Atlassian's own `acli`, we can let Claude Code interact with tickets and pages.\n\n```\nacli auth login  # just do it once\nacli jira workitem view FOO-1234\nacli jira workitem comment list --key FOO-1234 --json\nacli jira workitem search --jql \"sprint in openSprints() AND project = TEAM\" --paginate\n### Jira\n\nWhen working with Jira, interact with it using `acli`.\nThese are some common operations:\n\n- Use `acli jira workitem view` to read an issue\n- Use `acli jira workitem comment list --key FOO-1234 --json` to read issue comments\n- Use `acli jira workitem search --jql \"jql_expr\"` for JQL search\n- Use `acli confluence` to search the product docs\n# your specific workflow here\n```\n\n*CLAUDE.md*\n\nWith just `acli` and `gh`, Claude Code can close the entire issue lifecycle; we've personally solved simple issues with Plan Mode and prompts like `Read FOO-1234 from Jira, fix it, then post a PR and update the ticket.` Confluence (accessed with the `acli confluence` subcommands) fits in too: as a doc source or as storage for more complex flows like Spec-Driven Development (SDD), where Claude Code can pull PRDs and similar artifacts.\n\n### [*aws*](https://aws.amazon.com/cli/), [*gcloud*](https://docs.cloud.google.com/sdk/docs/install-sdk), [*az*](https://learn.microsoft.com/en-us/cli/azure/get-started-with-azure-cli?view=azure-cli-latest), and [*kubectl*](https://kubernetes.io/docs/reference/kubectl/)\n\n*aws*\n\n*gcloud*\n\n*az*\n\n*kubectl*\n\nWhy not extend our development loop to operations? Every major (and some minor!) cloud provider has a competent CLI. Reading logs, describing a failing deployment, listing errors - these are things we do constantly, and they're more useful when done directly by Claude Code while it gathers context.\n\n```\naws logs tail /ecs/my-service --since 1h --format short\nkubectl describe pod my-app-xxxx\nkubectl logs deploy/my-app --tail 200\n```\n\nAnd since these CLIs all speak JSON, `jq` comes right back into the picture. Need to track down every bucket that's been sitting around since before 2022, maybe to bill someone or just to clean up? `aws` hands you the data, `jq` does the rest:\n\n```\naws s3api list-buckets --output json \\\n  | jq -r '.Buckets[] | select(.CreationDate < \"2022-01-01\") | .Name'\n```\n\nIt's all read-only investigation, exactly how you want it. Mutations are a different story; layer permission rules in `settings.json` on top of your `CLAUDE.md` directives so they always go through you. Claude Code reads and parses freely, but asks for confirmation before anything sensitive.\n\n```\n- Use read-only kubectl commands to investigate; never mutate something without asking for permission\n```\n\n*CLAUDE.md*\n\n```\n{\n  \"permissions\": {\n    \"allow\": [\"Bash(kubectl get:*)\", \"Bash(kubectl logs:*)\"],\n    \"ask\": [\"Bash(kubectl scale:*)\", \"Bash(kubectl rollout:*)\"],\n    \"deny\": [\"Bash(kubectl delete:*)\"]\n  }\n}\n```\n\n*.claude/settings.json*\n\nBuild on this idea, based on your project's technical stack. Many other providers now offer CLIs to interact with their service; Sentry, Stripe, and Datadog all have official support, and for many others, there's some community CLI. Composable tools all the way!\n\n### [*hurl*](https://hurl.dev/)\n\n*hurl*\n\nWhen you need a CLI-based HTTP client, `curl` is all you want; it's fantastic software, and proof that open source is art. It's not incredibly ergonomic though, and it's **REALLY** difficult to use if you need to concatenate requests, send big JSON payloads, or handle the other things web devs do all the time.\n\nThere are easier-to-use alternatives (we recommend `xh`), but we want to explore a different execution model; `hurl` executes requests that are described in files with a `.hurl` extension, commonly called hurlfiles.\n\nWe can capture data from one request and use it in the next, use variables injected from the environment (or passed with flags), keep a record of what Claude Code ran, document bug reproductions, and lots of other use cases.\n\nTest mode probably deserves its own small section, as it's excellent in CI, for application testing, and it can produce a very thorough report in various formats, including JSON, for further programmatic analysis.\n\n```\n# login.hurl - tests the login flow\nPOST https://{{env}}.example.com/login\n{\"user\": \"demo\", \"pass\": \"demo\"}\nHTTP 200  # if not 200, the command fails\n[Captures]\ntoken: jsonpath \"$.access_token\"\n\nGET https://api.example.com/me\nAuthorization: Bearer {{token}}\nHTTP 200\n[Asserts]\njsonpath \"$.email\" exists\n# more advanced assertions and functions exist, like duration, regex match, base64 encode...\n```\n\ntest_profile_page.hurl\n\n```\nhurl --test smoke.hurl\nhurl --variable env=staging login.hurl\n```\n\nClaude Code is excellent at writing hurlfiles, even from documentation or OpenAPI files, and the grammar is structured but easy to write and remember, so it's not out of place in documentation or ADRs. `hurl` also ships with the companion command `hurlfmt`, which converts `curl` commands into `hurl`. This pairs beautifully with the browser's dev console \"Copy as cURL\" right-click option.\n\n```\n# macOS: pbpaste; Linux X11: xclip -selection clipboard -o; Wayland: wl-paste\n# copy the first request, create the file\npbpaste | hurlfmt --in curl >  flow.hurl\n# copy the next request, append it\npbpaste | hurlfmt --in curl >> flow.hurl\n# ...and so on, one request at a time\npbpaste | hurlfmt --in curl >> flow.hurl\n```\n\nThe HTTP exchange is now a reviewable artifact, living in version control next to the code it exercises. `hurl` is also well supported by many editors, with syntax highlighting and completions, so the file stays pleasant to read and edit long after you've captured it.\n\nWe had great success introducing customers to the tool; it's flexible and can be leveraged by Claude Code to accelerate its already top-class context-gathering.\n\n### [*hyperfine*](https://github.com/sharkdp/hyperfine)\n\n*hyperfine*\n\nThe venerable `time` utility is useful when you need to measure how much something takes in a pinch, but it is severely underequipped to deal with more rigorous benchmarking processes. That's where `hyperfine` comes in. It is a command-line benchmarking tool, essential when you're working on raw performance.\n\nIt runs a command many times, does warmup runs, accounts for shell spawn overhead, and reports mean/standard deviation/min/max with outlier detection. The reason it matters for agentic work is that LLMs are prone to \"it feels faster\" claims; `hyperfine` replaces vibes with numbers, and we cannot rave enough about how powerful this technique is to give Claude Code an objective to reach autonomously.\n\n```\nhyperfine --warmup 3 './build.sh'\n\n# head-to-head with relative speedup - prove something is actually faster!\nhyperfine 'node old.js' 'node new.js'\n\n# Prepare flag (clear some cache, empty some dir, etc.)\nhyperfine --prepare 'make clean' 'make'\n\n# Run a benchmark each time for a parameter value\nhyperfine -p 'make clean' --parameter-scan num_threads 1 10 'make --jobs {num_threads}'\n```\n\nLike many tools on this list, it can also export in formats like Markdown and JSON, which is quite important for data analysis and agent work.\n\n```\nhyperfine --warmup 3 --export-json bench.json 'node new.js'\n```\n\nWhy are we stressing the importance? Let's take a look at...\n\n### [*duckdb*](https://duckdb.org/)\n\n*duckdb*\n\n`duckdb` is an amazing project; born to offer an OLAP alternative to SQLite, leveraging columnar storage and a vectorised execution engine, it evolved into a real Swiss army knife for local data analysis. It can natively ingest and query CSV, JSON, Parquet, its own `.duckdb` format, has tons of functions to deal with nested and complex structures, and can be deployed on a web client with WASM. It really packs all of that into a single, no-dependencies binary.\n\nThat single-binary, file-as-database design is exactly why `duckdb` pairs so well with Claude Code: it turns \"raw data\" into \"queryable data\" with zero setup. We see three common scenarios in real production projects.\n\nThe first one is the *analytical scratchpad.* Point it at a file and query, no schema, no database, nothing to clean up.\n\n```\n# have some payload data to mine, test, whatever\ncurl -s 'https://swapi.info/api/people/' > people.json\n\n# query it - the -c flag is \"execute command\"\nduckdb -c \"\n  SELECT name, height::INT AS height_cm, gender\n  FROM read_json_auto('people.json')\n  WHERE height != 'unknown'\n  ORDER BY height_cm DESC\n  LIMIT 5;\n\"\n# you can annotate in your CLAUDE.md specific filenames, or preferred\n# queries, or table schemas, etc.\n```\n\nThis has more than one benefit; it saves you context first and foremost, and lets you query your JSON data extremely precisely, extracting just what is needed for the task at hand.\n\nThe second use case is the *mini time-series db.* Running incremental initiatives like tech debt fixing, benchmarks with `hyperfine`, optimisation/test runs? Track results in a `.duckdb` file and give Claude Code some data to optimise against.\n\n```\n# first run: create the table from the data, plus a self-stamping timestamp column\nnpx eslint -f json . | duckdb tech_debt.duckdb \"\n  CREATE TABLE warnings AS SELECT *, now() AS run_at FROM read_json_auto('/dev/stdin');\n  ALTER TABLE warnings ALTER run_at SET DEFAULT now();\n\"\n\n# every later run: one insert, run_at fills in automatically\nnpx eslint -f json . | duckdb tech_debt.duckdb \\\n  \"INSERT INTO warnings BY NAME SELECT * FROM read_json_auto('/dev/stdin')\"\n\n# warnings per run - unnest flattens structured data\nduckdb tech_debt.duckdb \"\n  SELECT run_at, count(*) AS total_warnings\n  FROM (SELECT run_at, unnest(messages) AS m FROM warnings)\n  GROUP BY run_at ORDER BY run_at;\n\"\n\n# Benchmarks are covered in the same way\n# Continue just like the eslint examples\nhyperfine --warmup 3 --export-json bench.json 'node new.js'\nduckdb perf.duckdb \"\n  CREATE TABLE runs AS SELECT *, now() AS run_at FROM read_json_auto('bench.json');\n  ALTER TABLE runs ALTER run_at SET DEFAULT now();\n\"\n```\n\nClaude Code is *awesome* at writing SQL, so get fancy: ask for a delta query across the last N runs, or which lint rule is the worst offender. Giving the agent a metric it can compare across runs is a game-changer.\n\nThe last use case is *agent memory.* This is getting rarer as Claude Code gets better and better, but it's still a useful technique to know, as using `duckdb` guarantees queryability and you can automatically inject it with hooks.\n\n```\n# a tiny state table; the defaults keep every later insert a one-liner\nduckdb .claude/state.duckdb \"\n  CREATE TABLE tasks (\n    id INTEGER, title TEXT,\n    status TEXT DEFAULT 'todo',\n    created_at TIMESTAMPTZ DEFAULT now()\n  );\n\"\n```\n\nThen wire a SessionStart hook that queries the open tasks. Its stdout lands straight in Claude Code's context at the start of every session:\n\n```\n{\n  \"hooks\": {\n    \"SessionStart\": [\n      { \"hooks\": [ {\n        \"type\": \"command\",\n        \"command\": \"echo 'Open TODOs:'; duckdb -noheader -list .claude/state.duckdb \\\"SELECT '- ' || title FROM tasks WHERE status = 'todo' ORDER BY created_at\\\"\"\n      } ] }\n    ]\n  }\n}\n```\n\n*.claude/settings.json*\n\nThe same pattern unlocks \"tooled\" SDD: rather than dropping specs, plans, and task lists into markdown files the agent has to re-read in full, keep them in a queryable store, let Claude Code update their status as work progresses, and pull in only the slice relevant to the current task.\n\n### [*scc*](https://github.com/boyter/scc)\n\n*scc*\n\nAnother easy source of trackable metrics is the codebase itself. This is especially handy if you are a tech lead, EM, or PM who lives in documents, ADRs and PRDs, where you regularly need to gauge work, size up codebases and modules, or just produce some visuals.\n\n`scc` (but there are many equivalent utilities, with more or less the same options) counts lines of code, blanks, and comments across a tree in milliseconds, broken down by language, with a complexity estimate per file. If you feel fancy, `scc` can also provide [COCOMO](https://en.wikipedia.org/wiki/COCOMO) estimations or count unique lines of code to provide a \"DRY-ness\" index.\n\n`scc` gives you a point-in-time snapshot; a natural companion is the family of tools that mine the git history, surfacing code churn, how much of each file survives over the years, and which areas are hotspots. Same spirit, with the time dimension baked in, and the output stores just as nicely in `duckdb` for trends.\n\n```\nscc  # whole-repo breakdown by language\nscc src/legacy  # size just the module you're about to touch\nscc --by-file --sort complexity | head  # more complex first\n```\n\n\"The payment module is 14k lines of high-complexity Python\" is a great hook for an ADR, and cleanup and refactors can be measured against an objective baseline. `--format json` for the usual structured output, ready to be stored, queried, and massaged with all the techniques we've covered so far.\n\n### [*just*](https://github.com/casey/just)\n\n*just*\n\nWe all love `make` as a staple utility of projects everywhere, but the problem with it is that, more often than not, it's used only as a command runner rather than the full build system it is. This is not cost-free; phony targets, indentation rules, `$$` for shell variables, every recipe line in its own subshell, and many more quirks that are worth dealing with only if you have a real need for `make` special sauce.\n\n`just` throws away the build system baggage, keeping only the command runner functionality, which excellently covers lots of mileage for many code projects.\n\n```\ntest:\n    go test ./...\n\n# Comments on a recipe become the help message for that recipe. \nlint:\n    golangci-lint run\n\n# Variables can have default values\ndeploy env=\"staging\":\n    ./scripts/deploy.sh {{env}}\n\n# Multicommand recipes:\nquality: lint test\n\n# Sets the working directory if you need to execute in a specific place\n[working-directory: 'frontend']\nfrontend-quality:\n    npm run quality\n```\n\n*justfile*\n\n```\n# Shows a list of recipes and their help message\njust --list\njust test\n# Variable passing without explicit assignment\njust deploy prod\n```\n\nClaude Code loves a well-done `justfile` as it's a win from a discoverability and correctness point of view. `just --list` is a self-documenting list of public recipes (`just` allows for private recipes). Parameters are first-class, it can automatically load environment files, and it can use `set shell` and allows for inline recipes with programming languages. We also get benefits in centralising naming for common operations, and we can recycle the `just` recipes in CI.\n\nWith Docker's multi-stage builds, having `just` around won't lead to wasting precious container space, as you'll just use it in the first stage, for building the project.\n\n### [*nix*](https://nixos.org/)\n\n*nix*\n\n\"But Giorgio, we'd really love to standardise these tools. But it's a chore to tell the whole team to install them, and we don't have a good distribution story on our laptops\". I know, I feel you. And this section is probably not for you, but if you want to live (relatively) dangerously and can accept the cuts that come with living on the bleeding edge of software, there's a big payoff for you. Enter Nix.\n\nNix is, long story short, a way to create reproducible, declarative and reliable systems. It sadly suffers from a naming problem: Nix Language is used to write `.nix` files, which are used by the `nix` package manager to deterministically install systems. NixOS is a Linux distribution completely based on Nix technology. It's incredibly powerful, though: you can declare exactly what is needed to build your project, or what is needed to develop it, and then you can package it with exactly the minimum amount of software needed to make it run.\n\nIf you use Nix, your dev environment is exactly the same as CI and Production, because it's built by the same exact *closure* of input packages. There's also zero conflict with any other software on your laptop, and you can use Nix to manage your system with `home-manager`. Nix is also able to generate OCI-compliant images without Docker for your deployments.\n\nClaude Code is surprisingly competent at working with Nix files, even if it's still a relatively niche technology; if you want to invest time in it, it will pay off in spades. You can pin all these tools in a `flake.nix` file:\n\n```\n# flake.nix snippet - devShell sketch - look ma', envvars too!\ndevShells.default = pkgs.mkShell {\n  packages = with pkgs; [ gh hurl duckdb hyperfine just jq yq scc ];\n  shellHook = ''\n    export DATABASE_URL=\"postgres://localhost/myapp_dev\"\n  '';\n};\n```\n\n*flake.nix*\n\nand just invoke `nix develop` to get into a shell with all these tools available. Resources like [Zero To Nix](https://zero-to-nix.com/) are starting to become more popular, so, in time, Nix might start to appear even more! And for the absolute sceptics, once installed, you can use Nix to try one-off commands without installing them with snippets like `nix run nixpkgs#hyperfine -- --help`.\n\n### [*timew*](https://timewarrior.net/) - Timewarrior\n\n*timew*\n\nWorking on multiple projects? Need some visibility on where your time went for your weekly timesheet? Look no further than `timew` (and, if you do not like `duckdb` as memory, its sibling project [Taskwarrior](https://taskwarrior.org/)), a plain-text, CLI-first time tracker that Claude Code can drive as it works.\n\nIt's very simple to use: you clock on against one or more *tags*, then clock off when done. You can ask Claude Code to use it via its harness directives or use hooks. This way, the time tracking happens as a side effect of the work itself. It's a thing of beauty if you are very busy or are juggling more than one activity at once.\n\n```\ntimew start payments bugfix\ntimew stop\n# retroactively tag an earlier interval\ntimew tag @3 billable\ntimew summary :week\ntimew summary :week payments\n## Time tracking\n\n- Wrap work in `timew start <project> <kind>` / `timew stop`; project is one of: payments, infra, web.\n- Use the same project tag the task belongs to, so summaries line up across the team.\n- Run `timew summary :week` when asked \"where did the time go?\".\n```\n\n*CLAUDE.md*\n\n`timewarrior` has many features and options, and it's also extendable; make sure to check it out if you need powerful time management for your Claude Code sessions!\n\n### [*pandoc*](https://pandoc.org/), [*poppler*](https://poppler.freedesktop.org/) and [*qpdf*](https://github.com/qpdf/qpdf)\n\n*pandoc*\n\n*poppler*\n\n*qpdf*\n\nSometimes you need to work on formats which are not exactly machine-friendly, or need to convert data that needs to land on the desk of less technical functions. PDF, Word and Excel documents are some of the most common formats in every company out there. They are simply that ubiquitous.\n\nWith `pandoc`, the universal document converter, you give Claude Code eyes and hands for formats that could have otherwise been touched only programmatically. Markdown to DOCX to HTML to LaTeX to PDF is absolutely doable!\n\n```\npandoc --list-input-formats\npandoc --list-output-formats\n# -f from, -t to, -o outfile\npandoc -f docx -t markdown A_doc.docx -o A_doc.md\n```\n\nIt also supports Lua scripts for the transformation; you can pipe output into it, and it can convert web pages if you pass a URL as the input document name.\n\nIf you work extensively with PDFs, we suggest adding two more arrows to your quiver: `poppler` and `qpdf`. `pandoc` can't work with PDFs as *input* files, and that's where you need `poppler` (and `poppler-utils` on certain Linux distributions). It will install a series of CLI utilities, like `pdftotext`, that will make working with PDF a little less maddening.\n\n```\n# -layout preserves the page layout\npdftotext -layout tabular_data.pdf\npdfinfo report.pdf\n\n# Output a screenshot per page\n# Remember Claude is multimodal, so you can use its vision capabilities\n# to check stuff like \"is the slide overflowing the page?\"\npdftocairo -png -r 300 -singlefile some_pdf.pdf some_pdf_screens\n```\n\n`qpdf` instead is for manipulating PDFs: split them, join them, reorder pages, rotate pages, and with many other useful editing tools.\n\n```\n# Extract just some pages\nqpdf --empty --pages input.pdf 1-3,5,6-10 -- output.pdf\n# Merge PDFs\nqpdf --empty --pages doc_1.pdf doc_2.pdf ... -- doc_join.pdf\n```\n\n### [*ffmpeg*](https://www.ffmpeg.org/), [*sox*](https://github.com/chirlu/sox), and [*imagemagick*](https://imagemagick.org/#gsc.tab=0)\n\n*ffmpeg*\n\n*sox*\n\n*imagemagick*\n\nMore and more applications today have rich media capabilities: video, live streams, audio, generated images. If your team ships any of that, or you’re interested in extracting data from some of these formats, we can use the media trio of ffmpeg (video, audio, images), sox (audio) and imagemagick (images) to process and work on that data.\n\n```\n# Video to text locally? Extract the audio track and feed it to a transcriber!\nffmpeg -i talk.mp4 -vn -ar 16000 -ac 1 audio.wav\n# Get video data\nffprobe -v quiet -print_format json -show_format -show_streams vid.mp4\n\n# resize and convert\nmagick in.png -resize 800x out.jpg\n# normalise an awkward upload format\nmagick in.heic out.png\n# Get image data\nmagick identify -format '%wx%h %m\\n' in.png\n\n# Get audio data\nsox --i in.wav\n```\n\nUsing these utilities, you can also make media *testable.* [SSIM filters](https://en.wikipedia.org/wiki/Structural_similarity_index_measure) in `ffmpeg` calculate video similarity, and you can extract various structured data with all tools, fingerprint media, calculate distance, and so on.\n\nThe media trio, driven by Claude Code, is also capable of carrying lots of weight for Content and Marketing teams, proving Claude Code is a transformative technology, suitable even for teams not strictly composed of developers. Want to experiment with TikTok-style video subtitles without committing to a vendor and skipping a potentially slow and costly procurement process? Been there, done that. It's a couple of Python scripts and an `ffmpeg` re-encode away, entirely driven by a Claude Code prompt. Have fun and experiment away!\n\n### [*marp*](https://marp.app/) and [*typst*](https://github.com/typst/typst)\n\n*marp*\n\n*typst*\n\nWe've seen tools to give Claude Code the ability to read and convert document formats, but in case your work is more focused on the production of documents, you want a slightly different toolkit.\n\nFor presentations, there are lots of relatively similar frameworks, and `slidev` earns a shoutout since it's incredibly powerful (you can write Vue.js components and embed them into your slides), but it's also quite complex and needs a JavaScript toolchain, which is sometimes too much for a quick slide deck.\n\nThat's why we absolutely love and recommend `marp` as it is incredibly simple and effective. You write your slides in Markdown, separating them with a horizontal rule (`---`) and then \"compile\" them to the format you prefer. There is the possibility of adding custom HTML tags (e.g. for 2-column layouts), adding custom CSS, theming, support for presenter notes, and more bells and whistles. It's fast, easy, and Claude helps a ton in drafting your presentations. Being plain text helps in making the artifact reviewable and composable with the other tools (generate a slide deck from a video transcription? Easy as pie!)\n\n```\n---\nmarp: true\npaginate: true\ntheme: some-theme\n---\n\n# Presentation title\n\n**Subtitle**\n\nSome Author\nYour Company\n\n---\n\n## First slide\n\nLorem ipsum...\n```\n\n*deck.md*\n\n```\n# Base output is HTML for presenting, but we can override with -o\nmarp --theme some-theme.css deck.md -o deck.pdf\nmarp deck.md --pptx -o deck.pptx\n```\n\nOn the other end of the spectrum, we want to highlight typst as a fully fledged typesetting engine. Basically, a modern alternative to LaTeX. When you need maximum control and precision, it's the tool you want. You describe your document in a .typ file, using a very powerful DSL (Domain-Specific Language). This DSL has various utilities to programmatically deal with your content, i.e. you can output documents based on structured data:\n\n```\n{\n  \"title\": \"Q2 Sales Report\",\n  \"rows\": [\n    { \"name\": \"Widget\",  \"units\": 1200, \"revenue\": 24000 },\n    { \"name\": \"Gadget\",  \"units\": 860,  \"revenue\": 43000 },\n    { \"name\": \"Gizmo\",   \"units\": 430,  \"revenue\": 12900 }\n  ]\n}\n```\n\n*sales.json*\n\n``` js\n#let data = json(sys.inputs.data)\n#set page(margin: 2cm)\n#set text(size: 11pt)\n\n= #data.title\n\n#table(\n  columns: 3,\n  align: (left, right, right),\n  table.header[*Product*][*Units*][*Revenue*],\n  ..for row in data.rows {\n    (row.name, str(row.units), \"$\" + str(row.revenue))\n  },\n)\n```\n\n*report.typ*\n\n```\ntypst compile --input data=sales.json report.typ report.pdf\n# watch mode when you're working on a document\ntypst watch book.typ\n```\n\nOther than single document production, `typst` has performed well in document generation pipelines and microservices; you can prepare the document template, add some styling, and use it programmatically to generate documents like invoices, transport documents, and whatnot. It is miles better than the usual approach of spinning a headless browser and using the print to PDF function.\n\nThere are no more excuses now to delay the book you always wanted to publish: Claude Code can even help typeset it!\n\n## Practical customisation and integration\n\n### **Creating your own CLI tools for Claude Code**\n\nWhat if your project (or company) could have its own special command? Investing in tooling is always an excellent idea; that runbook, that `kubectl` one-liner, that series of API calls, all could become a `mycompanycli logs payments --env prod --json` or `myprojectcli fix-timesheets`. You now have self-documenting and easy-to-reach commands, encoding knowledge that is often transmitted orally or passively, making it impossible to get wrong.\n\nOther than accessibility and discoverability, you're also compiling the guardrails for the agent (and the humans) into the binary. Your `execsql` command that only connects to a read replica is physically unable to `INSERT`. Your `reconcile-orders` will automatically convert the time zones. No one can skip or forget about that critical footnote of the business process if the process is executed by a binary.\n\nWhatever you build, give it some flags to output structured data; JSON mainly, but YAML has its uses in certain domains. Structured output means that you should now know the drill: composability into `jq`, storage in `duckdb`, compatibility with the rest of the kit.\n\nAlso be sure to build proper `--help` flags; think about agents, not just humans. Help text is the only documentation that's always there, always matches the installed version, and needs zero setup to find. When Claude Code meets a tool it doesn't know, it just runs `mycli --help`, so that output is your best shot at landing it on the right command immediately.\n\nA few best practices to build the help text:\n\n- Split functionality into well-named subcommands - the agent will query them one at a time, allowing for progressive disclosure;\n- Keep the top-level help small - just describe subcommands;\n- Put runnable examples in the text - they're the fastest way to make the agent do the right thing;\n- Document every flag, its default, and its units - agents and humans do not like to guess;\n- Have a `--dry-run` flag documenting what is going to happen;\n- Make error messages prescribe the fix; do not just report the failure.\n\n``` bash\n$ mycli --help\nYour personal CLI.\n\nUsage: mycli <COMMAND>\n\nCommands:\n  run   Do some stuff\n  show  Show some stuff\n  edit  Edit some other stuff in $EDITOR\n  help  Print this message or the help of the given subcommand(s)\n\nOptions:\n  -h, --help     Print help\n  -V, --version  Print version\n\n$ mycli run --help\nDo some stuff\n\nUsage: mycli run\n\nOptions:\n  -e, --environment One of prod, staging, test, dev\n  -h, --help  Print help\n```\n\nWe recommend building your CLI in Go or Rust. For Rust, we like `clap`, while for Go, there's `urfave/cli` v3. Both are fantastic libraries that set the bar very high and allow you to quickly and easily write great CLIs.\n\nThe killer feature of building on these languages is distribution. Both cross-compile to native Mac, Linux and Windows binaries that are statically linked, with no runtime or dependencies to install. The output is a single file that's modest on resources and starts instantly. That makes a shared internal tool trivial to distribute to every machine, and a shared toolset is a shared capability for every teammate in the org, human or agentic.\n\n### **Security considerations**\n\nIf you're worried about handing Claude Code the keys to your company, you're right; security is a top-tier concern for all of our customers, and we take it very seriously. We deal with it as a posture, integrating it from the first moment.\n\nOther than all the usual safeguards (disaster recovery plans, separation of concerns, etc.), we usually use two main techniques to keep Claude Code shell adventures secure.\n\nLeast privilege must be enforced at the credential level; `CLAUDE.md` directives are fine, but you do need to account for surprises. Use DSNs (Data Source Name, aka db connection string) that only point at read replicas, fine-grained GitHub tokens, and cloud roles with no write privileges. If the credential can't do the dangerous thing, no amount of clever prompting can make it happen. Heck, get creative; want to run a write on a production database? Do it via the project CLI that you built, encapsulating logic to clone some production pod only after asking for an approval on Slack via an integration, only with a witness present, whatever floats your boat. Keep yourself accountable.\n\nThe second layer of our defence in depth is Claude Code’s own permission rules. You can (and should) reinforce your `settings.json` - one good rule of thumb could be to allow for read-only operations, ask for mutations and outright deny irreversible operations.\n\n```\n{\n  \"permissions\": {\n    \"allow\": [\"Bash(gh pr view:*)\", \"Bash(kubectl get:*)\", \"Bash(aws logs:*)\"],\n    \"ask\":   [\"Bash(gh pr merge:*)\", \"Bash(kubectl scale:*)\"],\n    \"deny\":  [\"Bash(kubectl delete:*)\", \"Bash(aws s3 rb:*)\", \"Bash(:* --force:*)\"]\n  }\n}\n```\n\n*.claude/settings.json*\n\nUse [Enterprise Managed Settings](https://code.claude.com/docs/en/admin-setup) to push a series of non-overridable rules everywhere, and make sure each of your company projects correctly sets up some project-specific rules that are shared via VCS.\n\n## Conclusion\n\nHopefully, this article inspired you in building your CLI workflow; CLIs and TUIs are in a great renaissance period, owing a lot to the \"modern Unix\" community, and owing some to Claude Code, as the first real AI agent built on that fantastic, compounding platform and unified interface that is the shell.\n\nThere are lots more programs to explore and enjoy: CLIs that talk to your company workspace, faster alternatives for common commands, data wrangling tools, DevOps tooling. We picked what has performed the best in our projects. But if you have a different use case, try something small and try to fit it into your Claude Code experience. More often than not, it will be a net benefit.\n\nMcIlroy was right in the seventies, and he's still right now. Small tools, working together, talking text. It’s a great platform for developers to work on, and it turns out it’s a great platform for agents too.\n\nIf you or your team are interested in exploring these technologies/techniques more and setting you up for success with AI-Native Engineering and Claude Code, [get in touch](https://nearform.com/contact/). We’d love to talk about how to enhance your AI capabilities with tooling and more.\n\n## But wait - there's more.\n\nNearform publishes real-world learnings on data & AI, engineering, and digital strategy - with more merged in weekly.\n\n### Insights\n\nPerspectives on AI in engineering, product development, and strategy, for enterprise executives.\n\n### Community\n\nDeep dives and tutorials by engineers, for engineers.\n\n## You may also like\n\n### Nearform deepens its Anthropic engineering capability", "url": "https://wpnews.pro/news/terminal-velocity-the-shell-tools-that-make-claude-code-fly", "canonical_source": "https://nearform.com/digital-community/terminal-velocity-the-shell-tools-that-make-claude-code-fly/", "published_at": "2026-09-11 18:34:19+00:00", "updated_at": "2026-09-11 18:45:50.884801+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "developer-tools", "ai-products"], "entities": ["Claude Code", "Nearform", "jq", "yq", "GitHub CLI", "Douglas McIlroy", "Unix"], "alternates": {"html": "https://wpnews.pro/news/terminal-velocity-the-shell-tools-that-make-claude-code-fly", "markdown": "https://wpnews.pro/news/terminal-velocity-the-shell-tools-that-make-claude-code-fly.md", "text": "https://wpnews.pro/news/terminal-velocity-the-shell-tools-that-make-claude-code-fly.txt", "jsonld": "https://wpnews.pro/news/terminal-velocity-the-shell-tools-that-make-claude-code-fly.jsonld"}}