Independent AI intelligence Two editions daily · ET
FervorAI

Analysis · July 27, 2026 · repo

vercel-labs/scriptcVercel LabsQuickJSSimon Willisonagent-infrastructureai-skillsagent-harnesslocal-ai

scriptc Compiles TypeScript to Native Binaries With No JavaScript Engine Inside. Coding Agents Wrote Most of It.

Vercel Labs shipped a TypeScript-to-native compiler with a 178KB hello-world binary and roughly 2ms startup. Simon Willison points at the contributors graph and counts 918,000 lines landed in a week.

A compiler is the least forgiving thing you can build. It has no user-visible surface to polish, no place to hide a bad decision, and one job: never be wrong. So the interesting fact about vercel-labs/scriptc is not that it turns ordinary TypeScript into a native binary with no Node and no V8 inside. It is that the repo went from empty to a working three-tier compiler with 800-plus differential tests in about two weeks, and the loudest conversation around it is not about codegen at all. It is about whether anybody read it.

The npm package scriptc was created on July 13, 2026. Its latest published version this morning is 0.0.17. The GitHub repo carries 256 commits and an Apache-2.0 license. Under the Hacker News thread, a commenter asks how Vercel made so much progress so fast when a respected solo project has spent years on the same goal. Simon Willison answers in five words and a link: "Coding agents. They landed 918,000 lines of code in a single week."

That is the story. The compiler is the demo.

What the thing does

The pitch fits in a terminal transcript. You write a plain TypeScript file, type-checked by the real TypeScript compiler with your own tsconfig.json governing strictness. scriptc build fib.ts hands you a self-contained executable. The README's example lands at 178KB and starts in about 2ms.

The interesting design choice is that scriptc refuses to be vague about what it can handle. Every construct in your program falls into exactly one of three tiers, and the tier is visible.

Tier one is compiled statically: native code, no engine in the binary. This is the default and covers more than you would guess. Classes with single inheritance and virtual dispatch, closures with JavaScript capture semantics, monomorphized generics, discriminated unions driven by TypeScript's own narrowing, async/await on stackful fibers, exceptions with finally, regular expressions, UTF-16-exact strings, Maps and Sets with JavaScript-exact ordering. Node's surface is there too: fs, path, process, child_process, crypto, zlib, and the server stack including net, http, https, and tls over vendored mbedTLS. Real proxy servers compile.

Tier two is the fallback, turned on with --dynamic. An embedded quickjs-ng engine, roughly 620KB, executes what cannot be compiled: npm dependencies' shipped JavaScript, any-typed code. Values crossing back into static code get validated at runtime, so a lying type throws a catchable TypeError instead of corrupting memory.

Tier three is rejection. The build fails with a specific error code, a code frame, and usually a rewrite hint. The README's claim is the one that matters: "Nothing is ever silently miscompiled."

Backing that up are two enforcement lanes. Every corpus program runs under Node and as a native binary, and stdout, stderr, and exit codes must match byte for byte. The whole corpus re-runs under AddressSanitizer with a reference-count audit, where leaks and use-after-free are build failures. A few dozen deliberate divergences from Node exist, and they are documented and numbered.

The best feature is a report, not a binary

Run scriptc coverage app.ts and you get a statement-level accounting: how many statements were analyzed, how many compile statically, and a named blocker with an error code for each one that does not. The README's sample shows 4,451 of 4,481 statements compiling, with two blockers on functions with optional parameters used as values and one on Promise.reject.

That output is the most valuable thing in the project, and I would say that even if the compiler were three times slower than it claims. Every tool in this category has historically sold you a promise and let you discover the exceptions at 2am. scriptc makes the exception surface a first-class artifact you can run before you commit to anything.

It is also the tool you use to audit the project's own honesty, which brings us to the part the README does not cover.

Where the numbers argue with each other

The README's performance table reports about 2.4ms startup against Node's roughly 47ms, binaries of 170 to 200KB static, and resident memory of 1 to 4MB against Node's 67 to 116MB. The project's own site, scriptc.dev, describes a hello-world binary at roughly 320KB starting in about 4ms against Node's roughly 35ms. Those are the same claim measured two ways, published the same week, and they do not agree within a factor of two on binary size.

