@ewanc26/pkgs
@ewanc26/pkgs is a language-agnostic monorepo containing all of Ewan's publishable packages across multiple programming languages. It was created by extracting the packages/ subdirectories from their original host repositories (malachite and website) and migrating the entire svelte-standard-site repository — all with full git history preserved via git subtree. The standalone malachite repository has since been fully consolidated here too, with the CLI and web frontend split into separate packages.
The monorepo has since expanded to include Rust CLI tools, Python utilities, and a growing family of browser-based AT Protocol import/analysis tools — Jasper, Opal, Bismuth, Malachite, and Tourmaline — each with a CLI or library package plus a -web frontend, discoverable together at croft.click.
Packages
TypeScript
| Package | Version | Description |
|---|---|---|
@ewanc26/tid |
1.x | Zero-dependency AT Protocol TID generation |
@ewanc26/atproto |
0.x | AT Protocol service layer (profiles, posts, Standard.site, music status) |
@ewanc26/ui |
0.x | Svelte 5 UI component library (cards, layout, stores, themes) |
@ewanc26/landing-ui |
0.x | Shared landing page layout and CSS for the *-web tool sites |
@ewanc26/utils |
0.x | Shared utilities (dates, numbers, URLs, validators, RSS) |
@ewanc26/noise |
0.x | Generic deterministic value-noise generation — arbitrary dimensions, multi-octave FBM, multiple colour modes |
@ewanc26/noise-avatar |
0.x | Deterministic value-noise avatar generation from a string seed — thin wrapper around @ewanc26/noise |
@ewanc26/og |
0.x | Dynamic OpenGraph image generation with noise backgrounds — SvelteKit endpoints, edge runtimes, or plain build scripts |
@ewanc26/bismuth |
0.x | Convert pub.leaflet / blog.pckt / app.offprint richtext-block documents (site.standard.document records) to Markdown — CLI and library |
bismuth-web |
0.x | Browser-based converter at bismuth.croft.click (private, not published to npm) |
@ewanc26/svelte-standard-site |
0.x | SvelteKit library for site.standard.* AT Protocol records — design system, federated comments, publishing tools, and content verification |
@ewanc26/pds-landing |
2.x | Composable Svelte components for an ATProto PDS landing page — terminal-aesthetic UI with live status fetching |
@ewanc26/supporters |
0.x | SvelteKit component library for displaying Ko-fi supporters, backed by an ATProto PDS |
@ewanc26/wafrn-theme |
1.x | WAFRN CSS theme using the pds-landing Catppuccin terminal aesthetic — dark forest-green palette, JetBrains Mono |
@ewanc26/tangled-sync |
1.x | CLI tool for syncing GitHub repos to Tangled with ATProto records |
@ewanc26/malachite |
0.x | CLI tool for importing Last.fm, Spotify, Apple Music, YouTube Music & ListenBrainz history to AT Protocol as fm.teal.feed.play records |
malachite-web |
0.x | SvelteKit web frontend for Malachite at malachite.croft.click — browser-based import with ATProto OAuth (private, not published to npm) |
@ewanc26/jasper |
0.x | Convert Instagram data exports into posts, stories, and videos on Grain or Spark — CLI and library |
jasper-web |
0.x | Browser-based importer at jasper.croft.click (private, not published to npm) |
@ewanc26/opal |
0.x | Convert Twitter, Mastodon, Threads & Nostr posts to AT Protocol Bluesky posts — CLI and library |
opal-web |
0.x | Browser-based converter at opal.croft.click (private, not published to npm) |
tourmaline |
0.x | AT Protocol scrobble analyser for Teal.fm listening history at tourmaline.croft.click (private, not published to npm) |
@ewanc26/croft-click |
1.x | The croft.click tools directory (private, not published to npm) |
@ewanc26/croft-click-core |
0.x | Shared core logic (publishing, rate limiting, sync, data conversion) behind the croft.click import tools |
Rust
| Package | Version | Description |
|---|---|---|
nix-config-tools |
0.x | Management tools for nixos/nix-darwin configuration (flake-bump, health-check, gen-diff, server-config) |
Python
| Package | Version | Description |
|---|---|---|
llm-analyser |
0.x | Document analysis tool using Ollama LLM |
Why a monorepo?
The packages share overlapping concerns — @ewanc26/ui depends on @ewanc26/noise-avatar and optionally @ewanc26/atproto for its card components, and both consume types from @ewanc26/utils. @ewanc26/svelte-standard-site pulls in @ewanc26/atproto, @ewanc26/tid, and @ewanc26/utils. @ewanc26/bismuth converts the same site.standard.document records that @ewanc26/svelte-standard-site renders. Having them colocated means:
- Cross-package changes can land in a single PR
workspace:*references keep internal deps in sync without publishing intermediary versions- One place to update shared tooling (TypeScript config, build scripts, licence)
- Unified development experience across languages
Installation
TypeScript packages
pnpm add @ewanc26/tid
pnpm add @ewanc26/atproto
pnpm add @ewanc26/ui
pnpm add @ewanc26/landing-ui
pnpm add @ewanc26/utils
pnpm add @ewanc26/noise
pnpm add @ewanc26/noise-avatar
pnpm add @ewanc26/og
pnpm add @ewanc26/bismuth
pnpm add @ewanc26/svelte-standard-site
pnpm add @ewanc26/pds-landing
pnpm add @ewanc26/supporters
pnpm add @ewanc26/wafrn-theme
pnpm add @ewanc26/tangled-sync
pnpm add @ewanc26/malachite
pnpm add @ewanc26/jasper
pnpm add @ewanc26/opal
The -web sites, tourmaline, croft-click, and croft-click-core are private and not published to npm — they're deployed apps and internal shared logic, not libraries meant for external use.
Rust tools
nix run https://github.com/ewanc26/pkgs#health-check
Python tools
# Install dependencies
pip install -r packages/llm-analyser/requirements.txt
# Run the tool
python3 packages/llm-analyser/main.py
Development
git clone https://github.com/ewanc26/pkgs.git
cd pkgs
pnpm install
# Build all TypeScript packages
pnpm build
# Type-check all TypeScript packages
pnpm check
# Test all TypeScript packages
pnpm test
# Build all Rust packages
cargo build --release
# Work on a single package
pnpm --filter @ewanc26/tid build
pnpm --filter @ewanc26/bismuth dev
pnpm --filter @ewanc26/svelte-standard-site dev
cargo build -p nix-config-tools --bin health-check
How history was migrated
Each package was extracted from its source repository using one of two approaches:
Subdirectory packages (tid, atproto, ui, utils) — git subtree split was run inside the source repo to isolate that subdirectory's commit history into a standalone branch, which was then merged into pkgs via git subtree add. git log packages/tid in this repo shows the real history going back to when the package was first created in malachite.
Whole-repo package (svelte-standard-site) — the repo root was already the package, so no split step was needed. The full history was merged directly into packages/svelte-standard-site via git subtree add.
Consolidated packages (nix-config-tools, tangled-sync, llm-analyser) — these were copied from their original locations and integrated into the monorepo structure.
Archived repo consolidation (malachite, malachite-web) — the standalone malachite repository was fully migrated using git filter-repo, rewriting its commit history under packages/malachite/ (CLI) and packages/malachite-web/ (web frontend) separately. The original repo is now archived.
The source repos retain their original files for reference, but the canonical source of truth for all packages is this monorepo.
Publishing
TypeScript packages are published to npm automatically via GitHub Actions. Pushing a tag in the format <package-name>/v<semver> triggers the workflow, which resolves the package from the tag, builds it, and publishes to npm with provenance via OIDC.
# Publish a specific package
git tag bismuth/v0.1.0
git push origin bismuth/v0.1.0
The NPM_TOKEN secret must be set in the repository's GitHub Actions secrets. The -web frontends, tourmaline, croft-click, and croft-click-core are private and not published. Rust and Python packages are not published automatically.
Licence
AGPL-3.0-only.
← all docs