Interactive setup wizard. Default command when you run pharn with no subcommand.
pharn init
# equivalent
pharninit detects your project's archetype(s) and installs the PHARN capabilities that apply to them. It fetches nothing you did not ask for: only the capabilities matching your project, plus the fixed product surfaces (commands, hooks, docs, contracts, floor), are copied. There is no module catalog and no manifest.json fetch — capabilities are the install unit.
The
--archetypeflag is a deprecated no-op kept for one release: archetype detection is now the default, sopharn init --archetypebehaves identically topharn init.
init always asks which capabilities to install, and — only when any of its write targets already
exist — whether to overwrite them, so it needs a terminal. Off a TTY (CI, a pipe, a script) it exits 1
with a usage error instead of prompting into a stream nobody is reading:
$ echo "" | pharn init
▲ pharn init is interactive — run it in an interactive terminal. There is deliberately no --yes
for init: it confirms before overwriting existing files, and auto-confirming that in a pipeline
is what the prompt exists to prevent.
$ echo $?
1The refusal happens before the clone, so a non-interactive init costs no network round-trip and
leaves your project untouched.
There is deliberately no --yes for init — unlike pharn update, which has one. The
second of init's prompts is the destructive overwrite confirmation, and auto-confirming file overwrites
in a pipeline is precisely the hazard that prompt exists to prevent.
A directory with no .git still gets its own, more useful error first (see Prerequisites below) —
the interactivity check never masks it.
sequenceDiagram
participant User
participant CLI as pharn_init
participant Prereqs
participant Detect as detect_archetype
participant Fetch as fetch_pharn_oss
participant Resolve as resolve_capabilities
participant Summary
participant Conflict as write_target_check
participant Install
User->>CLI: pharn init
CLI->>Prereqs: git (.git present)
Prereqs-->>CLI: ok or exit
CLI->>Detect: package.json + file-tree signals
Detect-->>User: "Detected archetypes" note
CLI->>Fetch: clone pharn-dev/pharn-oss (degit)
CLI->>Resolve: capability index vs detected archetypes
CLI->>Summary: capabilities selected + skipped (with reason)
Summary-->>User: install / cancel
CLI->>Conflict: which write targets already exist?
Conflict-->>User: overwrite warning (only if any) · default No
CLI->>Install: copy capabilities + product surfaces, write config
Install-->>User: next steps
- Archetype — a closed set describing what your project is:
ssr,backend,spa, orlib(the frameworkless base). Detection merges two untrusted-but-name-only fact sources — yourpackage.jsondependency names and file-tree structural signals (e.g. a.tsxfile →spa) — then applies the archetype rule once. It is deterministic: the same project always yields the same archetypes. A wholly signal-less project resolves tolib. - Capability — one griller or lens (an auditor PHARN ships). Each declares
applies: 'universal'(always selected) or a set of archetypes. A capability is selected iff it is universal or itsappliesset intersects your detected archetypes; otherwise it is skipped, with the reason shown.
Shows the PHARN logo and CLI version.
.gitpresent — checked up front, before anything else (universal, framework-agnostic). Hard-fails if absent.
Reads package.json dependency names and walks the project tree (bounded and symlink-safe, skipping dependencies, VCS metadata, and build/deploy caches — node_modules, .git, dist, build, out, coverage, storybook-static, .next, .nuxt, .svelte-kit, .astro, .turbo, .vercel, .cache, .parcel-cache) for structural signals, then reduces both to an Archetype[]. Skipping those trees costs the walk nothing, so a large framework cache cannot exhaust its bound and hide your real source; the tradeoff is that a signal file you hand-authored inside one of those directories is not seen. The detected set is shown in a "Detected archetypes" note. Only names are tested against fixed in-code allowlists — no discovered file body is read (other than package.json) and no untrusted value is executed, interpolated, or logged.
Clones pharn-dev/pharn-oss into a temp dir via degit. If the fetch fails, the CLI exits — re-run with PHARN_DEBUG=1 for details. The temp clone is always cleaned up (even on cancel or error).
Parses the capability index from the fetched clone and selects the capabilities that apply to your archetypes (universal + archetype-matched), in the index's declared order. Skipped capabilities are kept with a reason (e.g. applies to [backend]; detected [ssr]).
Lists the selected capabilities (name, role, and why — universal or the matched archetype) and the skipped ones (with reason). Then:
| Action | Result |
|---|---|
| Yes, install | Copy the capabilities + product surfaces and write config |
| Cancel | Exit 0; nothing written |
After you choose install, init checks which of its actual write targets (the selected capability dirs, product pharn-* commands, .cjs hooks, pharn-contracts/, the floor checkers, the constitution, and pharn.config.json) already exist in your project. If any do, it lists them (capped at 10, then "…and N more") and asks you to confirm before overwriting — default no. If none do, there is no prompt (zero friction). .claude/settings.json is never overwritten, so it is excluded from the check. The target set is derived from the fetched clone's layout + your resolved selection (lib/install-manifest.ts), so it is exact — not a git-history heuristic.
| Action | Behavior |
|---|---|
| Copy capabilities | Each selected griller/lens dir (with its evals/) → the mirrored project path |
| Copy product surfaces | pharn-*.md commands (not pharn-dev-*), .cjs hooks, the trusted docs, pharn-contracts/, and .dev/floor/ (minus test files) |
| Preserve settings | An existing .claude/settings.json is never overwritten (a note tells you to wire the hooks by hand if needed) |
| Mirror the layout | Whichever layout the fetched clone uses — flat, or the relocated pharn/ — is mirrored verbatim; the CLI never rewrites copied file contents |
| Pin commit SHA | Best-effort (the SHA the tree was pinned to; null if unavailable) |
Write pharn.config.json |
skillsVersion (from the repo's SKILLS_VERSION), commit, archetypes, capabilities, layout, models, seam, modules: [] |
Write pharn.records.json |
A sha256 of every file the install wrote, so pharn update can keep your later edits (reference) |
The install copies pharn-oss's canonical CONSTITUTION.md verbatim — there is no privacy-posture / constitution-variant question in the archetype flow. Only capability contents are copied; the CLI never executes or parses them (your Claude Code runs them later).
On success, the CLI reports the capability count and suggests opening Claude Code and running /pharn-spec — intent capture for your first feature, which feeds /pharn-plan.
init always writes an archetype config, and every command is archetype-only. A pre-archetype module-based pharn.config.json (one with modules[] but no capabilities[], from a much older release) is no longer supported: add, remove, list, update, and status detect it up front and exit with a message to re-run pharn init — there is no module/manifest fallback (live pharn-oss ships no manifest.json). The config schema is additive, so a legacy config's now-unused fields (modules, constitution, stackAnswers, installedSkills) still parse; only the absence of capabilities[] triggers the rejection.