Star counts disagree harder. GitHub's own repository page served 41 stars, 0 forks, and 0 watchers when I checked it this morning. The badge on scriptc.dev showed 450 the same morning. Trendshift had the project first on its daily board. I have stopped treating any single one of these as the number and started treating the spread as the finding.

None of that is disqualifying on its own. A two-week-old project publishing inconsistent benchmark figures across two surfaces is normal. It matters here because the benchmarks are the argument, and because of who is objecting.

The critique worth reading

In the Hacker News thread, a commenter who says language runtime performance is his profession raises three specific objections, and they are not vibes.

First, quickjs-ng is slow, so the fallback tier is not a cheap escape hatch. Second, scriptc uses floats for all numbers and defers integer inference to the roadmap, and integer inference is roughly half the problem of making JavaScript fast. Willison went and checked this one: he compiled the README's own fib.ts example with --backend c and posted the output, which is double all the way down, including the loop counters. Third, the presence of quickjs suggests reference counting rather than garbage collection, which carries its own performance ceiling.

The sharpest disagreement is about how often you actually land in the fallback tier. The argument against scriptc is that any is everywhere in real TypeScript and most npm packages ship untyped JavaScript with separate declaration files, so the engine comes along for the ride on nearly every real project. The argument for it, which Willison makes, is that the compelling use case was never "compile my existing Node service." It is writing a small, fast binary in a language you already know instead of learning Rust or Go for a CLI tool.

Both are right, and they are describing different products. If you want scriptc to replace your dependency-heavy service, the honest answer is no. If you want it to replace the 200-line Node script you shell out to forty times a minute from an agent harness, the startup delta is real money and the dependency objection never applies.

Put this into practice

Do not start with your service. Start where the answer is knowable in ten minutes.

Install with npm install -g scriptc. You need clang, which comes with the Xcode Command Line Tools. macOS arm64 is the primary platform; Linux and Windows binaries come from cross-compilation with their own test lanes.

Pick the smallest script you own with no npm dependencies. Something that reads a file, transforms it, and prints. Run scriptc coverage on it first and read the report before building anything. If it says 100 percent static, you have a clean test. If it names blockers, you have just learned exactly what this compiler cannot do without spending an afternoon finding out sideways.

Then build it and time it against node the-same-file.ts with hyperfine or a loop and time. Measure on your hardware, not from the table. The startup claim is the one that survives scrutiny best and it is also the easiest to check yourself in under a minute.

The place this pays off soonest is agent tooling. If you have a harness spawning small helper processes on every tool call, Node's startup cost is a tax you pay thousands of times a day, and it is the single number scriptc is most credibly good at. Run the coverage report on those helpers first. They are usually dependency-free by nature, which puts them in the one category where the tradeoffs mostly vanish.

Honest limitations

Version 0.0.17, two weeks old, no GitHub releases cut. Treat it as an experiment you are running, not a dependency you are taking.

The fallback tier undercuts the headline for most real code. A binary that embeds a JavaScript engine plus your dependencies runs around 3MB rather than 178KB, and you are now shipping an interpreter you were specifically trying to avoid.

The performance table is vendor-measured against workloads the vendor chose. At least one experienced benchmarker has publicly said he intends to measure it with generally accepted methods and has not yet published results. Until someone does, the numbers are a claim.

The provenance question is real and unresolved. Nobody in that thread has found a specific miscompile. What they have found is a repo where the volume of code landed per week exceeds what a human team could review, a README written in a voice several commenters recognized on sight, and a maintainer team accepting drive-by PRs to clean up the prose. The differential test corpus is the strongest counterargument scriptc has, and it is a good one. Eight hundred programs matching Node byte for byte under ASan is real evidence, and it is evidence about behavior on the paths that were tested, not about the paths nobody thought to test.

That is not a reason to dismiss it. It is a reason to run scriptc coverage yourself rather than trusting the summary, which is exactly what the tool was built to let you do.

What I would actually do

Give it twenty minutes on a dependency-free script this week. Read the coverage report. Time the binary. You will learn more about whether agent-built infrastructure holds up from that one experiment than from another week of arguing about it.

Then keep the receipt. In three months either scriptc has real third-party benchmarks and a version number without two leading zeros, or it is another Vercel Labs repo that trended for a week. Both outcomes teach you something worth knowing about what agents can and cannot finish.

Sources: vercel-labs/scriptc on GitHub; scriptc.dev; Hacker News discussion; npm registry metadata for scriptc.