Stack is versioned in lockstep with the root repo. Pre-1.0 releases are additive by default — new commands ship, existing commands don't disappear — with the occasional breaking change called out inline.
Unreleased
0.2.2 (2026-10-17)
0.2.1 was tagged but its npm publish failed, so nothing from 0.2.1 reached npm. 0.2.2 supersedes it: it ships everything listed here and under 0.2.1 below. Source versions (core, cli, mcp, server.json, VERSION in packages/cli/src/index.ts) are 0.2.2, which scripts/publish.sh checks before it publishes core, then cli, then mcp.
Round 9: usage errors an agent can act on
Covers #67, #68, #70, #73, #74, #75 and #76 together with the sections below.
- An option before the subcommand is refused.
stack --json listpassed the unknown-option guard, because the root declares--jsonforstack --version, and citty never hands a subcommand the options typed before its name:listprinted the human error andstack env --json showthe human report. An option before a subcommand name now exits 1. When the subcommand takes it, the message says where it goes (Option --json goes after the subcommand: run \stack list --json`); otherwise it is an unknown option for that subcommand (stack -v list). With--jsonon the line the message is also{"error": ...}` on stdout. - A missing argument under
--jsonis a JSON error.stack info --jsonwith no service printed the usage text on stdout, so an agent parsing it got no JSON. A usage error that citty raises (a missing required positional) now prints{"error": "Missing required positional argument: SERVICE. Run \stack info --help` for usage."}on stdout and the same line on stderr when--jsonis on the line (exit 1). Without--json` the usage text is unchanged. stack add <typo>outside a project says the provider is unknown. It checked for.stack.tomlfirst, sostack add supabseanswered "Runstack initfirst." and only after init said the provider did not exist. The provider name (and a missing name without a terminal) is now checked before the project, and a name within two edits of a real provider ends withDid you mean supabase? Run \stack add supabase`.`stack recommend db --k -1names the value. citty reads-1after--kas a short option, so the message saidInvalid --k with no value. It now saysInvalid --k "-1", as--k=-1already did.stack onboard --client <unknown>exits 1. It exited 2, unlike every other usage error, and printed nothing on stdout with--json. It now exits 1 and, with--json, prints{"error": "Unknown client: ..."}.stack onboard --undo --jsonwith nothing to undo prints the same kind of error object.- MCP:
stack_statustakescwd. It reads.stack.tomlin its working directory like the other project-scoped tools, but refusedcwd, so a server started outside the project could only report.stack.toml missing. It now accepts the same optional absolutecwd(packages/mcp/README.md).
Silent acceptance
stack providers --category <category>. Onlyrecommendcould filter by category, so an agent asking which databases Stack supports listed all 39 providers and filtered them itself.stack providers --category database(any case) lists one category; with--jsonthe report carriescategoryandtotalcounts that category. An unknown category exits 1 and names the valid ones ({"error": ...}on stdout with--json).stack_providerstakescategory.stack upgrade --json.stack_upgradereturned a clack banner ("A newer version is available.") that an agent had to scrape for the versions.stack upgrade --jsonprintsashlr.stack.upgrade/v1withcurrent,latest,status(up_to_date,update_available,ahead) and theinstallcommand, and installs nothing; a registry failure prints{"error": ...}with exit 1.stack_upgradealways passes--json.stack import --jsonnames what was stored and skipped.stack_importreturned a banner and a count ("Imported 3 secret(s)"), so an agent could not tell which names reached Phantom, which were already in the vault and left unchanged, and which were dropped for having no value (those were never reported at all).stack import --jsonprintsashlr.stack.import/v1withstored,skipped_existing,skipped_empty, the detectedproviders,unattributednames andservices_added/services_kept, names only. An error prints{"error": ...}with exit 1 and, after a partial write, the names alreadystored.stack_importalways passes--json.stack env diff --json.stack_env_diffwas the one vault check with no JSON form, so an agent got a clack banner and red circles to scrape.stack env diff --jsonprintsashlr.stack.env-diff/v1:ok, each missing secret name with the service that declares it, and totals (names only, never values). It still exits 1 when anything is missing, so a CI gate works either way; a missing project or Phantom prints{"error": ...}.stack_env_difftakesjsonlikestack_env_show.stack remove --jsonnames the secrets Phantom kept. Phantom Secrets removes nothing without an attached terminal, so everystack_removeMCP call left the service's secrets stored while its output was a clack banner ending "Removed openai locally", and an agent reported a full cleanup.stack remove(with--alland--allOrphanstoo) now takes--json: it never prompts and prints oneashlr.stack.remove/v1object listing each removed service withsecrets_removed,secrets_keptandmcp_removed, plus a top-levelsecrets_keptfor the user to clear withphantom removein a terminal. An unknown service, a missing.stack.tomlor a missing Phantom prints{"error": ...}and exits 1 without changing anything.stack_removealways passes--json.- Extra arguments exit 1 instead of being dropped. citty keeps only the positionals a command declares and drops the rest, so
stack recommend build a saassearched for "build" alone,stack add supabase neonadded only supabase, andstack providers nopelisted every provider with exit 0. A positional with no slot now exits 1 withUnexpected argument "a" for \stack recommend`, which takes. Quote a value that contains spaces, or run the command once per value. ({"error": ...}on stdout with--json). Values of options such as--region us-east-1are not counted. (packages/cli/src/lib/unknown-flags.ts`) - Unknown CLI options exit 1 instead of being ignored. citty parses options a command does not declare and never complains, so
stack list --jsnprinted the human list with exit 0,stack upgrade --jsonprinted its banner, andstack --bogusprinted the welcome banner. Every command now refuses an option it does not declare, names the closest real one (Did you mean --json?) and points at its--help; with--jsonon the line the message is also{"error": ...}on stdout. Everything after--,stack exec's forwarded command,--no-<flag>negation and negative numbers are left alone. Barestack env,stack templatesandstack projectsnow declare the options of the subcommand they run (show,list,list). A test checks that every argv the MCP server builds passes the guard. (packages/cli/src/lib/unknown-flags.ts) stack scanwithout a terminal prints its report. It printed only a "no TTY detected" hint and exited 0, so an agent got neither results nor an error. Plainstack scannow prints the same read-only report (detections andstack addnext steps, nothing written);stack scan --autowithout--yesexits 1, since it would ask before each add.- MCP:
stack_recommend {"k": 0}is no longer the default of 6. A zerokwas dropped as falsy and a negative one reached the CLI as a flag (Invalid --k with no value). Everykis now passed as--k=<n>, so the CLI rejects 0, negatives and fractions by value. - MCP: blank strings are rejected.
stack_info {"service":" "}ranstack info " "andstack_sync {"platform":" "}ran the sync check with a blank platform. A whitespace-only value now counts as missing for a required argument and is an error for an optional one.
Output and usage order
stack env export --stdoutprints only the skeleton. Thestack env exportbanner and aprinted to stdout.outro went to stdout too, sostack env export --stdout > .env.examplewrote them into the file.- Bare
stack execsays a command is missing. It checked for.stack.tomland Phantom first, so with no command it answered "Runstack initfirst." or the Phantom install hint. It now saysNothing to run. Usage: stack exec -- <command>before anything else (exit 1).
MCP project targeting
- Project-scoped MCP tools take an optional
cwd. The server always ranstackin its own working directory and no tool took a project argument, so a host that starts the server outside the project (Claude Desktop, a global Cursor config) could not reach it:stack_listanswered "No .stack.toml at". stack_init,stack_import,stack_scan,stack_add,stack_remove,stack_list,stack_info,stack_status,stack_env_show,stack_env_diff,stack_doctor,stack_sync,stack_deps,stack_recommendandstack_applynow acceptcwd, an absolute path to an existing directory, and runstackthere. It is resolved with realpath; a relative path, a missing path or a file is refused with anisErrorresult before anything runs. Global tools (stack_providers,stack_projects_list,stack_templates_list,stack_upgrade) refusecwdwith their own message rather than silently ignoring it or answering "unknown argument". Resources still read the server's working directory.
Agent dead ends
stack cirefuses with one actionable line instead of printing help. Barestack ciprinted usage andNo command specifiedwith exit 1, and its only subcommandinitis intentionally unavailable (the old workflow needed unattended Phantom Cloud pull, which requires trusted-terminal approval).stack ciandstack ci initnow say what to run instead (stack doctorfrom a trusted terminal, orstack doctor --reconcile --jsonin CI) and exit 1. With--jsonthey print{"error": ...}on stdout. No workflow is ever written.stack swapreports an unknown provider before the Phantom check. A typo such asstack swap clerkk auth0on a machine without Phantom printed the Phantom install hint. Provider names, the category match and the.stack.tomlentry are now validated first, as instack add.
Agent-facing output
stack login <provider>without a terminal says what a person must do. It stopped at "no valid key in vault and session is non-interactive"; it now gives the same hint asstack add, namingstack login <provider>as the command to run in the user's own terminal (exit 1).stack templates list --jsonandstack projects list --json. Both ignored--jsonand printed the human banner. They now print one JSON object with no banner:ashlr.stack.templates/v1(each template's name and the services it adds) andashlr.stack.projects/v1(each registered project's name, path, project id, template, services and last-seen time). Barestack templates --jsonandstack projects --jsonprint the same reports. Thestack_templates_listandstack_projects_listMCP tools takejson: true.- MCP tools reject argument names they do not declare. A misspelled key was dropped and the tool ran without it, so
stack_recommend {"query":"db","catgory":"Database"}returned an unfiltered result as a success. Unknown keys are now anisErrorresult that lists the accepted arguments. - An unknown template names where to find the real ones.
stack templates apply <name>andstack init --template <name>now say to runstack templates list. llms-full.txtlists thejsoninput onstack_infoandstack_env_show. The agent reference still showed{ service: string }and{}after those tools gainedjson: true.- The last lines of a failing command reach a pipe on Bun 1.2.
outroErrorexits the process, and Bun 1.2 dropped stdout still queued at exit, so a pipedstack applylost its finalSome providers failed: ...line,stack doctorwith many services lost most of its report, and an MCPstack_addreturned only "Failed." The CLI now writes stdout and stderr synchronously under Bun, as Node already does for pipes, so nothing is pending when it exits. stack doctorwith no services says so. A.stack.tomlwith no services printed "All services healthy."; it now prints "No services to check" and how to add one (exit 0).stack_addwithout a terminal says what a person must do. A provider that needs a pasted key or a browser sign-in failed over MCP with "no valid key in vault and session is non-interactive" (or "No valid Neon API key in vault."). The error now says the session has no terminal and to ask the user to runstack add <provider>in their own terminal.stack info <service> --jsonandstack env show --json. One JSON object on stdout with no banner:ashlr.stack.info/v1(provider, resource, secret names within_vault, MCP entry, healthcheck result) andashlr.stack.env/v1(secret names per service within_vault, plusmissingandorphans). Values are never printed. A missing project or service prints{"error": ...}and exits 1. Thestack_infoandstack_env_showMCP tools takejson: true.
Vendor-neutral MCP wiring
Stack mirrors MCP entries into Cursor and Gemini CLI project configs. When
.cursor/or.gemini/already exists in the project,stack addalso writes the server into.cursor/mcp.jsonand.gemini/settings.json(Gemini'shttpUrl/urlnaming, notype), andstack removetakes it out again. Absent client dirs are never created, other keys and servers are preserved, and an unparseable client file is left untouched without failing the add. A same-name entry the user wrote by hand in one of those files is never replaced, andstack remove(or a rolled-backstack add) deletes a client entry only when it still matches what Stack mirrored from.mcp.json.User-written Cursor and Gemini entries are kept on
stack add,stack removeand rollback (#60).
Agent onboarding
stack onboarddetects Codex, Claude Code, Cursor and Gemini CLI and registers the Phantom gateway (ashlr-hub,ashlr mcp) in each. It prints a plan by default and writes nothing.--applysaves a timestamped backup of each existing config, adds only theashlr-hubentry, keeps every other key, and re-checks each client afterwards. An existingashlr-hubentry that differs is never overwritten.--undorestores the saved files byte for byte and refuses any file that changed since it was written.--client,--all(include clients that are not detected),--homeand--jsonare supported. It never touches the network or a vendor CLI and writes no secret values.
Machine-readable output
stack list --json,stack providers --jsonandstack deps --json. These commands accepted--jsonbut ignored it and printed human text with exit 0, so agents could not parse them. Each now prints one JSON object on stdout with no banner or ANSI:ashlr.stack.list/v1(project id, template, and each service with provider, resource id, region, secret names and MCP entry),ashlr.stack.providers/v1(every registered provider with category, auth kind and docs URL, plus any that failed to load), andashlr.stack.deps/v1(services grouped by category, services whose provider is not registered, and totals). Without a.stack.toml,list --jsonanddeps --jsonprint{"error": ...}and exit 1. Human output is unchanged. Thestack_list,stack_providersandstack_depsMCP tools takejson: truefor the same output.stack projects list --jsonandstack templates list --json. Both ignored--jsonand printed the decorated human list, so thestack_projects_listandstack_templates_listMCP tools returned banner text an agent had to scrape. They now print one JSON object:ashlr.stack.projects/v1(each registered project with name, path, project id, template, service names and last-seen time) andashlr.stack.templates/v1(each template with its services, providers, secret names and MCP entry). Barestack projects --jsonandstack templates --jsondo the same. Both MCP tools takejson: true.
Usage errors exit non-zero
stack recommendwith no query, or a bad--k, exits 1. No query (or a blank one, asstack_recommend {"query":" "}sends) printed "nothing to recommend" with exit 0, and--k abc,--k 0or--k 2.5were silently replaced by the default of 6. Both now exit 1;--jsonstill prints the "Provide a query" payload on stdout, and a bad--knames the value on stderr.stack remove <name>andstack open <name>name the configured services. An unknown name only said "is not in this stack."; it now lists what is configured, suggests a case-only match ("Did you mean openai?"), or says the stack has no services and to run stack add <provider>.stack projects removeonly reports what it removed. Any argument starting with/printed "Removed ... from registry." with exit 0, even when no project was registered there, and a relative path such as.was never matched. It now matches a registered name first, then the argument as a path from the current directory, and exits 1 with a pointer tostack projects listwhen nothing matched.unregisterProject()in@ashlr/stack-corenow returns whether it removed an entry.
Tests
- The site's agent-facing doc facts are pinned to their sources of truth by a test (#59).
Release process
- The release workflow runs a pack-and-install preflight on pull requests, and the npm publish no longer waits for the macOS build (#66).
Install fixes
- Packed npm tarballs include their bins.
@ashlr/stackandashlr-stack-mcpbuiltdist/only inprepublishOnly, whichnpm packskips, so a packed tarball had nodist/and installing it gave nostackorashlr-stack-mcpcommand. The build now runs inprepack, which bothnpm packandnpm publishrun. - Installers state the real provider count (39, not 23), and
release-artifacts.test.tschecks it against the registry and that the site serves the sameinstall.sh/install.ps1asscripts/. install.shsays how to get the MCP server after a binary install. The prebuilt release binary is onlystack; the installer now printsnpm i -g ashlr-stack-mcp/npx -y ashlr-stack-mcp, and the MCP README no longer says the one-liner always installs both.
Bug fixes
stack execno longer reports a killed command as success. A wrapped command terminated by a signal (OOM kill,kill -9) left the exit code null andstack execexited 0. It now exits 128 + the signal number (137 for SIGKILL) and says which signal. SIGINT, SIGTERM and SIGHUP sent tostack execitself (an agent timeout, a supervisor) are forwarded to the wrapped command instead of killing only the wrapper and leaving the command running.- One Ctrl-C reaches a command run by
stack execonce. On a terminal, Ctrl-C and hangup already go to the wrapped command (it shares the terminal's process group), andstack execrelayed them a second time, so tools that treat a second interrupt as "exit immediately" (terraform's "Two interrupts received") skipped their graceful shutdown. On a terminalstack execnow relays only SIGTERM and waits for the command; off a terminal (an agent or supervisor) it still relays SIGINT, SIGTERM and SIGHUP. - MCP tools check their input before running the CLI. A missing required argument reached the CLI as the literal string
undefined(stack_add {}ranstack add undefined), and a value starting with-was parsed as a flag:stack_remove {"service":"--all"}ranstack remove --alland targeted every service instead of one. The server now returns an error naming the argument for a missing required field, a wrong type, or a leading-, and runs nothing. stack applywith a missing recipe writes nothing. In a directory without.stack.toml,stack apply <id>for an id that does not exist (or barestack applywith no recipes) auto-ranstack initfirst, leaving.stack.toml,.stack.local.toml,.gitignoreand a~/.stack/projects.jsonentry behind before failing. The recipe is now resolved first.stack applyandstack init --templatekeep going after a provider fails. A failedstack addexited the process, so later providers never ran, the partial-failure rollback never happened and no summary was printed. Every provider is now tried, each failure is printed, and the run ends withSome providers failed: <name>: <reason>; ...and exit 1. A wrong recipe id now also lists the saved ids or says to runstack recommend "..." --save.
0.2.1 (2026-10-16)
0.2.1 was never published, so everything below ships in it, along with the 2026-10-12 notes further down. Source versions (core, cli, mcp, server.json, VERSION in packages/cli/src/index.ts) are already 0.2.1, which scripts/publish.sh checks before it publishes core, then cli, then mcp.
Setup and release safety
- Credentials are staged during setup. New login credentials are written only after materialization succeeds, ordinary inputs are validated before commit, and create-only Phantom refusals no longer strand Stack state. (#26, #28)
- Release inputs and downstream jobs are guarded. The release workflow refuses the legacy
npm_otpinput and checks that a tag run publishes the tagged commit. (#38) - CLI fixtures run on the Bun 1.2 floor. (#27)
Site
- Homepage wrapping, FAQ clipping, ecosystem examples and the Mechanics tab were corrected, and agent-facing claims now match real CLI behavior. (#32, #34, #35, #36, #37)
Docs
- Adding a provider — docs/adding-a-provider.md lists every file a new provider touches, and
provider-parity.test.tsfails when the loader registry, the catalog anddocs/provider-auth-matrix.mddisagree (#18).
Bug fixes
stack applystops writing placeholder secrets. Its wiring step ranphantom add <KEY> TODO_STACK_APPLY --rotate(then without--rotate) for every declared secret. Phantom Secrets refuses values in argv and has no--rotate, so every call failed and the printedenvelopescount was always 0; on an older Phantom it could storeTODO_STACK_APPLYand block the real value later. Apply now reads names fromphantom list --jsonand printssecrets in vault N · missing M · webhooks K, plus anot in Phantom:line pointing atphantom add <NAME>. It never writes to the vault.WireResult.envelopesis replaced bypresentandmissing. (Successor to #33.)Command errors are one line. A command that fails outside a Stack project (
stack list,deps,info,remove, ...) now prints✗ No .stack.toml at <path>. Run \stack init` first.on stderr and exits 1, instead of a stack trace followed by the same message again.STACK_DEBUG=1adds the stack. Usage errors still print usage. (packages/cli/src/lib/run-cli.ts`, #19)CI workflow generation fails closed.
stack ci initnow exits nonzero without creating or overwriting a workflow because the reviewed Phantom Secrets 0.7.9 source requires trusted-terminal approval for Cloud pull. Existing configuration and file guards remain;--forcestays available as a compatibility flag and does not enable generation.Stack works with Phantom Secrets 0.7.9. Phantom 0.7.3+ rejects
phantom add KEY VALUE(argv is visible to other processes), never replaces a stored name, and only reveals or removes a secret in an attached terminal. Stack still passed values on argv, so everystack addfailed against the released CLI, andphantom list's table was parsed as secret names. Stack now pipes values tophantom add KEY --stdin, treats a repeat of its own identical write as done, reports any other existing name asPHANTOM_SECRET_EXISTS, reads names fromphantom list --json, and hasstack removehand Phantom the terminal (or list the secrets Phantom kept). Reading a stored token back is no longer possible, so cached-token reuse falls back to prompting, and token-based healthchecks could not read the token (see thestack doctorfix below).packages/core/src/__tests__/phantom-contract.test.tsruns the wrapper against a real binary (STACK_PHANTOM_CONTRACT_BIN=/path/to/phantom bun test phantom-contract).stack doctorno longer calls a stored token missing. Phantom Secrets 0.7.9 only reveals values in an attached terminal, so every token-based healthcheck reported<TOKEN> missing from vaultand failed even when the token was stored. Doctor now checks names againstphantom list --json: a token Phantom holds but would not reveal reportsuncheckedwith a pointer tophantom validate --check-all, and only a name absent from the vault still fails as missing.
Agent-facing fixes
npx -y ashlr-stack-mcpcan run tools. The MCP server spawnedstackfrom PATH (absent under npx, and Haskell'sstackwhen present).ashlr-stack-mcpnow depends on@ashlr/stackand runs that package's bin with the current runtime;STACK_BINstill overrides, PATH is the last resort. Publish order is now core, cli, mcp. (packages/mcp/src/stack-bin.ts)stack status --jsonfor agents and Phantom. It prints oneashlr.stack.status/v1object: whether a.stack.tomlexists, whether Phantom is installed and how many secrets it holds, and each service with its provider and secret names. A missing.stack.tomlor Phantom is reported in the object with exit 0; an unreadable.stack.tomlprints{"error": ...}and exits 1. Before, Phantom parsed the human table and listed zero services. Thestack_statusMCP tool takesjson: truefor the same output. The secret count isnullwhen the vault cannot be read, andstack infoshows? unknowninstead of calling every secret missing.stack_scanover MCP returns results. It now always passes--json; before, every call answered "no TTY detected".stack recommend --categorymatches any case.--category databaseused to return no hits, because the filter compared againstDatabaseexactly. An unknown category now exits 1 and lists the valid ones, instead of returning an empty result.stack syncrejects an unknown platform first.stack sync --platform herokusaid "Unsupported platform" only after checking for Phantom. Thestack_syncMCP schema now names the supported platforms, and the docs quote the error the MCP server actually sends when it can't run the CLI.- No spinner escape codes in piped output.
prompts.spinner()prints plain lines when stdout is not a TTY orCIis set, so MCP results and CI logs stay clean. (packages/cli/src/ui.ts) stack execstdout belongs to the command. The intro banner is gone and errors go to stderr, sostack exec -- cmd > out.jsoncaptures onlycmd.- MCP
serverInfo.versionreads package.json instead of a hard-coded0.2.0. - Bare
stack env/stack templates/stack projects/stack telemetryrunshow/list/list/statusinstead of exiting 1 with "No command specified". stack add <typo>names the unknown provider first. It used to demand Phantom before checking the name, so a misspelled provider on a machine without Phantom only said "Phantom Secrets isn't installed".- Release + docs name the MCP package correctly (
ashlr-stack-mcp, not@ashlr/stack-mcp), including the site's MCP setup snippet. - CI smoke-tests the npm bundles under Node 20 (
stack --help, MCP initialize, tools/list,stack_scan) viascripts/node-smoke.mjs.
Earlier 0.2.1 notes (2026-10-12)
- Release workflow repaired. A multi-line
git commit -min the Homebrew job ended the YAML block early and left a stray top-level key, so GitHub rejectedrelease.ymlon every push and the v0.2.0 npm publish never ran. - Docs: Phantom Secrets + Locus. New
docs/phantom-locus.mdexplains how Stack stores keys through Phantom and runs provider CLIs inside a Locus-pinned account (personal / company / client).
Bug fixes
- Kebab-case flags now parse (
--dry-run,--keep-from,--all-orphans, …) — citty 0.1.6 silently dropped the documented kebab spelling of multi-word flags: a boolean arg withdefault: falseshadowed the kebab-parsed value, so--dry-runwas ignored while only--dryRunworked. Forstack addthis was high-severity — the documented "safe preview" form fell through to the real provisioning flow (live OAuth/network/vault writes). Raw argv is now normalized (--dry-run→--dryRun) before parsing, fixing every command at once while preserving camelCase,--no-*negation, and--passthrough. (packages/cli/src/lib/normalize-args.ts) stack addno longer hangs on interactive credential entry — the CLI started a @clack spinner, then a provider's PAT/key-paste path wrote its prompt to stderr and blocked on stdin while the spinner repainted over the (now invisible) prompt. With no cached credential — e.g. a fresh repo —stack addappeared to hang forever. The host now owns prompting: a newProviderContext.promptplus aspinnerBridgehelper pause the spinner around a masked @clack prompt, and all nine interactive providers route credential entry through a sharedpromptSecret. GitHub's device-code message goes throughctx.logfor the same reason. (packages/core/src/providers/_helpers.ts,packages/cli/src/ui.ts)
SEO + GEO content surface (major)
- 29 programmatic provider pages — new dynamic route at
/providers/[slug]driven bypackages/core/src/catalog.ts. Adding a provider to catalog auto-mints a page at build time. Each page: hero with brand logo, auth-flow explainer, secret-slot breakdown, MCP wiring status,stack addsnippet,stack recommendtrigger, related providers, templates that include it, FAQ, outbound dashboard + docs links. JSON-LD: TechArticle + BreadcrumbList + FAQPage. - 4 hand-authored competitor comparison pages —
/compare/stack-vs-pulumi,/compare/stack-vs-terraform,/compare/stack-vs-railway,/compare/stack-vs-doppler. Each ~1100 words with TL;DR, 10-row HTML comparison table, honest "where each wins" sections, "can I use both?", 3-question FAQ with FAQPage JSON-LD. Sidebar nav updated with a new "Comparisons" group. - Sitemap now indexes 51 URLs (up from 18) — every new page is discoverable by Google/Bing/Perplexity crawlers out of the gate.
GEO drift reconciliation (critical)
- Canonical counts unified — one number per surface. 29 providers · 19 MCP tools · 5 templates. Previously README said "27", llms.txt said "23", mcp.json claimed 18 where other surfaces claimed 19, FAQ said 26. LLM training pipelines distrust inconsistent sources; fixing this is the highest-value GEO move we had available.
- 3 missing adapters added to
packages/core/src/catalog.ts: Braintrust, Modal, Replicate. The providers had executable adapters but weren't in the canonical catalog, sostack recommenddidn't know about them and their counts drifted. - Disambiguation block added to
llms.txt+llms-full.txt: "Ashlr Stack is not Stack Overflow / Stacks blockchain / stack.js / stack.com / Pulumi or Terraform 'stack'." Explicit anti-confusion for LLM training. - Version + date stamps added to
llms.txt+llms-full.txtheaders:Version: 0.1.1 · Updated: April 2026. LLM corpora prefer fresh sources. structured-data.tsnow imports fromPROVIDERS_REF(core catalog) so theSoftwareApplicationJSON-LD count is derived, not hardcoded.softwareVersion: "0.1.0-pre-alpha"→"0.1.1".- Hero stat strip now derives from
PROVIDERS_REF.length(29, canonical) instead of the site's 39-entry display list. - Homepage BreadcrumbList JSON-LD added for SERP rich-result eligibility.
ai-plugin.jsondescription_for_modelprovider list now includes Modal, Replicate, Braintrust, SendGrid, Mailgun, Postmark.
Tech SEO micro-fixes
HeroIntroOverlay:client:load→client:idle. No longer blocks LCP on first paint.
Hero "Generate a stack" demo (from earlier tonight)
- Clickable in-hero animation that plays a realistic
stack recommend → stack applyloop with six real provider chips (Supabase, Vercel, Anthropic, PostHog, Sentry, GitHub) materializing then stamping ✓, ending with a summary line. Six seconds from click to "oh, I get it." Honorsprefers-reduced-motionby snapping to the final frame.packages/site/src/components/GenerateStackDemo.tsx(new, 220 lines, zero new deps)- Wired into
Hero.astrounder the copy-pasteable prompt block so the value prop ladder reads: one line you tell Claude → what happens when you click play.
[0.2.0] — 2026-04-23
Added
- Compiled node-compatible CLI bin (
dist/index.js);npm install -g @ashlr/stacknow works without bun on the host. stack init --templateruns the full provisioning pipeline for every template service via a sharedlib/provision-loop.tshelper;--noProvisionpreserves shape-only behavior.stack adddetects the project's package manager and prompts to install the provider's SDKs after persist;--install={ask,always,never}overrides.- 10 new provider adapters: Auth0, WorkOS, Mixpanel, Plausible, Datadog, Grafana, GCP, DigitalOcean, Hetzner, LaunchDarkly. Adds Observability and FeatureFlags categories. Catalog now 39 providers.
stack scan --yes --json --confidenceflags for headless CI adoption.stack doctor --reconcileflags configured services no longer referenced in source.stack remove --all-orphanscleans up services present in config but absent from source.- Transactional rollback on
stack addfailure; optionalProvider.deprovisionimplemented for supabase, neon, turso, vercel. stack env export --examplewrites a committable.env.examplefrom catalog secret keys (never values).stack swap <from> <to>with 38 preset pairs across six categories; env-key aliases preserve variable names across equivalent providers (e.g.DATABASE_URLon supabase↔neon).stack telemetry {status,enable,disable}— opt-in anonymous usage telemetry, default off;STACK_TELEMETRY=0always honored.- Update notifier checks npm once per 24 h (
~/.ashlr/stack/update-check.json); respectsSTACK_NO_UPDATE_CHECKandCI. - MCP server smoke tests: spawn
dist/server.js, sendtools/list, assert schemas. - Pre-commit hook via
bun run setup-hooks; CI lint flipped from advisory to blocking after a biome--writepass. - Site CLI reference auto-generated at prebuild from citty metadata (
scripts/gen-cli-ref.ts), removing 581 lines of hand-synced mirror. SECURITY.mdwith GitHub Security Advisory disclosure path and 7-day triage commitment.CODE_OF_CONDUCT.mdin plain language with confidential enforcement channel.docs/PRIVACY.mddocumenting every telemetry field collected, three opt-out paths, and retention policy.
Changed
- Tests: 186 → 315 passing, 0 failing.
- Typecheck clean across core, cli, mcp; biome clean on 177 files.
Fixed
- Rollback error now surfaces both the original failure and any teardown failure when
Provider.deprovisionthrows, so callers know whether a dangling upstream resource was left behind. stack swap X Xrejected up front with a clear message instead of falling through toSERVICE_ALREADY_ADDED.
0.1.1 — 2026-04-19
First fully-published release: binaries on GitHub Releases, npm scope claimed, all three packages live.
- npm:
@ashlr/stack-core,ashlr-stack-mcp,@ashlr/stackall published to the registry under the new@ashlrorg. - Binaries: darwin arm64/x64 + linux arm64/x64 on https://github.com/ashlrai/ashlr-stack/releases/tag/v0.1.1, all cross-compiled on
macos-latest(the macos-13 Intel runner queue was killing us). install.sh: now resolves a real release.curl -fsSL https://stack.ashlr.ai/install.sh | bashis the canonical install path.- Same feature surface as the 0.1.0 tag (below); 0.1.0 shipped a GitHub Release + binaries but no npm packages, so v0.1.1 is what users should pin.
0.1.0 — 2026-04-19 (superseded by 0.1.1)
Initial tag. Binaries for macOS (arm64 + x64) and Linux (arm64 + x64) on GitHub Releases; install.sh one-liner. npm publish deferred to 0.1.1.
AI recommendation layer — stack recommend + stack apply
Stack is now agent-native from day one. Describe what you're building; Stack picks the providers, drafts a reproducible Recipe, and hands it to either Claude Code (via MCP) or the local-SLM fallback for synthesis.
stack recommend [query]— free-text → top providers ranked by a zero-dep BM25 + IDF scorer over the curated catalog. Flags:--json,--k,--category,--save,--synth.--savefreezes the result to.stack/recipes/<id>.tomlso the user can follow up withstack apply <id>.--synthcalls the local SLM for model-authored rationales and falls back silently when no endpoint is reachable.stack apply [recipe-id]— re-runs the existingstack addpipeline for every provider in the Recipe, then wires Phantom rotating envelopes + webhook stubs (Stripe, Clerk, Supabase, GitHub). Default ON;--noWireopts out. Interactive@clack/promptspicker when no id is given.- MCP surface —
stack_recommend(withsave:true) andstack_applyexposed so Claude can drive the full loop from a coding session. Retrieval-only on the MCP path by design — Claude owns the reasoning, Stack owns the catalog + execution. - Canonical catalog — moved from
packages/site/src/lib/providers-ref.tsintopackages/core/src/catalog.ts; site now re-exports the shim. One source of truth for CLI, MCP, AI, and docs. - Local SLM backend —
packages/core/src/ai/inference.tstalks OpenAI-compatible HTTP to LM Studio (localhost:1234) with Ollama fallback (localhost:11434). Circuit breaker + cost tracker copied from the ashlrcode router pattern. Zero new npm deps, no OpenAI/Anthropic SDKs in the Stack repo — the user's LLM keys belong to Claude, not to us. - Site surface —
packages/site/src/pages/api/recommend.tsEdge-ready API route. StackBuilder gains a "Describe your project" textarea that pre-selects provider cards. StackREPL simulatesstack recommendclient-side. - Tests — 179 pass / 0 fail (up from 135 before the AI layer). New:
catalog-index.test.ts,inference.test.ts,circuit-breaker.test.ts,recipe.test.ts,phantom-wire.test.ts,recommend.test.ts(+ round-trip --save/apply). - CLI fix: parent
run()no longer trails a banner after every subcommand — was breaking machine-readable output (stack providers,stack recommend --json). Detects subcommand viaprocess.argv[2].
Reliability + hardening (auto-agent)
Retry + recovery + secret-redaction pass on packages/core/. No other package touched.
fetchWithRetryshipped (packages/core/src/http.ts) — exponential backoff with jitter,Retry-Afterheader honored (integer-seconds + HTTP-date), idempotency auto-derived frominit.method(GET/HEAD) with explicit opt-in for safe POSTs, abortable viaAbortSignal. Zero new runtime deps. Threaded through GET healthcheck + verify paths on all seven providers in scope (supabase, neon, vercel, github, cloudflare, sentry, plus everymakeApiKeyProvider-based provider via a newverifyFetchhelper in_helpers.ts). Provision POSTs are deliberately NOT retried — a second call could double-create an upstream resource or double-charge a provider API. Railway + Linear GraphQL POSTs explicitly opt in toidempotent: truebecause they use read-only{ me }/{ viewer }queries.- Partial-failure breadcrumb in the
addServicepipeline (packages/core/src/pipeline.ts) — ifmaterialize()throws afterprovision()succeeded, we now persist a minimalServiceEntry(provider,resource_id,region,meta, emptysecrets: [],created_by: "stack add (partial)") before re-throwing the original error. Leaves a breadcrumb forstack doctor --fixorstack removeto find and clean up the dangling upstream resource. Documented in a code comment with rationale. - Secret-redaction audit — 15 log sites reviewed across every provider (supabase, neon, vercel, github, cloudflare, sentry, turso, aws, plus the
_api-keyfactory); 0 were interpolating raw tokens — all cached-token-invalid warnings use static strings. Addedscrub(value, keepLast?)helper topackages/core/src/providers/_helpers.tsfor future use. VerifiedformatArgsForError()inphantom.tsstill redactsphantom add KEY VALUE's third argument correctly; added a regression test that exercises a failing phantom binary and asserts the raw value never appears in the thrownStackError. - Tests: 3 new files —
http.test.ts(13 cases: happy path, 429 retry, 500 max-out, Retry-After honored, POST no-retry, explicit idempotent opt-in, network-error retry for idempotent, non-idempotent network error bubbles, customretryOn, AbortSignal mid-retry, + 5parseRetryAftercases),pipeline-recovery.test.ts(2 cases: breadcrumb written on partial failure; dry-run skips breadcrumb),secret-redaction.test.ts(8 cases:scrubunit tests + cached-token-warning doesn't leak raw secret +formatArgsForErrorredactsphantom add's value). 120 tests, all green (was 97). - Sanity:
bun testgreen,bunx tsc --noEmit -p tsconfig.jsonclean.
Marketing surface (auto-agent)
Polish pass on packages/site/ — landing page + docs, no other package touched.
- Vercel Web Analytics —
@vercel/analyticsdependency added;/_vercel/insights/script.jsloaded defer-ed fromBase.astro, plus areferrer-when-downgrademeta for correct attribution. Ships regardless of plan; no-ops until the Vercel dashboard flag is on. /docs/changelog— renders the repo-rootCHANGELOG.mdas a docs page viamarked+ Vite?rawimport. Slots under a new "Meta" sidebar group./docs/roadmap— honest Shipped / In progress / Considering / Explicitly not doing list, dated where possible. Also under "Meta".- Launch copy drafts under
/launch/*—hn.md(Show HN, ≤200 words,noindex),twitter.md(7-tweet thread with the 🔺 mark,noindex),blog.mdx(~1000-word "Introducing Ashlr Stack" long-form, indexable, rendered through the Docs layout via a thinLaunchPost.astrowrapper). Non-indexable notes use a newLaunchNote.astrowith minimal self-contained prose styles. DemoReel.tsx— React island that replays astack init → stack add supabase → stack exec -- bun devsequence as an auto-looping scripted terminal. Shares visuals with the heroTerminal.tsx(magenta prompt, green checkmarks, dim notes), respectsprefers-reduced-motionby snapping to the last frame and pausing, and carriesrole="img"+ a descriptivearia-labelfor screen readers. Mounted on the homepage as "§ 03 · In motion — See it run" between the one-command section and the providers grid.- MDX integration —
@astrojs/mdxadded so the blog post can mix prose and code fences freely. - Build:
bunx astro check— 48 files, 0 errors / 0 warnings / 0 hints.bun run build— 17 pages, sitemap regenerated. Only the existing pre-launch@import-order CSS advisory remains.
Publish + Windows (auto-agent)
Windows support + npm-publish readiness without touching any owner-restricted code (packages/site/, packages/core/src/, packages/cli/src/, packages/mcp/src/, packages/plugin/):
scripts/install.ps1— Windows PowerShell one-liner installer mirroringscripts/install.sh. Detects Bun (installs viairm bun.sh/install.ps1 | iexif missing), falls back tonpm i -g phantom-secretsfor the vault (no brew on Windows), triesbun add -g @ashlr/stack ashlr-stack-mcpfirst, falls back to git-clone into$env:LOCALAPPDATA\ashlr-stackand writes astack.cmd+ashlr-stack-mcp.cmdshim into$env:USERPROFILE\.local\bin(preferred) or$env:LOCALAPPDATA\Programs\ashlr-stack\bin. Warns if the shim dir isn't on PATH. Install body wrapped inInstall-AshlrStack+ a top-level guard sopwsh -Command { . ./scripts/install.ps1 }parses without side-effects. Usage:irm stack.ashlr.ai/install.ps1 | iex.Per-package README + LICENSE:
packages/cli/README.md— technical overview, the three install paths, quickstart, command cheat-sheet for all 22 commands, links to docs.packages/core/README.md— short explainer that this is the shared library (not usually installed directly), public-API table (addService,readConfig/writeConfig,listProviderNames,getProvider,scanSource,detectProvider,parseEnv), example import.packages/mcp/README.md— what it is, install,.mcp.jsonsnippets for Claude Code / Cursor / Windsurf / Codex, 17 tools grouped by category, 3 resources, links tostack.ashlr.ai/docs/mcp.packages/cli/LICENSE,packages/core/LICENSE,packages/mcp/LICENSE— copies of the root MIT LICENSE (npm doesn't traverse up).
npm publish shape audit — verified each published package.json (
@ashlr/stack,@ashlr/stack-core,ashlr-stack-mcp) is ship-ready:filesarrays include onlysrc/**/*,README.md,LICENSE;publishConfig.access: "public"present on all three;binpaths correct (./src/index.ts);repository.directorycorrect;main/exportscorrect on core. Flagged:@ashlr/stackdepends on@ashlr/stack-coreviaworkspace:*— fine for monorepo dev, would break a barenpm install @ashlr/stack. Documented this in the root README "Publishing" section rather than hand-editing dev-critical dep ranges.scripts/publish.sh— automated publish dance. Accepts--version X.Y.Z(or prompts), backs up eachpackage.json, bumpsversion, rewrites everyworkspace:*→^X.Y.Z, runsnpm publish --dry-runfor each of the three packages first, prompts for explicit confirmation (bypass with--yes), publishes in dep ordercore → mcp → cli, always restoresworkspace:*via an EXIT trap so dev keeps working, then tagsv<version>and pushes the tag. Never publishes without an explicit confirmation.Root
README.mdpolish — added three badges at the top (CI, MIT license, npm version — latter renders "not found" until publish, by design). New "Install" section right under the tagline with three options (macOS/Linux one-liner, Windows PowerShell one-liner, manualbun add -g). Prominent link toSTACK.mdfor AI-agent readers. New "Publishing" subsection under "Monorepo layout" pointing atscripts/publish.shand explaining theworkspace:*caveat.Sanity checks:
bun install,bun test(97 / 0),bunx tsc --noEmit -p tsconfig.json, and PowerShell parse check forscripts/install.ps1all pass.
Documentation site — /docs subsection
10 documentation pages under packages/site/src/pages/docs/:
/docs— three-card hero to quickstart / providers / CLI/docs/quickstart— zero → first service in 5 minutes/docs/cli— full reference for all 22 commands (synopsis / flags / examples / subcommands)/docs/providers— all 23 providers with auth kind, secret slots, MCP wiring, dashboard + docs URLs/docs/templates— 5 starters with per-service breakdown/docs/config—.stack.tomlcommitted /.stack.local.tomllocal schema/docs/phantom— how Stack composes with Phantom Secrets/docs/mcp— 17 MCP tools + 3 resources for Claude Code / Cursor / Windsurf/docs/ci—stack doctor --jsonin CI, generated workflow, secrets setup/docs/faq— top 10 new-user questions
Docs layout (src/layouts/Docs.astro): sticky left sidebar (<select> on mobile), prose column with magenta-accented H2s + inline code, right on-this-page TOC with IntersectionObserver scrollspy, prev/next pagination at the foot of each page.
Reference data in src/lib/ (hand-mirrored from source — Node-only @ashlr/stack-core can't ship to the static site bundle):
cli-ref.ts— 22 commandsproviders-ref.ts— 23 providersmcp-ref.ts— 17 tools + 3 resourcestemplates-ref.ts— 5 templatesdocs-nav.ts— sidebar structure + prev/next helper
Build: bunx astro check — 41 files / 0 errors. bun run build — 11 pages (homepage + 10 docs routes) + sitemap-index.xml + sitemap-0.xml.
Landing page visual redesign + SEO/GEO package
Full editorial-technical makeover of the marketing site at packages/site/.
Typography & voice — swapped Inter for a two-font system: Instrument Serif (display, italic-capable) paired with Geist (body) and Geist Mono (code). Every section now opens with a small-caps § NN editorial rule + a display-serif headline that occasionally italicizes a single phrase for emphasis — trade-magazine-for-infrastructure vibe.
New motion primitives:
ScrollReveal.astro— IntersectionObserver-backed fade+lift on viewport enter; staggered children via--stagger-iCSS var. Idempotent install, honorsprefers-reduced-motion.Counter.tsx— ease-out-cubic count-up from 0→target on first view.MagneticButton.tsx— 6px-capped cursor-deflection on the primary CTA; falls back to plain button in reduced-motion.- On-load staggered hero sequence (7 elements, 40–660ms delays) via pure CSS — no JS cost.
- Provider card hover: border + background + 2px lift + brand-color radial underglow.
- Editorial
.edi-ruledividers (hairline with small-caps label, gradient-masked). - Sitewide SVG grain overlay for depth.
- Subtle sheen sweep on
btn-primaryvia::beforetransform. - Rotated display-serif "est. 2026" seal behind the hero terminal.
- "Vol. 01 · Issue 2026" corner marks on the hero — tiny but make the page feel designed.
Logo system: Logo.astro now ships three variants (mark, chip, wordmark) with a proper gradient fill and crisp SVG geometry. Same triangle, new precision.
SEO/GEO package (agent-delivered, verified):
- Favicon set (SVG + 32/192/512 sizes + apple-touch + PWA manifest).
- OG image (1200x630 SVG), Twitter card (1200x600 SVG).
Base.astrorewritten with typed props fortitle/description/canonical/ogImage/ogType/noindex/breadcrumbs; emits full OG + Twitter +theme-color+color-scheme+ manifest + RSS alternate.- Three JSON-LD schemas: Organization, WebSite, SoftwareApplication (version
0.1.0-pre-alpha, no fakeaggregateRating). @astrojs/sitemapwired;sitemap-index.xml+sitemap-0.xmlgenerate at build.robots.txtwith sitemap reference.llms.txt+llms-full.txt(llmstxt.org standard) — self-contained summaries so LLMs can answer "what is Stack?" without fetching anything else.
Build: bunx astro check — 0 errors / 0 warnings / 0 hints. bun run build — 1 page, ~45 KB gz client JS.
Commodity polish (env, dry-run, remove-all, picker, completion, CI)
Six week-one features comparable tools ship that Stack was missing:
stack add(no arg) → interactive picker — @clack/promptsselectwith the full 23-provider catalog grouped by category + authKind hints. Falls back to an error in non-TTY.stack add --dry-run— describes the four-stage pipeline (login → provision → materialize → persist) without touching Phantom, the network, or.mcp.json. Good for "what will this do?" confidence.stack env set <KEY> <VALUE>— direct secret injection. Auto-derives the service from the env-name pattern (falls back tomiscif unknown);--service <name>overrides.stack env unset <KEY>removes from both Phantom and.stack.toml.stack remove --all— tear down every service with a two-step confirmation (confirm+ typed"remove all"). Non-TTY runs without prompts.stack completion bash|zsh|fish— emits a completion script including live provider + template names baked at runtime. zsh uses_values, bash usescomplete -F, fish usescomplete -c.stack ci init— scaffolds.github/workflows/stack-ci.ymlthat runsstack doctor --jsonon push + pull_request + nightly cron, pulls from Phantom Cloud, and uploads the doctor report as an artifact.
22 CLI commands total · 5 new tests · 69 tests all green.
Landing page — stack.ashlr.ai
Premium marketing surface at packages/site/. Astro 5 + Tailwind v4 + Framer Motion + React islands. Dark-first, magenta accent.
Sections (in order):
- Hero — ▲ stack mark, gradient headline, working copy-button on the install one-liner, animated
<Terminal/>runningstack add supabaseend-to-end. - One-command moment — Framer Motion tab-group with shared
layoutIdpill; "Without Stack" (8 real steps, 45 min) vs "With Stack" (1 command). - Providers grid — all 23 providers grouped into 11 categories, simple-icons marks inlined at build time.
- How it works — 3-step panel (CLI → Phantom → wiring) with per-step mini terminals and a magenta connector.
- Templates — 5 real templates, each rendered as a card with the real
stack init --template <id>command. - Claude Code integration — scripted mock chat UI running
/stack:add supabase→ narration → tool_use → tool_result. - Ashlr constellation — Stack center + Phantom / ashlr-plugin / ashlrcode satellites connected in SVG.
- Footer — CTA panel + 3-column link tree + giant faded wordmark.
Ships with: preconnect on Google Fonts, skip-to-content link, focus-visible rings, role="tablist" keyboard nav, WCAG-AA body contrast, responsive 6→4→3→2 grid, prefers-reduced-motion respected at both CSS level and inside the two Framer islands. bun run build produces a static dist/ with ~45 KB gz client JS (framer-motion is the majority; only loads on the two interactive islands).
bunx astro check — 0 errors · 0 warnings · 0 hints.
Hardening pass (code review + UX polish)
Independent code review surfaced 6 real issues; independent UX review surfaced 10 gaps. All addressed:
- Security: redacted secret values from
phantom ${args}error messages (was leaking the raw value on non-zero exit). Fake-phantom test harness rewritten to pass all args viasys.argv— Python-injection surface closed.stack clonenow rejectsfile://,--option-prefixed, andext::URLs; passes--before positional args to git. AWSmaterializenow explicitly validates theaccessKeyId:secretAccessKeyshape instead of silently splitting. OAuth callback server binds port before registering the request handler — eliminates theredirectUri === ""race. - Correctness:
DATABASE_URLno longer auto-attributed to Neon (it's a generic Postgres convention — Supabase, Railway, Render, etc. all use it). Neon now stores connection URIs keyed by project id so cross-project pollution can't happen. SentryfetchIdentitywrapped in try/catch so network errors surface as healthcheck errors, not uncaught throws. - Polish:
stack --versionworks. Barestackprints a banner.outroErrorcallsprocess.exit(1)so CI actually sees non-zero. Unknown-provider errors group the catalog by category instead of wrapping a comma-soup line.stack doctor --fixasks for confirmation before re-running OAuth/provision flows.stack upgradereadspackage.jsonfor the current version instead of a hardcoded string. - Dedup: six providers shipped byte-identical
readLine+tryRevealSecret+bearerJsonHeaders— extracted topackages/core/src/providers/_helpers.ts.makeApiKeyProviderfactory now persists the verified key duringlogin()(matching hand-written providers), so a failure inprovision/materializedoesn't make users re-paste. - MCP server:
runStacknow surfacesENOENTas a structured"install the stack CLI or set STACK_BIN"hint instead of throwing an opaque error back to Claude. - Docs: README provider list updated from stale 20 → 23. Added a "dev install" path.
CHANGELOG.mdnow tracks hardening. - Tests: 4 new test files (security redaction, URL allowlist, helper behaviour, cross-project coexistence) — 64 tests, all green.
MCP richness, deps graph, CI output, 2 more providers
stack deps— ASCII tree of the current stack grouped by category, annotated with secret slots and MCP wirings. Good for onboarding and template sanity-checks.stack doctor --json— machine-readable per-project report ({ reports: [{ project, path, services: [{ name, status, detail, latencyMs }] }] }). Exits non-zero if anything errors. Drop-in for CI pipelines.- MCP server enrichment —
ashlr-stack-mcpnow exposes 17 tools (addedstack_import,stack_scan,stack_info,stack_env_show,stack_env_diff,stack_providers,stack_projects_list,stack_deps,stack_upgrade) and registers three resources (stack://current/.stack.toml,.stack.local.toml,.mcp.json) so Claude can read project state without a tool call. - Render — managed deploy platform. API-key paste +
/v1/ownersverification. - Firebase — service-account JSON blob storage; shape-verified (
type: service_account+project_id+client_email).
3 new providers + visibility commands
- Cloudflare — Workers / Pages / R2 / D1 / KV all behind one API token.
stack add cloudflareverifies the token and stashesCLOUDFLARE_API_TOKEN+CLOUDFLARE_ACCOUNT_ID. - Turso — LibSQL edge DB.
stack add tursocreates a new database, auto-mints an auth token, and writesTURSO_DATABASE_URL/TURSO_AUTH_TOKEN. - Convex — reactive backend. v1 accepts a deploy key; deploy-key shape is structurally validated before storage.
- Scanner rules for all three (including
wrangler.toml,@libsql/client,convex/,CONVEX_DEPLOY_KEYin.env.example, etc.). - New starter template
cloudflare-turso-clerkfor edge-first projects. stack info <service>— deep-dive on one service: resource, region, auth, docs, dashboard, MCP name, meta, vault presence, live healthcheck.stack env show/stack env diff— effective env-var surface. Shows which declared slots are present in Phantom (masked), surfaces vault orphans, or prints just the missing set.stack upgrade— checks the npm registry for a newer@ashlr/stackrelease.
Totals: 21 providers · 19 CLI commands · 5 templates · 49 tests.
Unified management system
Stack now works on any project or GitHub repo, not just one you started with stack init.
stack scan— detects providers from source:package.json/requirements.txt/pyproject.toml/go.mod/Cargo.tomlplus framework configs (vercel.json,fly.toml,railway.json,supabase/config.toml,sentry.*.config.ts,.github/workflows/) and.env.example. Confidence-tiered output.--autorunsstack addfor each detection.stack clone <github-url>— thin wrapper that git-clones a repo, then either recognizes a committed.stack.tomlor scans the checkout and prints next steps.stack projects list|register|remove|where— cross-project registry backed by~/.stack/projects.json. EverywriteConfigauto-registers. HonorsSTACK_REGISTRY_DIRfor test isolation.stack doctor --all— run healthchecks across every registered project in one command.- Config split —
.stack.toml(committed shape) +.stack.local.toml(gitignored instance). Safe to share in git without leaking another developer's Supabase project ref or timestamps. Legacy single-file configs are read transparently and migrated on next write..gitignoreis auto-appended on first write.
Foundations from earlier waves
- 18 curated providers across 7 categories (database / deploy / cloud / analytics / errors / ai / payments / code / tickets / email / auth).
stack import [--from .env]to migrate existing projects.- Phantom Secrets composition: every secret read/write shells out to
phantom, never stored by Stack. - MCP server (
ashlr-stack-mcp) + Claude Code plugin (/stack:add,/stack:list,/stack:doctor,/stack:status). - 47 tests across 12 files; fetch-mocked provider tests and fake-phantom integration harness.