Show navigationHide navigation

OctoCMS CLI

Command-line tools for managing an OctoCMS project. The CLI lives at octocms/cli/ and is the bin entry for the octocms/cms npm package.

Running the CLI#

During development (before npm publish):

bash
# Via npm script (recommended)
npm run octocms <command> [options]

# Examples
npm run octocms init
npm run octocms validate
npm run octocms types:gen
npm run octocms embeddings:gen
npm run octocms dev
npm run octocms update
npm run octocms agent-docs

After npm publish (future):

bash
npx octocms <command> [options]

Commands#

octocms init#

Initialize OctoCMS in an existing Next.js project. Creates the schema file, admin route files, demo content, and updates next.config.ts and tsconfig.json.

bash
npm run octocms init
npm run octocms init -- --yes    # Accept all defaults (non-interactive)
npm run octocms init -- -y       # Shorthand

init only scaffolds files — it doesn't touch your package.json. The "Next steps" output prints the exact npm install octocms@<version> <required peer deps> command you need to run before npm run dev. Copy-paste it (swap npm install for your package manager's add command if you use pnpm / yarn / bun).

Interactive prompts:

PromptDefaultDescription
Project nameMy CMSDisplay name for your CMS
Git base branchmainDefault branch for PRs and content
Use pointer branch?NoSeparate branch for cms/pointers/ pointer files
Pointer branch namecms/publish-pointerOnly if pointer branch is enabled

Files created:

FilePurpose
cms/octocms.config.tsCMS schema with a demo post collection
src/app/cms/layout.tsxAdmin layout (re-exports AdminLayout + metadata from octocms/admin)
src/app/cms/[[...path]]/page.tsxAdmin catch-all (re-exports AdminApp from octocms/admin)
src/app/cms/error.tsxAdmin error boundary (re-exports AdminError from octocms/admin)
app/api/auth/[...nextauth]/route.tsNextAuth handler for the GitHub App login flow
app/api/agent/proposals/accept/route.tsThin re-export of acceptProposalRoute from octocms/agent (chat-agent Phase 4)
app/api/agent/proposals/reject/route.tsThin re-export of rejectProposalRoute from octocms/agent (chat-agent Phase 4)
cms/content/post/post-001.jsonDemo post entry
cms/content/post/post-001.body.mdDemo post markdown body
cms/__generated__/Empty directory for generated types
public/media/Empty directory for uploaded images

Files modified:

