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 updateThe 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.tsIt 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-runActual 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
| Flag | Effect |
|---|---|
--from <version> | Override the recorded current version |
--to <version> | Skip the prompt and go straight to a version |
--dry-run | Report and diff without writing |
--force | Run with a dirty tree, and overwrite locally modified files |
-y, --yes | Apply without confirming |
--repo <url> | Point at a fork |
--ssh | Clone 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 thekreogen.versionstamp is updated, field by field. README.md,NOTICE,.gitlab-ci.yml,CLAUDE.md,AGENTS.md— every fileinitreplaced with your own copy. Restoring them would put kreogen's README and kreogen's pipeline back.- Template-only content —
apps/docs,packages/cli,.gitlab,ciand.claudenever 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 verifyRead 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-depsUpgrades 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-uiRe-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.