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):
bashnpx 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.
bashnpm 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:
| Prompt | Default | Description |
|---|---|---|
| Project name | My CMS | Display name for your CMS |
| Git base branch | main | Default branch for PRs and content |
| Use pointer branch? | No | Separate branch for cms/pointers/ pointer files |
| Pointer branch name | cms/publish-pointer | Only if pointer branch is enabled |
Files created:
| File | Purpose |
|---|---|
cms/octocms.config.ts | CMS schema with a demo post collection |
src/app/cms/layout.tsx | Admin layout (re-exports AdminLayout + metadata from octocms/admin) |
src/app/cms/[[...path]]/page.tsx | Admin catch-all (re-exports AdminApp from octocms/admin) |
src/app/cms/error.tsx | Admin error boundary (re-exports AdminError from octocms/admin) |
app/api/auth/[...nextauth]/route.ts | NextAuth handler for the GitHub App login flow |
app/api/agent/proposals/accept/route.ts | Thin re-export of acceptProposalRoute from octocms/agent (chat-agent Phase 4) |
app/api/agent/proposals/reject/route.ts | Thin re-export of rejectProposalRoute from octocms/agent (chat-agent Phase 4) |
cms/content/post/post-001.json | Demo post entry |
cms/content/post/post-001.body.md | Demo post markdown body |
cms/__generated__/ | Empty directory for generated types |
public/media/ | Empty directory for uploaded images |
Files modified:
| File | Change |
|---|---|
next.config.ts | Created as thin withOctoCMS() wrapper |
tsconfig.json | Added 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.tsmust not already exist (runoctocms updateinstead)package.jsonmust 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.
bashnpm run octocms dev npm run octocms dev -- --port 4000 # Custom port
Options:
| Flag | Default | Description |
|---|---|---|
--port <n> | 3000 | Port for the dev server |
What it does:
- Spawns
next dev -p <port>as a child process with inherited stdio - Watches
cms/octocms.config.tswithfs.watch(300ms debounce) - On config change, runs
scripts/generate-types.tsvia jiti - 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.tsmust existnode_modules/.bin/nextmust 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.
bashnpm run octocms types:gen
Generated files:
| File | Contents |
|---|---|
cms/__generated__/types.ts | *Fields and *Entry interfaces, AnyEntry union, EntryMap |
cms/__generated__/enums.ts | CollectionName const, COLLECTION_NAMES array, select option enums, FieldFormat const |
cms/__generated__/content.d.ts | Raw*Fields and Raw*Entry interfaces (on-disk types before query() processing) |
cms/__generated__/index.ts | Barrel 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
COLLECTIONSexists inconfig.collections - Each collection has at least one field
- Field names are valid TypeScript identifiers
- Slug fields have a valid
slugSourceor exactly oneentryTitlefield - 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).
bashnpx octocms embeddings:gen
Preconditions:
@huggingface/transformersinstalled (optional peer dep). The CLI surfaces a clear error if it's missing.
Output file:
| File | Contents |
|---|---|
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— runsoctocms 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.
bashnpm run octocms validate
What it checks:
| Check | Description |
|---|---|
| JSON validity | Files must be valid JSON |
sys structure | sys.id and sys.type must be present |
sys.type match | Must match the collection directory name |
fields structure | fields must be an object |
| Required fields | Non-empty when required: true in schema |
| String fields | Must be strings (or string arrays for list: true) |
| Boolean fields | Must be "true" or "false" |
| Number fields | Must be numbers or null; respects min/max |
| Datetime fields | Must be valid ISO 8601 strings or null |
| Select fields | Values must match configured options |
| Reference fields | Keys must point to existing entry files |
| Companion files | Required .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.
bashnpm run octocms update
Files checked/updated:
| File | Expected content |
|---|---|
src/app/cms/layout.tsx | Re-exports AdminLayout + metadata from octocms/admin |
src/app/cms/[[...path]]/page.tsx | Re-exports AdminApp from octocms/admin |
src/app/cms/error.tsx | Re-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.
bashnpm run octocms agent-docs
What it does:
- Looks for
AGENTS.mdin the project root - If missing, creates it with a default template including the OctoCMS agent docs section
- If present but missing the OctoCMS section (identified by
<!-- octocms:agent-docs -->marker), appends it - 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#
| Flag | Description |
|---|---|
--help, -h | Show help (global or per-command) |
--version, -v | Show version number |
bashnpm 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.