FileChange
next.config.tsCreated as thin withOctoCMS() wrapper
tsconfig.jsonAdded octocms/* path aliases

Example output:

  OctoCMS  Initialize a new project

  ? Project name (My CMS): My Blog
  ? Git base branch (main): main
  ? Use a separate published pointer branch? (y/N): n

  Creating files...
   cms/octocms.config.ts
   app/cms/layout.tsx
   app/cms/[[...path]]/page.tsx
   app/cms/error.tsx
   cms/content/post/post-001.json
   cms/content/post/post-001.body.md

  Updating configuration...
   cms/octocms.config.ts  OctoCMS schema
   next.config.ts  Next.js wrapper
   tsconfig.json  added octocms/* path aliases

  Next steps:
    1. Add GitHub App credentials to .env.local (see README.md)
    2. Run: npm run octocms types:gen
    3. Run: npm run dev
    4. Visit: http://localhost:3000/cms

Preconditions:

  • cms/octocms.config.ts must not already exist (run octocms update instead)
  • package.json must exist in the current directory

octocms dev#

Start the Next.js development server and watch cms/octocms.config.ts for changes. When the config file is modified, types are automatically regenerated.

bash
npm run octocms dev
npm run octocms dev -- --port 4000    # Custom port

Options:

FlagDefaultDescription
--port <n>3000Port for the dev server

What it does:

  1. Spawns next dev -p <port> as a child process with inherited stdio
  2. Watches cms/octocms.config.ts with fs.watch (300ms debounce)
  3. On config change, runs scripts/generate-types.ts via jiti
  4. Forwards SIGINT/SIGTERM to the child process

Example output:

  OctoCMS  Development server

  Starting Next.js dev server on port 3000...
  Watching cms/octocms.config.ts for changes...

   Next.js 16.2.2
  - Local: http://localhost:3000
  ...

   Config changed  regenerating types...
   Types regenerated

Preconditions:

  • cms/octocms.config.ts must exist
  • node_modules/.bin/next must be available

octocms types:gen#

Generate TypeScript types from cms/octocms.config.ts. Produces the files in cms/__generated__/ that power type-safe queries and autocomplete.

bash
npm run octocms types:gen

Generated files:

FileContents
cms/__generated__/types.ts*Fields and *Entry interfaces, AnyEntry union, EntryMap
cms/__generated__/enums.tsCollectionName const, COLLECTION_NAMES array, select option enums, FieldFormat const
cms/__generated__/content.d.tsRaw*Fields and Raw*Entry interfaces (on-disk types before query() processing)
cms/__generated__/index.tsBarrel re-export of types.ts and enums.ts

Example output:

  OctoCMS  Generate types

  Validating config...
   6 collections validated

  Generating types...
   cms/__generated__/types.ts
   cms/__generated__/enums.ts
   cms/__generated__/content.d.ts
   cms/__generated__/index.ts

Validation checks (before generating):

  • Every collection in COLLECTIONS exists in config.collections
  • Each collection has at least one field
  • Field names are valid TypeScript identifiers
  • Slug fields have a valid slugSource or exactly one entryTitle field
  • Select options have unique values and valid defaults
  • Reference fields point to existing collections
  • Conditional branches have unique keys and valid nested fields

Exit code: 1 if config validation fails, 0 on success.


octocms embeddings:gen#

Generate cms/__generated__/embeddings.json, the retrieval index used by the chat agent only — public site search, the admin CommandK palette, and the MediaManager search input do not use embeddings (see the Search-vs-Embeddings table in docs/chat-agent.md). Walks every content entry, embeds new or changed entries via @huggingface/transformers (Xenova/bge-small-en-v1.5, 384 dims), and writes the merged store. Re-running on unchanged content is a fast no-op (sha256 hash skip per entry).

bash
npx octocms embeddings:gen

Preconditions:

  • @huggingface/transformers installed (optional peer dep). The CLI surfaces a clear error if it's missing.

Output file:

FileContents
cms/__generated__/embeddings.json{ model, dim, entries: { [path]: { hash, vec } } } — base64-encoded Float32Array per entry, sorted keys for stable diffs

Example output:

  OctoCMS  Generate embeddings

  Validating config...
   6 collections validated

  Discovering entries...
   10 entries

  Loading existing store (if any)...
   10 existing vectors

  Embedding entries (model loads on first run; ~3–10s cold)...
   1 / 10
   5 / 10
   10 / 10
   cms/__generated__/embeddings.json (10 vectors, dim=384)

When to run:

  • Once after enabling the chat agent for the first time.
  • After a bulk content import outside the CMS UI.
  • In CI as a drift check (embeddings:check — runs octocms embeddings:gen && git diff --exit-code -- cms/__generated__/embeddings.json).

Day-to-day edits in the CMS update the store automatically as part of the same Git commit, so most users only run this manually after enabling the feature.

See docs/chat-agent.md for the full pipeline overview.


octocms validate#

Validate all content entries (cms/content/**/*.json) against the CMS schema. Checks structure, field types, required fields, select values, reference targets, and companion files.

bash
npm run octocms validate

What it checks:

CheckDescription
JSON validityFiles must be valid JSON
sys structuresys.id and sys.type must be present
sys.type matchMust match the collection directory name
fields structurefields must be an object
Required fieldsNon-empty when required: true in schema
String fieldsMust be strings (or string arrays for list: true)
Boolean fieldsMust be "true" or "false"
Number fieldsMust be numbers or null; respects min/max
Datetime fieldsMust be valid ISO 8601 strings or null
Select fieldsValues must match configured options
Reference fieldsKeys must point to existing entry files
Companion filesRequired .md/.mdx files must exist for markdown/richtext

Example output (all valid):

  OctoCMS  Validate content

  Validating 6 collections...

   post  2 entries
   item  3 entries
   homePage  1 entry
   blog  1 entry
   author  2 entries
   role  2 entries

   All 11 entries valid.

Example output (with errors):

  OctoCMS  Validate content

  Validating 6 collections...

   post  2 entries, 1 with errors
   item  3 entries
  ...

   post/post-456.json
     title: Required field "Title" is empty
     publishedAt: Invalid datetime format

   2 errors found.

Exit code: 1 if any errors found, 0 if all entries valid.


octocms update#

Regenerate admin route files in src/app/cms/. Useful after upgrading octocms to a new version that may have changed the admin route structure.

bash
npm run octocms update

Files checked/updated:

FileExpected content
src/app/cms/layout.tsxRe-exports AdminLayout + metadata from octocms/admin
src/app/cms/[[...path]]/page.tsxRe-exports AdminApp from octocms/admin
src/app/cms/error.tsxRe-exports AdminError from octocms/admin (client component)
app/api/agent/proposals/accept/route.ts (or src/app/...)Thin re-export of acceptProposalRoute from octocms/agent. Created if missing, never overwritten if you've customised it.
app/api/agent/proposals/reject/route.ts (or src/app/...)Thin re-export of rejectProposalRoute from octocms/agent. Same skip-if-customised behaviour.

