diff --git a/docs/site/i18n/es/code.json b/docs/site/i18n/es/code.json index 26965ccb..293e7653 100644 --- a/docs/site/i18n/es/code.json +++ b/docs/site/i18n/es/code.json @@ -344,6 +344,24 @@ "message": "Comando copiado", "description": "Accessible label after copying the install command" }, + "homepage.agent.jump": { + "message": "¿Usas IA? Copia nuestro prompt para empezar." + }, + "homepage.agent.heading": { + "message": "¿Desarrollas con un agente de IA?" + }, + "homepage.agent.copy": { + "message": "Copiar prompt" + }, + "homepage.agent.copied": { + "message": "Copiado" + }, + "homepage.agent.body": { + "message": "Pégalo en Claude Code, Codex, Cursor u otro agente de programación. Instala Scaffold, te pide el nombre del proyecto, crea el proyecto y le indica al agente nuestra Agent Skill para todo lo demás." + }, + "homepage.agent.cta": { + "message": "Sobre la Agent Skill" + }, "homepage.features.quickstart.title": { "message": "Inicio Rápido", "description": "Title for the quickstart card" @@ -404,27 +422,35 @@ "homepage.about.cta": { "message": "Lee la introducción" }, - "homepage.loop.heading": { - "message": "El ciclo" + "homepage.workflow.heading": { + "message": "El flujo de trabajo" + }, + "homepage.workflow.intro": { + "message": "Itera en local tan rápido como guardas y despliega en testnet y mainnet cuando lo decidas. Compilar contratos, ejecutar la red local y generar clientes TypeScript ocurre por sí solo." + }, + "homepage.workflow.init.label": { + "message": "Crear" + }, + "homepage.workflow.init": { + "message": "Configura los contratos, un frontend en el framework que elijas y el código utilitario que los conecta, ya integrados." + }, + "homepage.workflow.dev.label": { + "message": "Desarrollar en local" }, - "homepage.loop.intro": { - "message": "Cuatro comandos cubren todo el ciclo de vida. Todo lo que hay en medio —compilar contratos, desplegar en una red local, generar clientes TypeScript— ocurre por sí solo." + "homepage.workflow.dev": { + "message": "Ejecuta una red local y vigila tu código. Cada vez que guardas, recompila y vuelve a desplegar los contratos que cambiaron, regenera sus clientes tipados y recarga el frontend." }, - "homepage.loop.init": { - "message": "Prepara los contratos, un frontend en el framework que elijas y el código de utilidades que los conecta, ya listos para funcionar.", - "description": "Description of the init step" + "homepage.workflow.publish.label": { + "message": "Publicar" }, - "homepage.loop.generate": { - "message": "Añade un contrato al workspace y lo registra en la configuración de tu entorno.", - "description": "Description of the generate step" + "homepage.workflow.publish": { + "message": "Cuando un contrato está listo, publica su Wasm en el Stellar Registry y despliégalo por nombre: primero en testnet como staging y luego en mainnet. Estos despliegues son pasos deliberados, nunca un efecto secundario de guardar un archivo." }, - "homepage.loop.dev": { - "message": "Observa tus contratos, los recompila y redespliega al cambiar, regenera los clientes tipados y sirve el frontend.", - "description": "Description of the dev step" + "homepage.workflow.extras": { + "message": "Mientras desarrollas, un único scaffold.yml versionado declara tus redes, cuentas y contratos, para que todo tu equipo compile el mismo proyecto, y ese mismo archivo lo lleva de local a testnet y a mainnet. Las comprobaciones de configuración detectan errores antes de compilar y, cuando algo falla, Doctor revisa tu toolchain, la configuración y la red local de una vez y te dice cómo arreglar lo que encuentra." }, - "homepage.loop.publish": { - "message": "Publica tu WASM en el Stellar Registry para poder desplegarlo por nombre en testnet o mainnet.", - "description": "Description of the publish step" + "homepage.workflow.cta": { + "message": "Ver todos los comandos" }, "footer.banner.eyebrow": { "message": "Registro de Contratos" diff --git a/docs/site/src/pages/index.module.css b/docs/site/src/pages/index.module.css index f4ec21de..ab65b4ce 100644 --- a/docs/site/src/pages/index.module.css +++ b/docs/site/src/pages/index.module.css @@ -111,6 +111,29 @@ opacity: 0.85; } +/* Jump link to the agent prompt. Wide screens show the callout close enough + to the fold that the link would only add noise. */ +.heroAgentLink { + display: block; + width: fit-content; + margin: 1rem auto 0; + font-size: 0.875rem; + color: var(--ss-band-ink); + text-decoration: underline; + text-underline-offset: 0.2em; +} + +.heroAgentLink:hover { + color: var(--ss-band-ink); + opacity: 0.75; +} + +@media screen and (min-width: 997px) { + .heroAgentLink { + display: none; + } +} + /* ── Reading column ─────────────────────────────────── */ .main { @@ -118,13 +141,92 @@ margin: 0 auto; padding: 3rem 1.5rem 4rem; /* Docusaurus renders
as a flex item; without this the non-wrapping - commands in the loop set a min-content floor and scroll the whole page. */ + commands in the workflow set a min-content floor and scroll the whole page. */ min-width: 0; width: 100%; } +/* ── Agent prompt callout ───────────────────────────── */ + +.agent { + margin-top: 3rem; + padding: 1.5rem; + border: 1px solid var(--ifm-color-primary); + border-radius: 0.625rem; + background-color: color-mix( + in srgb, + var(--ifm-color-primary) 7%, + var(--ss-background) + ); + /* Keeps the heading clear of the sticky navbar after the hero link jumps here */ + scroll-margin-top: calc(var(--ifm-navbar-height) + 1rem); +} + +.agentHeader { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.75rem 1rem; +} + +.agentHeading { + font-family: var(--ss-font-display); + font-size: 1.375rem; + font-weight: 500; + margin: 0; +} + +.agentCopy { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border: none; + border-radius: 0.5rem; + background-color: var(--ifm-color-primary); + color: var(--ss-background); + font-family: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: opacity var(--ss-duration) var(--ss-easing); +} + +.agentCopy:hover { + opacity: 0.85; +} + +.agentCopy.copied { + background-color: #1f7a4d; + color: #ffffff; +} + +.agentBody { + font-size: 0.9375rem; + line-height: 1.7; + color: var(--ss-muted); + margin: 0.875rem 0 1rem; +} + +.agentPrompt { + font-family: var(--ss-font-mono); + font-size: 0.8125rem; + line-height: 1.6; + color: var(--ss-foreground); + background-color: var(--ss-background); + border: 1px solid var(--ss-border); + border-radius: 0.5rem; + padding: 1rem; + margin: 0 0 1rem; + /* Wraps instead of scrolling sideways; the skill URL is long enough that + it has to break mid-word on a phone. */ + white-space: pre-wrap; + overflow-wrap: anywhere; +} + .about, -.loop { +.workflow { margin-top: 3.5rem; padding-top: 3rem; border-top: 1px solid var(--ss-border); @@ -158,42 +260,31 @@ text-decoration: underline; } -/* ── Dev loop rail ──────────────────────────────────── */ +/* ── Workflow rail ──────────────────────────────────── */ /* The rail is the page's one piece of ornament: a scaffold pole with a clamp - at each command, drawn from the same joint color the cards pick up on hover. */ -.loopList { - position: relative; + at each step, drawn from the same joint color the cards pick up on hover. + Each step draws the pole down to the next clamp, so it ends at the last. */ +.workflowList { list-style: none; margin: 1.75rem 0 0; padding: 0 0 0 2rem; } -.loopList::before { - content: ""; - position: absolute; - left: 0.34375rem; - top: 0.5rem; - bottom: 0.75rem; - width: 1px; - background-color: var(--ss-joint); - opacity: 0.5; -} - -.loopStep { +.workflowStep { position: relative; padding-bottom: 1.5rem; } -.loopStep:last-child { +.workflowStep:last-child { padding-bottom: 0; } -.loopStep::before { +.workflowStep::before { content: ""; position: absolute; left: -2rem; - top: 0.375rem; + top: 0.25rem; width: 0.6875rem; height: 0.6875rem; border: 1.5px solid var(--ss-joint); @@ -201,7 +292,29 @@ background-color: var(--ss-background); } -.loopCommand { +/* Runs from the bottom of this clamp to the top of the next one */ +.workflowStep:not(:last-child)::after { + content: ""; + position: absolute; + left: calc(-2rem + 0.34375rem); + top: 0.9375rem; + bottom: -0.25rem; + width: 1px; + background-color: var(--ss-joint); + opacity: 0.5; +} + +.workflowLabel { + display: block; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ss-muted); + margin-bottom: 0.25rem; +} + +.workflowCommand { display: block; font-family: var(--ss-font-mono); font-size: 0.875rem; @@ -214,9 +327,13 @@ white-space: nowrap; } -.loopDetail { +.workflowDetail { font-size: 0.9375rem; line-height: 1.6; color: var(--ss-muted); margin: 0.375rem 0 0; } + +.workflowExtras { + margin-top: 2rem; +} diff --git a/docs/site/src/pages/index.tsx b/docs/site/src/pages/index.tsx index dda98d4d..e79d0c3e 100644 --- a/docs/site/src/pages/index.tsx +++ b/docs/site/src/pages/index.tsx @@ -11,27 +11,92 @@ import Heading from '@theme/Heading'; import styles from './index.module.css'; -const INSTALL_COMMAND = 'cargo install --locked stellar-scaffold-cli'; +const INSTALL_COMMAND = 'cargo binstall stellar-scaffold-cli'; -function InstallCommand() { +/** + * Copy-paste prompt that walks an AI coding agent through the Quickstart and + * hands it the Agent Skill, which covers everything after `init`. + */ +const AGENT_PROMPT = `Help me start a new Stellar Scaffold project. + +1. Read the Stellar Scaffold agent skill and follow it for everything below: https://raw.githubusercontent.com/stellar-scaffold/cli/main/skills/stellar-scaffold/SKILL.md +2. Check that I have Rust with the wasm32v1-none target, a C toolchain, the Stellar CLI (\`stellar\`), Node.js, and Docker running. If anything is missing, tell me how to install it before going further. +3. Tell me that Scaffold works best with Stellar Registry: if I'm going to be deploying to Testnet or Mainnet, I'm going to want stellar-registry-cli in addition to stellar-scaffold-cli. Do I want to install both now? +4. Install Scaffold CLI and, if I opted in, Registry CLI with \`cargo binstall stellar-scaffold-cli [stellar-registry-cli]\`, installing cargo-binstall first if I don't have it (see https://stellarscaffold.org/docs/quick-start). Confirm it works with \`stellar scaffold version\` and \`stellar registry version\`. +5. Ask me for a project name. Ask me if I want a frontend and, if so, do I want React, Svelte, or to use a community template (if the latter, coach me through the \`user/repo\` shorthand (optionally with a \`#branch\`/\`#tag\` suffix) that the \`--template\` option expects). Ask me if I am ok with NPM or if I'd like another package manager from the \`--package-manager\` choices. +6. Run \`stellar scaffold init --template [my choice] --package-manager [my choice]\`. The first run downloads the local network's Docker image and deploys the example contracts, so it can take several minutes. Warn me. +7. For a frontend project, start \`npm run dev\` in the project. It keeps running, so run it in the background or ask me to run it, then point me to http://localhost:5173. + +Then ask me what I'd like to build.`; + +/** Anchor the hero links to so small screens can jump past the cards. */ +const AGENT_PROMPT_ID = 'ai-prompt'; + +/** + * Copies `text` to the clipboard and reports `copied` for two seconds after, + * so the button can confirm it worked. + */ +function useCopy(text: string): [boolean, () => Promise] { const [copied, setCopied] = useState(false); - const handleCopy = async () => { + const copy = async () => { try { - await navigator.clipboard.writeText(INSTALL_COMMAND); + await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { - console.error('Failed to copy command:', err); + console.error('Failed to copy:', err); } }; + return [copied, copy]; +} + +/** A check mark once copied, otherwise the two-sheets copy icon. */ +function CopyIcon({copied}: {copied: boolean}) { + return copied ? ( + + ) : ( + + ); +} + +function InstallCommand() { + const [copied, copy] = useCopy(INSTALL_COMMAND); + return (
{INSTALL_COMMAND}
); } +/** + * Callout with the agent prompt. Set apart in the primary color because it's + * the one block on the page meant to be copied whole. + */ +function AgentPrompt() { + const [copied, copy] = useCopy(AGENT_PROMPT); + + return ( +
+
+ + + Building with an AI agent? + + + +
+

+ + Paste this into Claude Code, Codex, Cursor, or another coding agent. + It installs Scaffold, asks for your project name, creates the + project, and points the agent at our Agent Skill for everything + after that. + +

+
{AGENT_PROMPT}
+ + About the Agent Skill{' '} + → + +
+ ); +} + function HomepageHeader() { const {siteConfig} = useDocusaurusContext(); return ( @@ -100,80 +177,108 @@ function HomepageHeader() { Quickstart → + + + Using AI? Copy our prompt to get started. + + ); } -type LoopStep = { +type WorkflowStep = { + label: string; command: string; detail: string; }; /** - * The four commands that make up a working session, in the order they run. - * The sequence is the product, so the page renders it as one. + * The three stages of a project, in order: fast iteration against a local + * network, then deliberate deploys to testnet and mainnet. Each stage is + * labeled because the command alone doesn't say which stage it belongs to. */ -function DevLoop() { - const steps: LoopStep[] = [ +function Workflow() { + const steps: WorkflowStep[] = [ { + label: translate({ + id: 'homepage.workflow.init.label', + message: 'Create', + description: 'Label for the init step', + }), command: 'stellar scaffold init my-project', detail: translate({ - id: 'homepage.loop.init', + id: 'homepage.workflow.init', message: - 'Sets up contracts, a frontend in the framework you pick, and the utility code connecting them — already wired together.', + 'Sets up contracts, a frontend in the framework you pick, and the utility code connecting them, already wired together.', description: 'Description of the init step', }), }, { - command: 'stellar scaffold generate contract counter', - detail: translate({ - id: 'homepage.loop.generate', - message: - 'Adds a contract to the workspace and registers it in your environment config.', - description: 'Description of the generate step', + label: translate({ + id: 'homepage.workflow.dev.label', + message: 'Develop locally', + description: 'Label for the dev step', }), - }, - { command: 'npm run dev', detail: translate({ - id: 'homepage.loop.dev', + id: 'homepage.workflow.dev', message: - 'Watches your contracts, rebuilds and redeploys them on change, regenerates typed clients, and serves the frontend.', + 'Runs a local network and watches your code. Every save rebuilds and redeploys the contracts that changed, regenerates their typed clients, and reloads the frontend.', description: 'Description of the dev step', }), }, { + label: translate({ + id: 'homepage.workflow.publish.label', + message: 'Ship', + description: 'Label for the publish step', + }), command: 'stellar registry publish', detail: translate({ - id: 'homepage.loop.publish', + id: 'homepage.workflow.publish', message: - 'Publishes your WASM to the Stellar Registry so it can be deployed by name on testnet or mainnet.', + 'When a contract is ready, publish its Wasm to the Stellar Registry and deploy it by name: to testnet for staging, then to mainnet. These deploys are deliberate steps, never a side effect of saving a file.', description: 'Description of the publish step', }), }, ]; return ( -
+
- The loop + The workflow

- - Four commands cover the whole lifecycle. Everything in between — - building contracts, deploying to a local network, generating - TypeScript clients — happens on its own. + + Iterate locally as fast as you can save, then deploy to testnet and + mainnet when you mean to. Building contracts, running the local + network, and generating TypeScript clients happen on their own.

-
    +
      {steps.map((step) => ( -
    1. - {step.command} -

      {step.detail}

      +
    2. + {step.label} + {step.command} +

      {step.detail}

    3. ))}
    +

    + + While you develop, one committed scaffold.yml declares your networks, + accounts, and contracts, so everyone on your team builds the same + project, and the same file carries it from local to testnet to + mainnet. Config checks catch mistakes before a build runs, and when + something does break, Doctor checks your toolchain, config, and + local network in one pass and tells you how to fix what it finds. + +

    + + See every command{' '} + → +
); } @@ -193,6 +298,8 @@ export default function Home(): ReactNode {
+ +
@@ -222,7 +329,7 @@ export default function Home(): ReactNode {
- +
); diff --git a/skills/stellar-scaffold/SKILL.md b/skills/stellar-scaffold/SKILL.md index b3ef4367..44c01d73 100644 --- a/skills/stellar-scaffold/SKILL.md +++ b/skills/stellar-scaffold/SKILL.md @@ -1,24 +1,26 @@ --- name: stellar-scaffold -description: Build full-stack Stellar dApps with Stellar Scaffold, the `stellar scaffold` CLI plugin. Use when creating a new Soroban project with a frontend (`stellar scaffold init`), adding a frontend to an existing Soroban workspace (`upgrade`), configuring networks, accounts, and contracts in `scaffold.yml`, running the local dev loop (`npm run dev` / `stellar scaffold watch`), calling contracts from a React or Svelte app through the generated clients in `@stellar-scaffold/app-lib/clients`, adding example contracts (`generate contract`), building for testnet or mainnet, or debugging a Scaffold build with `config check` and `doctor`. +description: Build full-stack Stellar dApps with Stellar Scaffold, the `stellar scaffold` CLI plugin. Use when creating a new Soroban project with a frontend (`stellar scaffold init`), adding a frontend to an existing Soroban workspace (`upgrade`), configuring networks, accounts, and contracts in `scaffold.yml`, running the local dev loop (`npm run dev` / `stellar scaffold watch`), calling contracts from a React or Svelte app through the generated clients in `@stellar-scaffold/app-lib/clients`, adding example contracts (`generate contract`), pointing an app at contracts already deployed on testnet or mainnet, or debugging a Scaffold build with `config check` and `doctor`. --- # Stellar Scaffold Stellar Scaffold turns a Rust/Soroban contract workspace and a TypeScript frontend into one project: -- **`stellar scaffold` CLI plugin**: creates projects, builds and deploys contracts, and generates a typed TypeScript **client** for each one. -- **`scaffold.yml`**: one config file at the project root that says which networks you build for, which accounts exist on each, and which contracts to deploy or connect to on each network. +- **`stellar scaffold` CLI plugin**: creates projects, builds contracts, resolves each network's contract dependencies, and generates a typed TypeScript **client** for each one. +- **`scaffold.yml`**: one config file at the project root that declares your app's **contract dependencies, per network**: which contracts the app uses on each network and where to get each one (a crate in this workspace, or an existing deployment), plus the networks and accounts involved. - **`@stellar-scaffold/app-lib`**: a workspace package in every project with wallet, network, and formatting helpers, plus the generated clients at `@stellar-scaffold/app-lib/clients`. +**Scaffold is not a deploy tool.** It deploys only as a convenience on throwaway networks (`local`, by default), so the dev loop has something to call. On shared networks (testnet, futurenet, mainnet), contracts get deployed out of band, normally with Stellar Registry, and `scaffold.yml` just references those deployments. On every network, `build` fetches the contract that is actually there and regenerates its client, so the frontend is typechecked against that network's real interface. Contracts can differ subtly between networks, and a fresh client per network is how you find out. Don't reach for `allow-deploy: true` to "ship" with Scaffold. + Docs: https://stellarscaffold.org/docs ## Setup -Prerequisites: Rust (with the `wasm32v1-none` target), the Stellar CLI (`stellar`), Node.js, and Docker (for the local network). +Prerequisites: Rust (with the `wasm32v1-none` target) and a C toolchain, the Stellar CLI (`stellar`), Node.js, and Docker (for the local network). On Linux, install the Stellar CLI with `curl -fsSL https://github.com/stellar/stellar-cli/raw/main/install.sh | sh -s -- --install-deps` to also get the system libraries Scaffold needs. ```bash -cargo install --locked stellar-scaffold-cli # or: cargo binstall stellar-scaffold-cli +cargo binstall stellar-scaffold-cli # pre-built; or: cargo install --locked stellar-scaffold-cli stellar scaffold version ``` @@ -34,7 +36,7 @@ npm run dev # app at http://localhost:51 - `--template` takes `react`, `svelte`, `none` (contracts only, no JS at all), or a community repo as `org/repo[#branch]`. Omit it to be prompted. `--no-template` is the same as `--template none`. - `-p ` picks the package manager. With `--template` or `-y` it defaults to npm without prompting. -- `init` does everything up front: copies `.env.example` to `.env`, installs dependencies, compiles the contracts, deploys them to the local network and generates their clients, then `git init`s and commits. If that build fails (most often because Docker isn't running), `init` only warns and still creates the project. Fix the cause, then run `npm run dev`. +- `init` does everything up front: copies `app/.env.example` to `app/.env`, installs dependencies, compiles the contracts, deploys them to the local network and generates their clients, then `git init`s and commits. If that build fails (most often because Docker isn't running), `init` only warns and still creates the project. Fix the cause, then run `npm run dev`. Already have a Soroban workspace (`Cargo.toml` plus `contracts/`)? Run `stellar scaffold upgrade --template react` in it instead. It adds the frontend files and never overwrites files that already exist. **Then fix the config by hand**, because `upgrade` currently copies the template's `scaffold.yml`, whose `contracts:` lists the template's example crates rather than yours, and also writes a legacy `environments.toml` that version 2 ignores: @@ -48,10 +50,10 @@ Already have a Soroban workspace (`Cargo.toml` plus `contracts/`)? Run `stellar my-app/ ├── contracts// # Soroban contracts; the workspace builds every cdylib in contracts/* ├── app/ # your React or Svelte app +│ └── .env # optional STELLAR_NETWORK, secrets for ${env.…} (gitignored) ├── app-lib/ # @stellar-scaffold/app-lib: wallet, network, format helpers │ └── clients/ # GENERATED: one package per contract + index.ts. Never edit. ├── scaffold.yml # networks, accounts, contracts -├── .env # optional STELLAR_NETWORK, secrets for ${env.…} (gitignored) ├── .scaffold/ # GENERATED: what this checkout deployed (state.json). Ignores itself. ├── Cargo.toml # Rust workspace └── package.json # npm workspaces root @@ -64,8 +66,8 @@ my-app/ `npm run dev` runs two processes: `stellar scaffold watch` and Vite. On every change to a contract or to `scaffold.yml`, `watch`: 1. Builds the contracts (Wasm in `target/stellar//.wasm`, with `-` replaced by `_`). -2. For the selected network: starts the local container if needed, creates and funds the accounts, deploys or upgrades contracts whose Wasm changed, and runs their `after-deploy` calls. -3. Regenerates `app-lib/clients//` and `app-lib/clients/index.ts`, which Vite hot-reloads. +2. Resolves each contract dependency for the selected network (default: `local`). On `local`, that means starting the container if needed, creating and funding the accounts, deploying or upgrading `workspace` contracts whose Wasm changed, and running their `after-deploy` calls. On a network with deploys disabled, nothing is deployed: `type: contract` entries are fetched from the address given. +3. Regenerates `app-lib/clients//` and `app-lib/clients/index.ts` (gitignored), which Vite hot-reloads. Changing only constructor `args` or `after-deploy` does **not** redeploy. Run `stellar scaffold clean` and restart to get fresh deployments. @@ -77,26 +79,27 @@ version: 2 networks: local: accounts: [me] # created and funded; the first one signs deploys - testnet: # staging: bind to deployments you made deliberately, never deploy from the dev loop + testnet: # shared: Scaffold never deploys here; reference deployments made out of band accounts: [testnet-user] contracts: guess-the-number: # client name: exported as `guessTheNumber` - type: workspace # build + deploy this crate from contracts/ + type: workspace # built from contracts/; deployed only on networks that allow it (local) source: guess-the-number # the crate's package name - args: # __constructor arguments, by parameter name + args: # __constructor arguments, by parameter name (used only where Scaffold deploys) admin: ${account.me} - after-deploy: [reset] # no-argument methods called after deploy - networks: # the contract ONLY exists on networks listed here + after-deploy: [reset] # no-argument methods called after a Scaffold deploy + networks: # networks where the app depends on this contract; no client elsewhere local: testnet: - args: - admin: ${account.testnet-user} # accounts belong to one network + type: contract # already deployed (e.g. via Stellar Registry): fetch it + source: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC ``` Rules that trip agents up: -- **A contract only exists on the networks it lists.** Building any other network skips it, and its client is not exported, so imports of it fail to typecheck. +- **The app only depends on a contract on the networks it lists.** Building any other network skips it, and its client is not exported, so imports of it fail to typecheck. +- **`workspace` on a shared network is an error** (`deploy-not-allowed`). That's deliberate: override the network with `type: contract` pointing at a deployment you made out of band. - **`build` supports only `type: workspace` and `type: contract`** (a deployed `C…` address) today. `registry`, `asset`, `wasm-file`, `wasm-hash`, `wasm-registry`, `from-network` and `after-deploy-script` pass validation but fail at build time. - **Keys are kebab-case.** Unknown keys are errors, with a "did you mean" hint. - **`args` values are typed YAML, not shell.** Use `${account.}` for addresses, `${env.VAR}` for secrets. `$(…)` is literal text. @@ -155,9 +158,16 @@ The network comes from `--network `, else `STELLAR_NETWORK` (from the shel STELLAR_NETWORK=testnet npm run dev ``` -Local is for thrashing: deploy and redeploy freely. Testnet is staging, a dry run for mainnet. +Local is for thrashing: Scaffold deploys and redeploys there freely. Testnet is staging, a dry run for mainnet, and on both of those Scaffold only **consumes** deployments. It doesn't make them. Going to a shared network: + +1. **Set the network**: `STELLAR_NETWORK=testnet` in `app/.env`. +2. **Check each contract's `Cargo.toml` metadata** (`authors`, `homepage`, `repository`, version; see `[package.metadata.stellar]` in [references/config.md](references/config.md)). It is written into the Wasm and is permanent once published. +3. **Compile for that network**: `stellar scaffold build --compile-only` (no deploys, no clients). The network matters: contracts that use `stellar_registry::import_contract!` for cross-contract calls emit different Wasm per network. Prefer this over `stellar contract build`, which doesn't build crates in dependency order the way `stellar scaffold build` does. If your version lacks `--compile-only`, upgrade the plugin. +4. **Publish and deploy out of band** with Stellar Registry: https://raw.githubusercontent.com/stellar-registry/cli/main/skills/stellar-registry/SKILL.md. First publishes go under `unverified/`. +5. **Reference the deployment** in `scaffold.yml`: under the contract's `networks:`, override that network with `type: contract` and `source: ` (`stellar registry fetch-contract-id ` prints it). `type: registry` with a name is planned but not yet supported by `build`. +6. **Build for that network to typecheck and build the frontend**: `stellar scaffold build --network testnet` deploys nothing. It fetches each referenced contract and regenerates its client, so that `npm run build --workspace app` typechecks and builds the frontend against what is actually deployed (output in `app/dist/` for React, `app/build/` for Svelte). Type errors here usually mean the deployed contract differs from your local one. -`testnet`, `futurenet` and `mainnet` refuse Scaffold deploys until the network sets `allow-deploy: true`. For production, deploy once yourself and point Scaffold at the result instead of deploying from the dev loop: +Mainnet works the same way, and also needs an RPC provider: ```yaml networks: @@ -175,7 +185,7 @@ contracts: source: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC ``` -To publish and deploy by name, use the Stellar Registry: https://raw.githubusercontent.com/stellar-registry/cli/main/skills/stellar-registry/SKILL.md (then `stellar registry fetch-contract-id ` gives the `C…` address for `source`). Build the frontend with `npm run build --workspace app` (output in `app/dist/` for React, `app/build/` for Svelte). +`testnet`, `futurenet` and `mainnet` refuse Scaffold deploys unless the network sets `allow-deploy: true`. Don't turn that on to ship. If you truly need disposable deploys on testnet (say, a CI smoke test), add a separate network (`testnet-dev: { extends: testnet, allow-deploy: true }`) so `testnet` itself stays reference-only. ## Diagnosing problems @@ -191,8 +201,9 @@ Full command list: [references/cli.md](references/cli.md). - **Edited `app-lib/clients/`**: it is regenerated on every build. Wrap a client in your own module under `app/` instead. - **`Module has no exported member 'myContract'`**: the contract has no entry for the network you built (`networks:` under the contract), the build failed before codegen, or the client name doesn't match. Client `my-contract` exports as `myContract`. -- **Built for an unexpected network**: the first line of build output names the network and where it came from. Check `STELLAR_NETWORK` in your shell and `.env`. -- **`would be deployed to testnet, which has deploys disabled`**: point the contract at an existing deployment with `type: contract`. To redeploy freely on testnet, add a separate network (e.g. `testnet-dev: { extends: testnet, allow-deploy: true }`) rather than opting in `testnet` itself. +- **Built for an unexpected network**: the first line of build output names the network and where it came from. Check `STELLAR_NETWORK` in your shell and `app/.env`. +- **`would be deployed to testnet, which has deploys disabled`**: working as intended. Deploy out of band (Stellar Registry), then point the contract at that deployment with `type: contract`. Don't opt `testnet` into `allow-deploy`. +- **App works locally, breaks on testnet**: the deployed contract differs from your local build. Rerun `stellar scaffold build --network testnet` and fix the type errors it surfaces in the frontend. - **`account is not listed in network 's accounts`**: each network has its own accounts. Add it to `networks..accounts`, or override `args` for that network. - **`uses type: registry, which build does not support yet`**: see the supported types above. Use `type: contract` with the address for now. - **Changed constructor `args`, nothing happened**: only Wasm changes redeploy. Stop the dev server, `stellar scaffold clean`, restart. diff --git a/skills/stellar-scaffold/SKILL.pdf b/skills/stellar-scaffold/SKILL.pdf new file mode 100644 index 00000000..02aa9e58 Binary files /dev/null and b/skills/stellar-scaffold/SKILL.pdf differ diff --git a/skills/stellar-scaffold/references/cli.md b/skills/stellar-scaffold/references/cli.md index b7a4214f..6df4a736 100644 --- a/skills/stellar-scaffold/references/cli.md +++ b/skills/stellar-scaffold/references/cli.md @@ -24,11 +24,18 @@ Network selection, wherever a command takes `--network`: the flag, else `STELLAR | Command | Purpose | Key arguments | |---|---|---| -| `build` | Compile every `cdylib` crate in dependency order | `--network `, `--package `, `--manifest-path `, `--profile ` (default `release`), `--out-dir `, `--features`, `--all-features`, `--no-default-features`, `--locked`, `--optimize[=false]`, `--meta `, `--list/--ls`, `--print-commands-only` | -| `watch` | `build`, then rebuild on any change to a contract or `scaffold.yml` | Same as `build`. The project's `npm run dev` runs `stellar scaffold watch` | +| `build` | Compile every `cdylib` crate in dependency order, resolve each contract dependency for the network, and generate clients | `--network `, `--compile-only`, `--package `, `--manifest-path `, `--profile ` (default `release`), `--out-dir `, `--features`, `--all-features`, `--no-default-features`, `--locked`, `--optimize[=false]`, `--meta `, `--list/--ls`, `--print-commands-only` | +| `watch` | `build`, then rebuild on any change to a contract or `scaffold.yml` (redeploying only on networks that allow it, normally `local`) | Same as `build`. The project's `npm run dev` runs `stellar scaffold watch` | | `clean` | Remove Scaffold-generated artifacts | `--manifest-path ` | -After compiling, for the selected network `build` also: starts the local container (when the network's passphrase is the standalone one, or `start-container: true`), creates and funds the network's `accounts`, deploys or upgrades `workspace` contracts whose Wasm changed, runs their `after-deploy` methods, and regenerates `//` and `/index.ts`. If one contract fails, the rest still deploy and export, then the build exits non-zero. To only compile, use `stellar contract build`. `--build-clients` is only needed by projects still on `environments.toml`. +`build` is the default way to compile, even when you only want Wasm: it builds crates in dependency order, which `stellar contract build` does not. The selected network also matters at compile time, because contracts that use `stellar_registry::import_contract!` compile to different Wasm per network. + +After compiling, `build` resolves each contract dependency in `scaffold.yml` for the selected network, then regenerates `//` and `/index.ts`: + +- `type: contract` entries are fetched from their address. Nothing is deployed. +- `type: workspace` entries are deployed only where the network allows it (`local` by default). There, `build` starts the local container (when the network's passphrase is the standalone one, or `start-container: true`), creates and funds the network's `accounts`, deploys or upgrades contracts whose Wasm changed, and runs their `after-deploy` methods. On testnet, futurenet and mainnet a `workspace` entry is a `deploy-not-allowed` error by design: deploy out of band (Stellar Registry) and reference it with `type: contract`. + +If one contract fails, the rest still resolve and export, then the build exits non-zero. `--compile-only` stops after compiling: no accounts, deploys, or clients. Use it to produce the Wasm you publish with `stellar registry`. `--build-clients` is only needed by projects still on `environments.toml`. `build` and `watch` also accept a positional `[ENV]` (`development`, `testing`, `staging`, `production`, or `STELLAR_SCAFFOLD_ENV`). It only applies to legacy projects with `environments.toml`, and their `--help` text still describes that legacy flow. @@ -47,7 +54,7 @@ After compiling, for the selected network `build` also: starts the local contain `config show` applies `extends`, fills in built-in RPC URLs and passphrases, merges per-network contract overrides, and substitutes `${env.…}` and `${network.…}`. `${account.…}` stays symbolic. Contracts that don't list the network are omitted. -`doctor` checks `rustc` (against `rust-toolchain.toml`), the `wasm32v1-none` target, the `stellar` CLI version, Node and the package manager named by `project.package-manager` (or a legacy `package.json` `packageManager` pin), `scaffold.yml` (listing every `config check` problem), the `contracts-dir` and `clients-dir` directories, the project's `engines.stellar-scaffold` constraint, and `.env`. Its Docker, local RPC, and extension checks currently run only for legacy `environments.toml` projects. Exit code is `1` on any error, or on warnings with `--strict`. +`doctor` checks `rustc` (against `rust-toolchain.toml`), the `wasm32v1-none` target, the `stellar` CLI version, Node and the package manager named by `project.package-manager` (or a legacy `package.json` `packageManager` pin), `scaffold.yml` (listing every `config check` problem), the `contracts-dir` and `clients-dir` directories, the project's `engines.stellar-scaffold` constraint, and `.env`. When any network runs in a local container, it also checks the Docker daemon and each local network's RPC and friendbot. Extension checks currently run only for legacy `environments.toml` projects. Exit code is `1` on any error, or on warnings with `--strict`. ## Examples @@ -58,7 +65,10 @@ stellar scaffold init my-app --template react -p pnpm -y # Contracts-only project stellar scaffold init my-contracts --template none -# Build clients for testnet (bound to existing deployments), without the dev server +# Compile testnet Wasm to publish with stellar registry (no deploys, no clients) +stellar scaffold build --network testnet --compile-only + +# Fetch testnet deployments and regenerate their clients; deploys nothing stellar scaffold build --network testnet # Validate in CI diff --git a/skills/stellar-scaffold/references/config.md b/skills/stellar-scaffold/references/config.md index be214acc..11202c5e 100644 --- a/skills/stellar-scaffold/references/config.md +++ b/skills/stellar-scaffold/references/config.md @@ -5,7 +5,7 @@ A Scaffold project has three config surfaces: | File | Holds | Committed | |---|---|---| | `scaffold.yml` | Networks, accounts, contracts, extensions, project directories | Yes | -| `.env` | Optional `STELLAR_NETWORK`, secrets referenced by `${env.…}` | No (copy from `.env.example`) | +| `app/.env` | Optional `STELLAR_NETWORK`, secrets referenced by `${env.…}` | No (`init` copies it from `app/.env.example`) | | `Cargo.toml` | Rust workspace (`members = ["contracts/*"]`), per-crate `[package.metadata.stellar]` contract metadata | Yes | ## `scaffold.yml` (schema version 2) @@ -150,6 +150,8 @@ Map of extension name to its config, run in order on every network. The value is ## `.env` +New projects keep it in `app/`, because `npm run dev` runs `stellar scaffold watch` from there and Vite reads it from there too. + ```bash # STELLAR_NETWORK=testnet # network the CLI builds for # MAINNET_RPC_TOKEN= # secrets referenced as ${env.NAME}