Skip to content
HomePagesHomePages template kit

Ship it

Pack a checked template into its submission zip — what pack puts in it, what makes it refuse, and what HomePages does with it next.

You have a section that renders and a check that passes. One command turns that into the only artifact HomePages accepts: a zip holding the template’s source, the lockfile that reproduces its build, and a record that the gate was green when it was built. Run it, read what came out, and the track is done.

Run the gate first:

Terminal window
npm run check
> check
> template-kit check
✓ starter
check passed

Then pack:

Terminal window
npm run pack -- starter
> pack
> template-kit pack starter
template-kit pack
wrote starter.zip
sha256:8dafb3f2f77a1f0b19ebcaa4e4295479757988ff82e2f82b2750d617154d1063
kit 2.0.0-dev-20260728131506

Both of those lines will read differently for you: the hash is of your source, and the version is whichever kit your workspace installed.

The -- belongs to npm, not to the command — it passes what follows through to template-kit pack instead of consuming it. Naming the template is optional; omit it and pack infers one from the directory you are standing in. Name it anyway. The submission unit is a single template, and in a workspace holding several, which zip you get should not depend on where you happened to be.

The gate runs whether or not you ask for it. pack re-runs the same check before it writes anything, so running it yourself first is a faster feedback loop rather than a separate requirement — a red check reports every problem in one pass, and a red pack reports the same list with a longer wait in front of it. Expect that wait: on a workspace this small, each of these two commands takes a minute or two.

Terminal window
unzip -l starter.zip
Archive: starter.zip
Length Date Time Name
--------- ---------- ----- ----
239 1980-01-01 00:00 pack-manifest.json
895 1980-01-01 00:00 package.json
168330 1980-01-01 00:00 package-lock.json
945 1980-01-01 00:00 templates/starter/sections/main/Renderer.tsx
1171 1980-01-01 00:00 templates/starter/sections/main/fixtures.ts
1706 1980-01-01 00:00 templates/starter/sections/main/section.ts
921 1980-01-01 00:00 templates/starter/template.ts
679 1980-01-01 00:00 templates/starter/theme.css
669 1980-01-01 00:00 templates/starter/theme.ts
--------- -------
175555 9 files

Nine files, and three things.

The manifestpack-manifest.json, written by the pack itself:

{
"kitVersion": "2.0.0-dev-20260728131506",
"template": "starter",
"contentHash": "sha256:8dafb3f2f77a1f0b19ebcaa4e4295479757988ff82e2f82b2750d617154d1063",
"packedAt": "2026-07-28T13:47:28.486Z",
"check": {
"ok": true
}
}

Those two values will read differently for you as well, for the same reasons as above — and the version especially: a …-dev-… suffix marks a development build of the kit, so on a released one you will see that release’s own version in its place.

Five fields, each a claim about this submission and nothing else: which template it is, the kit version its source was validated against, a content hash of the template tree — the same one the command printed — the moment it was packed, and a flag recording that the check passed. That last field is why packing and checking are not two independent acts: the verdict travels inside the artifact. The kit version travels with it for a different reason, and it is the one worth remembering — a submission is judged against the version it names, not against whatever is current. See Versioning.

The two root manifestspackage.json and package-lock.json, copied from the workspace root so the build reproduces with npm ci. The lockfile is what pins the versions your template is built against, which is the argument for committing it. It is also most of the payload: 168 KB of the 175 KB the archive holds.

One template’s sourcetemplates/starter/, and no other template. Its sections (all three files of each, fixtures.ts included), template.ts, theme.ts and the theme.css generated from it. Nothing else from the workspace root travels: not README.md, not tsconfig.json, not the lint config, and certainly not node_modules. Those are yours; the reserved files are the contract.

Every entry is dated 1980-01-01 00:00. That is deliberate — a zip entry carries a modification time, and real ones would make the same source pack to a different byte stream on every run. Fixing the date is what lets identical source produce an identical archive, which is what makes a content hash worth recording.

One rough edge to know about: the scaffolded .gitignore does not mention the zip. Commit with a wide net after packing and you will commit the archive too.

It writes no zip at all when:

  • the check is not green — fix the problems it reports and pack again.
  • the workspace has no lockfile — run npm install so one exists, and commit it.
  • a dependency points at a local path (file: or link:) — nothing outside the zip reaches our build, so depend on a published version instead.

Each refusal names its cause. Prepare a submission has the full mechanics, and pack its arguments and exit codes.

You send the zip to HomePages. We ingest it into our own registry, review it, and publish it; from then on the platform can build deliverables from your template, fill it from a property’s facts, and hand the result to an agent to edit — the pipeline the first page of this track described, now running on your section.

You never see our repositories, and nothing in your workspace depends on them. The zip is the entire interface between what you author and what we run, which is why it holds so little: one template’s source, the lockfile that rebuilds it, and a manifest saying what it was validated against.

  • starter.zip sits at the workspace root, named in the pack output.
  • unzip -l starter.zip lists three things and only three: pack-manifest.json, the root package.json and package-lock.json, and templates/starter/ — one template’s source, with nothing else from the workspace beside it.
  • unzip -p starter.zip pack-manifest.json prints the same sha256: hash the pack output showed, and records the check as having passed.

That is the track. You can scaffold a workspace, render a section, read the three files it is made of, add slots the platform fills two different ways, and pack the result for submission. Everything after this is more of the same loop with a wider vocabulary:

  • Guides — the task router. An image slot the editor can crop, a repeating list, an interactive island, a second template: find the task, copy the shape.
  • The pipeline — the platform’s stages at concept depth, and the doorway to the rest of the concepts tier.