atom v0.2.1 — Distribution architecture: install once, project anywhere
About this release. v0.2.1 is a bundled release. It covers everything since the public GitHub release for
v0.1.1— including the previously tagged-but-unpublishedv0.1.2,v0.1.3, andv0.2.0. Their CHANGELOG entries are folded in below so this is the single canonical record. All future releases go throughatom upgrade.
Install
curl -fsSL https://raw.githubusercontent.com/machbuilds/atom/main/install.sh | bashThen anywhere on your machine:
atom-setup new my-projectAlready on a 0.1.x in-place install? See docs/MIGRATING.md — one command (atom migrate-install) relocates you to the new layout.
Trust model for curl | bash is documented in SECURITY.md; the manual git clone install path produces the same result.
Headlines
atom-setup new <name>— install once, scaffold projects anywhereinstall.shcurl one-liner + manual install pathatom migrate-install+docs/MIGRATING.md— relocate 0.1.x users to~/.atom/atom/atom-update-check+atom upgrade --snooze— lazy update notifier, 24h/48h/7d snooze- Five new stack presets: Python/FastAPI, Swift/Vapor, Rust/Axum, Go/Cobra, TypeScript library — plus 15 seed learnings
- Inline constitution generation in wizard §9 (no more deferred TODO)
nucleus review+ honest capture framing across the docsatom upgrade+nucleus migrate+gh repo create(previously in v0.2.0)- VERSION file + 0.1.2 install-loop fix (previously in v0.1.3 / v0.1.2)
Test totals
199 passing: 164 fast + 35 slow atom-setup tests + 20 nucleus tests.
Full notes — versions folded into this release
[0.2.1] — 2026-05-14
Bundled release for Wave 2 (user-facing features) + Wave 3 (distribution architecture). All five CLIs aligned to 0.2.1; new bin/atom-update-check ships at 0.1.0.
Added — distribution architecture
atom-setup new <name>. Separates "atom source" from "the project being bootstrapped." The wizard now readsscaffold/andextras/from$ATOM_SOURCE_DIR(default~/.atom/atom/) and writes into a fresh./name/directory, leaving the source byte-identical. The legacy in-place mode (runningatom-setupinside an atom clone) still works for one release with a one-line deprecation notice pointing at the new verb. Refuses non-empty targets; refuses when$ATOM_SOURCE_DIRdoesn't look like an atom checkout.writer.js'sapplyStategainedopts.mode('new'|'in-place') andopts.sourceDir; innewmode it skipsREMOVE_BEFORE_PROMOTE/REMOVE_AFTER_PROMOTEsince the target starts empty. Seedocs/planning/v0.2.md§9.install.shat the repo root. Curl-pipe one-liner:curl -fsSL https://raw.githubusercontent.com/machbuilds/atom/main/install.sh | bashclones to~/.atom/atom/and installs all six globals (atom,atom-setup,nucleus,learnings,model-race,atom-update-check). Idempotent: re-running on an existing install short-circuits with a hint to runatom upgrade. Env overridesATOM_HOME,ATOM_GIT_URL,ATOM_BRANCHfor tests. README Quick Start rewritten to lead with the one-liner; the manualgit clone+npm install -g .path is documented immediately below ("Don't trust curl-pipe-bash? Install manually.").SECURITY.mdadds a "Trust model for the curl-pipe installer" section covering what's trusted, what the script does, and what we'll add when there's a concrete threat model (signed checksums, tag pinning,npm ci).atom migrate-installsubcommand for relocating a 0.1.x in-place install to~/.atom/atom/. One shot: clones fresh, re-installs every CLI globally, leaves the old in-place clone alone (user's data, their call). Refuses if~/.atom/atom/already exists. Newdocs/MIGRATING.mdwalks through both the auto path (one command) and the manual four-step path, with troubleshooting forEACCESand stale shell lookups.bin/atom-update-check/— lazy update notifier. Every atom CLI prints a one-line notice on startup when a newer release is published upstream ("atom 0.2.5 is available. (runatom upgrade, or snooze:atom upgrade --snooze 7d)"). State at~/.atom/state/update-check.jsontrackslastChecked,latestVersion,lastNotified,snoozeUntil. Per-CLI startup cost is ~2ms (one JSON read + a synchronous decision; the network poll runs in a detached childatom-update-check tick). Throttle: 6h between polls, 24h between repeat notices. Snooze tiers: 24h, 48h, 7d viaatom upgrade --snooze <tier>, which delegates to the worker so there's only one writer of the state file. Env overridesATOM_STATE_DIR,ATOM_VERSION_URL,ATOM_VERSION_FILE,ATOM_INSTALL,ATOM_UPDATE_CHECK_DISABLEDfor tests. Inlined client lives atbin/atom-update-check/src/client.jsand is mirrored byte-identically into each CLI'ssrc/lib/update-check-client.js(drift detection in Test 20.1).
Added — Wave 2 features
nucleus reviewcommand + CLI backlog nudge.nucleus addandnucleus searchnow print a one-line footer when you have ≥10 unpromoted entries or any entry older than 14 days, debounced to once per 24h via a newconfig.lastNudgeAtfield.nucleus reviewlists unpromoted entries grouped by project (oldest first), each with a copy-pastenucleus promote <ULID>line, supports--limit,--project,--type, and--json. New helperbin/nucleus/src/lib/promote-state.jsexposesgetBacklog,shouldNudge,markNudged,formatNudge; detection of "already promoted" entries reuses the path convention~/.atom/learnings/<type>/<key>.mdfrompromote.js. New test harnessscripts/test-nucleus.shcovers the thresholds, debounce, filters, and the promoted-entry skip (20 / 20 passing).- Sharper Claude promotion-surfacing in the nucleus skill.
scaffold/.claude/skills/nucleus/SKILL.mdandscaffold/AGENTS.mdnow instruct Claude to surface 1–2 promotion candidates after ≥3 captures in a session, or when the user signals end-of-task ("ship it", "we're done",/clear). Behavior is explicit: list the captures, propose the strongest generalization candidates, and print thenucleus promote <ULID>command rather than invoking it (becausenucleus promoteopens$EDITORinteractively, which doesn't work in agent flows). - Five new stack presets under
extras/<category>/<preset>/, raising the wizard's preset coverage from 1 to 6:- Python / FastAPI (
extras/web/python-fastapi/) —pyproject.tomlpinned to Python 3.12 + FastAPI + Pydantic v2;app/main.pywith/healthzand lifespan; multi-stage Dockerfile (builder venv → runtime, non-root, healthcheck); Railway as default deploy target. - Swift / Vapor (
extras/web/swift-vapor/) —Package.swiftpinned to Vapor 4 + Swift 5.10;Sources/App/configure.swiftbinds0.0.0.0:PORT; multi-stage Dockerfile with static-Swift-stdlib build; Fly.io as default deploy target. - Rust / Axum (
extras/web/rust-axum/) —Cargo.tomlpinned to Axum 0.7 + Tokio 1 + tracing;src/main.rswith/healthzandEnvFilterlog setup; cargo-chef Dockerfile + distroless runtime; Fly.io as default. - Go CLI / Cobra (
extras/cli/go-cobra/) —go.modpinned to Go 1.23 + Cobra 1.8; root + sample subcommand;.goreleaser.yamlfor cross-platform release binaries; tag-push GitHub Action; no Docker (binaries distributed via GitHub Releases). - TypeScript library (
extras/lib/typescript-library/) — strict-modetsconfig.json;tsup.config.tsfor dual ESM + CJS +.d.tsoutput;package.jsonwithexportsmap,filesallowlist,publishConfig.provenance: true; vitest + npm-publish workflow with provenance.
- Python / FastAPI (
- 3 seeded learnings per preset (15 total) with
applies_to:matching the preset's stack tags. Each learning is a real best practice, not invented filler — e.g., "Use cargo-chef for Rust Docker builds", "Static-link the Swift stdlib for Linux deploys", "Bind to 0.0.0.0, not localhost, inside containers". - Stack-specific Quick Start in the project README. Each preset ships a
README.snippet.mdthat the writer splices into<root>/README.md(replacing a{{QUICK_START}}placeholder), and{{PROJECT_NAME}}/{{DESCRIPTION}}are substituted from the wizard's answers. Before this, bootstrapped projects had no README; now they have one tuned to whatever stack was picked. - Language values in
LEARNINGS_TAXONOMY.md'sapplies_tovocabulary —node,python,rust,go,swift. Pre-v0.2 the taxonomy said language tags belong only intags:; with per-language presets, language-level filtering became necessary so a Pydantic learning doesn't ship to a Rust project.
Changed
- Honest nucleus capture framing across the README and scaffold. The previous copy described
claude-managedcapture as "automated" — implying a daemon or hook. There isn't one: capture only happens when Claude is following AGENTS.md / SKILL.md during a session.README.md,scaffold/README.md,scaffold/AGENTS.md, andscaffold/.claude/skills/nucleus/SKILL.mdnow describe capture as Claude-led-during-sessions and promote as always-manual ("graduation needs human judgment"). The AGENTS.md and SKILL.md "when to capture" sections were rewritten from passive ("at session boundaries") to active triggers (after a non-obvious bug fix, after a design decision with rationale, on/clear). The wizard'sclaude-managedhint andauto-timerhint were rewritten to match — auto-timer is now labeled as reserved since the daemon doesn't ship yet. The post-setup cheatsheet now points atnucleus reviewfor backlog triage. - Inline constitution generation in wizard §9. When the user opts in, the writer now produces
<project>/CONSTITUTION.md(a v0.1.0 draft) populated from §1 (project name, description, visibility, multi-agent y/n) and §2 (stack, deploy target). Replaces the previous deferred-TODO flow where the cheatsheet pointed users atspeckit-constitutionand most of them forgot. The draft followsdocs/HOW_TO_WRITE_CONSTITUTION.md's methodology: at-most-5 principles (placeholders with stack-specific hints in HTML comments), locked tech stack pinned to the chosen preset, agent ownership matrix (solo or multi-agent based on §1), phase-gate skeleton, semver policy, change log seed. Implementation: newbin/atom-setup/src/lib/constitution.jsexportingrenderConstitution(answers);writer.jscalls it afterwriteLicensewhenanswers.constitution === true. Cheatsheet now points at the file ("Refine CONSTITUTION.md") instead of asking the user to run a second command. Thespeckit-constitutionskill in Claude Code is still recommended as a downstream verification pass once the user has drafted real principles.
Fixed
<project>/learnings/was being deleted after copy.manifest.js'sREMOVE_AFTER_PROMOTEhad'learnings'in it — a leftover from v0.1.0 when the repo carried a maintainer-curatedlearnings/directory. v0.1.1 removed that directory but the cleanup entry stayed, silently wiping every user playbook learning + every preset seed learning that landed in<project>/learnings/. Removed the entry; verified by new test assertions (8.7,9.5,10.5,11.6,12.7).- Generic Docker tier no longer overwrites preset's stack-tuned Dockerfile. Previously, picking
dockerTier = 'dockerfile'after a stack with its own preset Dockerfile (now: any of the 4 web presets) would overwrite the stack-tuned file with the generic one fromextras/docker/.copyDockerTiernow skips destinations that already exist; the generic workflow file at.github/workflows/docker.ymlstill lands because the preset doesn't ship that.
Wizard plumbing
- Stack list expanded from 13 → 18 entries. The 6 preset-mapped values are grouped at the top with
preset:hints; non-preset stacks (React, Astro, Node API, etc.) are below with(no preset)hints so users can see at a glance which paths get opinionated scaffolding. Existing values stay for back-compat with older.atom-setup-state.jsonfiles. STACK_PRESET_DIRandSTACK_TAGSinmanifest.jsupdated to map each new preset to its directory and tag set.suggestDockerTier()extended to return'none'forgo-cobraandts-library(CLI / library distributions don't ship Docker images).
Tests
- Wave 3 added six test sections to
scripts/test-atom-setup.sh:- Test 14–17 for
atom-setup new: full round-trip with byte-identical-source check, target-collision refusal, missing-source refusal, deprecation notice on legacy in-place mode. - Test 18 for
atom migrate-install: dry-run output, refusal-when-exists, help text. Test 18d (gated byATOM_TEST_SLOW=1) runs the full clone + 6×npm install -ground-trip in a scratch npm prefix. - Test 19 for
install.sh: structural checks + the slow round-trip (gated) verifying fresh install, idempotent re-run, success message. - Test 20 for
atom-update-check: drift detection across the 5 inlined client copies, tick writes state, notice prints once, 24h debounce,ATOM_UPDATE_CHECK_DISABLEDkill-switch, snooze sets + clearslastNotified, bogus duration rejected, futuresnoozeUntilsuppresses notice,atom upgrade --snoozedelegation,--helpadvertises the flag.
- Test 14–17 for
- Wave 2's stack-preset additions contributed 32 assertions covering: every preset's signature files at root,
/healthzroute presence in web presets, no Dockerfile in CLI / library presets, the docker-tier-skips-preset behavior, README placeholder substitution, README snippet splice + cleanup. - Suite totals on v0.2.1: 164 / 164 fast, 179 / 179 with
ATOM_TEST_SLOW=1(atom-setup) + 20 / 20 (nucleus).
[0.2.0] — 2026-05-08
Added
atom upgradesubcommand on theatomhelp dispatcher. Detects the install location ($ATOM_INSTALLenv override →~/.atom/atom/→ realpath walk-up from this script), reads the localVERSION(added in 0.1.3), fetches upstream, and on a mismatch runsgit pull --ff-onlyfollowed bynpm install+npm install -g .for every CLI. Refuses to upgrade if the install dir's git tree is dirty. Network failure is silent and degrades to a no-op.atom upgrade --checkpolls without installing.ATOM_VERSION_URLenv override (testing affordance) — defaults to theraw.githubusercontent.comURL;data:URLs are accepted for offline tests../atom-setup --reinstallflag on the bash wrapper. Forces re-install of every atom CLI globally even when they're already on PATH. Useful when a global is stale or pointing at an old clone. Stop-gap untilatom upgradeis the daily refresh path; both flow through the samenpm install+npm install -g .shape that landed in 0.1.2. The flag is consumed by the wrapper and not forwarded to the wizard.- nucleus schema migration framework. New
nucleus migratesubcommand walks every~/.atom/nucleus/projects/*/learnings.jsonland applies pending migrations from a versioned registry atbin/nucleus/src/migrations/00X-*.js. Each migration takes{header, entries}and returns{header, entries}; the runner writes back atomically (.tmp+ rename) under the sameproper-lockfilelock thatappendEntryuses, so concurrentnucleus addcannot race.--dry-runpreviews;--quietsuppresses per-file lines. Auto-triggered lazily onnucleus addandnucleus search: O(1) per-file check (read first line, compare header version) — silent when up-to-date, prints a one-line "migrated N file(s)" notice when not. - First migration:
001-add-header.js. Prepends{"_atom_nucleus": true, "_schema": 1}to legacy v0.1.x JSONL files lacking it. Entries are unchanged; the header gives future migrations an O(1) version-check path.readEntries()now skips header lines so existing search/promote/sync flows keep working before and after migration. gh repo createintegration in wizard §10. When the user picks "Create new GitHub repo via gh", the writer now callsgh repo create <user>/<name> --private|--public --source <root> [--push]after the initial commit. Visibility (private/public) is collected from a new prompt in section 10. On failure, the wizard logs the exact retry command and continues — the project is always left in a working local state.choice = 'existing'similarly runsgit remote add origin <url>and optionalgit push -u origin main, both with non-fatal error handling.
Notes
- Existing 0.1.x users won't have
atom upgradeuntil they install v0.2.0 once manually (it's the verb that enables itself). After that, every future release is one command. - v0.2 is being shipped incrementally; remaining items (stack presets, speckit-constitution, distribution overhaul) will land as patch releases on the v0.2 line.
[0.1.3] — 2026-05-07
Hotfix surfaced by an end-to-end isolated install test of release/v0.2. Existing 0.1.2 users in any working state are unaffected; the bug only bites a fresh git clone + ./atom-setup on a machine that doesn't already have atom-setup on PATH.
Fixed
./atom-setupsilently skipped installingatom-setupitself. The bash wrapper'sfind_global()rebuilds PATH viaecho "$PATH" | tr ':' '\n' | grep -v ^$ATOM_DIR$ | tr '\n' ':'. The finaltrleft a trailing colon, which bash interprets as cwd. When the wrapper is run from inside the source dir, cwd contains a file namedatom-setup(the wrapper itself), socommand -v atom-setupreturned./atom-setupand the install loop took the "already installed, skipping" path.find_globalnow strips both empty PATH components and the trailing colon.
Added
VERSIONat the repo root. Plain text, single line. Forward-compatible with theatom upgradeverb landing in v0.2 (the upgrade verb polls this file onmainto decide whether a new release is available).
[0.1.2] — 2026-05-07
Patch release. Single fix to the install path so a fresh git clone + ./atom-setup actually works.
Fixed
./atom-setupinstall crash on fresh clones. The bash wrapper rannpm install -g .in eachbin/<cli>/without first runningnpm install(no-g) to populate the source'snode_modules/. Result: the global install "succeeded" but invoking any installed CLI immediately crashed withERR_MODULE_NOT_FOUNDforcommander(and other ESM deps) — Node was resolving imports against the source dir's missingnode_modules/. Wrapper now runsnpm installfor each CLI before the global install. Existing users on0.1.1who already have the CLIs working are unaffected; the bug only bites a true fresh clone.