An existing project needs a package.json, a build that writes to its own folder (dist, www), and an index.html there with a <head> tag, where the native side injects its bridge; without one, no plugin call reaches native code.
Here the app is a stock npm 2,036 create vite@latest booknest -- --template vanilla project whose src/main.js lists three books and reports where it runs:
import { Capacitor } from '@capacitor/core';
// ...
<p class="where">Running on: <b>${Capacitor.getPlatform()}</b>
(native: ${Capacitor.isNativePlatform()})</p>
<p class="where">Origin: <code>${location.origin}</code></p>@capacitor/core is a runtime dependency, since the bundle imports it; @capacitor/cli is a development dependency. Install both, build, and run cap init with the app name, app ID and build folder:
npm i @capacitor/core
npm i -D @capacitor/cli
npm run build
npx cap init BookNest com.example.booknest --web-dir dist... ✓ built in 165ms ✔ Creating capacitor.config.json in /home/dev/ch04-02/booknest in 3.90ms [success] capacitor.config.json created!
The CLI wrote JSON, not the .ts file the documentation shows. It writes TypeScript only for a project new to Capacitor 243,123 and only if the typescript package resolves from the project folder, which a vanilla template lacks. To switch, run npm i -D typescript@6, delete the JSON file (init refuses to overwrite a .ts config), and run the same init again; it then reports Creating capacitor.config.ts. The @6 pin matters, because TypeScript 7 breaks the CLI's .ts loader (Environment Config). Build before cap add (cap add): it stops with "Could not find the web assets directory" until webDir holds an index.html.