Skip to main content

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

OptionDescription
--config=<path>Use a specific config file
--root=<path>Run against a different project root
--helpShow 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.tsLocal backend by default; --repo switches it to GitHub
copy-ink.setup.tsThe one setupCopyInk() call
middleware.tsOnly when there is no middleware at all
app/api/copy-ink/[...copyInk]/route.tsHandler, with the right relative import back to the setup module
app/admin/page.tsxThe sign-in page
content/_index.ymlA 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-ink to include, in place, so comments and key order survive.
  • package.json — adds predev and prebuild hooks running copy-ink types, for the dev and build scripts 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.

OptionDescription
--repo=<owner/repo>Write a GitHub backend rather than the local one
--locale=<code>defaultLocale for the generated config (default en)
--dry-runPrint the plan, write nothing
--forceOverwrite 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:

CodeSeverityMeaning
ambiguous-content-fileerrorA scope resolves to more than one file
not-camel-caseerrorA key is not camelCase
dot-in-keyerrorA key contains ., the path separator
meta-conflicterrormeta used as content while autoMetadata is on
missing-keyerrorJSX references a field no file defines
unknown-collectionerrorA component names a collection not in the config
unknown-collection-fielderrorA field is not part of that collection's schema
locale-collisionerrorA route segment matches a declared locale (root mode)
dead keyswarningA 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/:

FileContents
manifest.jsonEvery <Copy> usage, its resolved scope, field path and source location, and each collection's item route
copy-ink-env.d.tsField, scope, collection and locale unions
image-hosts.jsonThe 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:

CodeSeverityMeaning
content-missingerrorcontentDir does not exist
static-exporterroroutput: 'export' with the GitHub backend
middleware-missingerrorNo middleware file
middleware-not-wirederrorMiddleware exists but does not use copy-ink's
setup-missingerrorNothing calls setupCopyInk()
handler-missingerrorNothing mounts createCopyInkHandler()
handler-not-wirederrorThe handler is mounted without its runtime
github-credentialserrorGitHub backend with no credentials
oauth-credentialserrorNo COPY_INK_GITHUB_CLIENT_ID / _SECRET
next-config-missingwarningNo next.config found
remote-patternswarningnext.config does not import image-hosts.json
scripts-missingwarningNothing renders <CopyInkScripts />
pat-backendwarningA personal access token is committing
session-secretwarningNo COPY_INK_SESSION_SECRET, so sessions live in one process
tsconfig-includewarningtsconfig.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

CodeMeaning
0No errors (warnings may have been printed)
1Errors found, an unknown command, or a config that would not load