Source: https://www.hype-stack.dev/docs/packs-templates/build-your-own

# Build your own pack

A pack is a folder with a `manifest.json` and a `files/` directory. The manifest says where each file goes and how it
wires into the app. That's the whole format. Nothing gets compiled, nothing gets published to us, and no code of yours
runs on anyone else's machine.

Packs you build are **community packs**. They install through their own command, `hype-stack community`, so it's always
obvious when a project is pulling in code that didn't come from the Hype Stack catalog.

## Start one

```bash
npx @hype-stack/cli community init pack-analytics
```

You get a manifest, a `files/` tree for the surfaces you picked, and a README. It validates as-is, so you can install it
immediately and then fill it in.

## The loop

Write code under `files/`, declare it in the manifest, then:

```bash
npx @hype-stack/cli community validate
```

Run it as often as you like. It checks the same things the installer does, plus the ones that fail silently: a file
sitting in `files/` that no manifest entry declares never gets copied, so the pack installs "successfully" and the
feature is missing a piece.

Install it into a real project to try it:

```bash
npx @hype-stack/cli community add ../pack-analytics
```

Re-run it with `--force` after each change to overwrite what you installed last time.

## Files and where they land

Every entry in `files` has a `source` (relative to the pack) and a `target`:

```json
{
  "source": "files/frontend/src/features/analytics",
  "target": "{{frontend.features}}/analytics",
  "strategy": "create"
}
```

Targets are **path templates**, not literal paths. `{{frontend.features}}` resolves against the project's own
`stack.json`, so your pack lands correctly in a project whose directories are laid out differently from yours. The apps
you can address are `backend`, `frontend`, `admin`, `mobile`, `extension`, and `enums`.

A `source` can be a single file or a whole directory. A directory copies everything under it.

### Strategies

| Strategy         | What it does                                                              |
| ---------------- | ------------------------------------------------------------------------- |
| `create`         | Writes the file, and asks before overwriting an existing one. The default |
| `skip-if-exists` | Writes it only if nothing is there. Never overwrites, never asks          |
| `override`       | Always wins. For files the pack cannot function without                   |
| `merge`          | Splices the source into the target at a `marker` string                   |
| `merge-json`     | Deep-merges a JSON source into an existing JSON target                    |

### Slots

Tag a file with a `slot` (`frontend`, `admin`, `mobile`, `extension`) and it only installs when the project actually
ships that app. Files with no slot always install.

## From your editor

The [MCP server](/docs/cli/mcp) has two tools for this loop. `pack_authoring_guide` returns the format, the rules, and,
inside a project, its real path templates, so an agent reads the conventions instead of guessing them. `validate_pack`
runs the same checks as `community validate` on a directory and hands back the errors and warnings. Then
`add_packs { community: ["./pack-analytics"] }` installs it.

## Wiring into the app

Copying files isn't usually enough: a backend route has to get registered, a nav entry has to appear in the sidebar. You
declare those in the manifest and the CLI patches the right file at install time. The full field list is in the
[manifest reference](/docs/packs-templates/pack-manifest).

## Database models

Ship `.prisma` files, never migrations.

Prisma's multi-file schema means a pack contributes models by dropping a file into the schema directory:

```json
{
  "source": "files/backend/prisma/schema/analytics.prisma",
  "target": "{{backend.prisma}}/schema/analytics.prisma",
  "strategy": "create"
}
```

The CLI notices and offers to run `prisma migrate dev` in the project, which generates a migration against that
project's own history. A migration you ship instead would diverge the first time anyone ran `migrate dev` themselves.

That schema directory is a shared namespace: your models sit next to the starter's and every other pack's. Before
writing anything, the CLI checks whether a model, enum, type, or view you declare is already taken, and refuses with
both filenames if it is. Prefix your models if there's any chance of a clash.

## Dependencies

`dependencies` names what must be installed first. If your code imports from a starter, name the starter family, not one
engine, so your pack works with whichever starter the project has:

```json
"dependencies": ["starter-saas"]
```

A family id (`starter-saas`, `pack-billing`) means "any variant of this family": the CLI pins it to the variant already
selected or installed, and pulls the family default in only when there is none. A concrete pack name works too, for
packs that are not part of a family.

npm packages go in `dependencies_npm`, per app:

```json
"dependencies_npm": { "frontend": { "recharts": "^2.15.0" } }
```

## Publish it

There's no registry to submit to. Push the folder to a repository or publish it to npm, and people install it directly:

```bash
npx @hype-stack/cli community add github:you/pack-analytics#v1.0.0
npx @hype-stack/cli community add npm:@you/pack-analytics
```

Both are pinned the same way anything else is: a git ref or an npm version. A private repo or a private package is
already a paid pack, gated by the buyer's own git or npm credentials.

To host packs on your own URL, with your own keys and a purchase link when someone isn't licensed, see
[Run your own registry](/docs/packs-templates/run-your-own-registry). Buyers then install with `@you/pack-analytics`.

Either way, the CLI records where each pack came from in the project's `stack.json`, so anyone can run `community list`
later and see whose code is in there.

## Editor support

Point `$schema` at the published schema and your editor will complete and check the manifest as you type.
`community init` does this for you:

```json
{
  "$schema": "https://www.hype-stack.dev/schema/pack-manifest-v1.json"
}
```
