Introduction

OverviewPhilosophyStructureUpdatesFAQ

Usage

Other

The standard

Updates

Pulling template changes into a project, and keeping dependencies current.

Updating from the template

kreogen is a template, not a dependency. A generated project is your code from the moment it is created, so there is no version to bump — updating means applying the diff between two template versions to files you may since have edited.

npx @kreotic/kreogen@latest update

The command reads kreogen.version from your package.json to know where you are, lists the versions available on the remote, and asks which to move to:

┌  Update from the kreogen template

◇  Found 4 versions

◆  Update to which version?
│  ● 0.1.3
│  ○ 0.1.2
│  ○ 0.1.1

It refuses to run over uncommitted work

The update rewrites and deletes files in place, and git is what makes that reviewable and revertable. A dirty working tree stops it:

Working tree has uncommitted changes. Commit them first, or pass --force.

It tells you what it will touch

Every affected path is listed, grouped by what happens to it — not a count:

added (3):
  + packages/validation/schemas.ts
  + packages/validation/action.ts
  + apps/api/app/ready/route.ts

modified (2):
  ~ packages/auth/lib/options.ts
  ~ turbo.json

renamed (1):
  > packages/auth/middleware.ts -> packages/auth/proxy.ts

It skips files you have edited

Before writing anything it hashes each affected file in your project and compares it with the blob from the version you are updating from. Anything that differs is a file you have changed since generation, and it is skipped so your work survives:

! packages/auth/lib/options.ts
2 file(s) differ from v0.1.2 in your project and will be SKIPPED, so your
changes survive. Re-run with --force to overwrite them, or merge them by hand
afterwards.

Note the comparison is against the source version, not the target. Differing from the target only means the update has something to do.

Hashing uses git hash-object rather than comparing bytes, so a CRLF working copy of an LF blob still matches. A byte comparison reports every text file as locally modified on Windows.

--dry-run prints real diffs

npx @kreotic/kreogen@latest update --dry-run

Actual git diff output for each changed file, capped at 25 files and 120 lines each, with a command to see the rest. Printing forty thousand lines of template churn scrolls the answer off the terminal, which is the same as not printing it.

Nothing is written, and the clean-tree check is skipped, so this is safe to run at any time.

Flags

FlagEffect
--from <version>Override the recorded current version
--to <version>Skip the prompt and go straight to a version
--dry-runReport and diff without writing
--forceRun with a dirty tree, and overwrite locally modified files
-y, --yesApply without confirming
--repo <url>Point at a fork
--sshClone over SSH

--yes is what makes the command usable from a script. Without it the confirmation prompt reads from a TTY and hangs indefinitely in CI rather than failing.

What it never touches

Some files are yours by definition and are excluded outright:

  • The root package.json, which carries your project's name, version and dependencies. Only the kreogen.version stamp is updated, field by field.
  • README.md, NOTICE, .gitlab-ci.yml, CLAUDE.md, AGENTS.md — every file init replaced with your own copy. Restoring them would put kreogen's README and kreogen's pipeline back.
  • Template-only contentapps/docs, packages/cli, .gitlab, ci and .claude never existed in your project.
  • bun.lock, if your project uses pnpm or npm. Restoring bun's lockfile into an npm project pins a resolution its package manager cannot read.

If you chose pnpm or npm, the command also warns which workspace manifests it just updated, since those come from the template and may reintroduce bun-specific scripts. An npm project is warned about workspace:* ranges as well; a pnpm one is not, because init deliberately leaves those in place — the protocol is pnpm's own invention, and pnpm 10 with link-workspace-packages=false treats a bare * as a registry fetch rather than a link. update does not re-run the package manager conversion; it avoids making it worse, nothing more.

The scope

Every template file update writes has your project's own npm scope re-applied to it first, and the conflict baseline is rebuilt through the same rename before it is hashed. Without that second half the comparison was against the unrenamed template blob, which could never match — so a project generated with --scope saw every file reported as locally modified and took no updates at all. Expect the first update after this change to apply real changes it had been silently skipping.

The scope is read from kreogen.scope in package.json. A project generated before that field existed falls back to the name of packages/typescript-config, then to a scan of every workspace manifest — and if those disagree, update refuses with the scopes it found rather than guessing. Add kreogen.scope and re-run.

Afterwards

bun install
bun run migrate
bun run verify

Read the diff before committing. This is a boilerplate, not a library: where the template and your changes overlap, merging is a judgement only you can make.

Updating dependencies

bun run bump-deps

Upgrades every package.json in the workspace and installs the result. On a project with CI, prefer letting Renovate do it — grouped, one MR at a time, with a pipeline behind each.

Run bun run build and bun run dev afterwards. A dependency upgrade that typechecks can still fail at runtime, and bump-deps takes every major bump in one go.

Updating shadcn/ui components

bun run bump-ui

Re-adds every shadcn/ui component in the design system with --overwrite.

This discards every customisation you have made to those files. If you need to change a component's behaviour, wrap it in your own component rather than editing the generated one.

The shadcn CLI also writes to globals.css, and it does not understand the brand ramp or the @theme inline mapping. Review that file's diff specifically — an overwritten token layer is easy to miss and breaks colours everywhere at once.