Install and update PHARN in any git project.
PHARN gets features shipped right with Claude Code. The spec, plan, and checks stay in plain files in Git, so months later you can answer "what was done and why?".
npx @pharn-dev/pharn@latest init- What is PHARN?
- Why use it?
- Quick start
- How it works
- What gets installed
- After install
- Day-to-day workflow
- CLI commands
- Agent skills
- Safety model
- Current scope
- Documentation
- Development
- Security
- License
PHARN is a repo-local methodology for making AI-assisted development easier to review, repeat, and maintain. It gives Claude Code a structured path from feature intent to implementation review:
spec -> plan -> grill -> test -> build -> regress -> verify -> ship
The npm package, @pharn-dev/pharn, is the installer. It does not scaffold your
app. It detects what kind of project you already have, installs the matching
PHARN capabilities from pharn-dev/pharn-oss,
and records the install in pharn.config.json and pharn.records.json.
AI agents can move fast, but a fast diff without durable context is hard to trust. PHARN is built around a simple idea: the reasoning behind a change should be committed beside the change.
- Preserve intent. Specs, plans, constraints, and review notes live in the repository, not only in an agent transcript.
- Make work inspectable. PHARN installs grillers and lenses: pipeline checks and review perspectives that help interrogate a plan or diff before it ships.
- Keep updates safe.
pharn updateuses per-file hashes to avoid overwriting local edits unless you explicitly choose--force. - Stay tool-shaped, not service-shaped. The CLI fetches open content, copies files into your project, and leaves the state in version-controlled markdown and JSON.
Run PHARN inside an existing git project:
npx @pharn-dev/pharn@latest initFor a new app, initialize git before installing PHARN:
npx create-next-app@latest my-app
cd my-app
git init
git add -A
git commit -m "init"
npx @pharn-dev/pharn@latest initAfter install, open Claude Code in the project and start with:
/pharn-spec
Or run the whole chain in one command:
/pharn-loop implement password reset with a one-time token
Either way the run begins at the spec, and it has to: /pharn-plan enforces a
deterministic input gate and halts unless an Approved, un-drifted SPEC.md
already exists, so there is no entry point further down the chain. The exact
command files come from the pharn-dev/pharn-oss version installed into your
repo.
Your project needs a test runner with per-test results. The test stage
writes each acceptance criterion's test before any code exists and requires it
to fail, so it needs a test script (plus test:e2e or e2e for end-to-end
criteria) whose reporter writes per-test JSON, named under testResults in
pharn.config.json. Without one, /pharn-test stops and asks, and
/pharn-loop ends with blocked: no-test-runner. A fresh app like the
create-next-app one above has no test script, so set the runner up first,
as its own increment (a SPEC with spec_kind: test-infra). Setup is in
upstream's
Per-test results.
pharn init is archetype-driven. There is no stack questionnaire and no module
catalog.
| Step | What happens |
|---|---|
| Detect | Reads package.json dependency names and performs a bounded, symlink-safe file-tree scan to classify the project as ssr, backend, spa, lib, or a combination. |
| Fetch | Resolves the tip of pharn-dev/pharn-oss main to its commit, downloads that commit's GitHub codeload tarball, and extracts it into a temp directory. --ref latest installs the newest verified release instead (a tag pharn-oss creates only after its post-merge CI passed), checking its SKILLS_VERSION matches the tag. pharn-oss has not published a release yet, so until it does --ref latest exits 1 and the default main is the channel that works (details). |
| Resolve | Selects capabilities whose applies value is universal or intersects your detected archetypes. Skipped capabilities are named with the reason. |
| Install | Copies the selected capabilities plus fixed PHARN product surfaces into your repo, mirroring the upstream layout. |
| Record | Writes pharn.config.json and pharn.records.json so future updates can tell PHARN-owned files from local edits. |
The current upstream install layout places most runtime surfaces under pharn/
and Claude Code integration under .claude/. Older flat-layout installs are
still understood through the recorded layout field.
| Artifact | Purpose |
|---|---|
pharn/pharn-pipeline/grillers/<name>/, pharn/pharn-review/<name>/ |
Pipeline grillers and review lenses selected for your project archetype(s). |
.claude/commands/, .claude/hooks/ |
The installed /pharn-* Claude Code slash commands and deterministic .cjs/.mjs hooks. |
pharn/pharn-contracts/, pharn/floor/ |
Schemas shared across PHARN stages and floor checkers invoked by the workflow. |
pharn/pharn-core/ |
Agent-readable mechanism skills, including the seam resolver. |
pharn/CONSTITUTION.md, pharn/ARCHITECTURE.md |
Trusted PHARN docs copied from upstream in the current layout. |
THREAT-MODEL.md, LIMITS.md |
Trusted PHARN docs copied at the project root in the current layout. |
pharn/LICENSE (flat: PHARN-LICENSE) |
PHARN's Apache-2.0 license copy. Your root LICENSE is never overwritten. |
pharn/features/README.md |
The feature-loop boundary contract referenced by installed commands (pharn layout). |
pharn.config.json |
Installed version, commit, layout, archetypes, capabilities, and defaults. |
pharn.records.json |
Per-file hashes for drift-safe updates. Commit this with the config. |
An existing .claude/settings.json is preserved. PHARN may create it when
absent, but it never overwrites your Claude Code settings.
As you run the workflow, PHARN writes one directory per increment —
pharn/features/<name>/ — holding that increment's SPEC.md, PLAN.md,
AC-TESTS.md, GRILL.md, BUILD.md, REGRESSION.md, VERIFY.md, then
SHIP.md + BRIEFING.md (/pharn-ship) or LOOP.md (/pharn-loop), a
RUN-REPORT.md, and the machine-readable records beside them
(AC-TESTS.lock.json, cost.json, the *-report.json files). Those are the
durable record; commit them. .pharn/ (runtime scratch), .pharn-backup/ (see
Safety model) and .pharn.lock are not — add them to your
.gitignore. PHARN never edits .gitignore for you.
Two version numbers, on purpose. The npm package @pharn-dev/pharn carries
the installer's version. The content it installs carries its own,
recorded as skillsVersion in pharn.config.json and taken from upstream's
SKILLS_VERSION. They move independently, and pharn status / pharn update
are keyed to the latter.
Three things about the installed hooks are worth knowing on day one.
Hooks enforce only once they are registered in .claude/settings.json. If
your project already had that file, PHARN preserved it instead of overwriting
it and printed a HOOKS note listing each hook it does not wire, as
paste-ready JSON — until you copy those entries across, every guarantee that
depends on a PreToolUse hook is inactive, and pharn status --strict fails.
Once wired, the default depends on your tree (pharn-oss 6.24.0+). With no
active scope, enforce-writes-scope.cjs applies that default.
In an installed project (pharn.config.json carries a non-empty
skillsVersion), you can edit ordinary application source outside an open
PHARN run. The narrow fail-closed posture — Claude Code's
Write/Edit/MultiEdit/NotebookEdit tools restricted to pharn/features/** and
.pharn/** — applies only while /pharn-ship, /pharn-loop, or
/pharn-review has an open marker (.pharn/<command>/<name>/active.json; ship
and review via pharn/floor/run-marker.mjs, loop via
require-loop-record.cjs). Outside those runs the guard denies PHARN's
installed surface (pharn/** except pharn/features/**, .claude/**,
pharn.config.json, and .pharn/writes-scope.json) and allows the rest of
your project, including app source. While a stage is running it still sets
explicit scope from the concrete paths your PLAN.md declared.
In PHARN's own dev repo or an unsignalled tree, the pre-6.24.0 posture
remains: fail-closed everywhere with no active scope. Clearing the scope
(set-writes-scope.cjs --clear, or deleting .pharn/writes-scope.json)
returns to whichever default your tree and open-run state compute; it does not
by itself re-open source in those postures, or during an open install run.
Writes issued through Bash bypass both write guards entirely.
/pharn-loop has a Stop guard too. require-loop-record.cjs is not a
write guard: while an unattended /pharn-loop run has written no LOOP.md, it
refuses to let the turn end, a bounded number of times, and it fails open. It
also does nothing until registered — under Stop, with no matcher. The
settings.json upstream ships has registered it since pharn-oss 6.12.0, but a
file PHARN preserved, or one created by an earlier install, lacks the entry:
copy it across by hand. pharn update never edits .claude/settings.json;
pharn init, pharn status and pharn update name the missing entry in a
HOOKS note.
The full set of bounds lives in upstream's
LIMITS.md and
README.
Once PHARN is installed, use the slash commands from Claude Code. The pipeline is eight typed stages, each reading what the previous one produced:
| Stage | Command | Purpose |
|---|---|---|
| Spec | /pharn-spec |
Capture feature intent and scope. Stops for your approval. |
| Plan | /pharn-plan |
Turn the approved spec into an implementation plan, mapping each acceptance criterion to a test (AC-TESTS.md). |
| Grill | /pharn-grill |
Challenge the plan before code is written. |
| Test | /pharn-test |
Write each acceptance criterion's test before the build, run it, and require it to fail. /pharn-build refuses to start without that evidence. |
| Build | /pharn-build |
Implement an approved increment. |
| Regress | /pharn-regress |
Look for regressions outside the just-built feature. |
| Verify | /pharn-verify |
Verify behavior and PHARN floor requirements, including that every acceptance criterion's test now passes. |
| Ship | /pharn-ship |
Run stages 1-7 in order, then stop at the merge/fix/abandon gate. |
/pharn-ship is itself the eighth stage: it orchestrates the seven before it in
one pass, so you rarely run them by hand, and it keeps both human gates —
approve the spec before code is written, decide merge/fix/abandon after
verification. /pharn-loop runs that same chain unattended: the model approves
the spec, iterates build -> regress -> verify until green, an iteration cap, or
a terminal failure, commits a green result to a new local branch (never pushed
or merged), and leaves the merge/fix/abandon decision to you after the run.
/pharn-spec fills PHARN's default SPEC template, or your own if you put one at
pharn.spec-template.md in the project root. The write guard denies Claude
Code's edit tools on that path, so edit it yourself; see upstream's
Your own SPEC template.
Two commands sit outside the pipeline:
| Command | Purpose |
|---|---|
/pharn-review |
Run the review lenses in parallel over any code and merge their findings deterministically. Standalone — no pipeline stage invokes it. |
/pharn-memory-promote |
Promote one lesson into memory-bank/ through a gated provenance check. |
| Command | Description |
|---|---|
pharn init |
Detect archetypes and install matching capabilities. This is also the default when no command is given. --dry-run prints what it would install and writes nothing (add --json for one machine-readable object); --yes/-y installs without prompts, but only when no file would be overwritten. |
pharn add [capability] |
Add one capability manually, for example a11y or lens:n-plus-one. With no argument, opens an interactive picker. |
pharn remove [capability] |
Remove one installed capability. With no argument, opens an interactive picker. |
pharn update |
Re-fetch and apply the latest PHARN content using drift-safe per-file decisions. --force overwrites files you changed; --yes/-y skips the confirmation prompt; --dry-run prints what it would do and writes nothing (add --json for the plan as one machine-readable object, and --compact to leave the unchanged files out of it). |
pharn list |
Show installed archetypes and capabilities. Use --json for machine-readable output. |
pharn status |
Read-only version and drift report. Use --strict to make drift fail CI, --no-drift to skip byte comparison, or --json for one machine-readable report. |
pharn uninstall |
Remove PHARN: deletes only the files pharn recorded and you did not change, then pharn.records.json and pharn.config.json (backed up first). Never touches .claude/settings.json or .pharn-backup/. --yes/-y skips the confirmation prompt. |
pharn --help |
Show every command, option and environment variable. pharn <command> --help shows one command and only the options it takes. |
pharn --version |
Show the installed CLI version. |
init also takes --archetype <list> (for example --archetype backend or
--archetype ssr,backend) to install for those archetypes instead of the
detected ones — the in-product correction for a misdetected project. The value
must be a comma-separated list of ssr, backend and spa, or lib alone,
each at most once; anything else exits 1. The choice is recorded, so update
keeps it. The install summary shows what triggered each detected archetype
(backend ← src/app/api/), or that they came from the flag.
Three behaviours matter if you script the CLI:
- Options are per-command. Passing one a command does not take prints
Unsupported option for `status`: "--force"to stderr withRun `pharn --help` for usage.and exits 1; an extra positional is refused the same way. Both previously parsed, were silently dropped, and exited 0. initandupdateprompt. Off a TTY a plain run exits 1 rather than prompting into a dead stream.update --yesis the way through in CI. Forinit,--yesskips the summary confirm but never the overwrite one: it installs only into a project where none of its files exists yet, and otherwise exits 1 — so a re-install always refuses it (useupdate). Both commands'--dry-runneeds no terminal and writes nothing.uninstalllikewise exits 1 off a TTY unless it is given--yes.- One writer at a time.
init,add,remove,updateanduninstalltake an advisory lock at.pharn.lock; a second run refuses rather than queueing.add,remove,updateanduninstallalso re-readpharn.config.jsononce they hold the lock, and refuse — writing nothing — if another run changed it after they read it (for example while a confirm prompt was open).listandstatusnever take it and are never blocked by one, sopharn status --strictstays runnable in CI while an update is in flight.
If you would rather ask than remember flags, the
pharn-dev/skills repository ships a
pharn-cli skill that operates this CLI for you. Ask your agent "Is PHARN up to
date in this project?" or "What would an update change?" and it runs the
read-only checks and a --dry-run preview first, applies the update without
overwriting your edits, and adds or removes grillers and lenses. The same
repository also carries phargus skills for setting up and diagnosing Node.js
applications with Phargus.
In Claude Code:
/plugin marketplace add pharn-dev/skills
/plugin install pharn@pharn-dev
Or with the Skills CLI: npx skills add pharn-dev/skills --skill pharn-cli.
The skills are optional; every command above works without them.
PHARN is intentionally conservative about writes:
initchecks for existing install targets and asks before overwriting them, and refuses the whole install — writing nothing — when a destination path crosses a symlinked directory.updateusespharn.records.jsonto skip files it cannot prove are untouched. The only thing it deletes is the directory of a capability it drops frompharn.config.json(PHARN runs whatever capability directories are on disk), after backing up any file in it thatpharncannot prove it wrote; it never touches.claude/settings.json. A re-runinitdoes the same, after listing those directories in its prompt.update --forcebacks up overwritten files under.pharn-backup/<timestamp>/before writing.adddoes the same for any destination file that differs from upstream, and refuses the whole install — writing nothing — when a destination path crosses a symlinked directory.statusandlistare read-only.removedeletes only the selected capability directory and prunes its records, after backing up to.pharn-backup/<timestamp>/any file in it thatpharncannot prove it wrote unchanged; it never touchesCONSTITUTION.md,memory-bank/, or your detected archetypes, and refuses when that directory's path crosses a symlink.uninstalldeletes only files recorded inpharn.records.jsonthat still match their recorded hash, insidepharn's install paths; it keeps every file you changed or added and lists the ones it kept, backs uppharn.config.jsonbefore deleting it, never writes.claude/settings.json(it prints the hooks you need to unwire instead), and refuses — deleting nothing — without a usable record store.
PHARN can also refuse to install at all: if upstream declares a minimum CLI
version newer than yours, init/add/update stop with a named error before
writing anything.
Remote content is treated as untrusted input. Capability names, copyable file names, versions, commit SHAs, and paths are validated against strict allowlists; path traversal and symlink escapes are rejected; copied file contents are not executed by the CLI.
PHARN is intentionally scoped:
- It targets Claude Code today. Codex and Cursor support are planned, not shipped.
- It requires a git-initialized project and Node >= 24.2.0, for the CLI
(
@pharn-dev/pharn,engines.node) and for the installed floor (pharn/floor/*.mjs) alike. The floor's checkers gate onimport.meta.main; on an older Node a guarded tool used to exit 0 without running its checks, so they now refuse to run there (one stderr line, exit 2).pharn initandpharn updatetherefore refuse up front on an older Node, with nothing written, andpharn statusprints the floor beside the Node you are running. CI runs on Node 24, and a smoke job starts the packed CLI on exactly 24.2.0. - Archetype detection is JS/TS-shaped. The signals are
package.jsondependency names,.tsx/.jsxfiles, and — in a Next.js app only —next.config.*,app/route handlers andapp/api/pages/api. A rootapi/, amigrations/directory and.sqlfiles in a database directory count only when the project has a rootpackage.json. A Python, Go, Rust, Ruby or PHP repo without one produces no signal, resolves tolib, and receives the universal capabilities only — a correct outcome, not a failure. One that also has a rootpackage.json(a Vite frontend, or tooling only) can resolvebackendfrom its migrations;pharn init --archetypecorrects either way. - Server and database packages count only as runtime dependencies. A
runtime database driver dependency (
pg,mongodb, an ORM) meansbackend, like a server framework; the same package only indevDependencies(a mock server, a test database) does not. A backend bundled so that its framework is only a devDependency needspharn init --archetype backend. SSR and UI frameworks count fromdevDependenciestoo. - Detection is bounded. The file-tree scan examines at most 50,000 entries,
24 directories deep (skipped directories such as
node_modulesdo not count). When it stops at a bound before its result is settled, the install summary saysdetection stopped earlyandinit --dry-run --jsonreports"detectionTruncated": true; check the archetypes and pass--archetypeif they are wrong. - It does not scaffold your application or install framework packages.
- It does not replace tests, human review, or release judgment. It gives those activities a structured record and repeatable workflow.
statusreports modified, missing, and unreadable PHARN-owned paths, and the files upstream stopped shipping that are still in your project (orphans). Nothing deletes an orphan; that is yours to do by hand.
See the roadmap for shipped versus planned work.
Start here: docs/
npm install
npm run dev -- init
npm run build
npm run test
npm run checkUseful scripts:
| Script | Purpose |
|---|---|
npm run typecheck |
Type-check source and tests. |
npm run lint |
Run ESLint with zero warnings. |
npm run lint:md |
Run markdownlint over docs and root markdown. |
npm run test:coverage |
Run the Vitest coverage gate used by CI. |
npm run build:install-local |
Build and install the local CLI into local test apps. |
Set PHARN_DEBUG=1 for verbose fetch and install errors; NO_COLOR / FORCE_COLOR
control colour (see Colour). See
CONTRIBUTING.md for contributor notes and release discipline.
The CLI has a small trust boundary: it downloads PHARN content from GitHub, validates and extracts it, then copies selected files into your project. The security-sensitive path validation, tar extraction, symlink handling, and update decision tables are covered by tests.
Please report vulnerabilities through SECURITY.md, not public issues. The detailed model is in THREAT-MODEL.md and LIMITS.md.