Skip to content

Latest commit

 

History

History
378 lines (300 loc) · 17.8 KB

File metadata and controls

378 lines (300 loc) · 17.8 KB
Pharn

Install PHARN into an existing project: a reviewable workflow for AI-written code.

PHARN is an open, audit-grade methodology for Claude Code. It keeps the intent, constraints, plans, checks, and review trail for AI-assisted changes in plain files inside your repo, so the reasoning behind a change survives beyond the chat window.

npm CI CodeQL License: Apache 2.0 Node

npx @pharn-dev/pharn@latest init

Contents


What is PHARN?

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.

Why use it?

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 auditors and review perspectives that help interrogate a plan or diff before it ships.
  • Keep updates safe. pharn update uses 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.

Quick start

Run PHARN inside an existing git project:

npx @pharn-dev/pharn@latest init

For 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 init

After 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.

How it works

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 pharn-dev/pharn-oss@main to a commit SHA, downloads the GitHub codeload tarball, and extracts it into a temp directory.
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.

What gets installed

Artifact Purpose
pharn/pharn-pipeline/grillers/<name>/, pharn/pharn-review/<name>/ Pipeline auditors and review lenses selected for your project archetype(s).
.claude/commands/, .claude/hooks/ The installed /pharn-* Claude Code slash commands and deterministic .cjs 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.

After install

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 and printed a warning instead of overwriting it — so until you copy the hook wiring across, every guarantee that depends on a PreToolUse hook is inactive.

Once wired, the write guard is fail-closed. With no active scope, Claude Code's Write/Edit/MultiEdit/NotebookEdit tools are restricted to pharn/features/** and .pharn/**; ordinary edits to your own source are denied. That is the intended posture — a stage sets the scope from the concrete paths your PLAN.md declared — but it means the guard is not a drop-in for editing outside a PHARN run. Clearing the scope returns to this default; it does not re-open your source.

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 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.

Day-to-day workflow

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.

CLI commands

Command Description
pharn init Detect archetypes and install matching capabilities. This is also the default when no command is given.
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.
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, or --no-drift to skip byte comparison.
pharn --help Show command help.
pharn --version Show the installed CLI version.

init also accepts --archetype, a deprecated no-op kept for one release — archetype detection is now the default.

Three behaviours matter if you script the CLI:

  • Options are per-command. Passing one a command does not take prints Unsupported option for `status`: "--json" to stderr with the usage text and exits 1; an extra positional is refused the same way. Both previously parsed, were silently dropped, and exited 0.
  • init and update are interactive-only. Off a TTY they exit 1 rather than prompting into a dead stream. update --yes is the way through in CI; init deliberately has no --yes, because its second prompt is the destructive overwrite confirmation.
  • One writer at a time. init, add, remove and update take an advisory lock at .pharn.lock; a second run refuses rather than queueing. add, remove and update also re-read pharn.config.json once 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). list and status never take it and are never blocked by one, so pharn status --strict stays runnable in CI while an update is in flight.

Safety model

PHARN is intentionally conservative about writes:

  • init checks for existing install targets and asks before overwriting them, and refuses the whole install — writing nothing — when a destination path crosses a symlinked directory.
  • update uses pharn.records.json to skip files it cannot prove are untouched.
  • update --force backs up overwritten files under .pharn-backup/<timestamp>/ before writing. add does 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.
  • status and list are read-only.
  • remove deletes only the selected capability directory and prunes its records; it never touches CONSTITUTION.md, memory-bank/, or your detected archetypes, and refuses when that directory's path crosses a symlink.

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.

Current scope

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 >= 20.13.0 (the floor its prompt library needs). CI runs on Node 24, and a smoke job starts the packed CLI on exactly Node 20.13.0.
  • Archetype detection is JS/TS-shaped. The signals are package.json dependency names plus next.config.*, app/ route handlers, .tsx/.jsx, migrations/ and .sql. A Python, Go or Rust repo produces no signal, resolves to lib, and receives the universal capabilities only — a correct outcome, not a failure.
  • 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.
  • status reports modified, missing, and unreadable PHARN-owned paths; orphaned-file detection is still planned.

See the roadmap for shipped versus planned work.

Documentation

Start here: docs/

Development

npm install
npm run dev -- init
npm run build
npm run test
npm run check

Useful 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. See CONTRIBUTING.md for contributor notes and release discipline.

Security

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.

License

Apache 2.0.