CLI
npx copy-ink <command> [options]
copy-ink init scaffold copy-ink into an existing Next.js app
copy-ink check lint naming, reserved words, file forms, missing and dead keys
copy-ink i18n compare locales key by key
copy-ink types regenerate the manifest and TypeScript types
copy-ink prune find (and with --delete, remove) orphaned images
copy-ink migrate --locale-folders: move root content into content/<defaultLocale>/
copy-ink doctor check config, credentials and Next wiring
These are developer-facing only. None of them is exposed in the editor UI.
Global options
| Option | Description |
|---|---|
--config=<path> | Use a specific config file |
--root=<path> | Run against a different project root |
--help | Show help |
Output is colourised when the terminal supports it and NO_COLOR is unset.
init
npx copy-ink init
npx copy-ink init --dry-run
npx copy-ink init --repo=you/your-site
Does the quick start for you, in an
existing App Router project. There is no separate starter template: run
create-next-app for a new site, then init inside it.
It writes what is missing:
| File | |
|---|---|
copy-ink.config.ts | Local backend by default; --repo switches it to GitHub |
copy-ink.setup.ts | The one setupCopyInk() call |
middleware.ts | Only when there is no middleware at all |
app/api/copy-ink/[...copyInk]/route.ts | Handler, with the right relative import back to the setup module |
app/admin/page.tsx | The sign-in page |
content/_index.yml | A first field to click |
And edits what you already own:
- The root layout — adds the setup import and renders
<CopyInkScripts />before</body>. tsconfig.json— adds.copy-inktoinclude, in place, so comments and key order survive.package.json— addspredevandprebuildhooks runningcopy-ink types, for thedevandbuildscripts that exist..gitignore— ignores.copy-ink/, if you have one.
src/app is detected and followed; a project with no tsconfig.json gets the
JavaScript forms instead.
| Option | Description |
|---|---|
--repo=<owner/repo> | Write a GitHub backend rather than the local one |
--locale=<code> | defaultLocale for the generated config (default en) |
--dry-run | Print the plan, write nothing |
--force | Overwrite the generated files it would otherwise keep |
Nothing is overwritten without --force, and re-running it is safe. It refuses
outright — exit 1, nothing written — on a directory with no package.json, no
app/ or src/app/, or a --repo that is not owner/repo.
Two things it will not do, and warns about instead: rewrite middleware that is
not copy-ink's (compose it yourself with createCopyInkMiddleware), and edit a
root layout it cannot find a </body> in.
Finish with npx copy-ink types and npx copy-ink doctor.
check
npx copy-ink check
npx copy-ink check --no-dead-keys
One pass over the config, the content tree and the JSX manifest:
| Code | Severity | Meaning |
|---|---|---|
ambiguous-content-file | error | A scope resolves to more than one file |
not-camel-case | error | A key is not camelCase |
dot-in-key | error | A key contains ., the path separator |
meta-conflict | error | meta used as content while autoMetadata is on |
missing-key | error | JSX references a field no file defines |
unknown-collection | error | A component names a collection not in the config |
unknown-collection-field | error | A field is not part of that collection's schema |
locale-collision | error | A route segment matches a declared locale (root mode) |
| dead keys | warning | A key no JSX reads |
--no-dead-keys suppresses the last one, which is the one that fires falsely
when content is read dynamically.
Exits non-zero on errors, so it drops straight into CI. It reads the manifest
written by types, so run that first.
i18n
npx copy-ink i18n
Compares locales key by key and reports what is missing where, including
collection items whose slug exists in one locale but not another
(locale-parity, collection-slug-parity). Exits non-zero on errors.
types
npx copy-ink types
Performs an AST scan of your JSX and writes .copy-ink/:
| File | Contents |
|---|---|
manifest.json | Every <Copy> usage, its resolved scope, field path and source location, and each collection's item route |
copy-ink-env.d.ts | Field, scope, collection and locale unions |
image-hosts.json | The allowedHosts list, for next.config |
Re-run after changing content or <Copy> usage. See Generated
types.
prune
npx copy-ink prune # list
npx copy-ink prune --delete # remove
Finds images under public/content/ that no content file references. --yes
is accepted as a synonym for --delete.
Deleting an item or replacing an image leaves the old file behind; nothing else can safely decide it is unreferenced.
migrate
npx copy-ink migrate --locale-folders --dry-run
npx copy-ink migrate --locale-folders
Moves root-mode content into content/<defaultLocale>/, and moves the public
image mirror alongside. A pure file move — content is never rewritten.
--locale-folders is required; the command refuses to guess what you meant.
--dry-run prints the moves and changes nothing.
doctor
npx copy-ink doctor
Reads your config, the environment and your Next files, then reports what is wrong:
| Code | Severity | Meaning |
|---|---|---|
content-missing | error | contentDir does not exist |
static-export | error | output: 'export' with the GitHub backend |
middleware-missing | error | No middleware file |
middleware-not-wired | error | Middleware exists but does not use copy-ink's |
setup-missing | error | Nothing calls setupCopyInk() |
handler-missing | error | Nothing mounts createCopyInkHandler() |
handler-not-wired | error | The handler is mounted without its runtime |
github-credentials | error | GitHub backend with no credentials |
oauth-credentials | error | No COPY_INK_GITHUB_CLIENT_ID / _SECRET |
next-config-missing | warning | No next.config found |
remote-patterns | warning | next.config does not import image-hosts.json |
scripts-missing | warning | Nothing renders <CopyInkScripts /> |
pat-backend | warning | A personal access token is committing |
session-secret | warning | No COPY_INK_SESSION_SECRET, so sessions live in one process |
tsconfig-include | warning | tsconfig.json does not include .copy-ink |
It also prints what it found: the content directory and locale layout, the middleware and setup files, the backend in use and the allowlist.
Run it against the production environment before handing a site over — most of these are only wrong there.
Exit codes
| Code | Meaning |
|---|---|
0 | No errors (warnings may have been printed) |
1 | Errors found, an unknown command, or a config that would not load |