Software engineer. Computer science graduate. I build products end to end, and lately I build the tooling that keeps AI-assisted code honest.
Started in Python and Django. These days mostly TypeScript, Node, and Flutter.
Three small tools, each solving one problem properly. Zero runtime dependencies, all of them — nothing to audit, nothing to break, and they work anywhere Node runs.
Structure-aware compression for large tool output.
Truncation keeps the wrong part: the first 4,000 characters of a 40,000-line test run give you the header and some passing lines, and throw the failure away. gistline knows what it is reading — it keeps test failures and their diagnostics, preserves JSON shape while shrinking values, keeps your stack frames and drops the library noise, and picks log lines by content rather than position.
Measured on a real 605-test suite: 96,443 characters to 2,284 — 98% smaller, failure intact.
Originals are kept in a content-addressed store, so retrieve, slice, and grep are real
operations rather than a reassuring promise.
A linter for AI code slop.
Coding agents rarely fail by writing wrong code. They fail by writing too much: a stray
console.log, a @ts-ignore to quiet the compiler, a .skip on the test that broke, a block
commented out "just in case", a dependency nobody asked for. Each is small. Together they are how a
codebase rots — and every one of them passes CI.
tightdiff parses the actual diff, names each violation with its file and line, and exits non-zero. It is a gate, not a suggestion. 24 rules across three axes — noise, lies, and taste — with a baseline for adopting it on an existing codebase, and suppressions that require a stated reason and are reported rather than hidden.
A map of your code, so tools navigate instead of grepping.
Where is this defined? Who imports it? Who actually uses it — not just mentions it in a comment? What breaks if I change it? Blast radius is transitive and shows distance, which is the question worth asking before touching anything.
It is honest about its limits: no dynamic dispatch, no type resolution. "Unused" is a strong hint, never proof, and it says so every time it prints.
| Project | What it is |
|---|---|
| teamify-ai | A multi-agent orchestration framework. Turns one AI coding agent into a software organisation with enforced write lanes, human checkpoints, and completion that has to be proven rather than claimed. |
| fiscalgrove | Mortgage and home-finance guides for the UK, US, Canada, Australia, New Zealand, and Ireland. TypeScript. |
| Cashbook | Personal finance app. Flutter / Dart. |
| DMS | Document management system, built as a SaaS product. |
| VoiceNotes | Voice notes and meeting recording. |
| intake | Client intake for personal injury law firms. TypeScript. |
- Zero dependencies where it is possible. Fewer moving parts, nothing to audit, nothing that rots.
- A gate that cannot fail is decoration. If a check cannot actually stop a bad change, it is worse than no check — because everyone downstream believes they are protected.
- Correctness before coverage. A false positive costs more than a missed finding: it is how a tool gets switched off.
- Say what you do not know. A shallow answer you can trust beats a deep one you cannot.
TypeScript · Node.js · Python / Django · Flutter / Dart · React · JavaScript