Terminal velocity: the shell tools that make Claude Code fly 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. 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." One 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. Another 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. At 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. So 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. A tour of CLI tools for Claude Code jq https://jqlang.org/ and yq https://github.com/mikefarah/yq jq yq These 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. 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. 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. gh pr list --json number,title,author | jq '. | select .author.login=="giorgiovilardo" | .title' acli jira workitem view FOO-1234 --json | jq '.fields.summary' yq -o=json '.services | keys' docker-compose.yml Claude Code usually uses both on its own, but if you want to give a gentle nudge: - When working with JSON data, just extract the data you need using jq - When working with YAML data, just extract the data you need using yq - Use the -o=json flag on yq if you want JSON to pass to jq CLAUDE.md jq is a tiny programming language of its own; we strongly advise taking a look at the documentation to understand its mental model. Transforming 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. gh https://cli.github.com/ - the GitHub CLI gh Many 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. gh is very easy to use: install it, set it up with gh auth , and you're ready to go. gh repo clone owner/repository gh issue view issue number gh pr create We 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. - Use the gh CLI to interact with GitHub - Segment your work into atomic and logical commits, so a PR can be reviewed commit by commit - Use @.github/pull request template.md when writing PR text CLAUDE.md For folks on GitLab, the glab CLI https://docs.gitlab.com/cli/ is also available. acli https://developer.atlassian.com/cloud/acli/guides/introduction/ - the Atlassian CLI acli Love 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. acli auth login just do it once acli jira workitem view FOO-1234 acli jira workitem comment list --key FOO-1234 --json acli jira workitem search --jql "sprint in openSprints AND project = TEAM" --paginate Jira When working with Jira, interact with it using acli . These are some common operations: - Use acli jira workitem view to read an issue - Use acli jira workitem comment list --key FOO-1234 --json to read issue comments - Use acli jira workitem search --jql "jql expr" for JQL search - Use acli confluence to search the product docs your specific workflow here CLAUDE.md With 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. 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/ aws gcloud az kubectl Why 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. aws logs tail /ecs/my-service --since 1h --format short kubectl describe pod my-app-xxxx kubectl logs deploy/my-app --tail 200 And 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: aws s3api list-buckets --output json \ | jq -r '.Buckets | select .CreationDate < "2022-01-01" | .Name' It'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. - Use read-only kubectl commands to investigate; never mutate something without asking for permission CLAUDE.md { "permissions": { "allow": "Bash kubectl get: ", "Bash kubectl logs: " , "ask": "Bash kubectl scale: ", "Bash kubectl rollout: " , "deny": "Bash kubectl delete: " } } .claude/settings.json Build 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 hurl https://hurl.dev/ hurl When 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. There 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. We 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. Test 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. login.hurl - tests the login flow POST https://{{env}}.example.com/login {"user": "demo", "pass": "demo"} HTTP 200 if not 200, the command fails Captures token: jsonpath "$.access token" GET https://api.example.com/me Authorization: Bearer {{token}} HTTP 200 Asserts jsonpath "$.email" exists more advanced assertions and functions exist, like duration, regex match, base64 encode... test profile page.hurl hurl --test smoke.hurl hurl --variable env=staging login.hurl Claude 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. macOS: pbpaste; Linux X11: xclip -selection clipboard -o; Wayland: wl-paste copy the first request, create the file pbpaste | hurlfmt --in curl flow.hurl copy the next request, append it pbpaste | hurlfmt --in curl flow.hurl ...and so on, one request at a time pbpaste | hurlfmt --in curl flow.hurl The 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. We 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. hyperfine https://github.com/sharkdp/hyperfine hyperfine The 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. It 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. hyperfine --warmup 3 './build.sh' head-to-head with relative speedup - prove something is actually faster hyperfine 'node old.js' 'node new.js' Prepare flag clear some cache, empty some dir, etc. hyperfine --prepare 'make clean' 'make' Run a benchmark each time for a parameter value hyperfine -p 'make clean' --parameter-scan num threads 1 10 'make --jobs {num threads}' Like 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. hyperfine --warmup 3 --export-json bench.json 'node new.js' Why are we stressing the importance? Let's take a look at... duckdb https://duckdb.org/ duckdb 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. That 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. The first one is the analytical scratchpad. Point it at a file and query, no schema, no database, nothing to clean up. have some payload data to mine, test, whatever curl -s 'https://swapi.info/api/people/' people.json query it - the -c flag is "execute command" duckdb -c " SELECT name, height::INT AS height cm, gender FROM read json auto 'people.json' WHERE height = 'unknown' ORDER BY height cm DESC LIMIT 5; " you can annotate in your CLAUDE.md specific filenames, or preferred queries, or table schemas, etc. This 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. The 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. first run: create the table from the data, plus a self-stamping timestamp column npx eslint -f json . | duckdb tech debt.duckdb " CREATE TABLE warnings AS SELECT , now AS run at FROM read json auto '/dev/stdin' ; ALTER TABLE warnings ALTER run at SET DEFAULT now ; " every later run: one insert, run at fills in automatically npx eslint -f json . | duckdb tech debt.duckdb \ "INSERT INTO warnings BY NAME SELECT FROM read json auto '/dev/stdin' " warnings per run - unnest flattens structured data duckdb tech debt.duckdb " SELECT run at, count AS total warnings FROM SELECT run at, unnest messages AS m FROM warnings GROUP BY run at ORDER BY run at; " Benchmarks are covered in the same way Continue just like the eslint examples hyperfine --warmup 3 --export-json bench.json 'node new.js' duckdb perf.duckdb " CREATE TABLE runs AS SELECT , now AS run at FROM read json auto 'bench.json' ; ALTER TABLE runs ALTER run at SET DEFAULT now ; " Claude 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. The 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. a tiny state table; the defaults keep every later insert a one-liner duckdb .claude/state.duckdb " CREATE TABLE tasks id INTEGER, title TEXT, status TEXT DEFAULT 'todo', created at TIMESTAMPTZ DEFAULT now ; " Then wire a SessionStart hook that queries the open tasks. Its stdout lands straight in Claude Code's context at the start of every session: { "hooks": { "SessionStart": { "hooks": { "type": "command", "command": "echo 'Open TODOs:'; duckdb -noheader -list .claude/state.duckdb \"SELECT '- ' || title FROM tasks WHERE status = 'todo' ORDER BY created at\"" } } } } .claude/settings.json The 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. scc https://github.com/boyter/scc scc Another 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. 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. 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. scc whole-repo breakdown by language scc src/legacy size just the module you're about to touch scc --by-file --sort complexity | head more complex first "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. just https://github.com/casey/just just We 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. just throws away the build system baggage, keeping only the command runner functionality, which excellently covers lots of mileage for many code projects. test: go test ./... Comments on a recipe become the help message for that recipe. lint: golangci-lint run Variables can have default values deploy env="staging": ./scripts/deploy.sh {{env}} Multicommand recipes: quality: lint test Sets the working directory if you need to execute in a specific place working-directory: 'frontend' frontend-quality: npm run quality justfile Shows a list of recipes and their help message just --list just test Variable passing without explicit assignment just deploy prod Claude 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. With 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. nix https://nixos.org/ nix "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. Nix 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. If 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. Claude 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: flake.nix snippet - devShell sketch - look ma', envvars too devShells.default = pkgs.mkShell { packages = with pkgs; gh hurl duckdb hyperfine just jq yq scc ; shellHook = '' export DATABASE URL="postgres://localhost/myapp dev" ''; }; flake.nix and 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 . timew https://timewarrior.net/ - Timewarrior timew Working 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. It'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. timew start payments bugfix timew stop retroactively tag an earlier interval timew tag @3 billable timew summary :week timew summary :week payments Time tracking - Wrap work in timew start