Everything that happened on github.com/Cocoanetics since the SwiftCross post (June 2 – June 13, 2026). Four new kits, a fourteen-year-old flagship that came home, and the stretch where the extraction pattern started shipping to users.
When I published the SwiftCross post on June 2nd, I left two threads dangling: an agent was refactoring SwiftMCP’s package structure so client-only consumers wouldn’t drag in SwiftNIO, and if that worked it would unlock SwiftAgents on Windows. I expected to report on that “eventually.” Both landed before the day was over. That set the tone for the week — I keep discovering that the limiting factor is no longer how fast things get built, but how fast I can decide what to build next.
The loose ends didn’t stay loose for a day
SwiftCross itself didn’t sit still past lunch. By the evening of June 2nd it had grown WallClock — a portable nanosecond wall-clock (clock_gettime on POSIX, GetSystemTimePreciseAsFileTime on Windows) that I needed for span timing in agent traces — released as 1.1.0, and Environment.set, a portable setenv for .env loaders, released as 1.2.0. Two tags in one evening for a package I had announced that morning.
The same evening, SwiftMCP 1.5.0 shipped the package-traits refactor from the post: Server, Client, and OpenAPI traits, all on by default, with swift-nio, swift-crypto, and swift-certificates gated behind Server. A client-only consumer now gets a fully NIO-free build — which is the whole ballgame for Windows. (1.5.1 followed the next morning to widen the swift-crypto range; dependency hygiene never sleeps.)
And SwiftAgents cashed the check immediately: it now links SwiftMCP with traits: [Client] only, dropped swift-nio entirely, migrated its remaining shims to SwiftCross, and the Windows CI probe went green. The thing I described as “if that works out” in the last post was a merged PR about three hours later.
SwiftMCP then got two more modernization passes during the week: all three server transports now conform to swift-service-lifecycle‘s Service (graceful SIGTERM/SIGINT shutdown replaces my bespoke signal handler, and the stdio transport finally exits on EOF instead of politely polling at 10 Hz), and the HTTP transport was rebuilt on apple/swift-http-types — typed header fields and methods instead of strings, with NIOHTTPTypesHTTP1 deleting a pile of hand-rolled conversions.
The cascade reached SwiftMail
This is my favorite part of having a small ecosystem: a release in one package becomes a one-line diff in the next. SwiftMail 1.7.0 (June 3) adopted SwiftCross 1.2.0, dropped my swift-nio-imap fork in favor of Apple’s upstream, refreshed every dependency, and got Windows and Android CI. A day later 1.7.1 fixed a genuinely subtle bug — and not one of mine: contributor tabmail-kmyi found that the pipelined FETCH dispatcher could drop later body parts when a server batches untagged responses. Fixed with proper EmbeddedChannel regression tests. The packages being presentable enough that strangers debug IMAP pipelining for you is a quiet milestone of its own.
The agents audited their own playground
On June 4th I did something that still makes me smile: I let an agent loose inside the SwiftBash sandbox and told it to poke around like a suspicious user. It came back with a stack of issues — stat leaked my real host uid/gid through the virtual filesystem, the time keyword didn’t parse, ps couldn’t see the shell it was running in, /tmp didn’t show up in ls /, assorted GNU options were missing from xxd, od, and realpath.
Then, over that night and the next day, the agents fixed everything on their own list. SwiftBash now reports virtual identity at the filesystem boundary, has a real mount command with a virtual mount table, parses time and reports real/user/sys, and its ps is no longer blind to itself. ShellKit got the matching ProcessTable lifecycle work, and BinCatalog moved from ShellKit into SwiftBash where it belongs. The README gained a “What’s virtual (and what that means)” section, because if an agent needed to discover those answers empirically, so would you. There’s something pleasingly self-referential about an agent filing bugs against the sandbox built to contain agents — and then resolving them.
sqlite3 to byte-parity, then out the door
Also on June 4th, the SwiftPorts sqlite3 shell port went through a parity marathon: .dump fidelity with proper identifier quoting, byte-exact floating-point formatting via a small C shim, .databases/.schema/.show/.width/.limit behavior matched against the real thing.
It turned out that it was exceptionally easy to get sqlite3 to build for all platforms because the code is essentially a single c file. And then I remembered that there was a full text search extension (FTS) that Apple chose not to ship in sqlite3. I asked the agents, if we could that. They sayed “Yes!” and built it. Then I remembered that there was also a vector search extension for sqlite3. Same story: if you can think it, they can make it.
This is why we got FTS5 and sqlite-vec which you can enable individually via SPM traits.
At which point the SQLite stack had clearly outgrown its host, and on June 8th it was extracted into SQLiteKit — a new repo: a cross-platform Swift wrapper over a vendored SQLite amalgamation with prepared statements, FTS5 full-text search, and sqlite-vec semantic search behind traits. Twelve thousand lines left SwiftPorts in a single commit. If that move sounds familiar, it’s exactly what SwiftCross was: the third time you copy something, it wants to be a package.
Teaching the agents to search
SQLiteKit immediately had a customer. For a long time I kept saying that macOS and iOS need to get an un-neutered version of sqlite3. Since last year CoreSpotlight clearly has a semantic index, which cries out “vector DB”. But there is no first-party public semantic DB capability to be found on Apple platforms.
Last year I experimented with a simple linear vector search in SwiftAgents, as well as the semantic search that OpenAI gives us. There you upload documents to OpenAI and you can use a file search tool to enrich your completions.
Now it was within my grasp to grow SwiftAgents a persistent SQLiteVectorStore — vec0 KNN plus FTS5 bm25 plus hybrid search with line-span provenance — and then, in a single day of eight consecutive PRs, absorbed the retrieval techniques from tobi’s qmd: smart markdown chunking that respects code fences, reciprocal rank fusion, lex/vec/hyde query expansion, LLM-gated expansion, a batch reranker, and query/document embedding asymmetry. On-device Apple NL embeddings by default; OpenAI only when a key is present.
The deliberate part was the order. qmd — query markdown — and the OpenClaw equivalent each carried a lot of hard-won, embedded knowledge about how to build a memory-retrieval pipeline, and I didn’t want that knowledge trapped inside a CLI. So I had the agent rebuild the index engine inside SwiftAgents first, where anything can reuse it, and only then — because the engine existed — QMDKit got built on top of it in about two days: another new repo, a qmd clone that indexes and semantically searches Markdown, available as a standalone CLI and as a sandboxed ShellKit builtin. Which means SwiftBash sessions — the same sandbox the agents work in — now have semantic search over my notes as a shell command. The agents built their own retrieval tooling, then installed it in their own environment. I just watched.
GitKit, and a lesson in humility from libgit2
June 9th: I wanted SwiftPorts’ git to stop depending on a personal libgit2 fork. The fork I’d been leaning on minted a fresh branch and package for every single libgit2 release; I wanted the opposite shape — one build that pulls the C source straight from upstream as a git submodule, version-matched, so bumping libgit2 becomes a submodule pointer rather than a brand-new repo. So GitKit became the third new repo of the week — libgit2 1.9.4 packaged for SwiftPM with a pristine upstream submodule, driven to green on macOS, iOS, Linux, Windows, and Android in a single afternoon (Windows needed a curated header umbrella; Android needed its own toolchain rituals; tvOS and watchOS were dropped because libgit2 forks processes, and they don’t).
Then reality arrived on schedule. SwiftPorts adopted GitKit, and git stash apply broke. The culprit was wonderfully obscure: feature-define names inherited from another fork that no longer matched libgit2 1.9.4’s dialect, silently disabling things like nanosecond timestamps. Adopt, regress, revert, fix, re-adopt — all within about nine hours, every leg verified by the five-platform CI. This is the outer loop from the SwiftCross post doing exactly what I claimed it does, except this time the red checkmark saved me from shipping a subtly broken git.
The week closed with the now-familiar move: SwiftPorts’ entire Swift git layer was lifted out and became the GitKit 2.0.0 SDK — a full idiomatic Repository API over libgit2 — leaving SwiftPorts with just the sandbox-aware face and CLI. Zero duplication. (There was also a brief comedy in three acts about how a tagged package may depend on the untagged swift-archive; the answer, after one rejected fork-with-a-minted-tag, is: pin the upstream commit and let traits prune it.)
The parser wanted to be a kit too
If the first nine days had a refrain, it was “the third time you copy something, it wants to be a package.” Day eleven onward just kept proving it — starting, of all places, with the oldest piece of code in the whole org.
For years the tolerant HTML parser inside SwiftText was a quiet workhorse: a Swift rewrite of DTHTMLParser, itself a piece of DTFoundation that predates most of these repos by more than a decade. On June 12 it finally moved out on its own. XMLKit — an async streaming HTMLParser built on libxml2’s SAX interface, so it handles real-world malformed HTML gracefully and never builds a DOM unless you ask it to. It left SwiftText carrying its full git history, and SwiftText immediately adopted it back as a dependency. Released as 1.0.0 that afternoon, then 1.0.1 a few hours later once CI was building and running the test suite on all five platforms — macOS, iOS, Linux, Windows, Android. The name is a promise: an XML SAX module along the same lines is the obvious next tenant.
The honest trigger was narrower than the result. I didn’t want to drag swift-markdown into DTCoreText just to reach the HTML parser, and my first instinct was the cheap fix — a trait inside SwiftText that gates the dependency away. But the earlier extractions had taught me better: the third time you reach for a thing through a wall of unrelated dependencies, it wants to be its own package. XMLKit is, at heart, libxml2 plus a thin layer of Swift wrappers — and because it stands alone, with nothing else cluttering the manifest, I could chase it all the way to green CI on all five platforms. That five-platform checkmark is my current gold standard, and realistically you only earn it one library at a time.
And then the fourteen-year-old flagship came home
This is the part I didn’t see coming. DTCoreText — the CoreText-meets-HTML library with six thousand stars and a decade and a half of history — shipped 2.1.0 on June 13, and it’s the most interesting release of the stretch precisely because it’s the least new.
Two things happened to it at once. It got features: HTML tables with a real NSTextTable-compatible model (colspan/rowspan, border-collapse, row-by-row pagination), CSS floats (float/clear with text wrapping), CSS border-spacing, and — the one that made me grin — native iOS 27 TextKit bridging, so DTCoreText’s text blocks and tables lay out as real NSTextBlock/NSTextTable on the new SDK. Roughly 8,600 lines across five PRs, the first feature release built on this spring’s Swift 2.0 rewrite.
The tables especially are a fourteen-year finally. The first request to render an HTML


