| name | caffeine-app |
| description | Scaffold and build a complete Caffeine app (caffeine.ai) from scratch — the project layout, the caffeine.toml workspace + canister manifests, the mops.toml for the single Motoko `backend` canister, the React/Vite frontend, and the caffeine CLI build loop (auth login, doctor, install, check --fix, preview --build). Use whenever the user wants to create, scaffold, set up, or build a Caffeine app or a caffeine.ai project, asks about caffeine.toml or mops.toml structure, or needs the caffeine CLI workflow — even if they do not say the words 'from scratch'. Always pair it with the `writing-motoko` skill for the backend code. Do NOT use it for Motoko language syntax or compiler errors (use the `writing-motoko` skill), and do NOT use it for generic Internet Computer / dfx / icp deployment (use the `icp-cli` skill). |
| license | Apache-2.0 |
| compatibility | @caffeineai/cli (the `caffeine` command), Node.js >= 18.17, pnpm, network access + a caffeine.ai account |
| metadata | {"title":"Caffeine App (build from scratch)","category":"Integration"} |
Caffeine App (build from scratch)
Caffeine (caffeine.ai) builds and hosts full-stack apps on the Internet Computer: a
single Motoko backend canister plus a React/Vite frontend served as an assets
canister. This skill teaches the exact project shape and the CLI build loop so
you can produce a buildable Caffeine app from nothing.
This skill owns the project shape and the build workflow. It does NOT own the Motoko
language. Before you write or edit any backend code, fetch and follow the writing-motoko
skill: https://skills.internetcomputer.org/skills/writing-motoko/SKILL.md. That skill is
authoritative for actor syntax, stable state, mo:core, and compiler errors. This
skill only shows the minimal empty actor and where it lives.
When to use this skill
- Creating/scaffolding a new Caffeine app or caffeine.ai project from scratch.
- Writing or fixing
caffeine.toml (workspace or canister manifest) or mops.toml.
- Running the caffeine CLI loop:
auth login, doctor, install, check, preview.
Do not use it for Motoko language details (use writing-motoko) or for raw IC/dfx/icp
deployment (use icp-cli). Caffeine apps are built and deployed through the caffeine
CLI and the caffeine.ai web app, not through dfx.
Critical facts
- There is no
caffeine init / caffeine new. "From scratch" means you create the
files in this skill by hand, then run the build loop. (caffeine projects clone <id>
only downloads an existing cloud project — it is not a greenfield scaffold.)
- Exactly one backend canister, and it must be named
backend. mops.toml declares
a single [canisters.backend].
- The frontend↔backend binding files are generated, never hand-written.
src/frontend/src/backend.ts and src/frontend/src/declarations/ are produced by
caffeine-bindgen during the build. Editing them is pointless — they are overwritten.
- Deploying to a live URL is done in the caffeine.ai web app, not the CLI. The CLI
takes you as far as a built draft (
caffeine preview --build, which needs a cloud
project.id — see the build loop). There is no caffeine deploy/publish command.
Prerequisites (run once)
npm install -g @caffeineai/cli
caffeine auth login
caffeine auth status
caffeine doctor --fix
If a global install is undesirable or fails, run every caffeine command through an
npx -y @caffeineai/cli@latest prefix instead — e.g. npx -y @caffeineai/cli@latest auth status.
caffeine doctor --fix checks Node.js, pnpm, mops, and caffeine-bindgen, confirms you
are logged in, and verifies the Caffeine API is reachable — installing or repairing what
it can. It does not install the Motoko compiler (moc) or linter (lintoko): mops
fetches those automatically from the [toolchain] pins in mops.toml on the first
mops install / build. dfx is not part of the Caffeine build.
Project shape
A Caffeine app is a pnpm workspace with a root manifest and two canisters. Create this
exact tree (file contents below):
my-app/
├── caffeine.toml # root workspace manifest — MUST contain [workspace]
├── mops.toml # Motoko build config — ONE per project, at the root
├── package.json # pnpm workspace root + the `bindgen` script
├── pnpm-workspace.yaml # declares src/**/* as workspace packages
├── tsconfig.json # root TS config
└── src/
├── backend/ # the single Motoko canister, named "backend"
│ ├── caffeine.toml # canister manifest: type = "motoko"
│ ├── main.mo # actor entry point (write with the `writing-motoko` skill)
│ └── system-idl/ # Candid for IC system canisters (see note below)
│ └── aaaaa-aa.did # IC management canister interface
└── frontend/ # the assets canister (React + Vite)
├── caffeine.toml # canister manifest: type = "assets"
├── package.json # frontend deps + scripts (see references/frontend-template.md)
├── vite.config.js # (see references/frontend-template.md)
├── tsconfig.json # (see references/frontend-template.md)
├── biome.json # (see references/frontend-template.md)
├── tailwind.config.js # (see references/frontend-template.md)
├── postcss.config.js # (see references/frontend-template.md)
├── index.html
├── env.json # runtime config — copied into dist/ on build
└── src/
├── main.tsx # React entry (providers)
├── App.tsx
├── index.css
├── backend.ts # GENERATED by caffeine-bindgen — do NOT edit
└── declarations/ # GENERATED by caffeine-bindgen — do NOT edit
Only the root manifest, the two canister manifests, mops.toml, main.mo, and
the small root config files are shown inline below. The full frontend boilerplate
(package.json, vite.config.js, the Tailwind/PostCSS/Biome configs, index.html,
env.json, main.tsx, a starter App.tsx) is in
references/frontend-template.md — read it when you
create the frontend.
Manifests
Root caffeine.toml — the workspace
The root manifest must contain a [workspace] section. That section is what marks
it as the workspace root; without it, the CLI treats this file as an ordinary canister
manifest and discovery fails.
manifest_version = "0.1.0"
[project]
name = "My App"
[workspace]
include = [ "src/**" ]
[canisters.frontend]
depends_on = [ "backend" ]
[workspace].include is a list of globs the CLI scans for nested canister manifests.
src/** finds src/backend/caffeine.toml and src/frontend/caffeine.toml.
src/backend/caffeine.toml — the Motoko canister
manifest_version = "0.1.0"
[project]
name = "backend"
type = "motoko"
main = "main.mo"
[build]
commands = ["mops build", "pnpm bindgen"]
out = "dist"
[check]
commands = ["mops check"]
[check.fix]
commands = ["mops check --fix"]
[build].commands run in order: mops build compiles main.mo to Wasm + Candid, then
pnpm bindgen (defined in the root package.json) turns that Candid into the frontend's
TypeScript client. caffeine check --fix runs [check.fix].commands.
src/frontend/caffeine.toml — the assets canister
manifest_version = "0.1.0"
[project]
name = "frontend"
type = "assets"
[build]
commands = ["pnpm build"]
out = "dist"
[check]
commands = ["pnpm typecheck", "pnpm check"]
[check.fix]
commands = ["pnpm typecheck", "pnpm fix"]
mops.toml — the Motoko build (one per project, at the root)
There is exactly one [canisters.backend]. The structural pieces are invariant —
the single [canisters.backend], [canisters.backend.check-stable], and the [moc]
flags --default-persistent-actors, --actor-idl=src/backend/system-idl, and
--implicit-package=core. The toolchain versions, the -E/-W/-A diagnostic codes,
and the dependency list are a current snapshot that the Caffeine template bumps over
time — see "Versions and dependencies drift" below.
[package]
name = "backend"
version = "0.1.0"
[build]
outputDir = "src/backend/dist"
args = ["--release"]
[canisters.backend]
main = "src/backend/main.mo"
[canisters.backend.check-stable]
path = ".old/src/backend/dist/backend.most"
skipIfMissing = true
[toolchain]
moc = "1.8.1"
lintoko = "0.10.0"
[lint]
extends = true
[moc]
args = [
"--default-persistent-actors",
"--actor-idl=src/backend/system-idl",
"--implicit-package=core",
"-no-check-ir",
"-E=M0236,M0223,M0237,M0254",
"-W=M0235",
"-A=M0241",
"--generate-view-queries",
]
[dependencies]
core = "2.5.0"
caffeineai-data-viewer = "0.1.0"
Notes:
check-stable points at .old/src/backend/dist/backend.most. The CLI saves the
previous build's .most (stable-signature snapshot) under .old/ so the next build
can verify your stable state did not change incompatibly. Do not delete .old/ between
builds; do not commit it as source — it is build state. skipIfMissing = true is
required for a from-scratch project: on the first run that snapshot does not exist
yet, and without it caffeine check fails with
Deployed file not found: .old/src/backend/dist/backend.most.
--actor-idl=src/backend/system-idl tells moc where to find the Candid interfaces
of IC system canisters. src/backend/system-idl/ must exist; the canonical template
ships aaaaa-aa.did (the IC management-canister interface). A minimal backend needs only
the directory.
core = "2.5.0" is the mo:core standard library. Use mo:core, never the
deprecated mo:base — see the writing-motoko skill. caffeineai-data-viewer backs the
built-in data-viewer mixin used by the default main.mo (below) and pairs with the
--generate-view-queries flag.
Versions and dependencies drift. The [toolchain] versions, the [moc]
-E/-W/-A codes, and the frontend dependency set are template-managed and change
over time (e.g. the @dfinity/* → @icp-sdk/* migration, moc/core bumps). Treat the
exact values here as a snapshot — for current ones, caffeine projects clone a recent
project and copy its mops.toml + src/frontend/package.json. Stable across versions:
the file layout, the single backend canister, the structural [moc] flags,
check-stable with skipIfMissing, mo:core, and useActor(createActor).
package.json (root) — pnpm workspace + the bindgen script
The bindgen script is the bridge from backend Candid to the frontend TS client. It is
invoked by the backend canister's [build] step (pnpm bindgen).
{
"name": "@caffeine/template-app",
"type": "module",
"engines": {
"node": ">=18.17.0",
"pnpm": ">=7.0.0",
"npm": "please use pnpm"
},
"scripts": {
"build": "pnpm -r --if-present run build",
"typecheck": "pnpm -r --if-present run typecheck",
"check": "pnpm -r --if-present run check",
"fix": "pnpm -r --if-present run fix",
"bindgen": "caffeine-bindgen --did-file ./src/backend/dist/backend.did --out-dir ./src/frontend/src --actor-interface-file --force"
},
"devDependencies": {
"sharp": "^0.34.4"
}
}
pnpm-workspace.yaml
packages:
- src/**/*
onlyBuiltDependencies:
- esbuild
tsconfig.json (root)
{
"compilerOptions": {
"strict": true,
"target": "ES2020",
"experimentalDecorators": true,
"strictPropertyInitialization": false,
"moduleResolution": "node",
"allowJs": true,
"outDir": "HACK_BECAUSE_OF_ALLOW_JS"
}
}
src/backend/main.mo — the starting backend
The canonical template's starting main.mo includes the built-in data-viewer mixin
(include MixinViews()), which pairs with the caffeineai-data-viewer dependency and the
--generate-view-queries flag in mops.toml. With --default-persistent-actors the
actor is already persistent. Add your own functions inside the actor, following the
writing-motoko skill.
import MixinViews "mo:caffeineai-data-viewer/MixinViews";
actor {
include MixinViews();
};
(A bare actor {} also compiles if you drop the caffeineai-data-viewer dependency and
the --generate-view-queries flag.) Do not write more Motoko than this without loading
the writing-motoko skill — it covers persistent actors, stable types, mo:core, and the
compiler-error pitfalls that an agent will otherwise hallucinate.
The frontend
Fetch this skill's companion reference file —
references/frontend-template.md, which sits next to
this SKILL.md (i.e. …/skills/caffeine-app/references/frontend-template.md) — and create
the frontend files from it; it holds the verified package.json, the Vite/Tailwind/Biome
configs, main.tsx, and the worked useActor(createActor) backend call. The essentials:
- The entry point
src/frontend/src/main.tsx wraps the app in
InternetIdentityProvider (from @caffeineai/core-infrastructure) and a TanStack
QueryClientProvider.
- The frontend talks to the backend through the generated module
src/frontend/src/backend.ts (and src/frontend/src/declarations/), which exports a