Auto-migration from the legacy deep-import catch-all. When [[...path]]/page.tsx matches export { AdminApp as default } from 'octocms/admin/AdminApp' byte-for-byte, it's rewritten to import from the octocms/admin barrel. Same for layout.tsx if it uses the deep import. Customised files are preserved.

The migration is idempotent — re-running on an already-migrated tree is a no-op.

Example output (after deep-import → barrel migration):

  OctoCMS  Update admin routes

  Using src/app/ as the Next.js root.
   src/app/cms/layout.tsx  updated (legacy  barrel import)
   src/app/cms/[[...path]]/page.tsx  updated (legacy  barrel import)
   src/app/cms/error.tsx  created
  Admin routes have been updated.

octocms agent-docs#

Inject AI agent documentation links into AGENTS.md. Creates the file if it doesn't exist, or appends the OctoCMS section if missing.

bash
npm run octocms agent-docs

What it does:

  1. Looks for AGENTS.md in the project root
  2. If missing, creates it with a default template including the OctoCMS agent docs section
  3. If present but missing the OctoCMS section (identified by <!-- octocms:agent-docs --> marker), appends it
  4. If the section already exists, reports it and makes no changes

Section injected:

The command adds a section pointing AI agents to the auto-generated docs:

  • octocms/docs/overview.md — content management instructions (CRUD, file paths, companion files, URL mapping)
  • octocms/docs/schema.md — per-collection field definitions and example JSON entries

Example output (file created):

  OctoCMS  Agent documentation

   AGENTS.md  created with OctoCMS agent docs section

  AI agents can now read octocms/docs/ for content management instructions.
  Regenerate the docs after schema changes: npm run agent-docs:gen

Example output (section appended):

  OctoCMS  Agent documentation

   AGENTS.md  appended OctoCMS agent docs section

  AI agents can now read octocms/docs/ for content management instructions.
  Regenerate the docs after schema changes: npm run agent-docs:gen

Example output (already present):

  OctoCMS  Agent documentation

   AGENTS.md  OctoCMS section already present

  AI agents can now read octocms/docs/ for content management instructions.
  Regenerate the docs after schema changes: npm run agent-docs:gen

Global Options#

FlagDescription
--help, -hShow help (global or per-command)
--version, -vShow version number
bash
npm run octocms -- --help
npm run octocms -- --version
npm run octocms init -- --help

Architecture#

File structure#

octocms/cli/
  index.ts                      # CLI entry point  argv parser + command dispatcher
  index.test.ts                 # Argument parser tests
  commands/
    init.ts                     # octocms init
    init.test.ts
    dev.ts                      # octocms dev
    dev.test.ts
    typesGen.ts                 # octocms types:gen
    typesGen.test.ts
    validate.ts                 # octocms validate
    validate.test.ts
    update.ts                   # octocms update
    update.test.ts
    agentDocs.ts                # octocms agent-docs
    agentDocs.test.ts
  lib/
    logger.ts                   # ANSI-colored CLI output helpers
    project.ts                  # resolveProjectRoot(), loadProjectConfig()
    project.test.ts
    codegen.ts                  # Type generation functions (shared with scripts/)
    codegen.test.ts
    validateConfig.ts           # Schema config validation (shared with scripts/)
    validateConfig.test.ts
    contentValidator.ts         # Content entry validation for `octocms validate`
    contentValidator.test.ts
    templates.ts                # File templates for init/update commands
    templates.test.ts

Shared code#

The codegen and config validation functions are shared between the CLI and the existing npm scripts:

  • octocms/cli/lib/codegen.ts — type generator functions (generateTypes, generateEnums, etc.)
  • octocms/cli/lib/validateConfig.ts — schema config validation (validateConfig)

Both scripts/generate-types.ts and scripts/generate-docs.ts import from these modules. This ensures the CLI and scripts always use the same logic.

Config loading#

The CLI resolves the project root by walking up from process.cwd() until it finds a next.config.ts containing withOctoCMS. For commands that need the config object (like types:gen and validate), it uses jiti to dynamically load cms/octocms.config.ts with path alias resolution.

npm package integration#

The octocms/package.json declares:

json
{
  "bin": { "octocms": "./dist/cli/index.js" }
}

When octocms/cms is published and installed via npm, users will be able to run npx octocms <command> directly.