create-package CLI
@tinycld/create-package is the scaffolder for new sibling packages. One command produces a repo that already passes the app shell’s generator checks, CI, and typecheck. It can run interactively (prompts for everything) or fully non-interactively (every prompt has a matching flag).
Usage
npx @tinycld/create-package <slug>
The positional argument is the package slug — kebab-case, 3–40 characters. It becomes:
- The npm name:
@tinycld/<slug> - The URL segment:
/a/[orgSlug]/<slug>/ - The Go module path:
tinycld.org/packages/<slug>
Omit the positional to be prompted for it.
Flags
Every prompt has a matching flag. Pass --yes to take defaults for everything else.
| Flag | Type | Description |
|---|---|---|
<slug> | positional | Package slug. Required when --yes is set; otherwise prompted. |
--yes, -y | boolean | Skip all prompts and use defaults. Requires the positional slug. |
--name | string | Human-readable name. Defaults to title-cased slug. |
--description | string | One-sentence description. |
--preset | full | settings-only | Defaults to full under --yes. |
--icon | string | Lucide icon name. Full preset only. Default box. |
--nav-order | number | Integer 0–99. Full preset only. Default 20. |
--shortcut | string | Single lowercase letter, or empty. Full preset only. |
--server, --no-server | boolean | Include the Go server stub. Full preset only. Default true. |
--target | string | Output directory. Default depends on the cwd: if a tinycld/ app shell exists as a child, defaults to ./<slug>/; otherwise to ./tinycld-<slug>/<slug>/ (bootstrap mode — see below). |
--link, --no-link | boolean | Link the new package into the app shell. Without either, you’ll be prompted; under --yes, defaults to linking. |
--no-link always wins over --yes — pass both when you want to scaffold without touching the app shell.
Non-interactive example
npx @tinycld/create-package my-feature \
--yes \
--preset full \
--icon check-square \
--no-server \
--no-link
This scaffolds with all defaults, no Go server, and no app-shell linking. Suitable for CI, scripted setups, and autonomous coding agents.
Workspace detection
When --target is not set, the scaffolder picks one of two layouts based on the current directory:
- Attach — if
<cwd>/tinycld/package.jsondeclares"name": "tinycld", the new package goes to<cwd>/<slug>/. The existing app shell handles linking. - Bootstrap — otherwise, the scaffolder creates
<cwd>/tinycld-<slug>/, places the package at<cwd>/tinycld-<slug>/<slug>/, and (with--link) clones the app shell to<cwd>/tinycld-<slug>/tinycld/.
This means npx @tinycld/create-package my-todo from an empty directory produces a self-contained ./tinycld-my-todo/ workspace with everything you need to run the app, while running it from inside an existing ~/code/tinycld/ workspace just adds another sibling package next to your existing tinycld checkout.
The detection check is strict: it reads tinycld/package.json and matches name === "tinycld". A coincidentally-named directory won’t false-match.
Prompts (interactive mode)
| Prompt | Example | Notes |
|---|---|---|
| Package slug | my-feature | Skipped if passed as argv. Validates kebab-case; minimum 3 chars. |
| Human-readable name | My Feature | Defaults to title-cased slug. Used in manifest name and nav label. |
| One-sentence description | Does a thing well. | Used in manifest description, package.json, and README.md. |
| Preset | full or settings-only | See Presets. |
| Lucide icon | box | Full preset only. Any lucide-react-native name. Default box. |
| Nav order | 20 | Full preset only. Integer 0–99; controls sidebar position. |
| Keyboard shortcut | f | Full preset only. Single lowercase letter, or blank. |
| Include Go server? | y / n | Full preset only. If n, server/ and the manifest’s server field are omitted. |
| Target directory | ../my-feature | Defaults alongside tinycld/. Must not exist or must be empty. |
| Link into app shell? | y / n | If yes, the scaffolder clones tinycld/tinycld (shallow) next to your new package and runs packages:link. |
Presets
full — data package
The shape of @tinycld/contacts, @tinycld/mail, @tinycld/calendar, @tinycld/drive. You get routes, a sidebar, an optional provider, pbtsdb collections, PocketBase migrations, seed data, and (optionally) a Go server stub.
settings-only — service package
The shape of @tinycld/google-takeout-import. The package contributes a single Personal Settings panel — no routes, no nav entry, no collections, no server. Use this for integrations and admin-style tools.
Generated files
Both presets produce:
manifest.ts,package.json,tsconfig.json— lint config is not duplicated;tinycld/biome.jsoncovers every linked packageREADME.md,.gitignore.github/workflows/ci.yml— clones the app shell, links the package, runs lint (from the app shell) + typecheck + unit + e2etests/manifest.test.ts— smoke test asserting the manifest shape
The full preset additionally produces:
pb-migrations/<timestamp>_create_<slug>.jsserver/go.mod,server/register.go(if the Go server prompt is yes)tinycld/<slug>/{collections,provider,seed,sidebar,types}.tstinycld/<slug>/screens/{_layout,[id],index}.tsx
The settings-only preset additionally produces:
tinycld/<slug>/types.tstinycld/<slug>/settings/main.tsx
After scaffolding
The CLI prints the steps it didn’t run — it never touches git or gh. Typical next steps:
cd my-feature
git init
git add .
git commit -m 'chore: initial scaffold'
gh repo create tinycld/my-feature --public --source=. --push
If you didn’t pass --link (or said no to the prompt), also link the package now:
cd ../tinycld
npm run packages:link ../my-feature
npm run checks
Linking the new package into the app shell is enough to see it in navigation; the generator wires routes, collections, migrations, and settings panels automatically.
Import conventions in scaffolded code
Templates use the scoped @tinycld/core path:
import { useOrgLiveQuery } from '@tinycld/core/lib/use-org-live-query'
import { Modal } from '@tinycld/core/ui/modal'
@tinycld/core isn’t a separate npm package or git repo — it’s bundled inside the app shell at tinycld/packages/@tinycld/core/. Resolution works because the sibling compiles in the app shell’s tsconfig context once linked.
Intra-package imports use relative paths; ~/tinycld/<slug>/* is also aliased to the package’s own nested source if you want an absolute form.
For the published source and template internals, see the create-package repo.