pf-cli — read, write and validate

pf-cli is the command-line frontend for the projectfile: it reads, writes and validates fields, resolves remote includes, and generates derived artifacts. It embeds the v1 JSON Schema for offline validation and is the canonical tool for editing projectfiles locally and in CI. Its extension namespace is org.projectfile.cli.

File and forge sync lives in pf-bridge, not here — pf-cli has no bridge, scan or forge commands. This page matches pf-cli --help exactly.

Step Tool What it does
1. Read / write pf-cli (this page) get, set, add, del, validate, convert
2. Sync files pf-bridge package.json, CITATION.cff, README.md, …
3. Lower to CI pf-ci org.projectfile.ci → workflow YAML

Release binary

Latest release 3.4.10. Published for 5 platforms: macOS, Linux.

Terminal window
base=https://kiota.ch/projectfile/cli/releases/download/v3.4.10curl --fail --location --output ~/.local/bin/pf-cli \    "$base/pf-cli-$(uname -s | tr A-Z a-z)-$(uname -m)"chmod +x ~/.local/bin/pf-cli
PlatformAsset
macOS / ARM64pf-cli-darwin-arm64
macOS / x86-64pf-cli-darwin-x86_64

Verify what you downloaded

Every binary ships a detached signature beside it. Confirm it with the maintainer's release key, which the keyserver serves:

Terminal window
gpg --keyserver hkps://keys.openpgp.org --recv-keys 8F89CBF7C8D51F8BEC7533C00925F8F79EBD3257curl --fail --location --output pf-cli.asc \    "https://kiota.ch/projectfile/cli/releases/download/v3.4.10/pf-cli-$(uname -s | tr A-Z a-z)-$(uname -m).asc"gpg --verify pf-cli.asc ~/.local/bin/pf-cli

Container image

Run the tool without installing it. The alias makes every command on this site work unchanged against your working directory:

ghcr.io/projectfile-org/cli — linux/amd64, linux/arm64, linux/riscv64

Terminal window
docker pull ghcr.io/projectfile-org/cli:3.4.10alias pf-cli='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \    --volume "$PWD:/app/ws" --workdir /app/ws ghcr.io/projectfile-org/cli:3.4.10 pf-cli'

damianbuho/projectfile-cli — linux/amd64

Terminal window
docker pull damianbuho/projectfile-cli:3.4.10alias pf-cli='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \    --volume "$PWD:/app/ws" --workdir /app/ws damianbuho/projectfile-cli:3.4.10 pf-cli'

kiota.ch/projectfile/cli — linux/amd64

Terminal window
docker pull kiota.ch/projectfile/cli:3.4.10alias pf-cli='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \    --volume "$PWD:/app/ws" --workdir /app/ws kiota.ch/projectfile/cli:3.4.10 pf-cli'

Stable releases also publish X.Y.Z, X.Y and X tags — pull the precision you want to pin.

Uninstall

Terminal window
rm -f ~/.local/bin/pf-cli          # release binarydocker image rm ghcr.io/projectfile-org/cli   # container image

The tool’s own listing, so a command added upstream appears here without a page edit; the sections below then cover the ones a project touches most.

CommandGroupWhat it does
getReadRead one or more projectfile fields
addWriteAppend items to a projectfile list
delWriteRemove a field, list item, or map entry from projectfile
setWriteWrite a value into a projectfile field
validateValidateValidate a projectfile against the v1 JSON Schema
cacheMaintainManage pf-cli’s local cache for offline use
convertMaintainConvert a projectfile between encodings (toml/yaml/json)
includesMaintainManage the includes list
initMaintainScaffold a new projectfile document
optimizeMaintainRemove local fields that duplicate include values
setupMaintainEdit your per-user configuration interactively
completionOtherGenerate the autocompletion script for the specified shell
helpOtherHelp about any command

13 commands — the listing pf-cli v3.4.10 reported when the page was built.

Read values out of the projectfile by path. Prints plain text, one value per line. Exits 3 when a path is missing, 2 when the invocation is wrong. With --suggest a missing path prints the nearest existing address instead.

pf-cli get <path>… [flags]
Terminal window
pf-cli get identity.namepf-cli get repositories[role=origin].urlpf-cli get identity.bogus --suggestpf-cli get repositories[].urlpf-cli get identity --format jsonpf-cli get org.projectfile.sinks.kiota.ref --scope org.projectfile.image
Flag Description
--batch read several paths in one run
--default <value> value to print when the path is missing
--exists exit 0 if present, 1 if absent; prints nothing
--expand fill ${…} from the document; leave the rest as written
--expand-env fill ${VAR} from the environment first
--file <path> explicit projectfile path (skips detection)
--format raw, json, yaml, toml, sh, flat
--lang <tag> choose a language for translated fields
--or-default use the built-in default if missing
--path KEY=ADDR label a path as KEY=ADDR (repeatable)
--print-path print the projectfile path and exit
--scope <address> fill ${…} from this address (repeatable)
--suggest on a missing path, print the nearest existing address instead

Replace the value at <path>. A bare value is stored as text, exactly as typed. Use --value-json or --csv for a typed value. Missing sections are created.

pf-cli set <path> [value] [flags]
Terminal window
pf-cli set license.spdx MITpf-cli set identity.version 1.10pf-cli set keywords --csv rust,wasmpf-cli set contacts --value-json '{"email":"a@example.com"}'
Flag Description
--create-only fail if the path already has a value
--csv <list> value as a comma-separated string list
--dry-run show the write without saving it
--value-json <j> value as JSON (a bare value is text)

Append items to the list at <path>. Skips items that are already there.

pf-cli add <path> [value…] [flags]
Terminal window
pf-cli add keywords rust wasmpf-cli add repositories --field url=https://example.com/rpf-cli add keywords rust -n
Flag Description
--allow-duplicate append even if already present
--dry-run show the append without saving it
--field k=v set one item field as k=v (repeatable)
--value-json <j> the whole item as JSON

Delete the value at <path>. Missing paths are not an error. Alias: delete.

pf-cli del <path> [flags]
Terminal window
pf-cli del keywords[0]pf-cli del keywords[99] --strict
Flag Description
--dry-run show the deletion without saving it
--strict exit 1 when the path is already absent

Check that the projectfile is well-formed. Warns about repeated includes. Alias: lint.

pf-cli validate [directory] [flags]
Terminal window
pf-cli validatepf-cli validate /tmp/demopf-cli validate --strict-includes
Flag Description
--strict-includes fail on repeated includes (default: warn)

Create a new projectfile.yaml (or .toml/.json) in [directory]. With pf-bridge installed this delegates to pf-bridge-init: full detection from sources (package.json, CITATION.cff, …) plus scanner gap-fill (git history, stack detection). Without it, a basic local scaffold prompts for namespace, name and title only — no scanners. Alias: scaffold.

pf-cli init [directory] [flags]
Terminal window
pf-cli initpf-cli init /tmp/demo --namespace org.example --name demo --non-interactive
Flag Description
--format output format: yaml, toml, json (default: prompt)
--license <spdx> license SPDX expression (e.g. MIT)
--name <slug> identity.name (project slug)
--namespace <rdns> identity.namespace (reverse-DNS, e.g. org.example)
--no-scan skip init-time scanners
--non-interactive fail if required fields are missing instead of prompting

Rewrite the projectfile from one format to another. Checks the result before writing; keeps the source file. Alias: conv.

pf-cli convert <from-format> <to-format> [directory] [flags]
Terminal window
pf-cli convert yaml tomlpf-cli convert yaml json --delete-sourcepf-cli convert toml yaml --force
Flag Description
--delete-source remove the source file after converting
--force overwrite the output file if it already exists

Supported formats: toml, yaml (or yml), json.

Remove values your includes already provide. Keeps includes, $schema and spec_version. Alias: opt.

pf-cli optimize [directory] [flags]
Terminal window
pf-cli optimizepf-cli optimize --dry-run
Flag Description
--dry-run list removals without saving

Manage the includes list.

pf-cli includes [command]
Subcommand Description
pin pin remote includes to the SHA-256 of their current bytes

Keep downloaded includes on disk for offline reads. Shared with pf-bridge and pf-ci.

pf-cli cache [command]
Subcommand Description
purge delete cached includes (all, or one URL)
refresh alias for cache warm --force
status show what is cached and where
warm download includes now so later reads work offline
Terminal window
pf-cli cache statuspf-cli cache warm

Answer questions once; answers prefill future prompts. Enter accepts, Esc skips a section.

pf-cli setup [flags]
Terminal window
pf-cli setuppf-cli setup --format yaml

Personal defaults live at $XDG_CONFIG_HOME/projectfile/cli.{yaml,toml,json}. Fields set there (author name, ORCID, funding URLs, preferred format, …) pre-fill init prompts and renderer templates so the same flag is not repeated on every project.

Each failure class has its own code, so a script can tell a bad invocation from an absent field from an invalid document:

Code Meaning
0 success
1 the run failed: a parse error, no document found, a refused write
2 the invocation is wrong: unknown command, bad flag, bad path grammar
3 the requested path is absent (also --exists on an absent path)
4 the document does not validate against the v1 JSON Schema
Flag Description
--colors colour output: auto, always, never; also PF_CLI_NO_COLOR=1
--fail-on abort includes at error or warning (default error)
--ignore-user-config skip $XDG_CONFIG_HOME/projectfile/cli.* loading
--offline refuse network; use cache and embedded data
-q, --quiet mute info; warnings and errors still print
--sorted write YAML keys in sorted order
--timeout <duration> per-attempt network timeout (default 10s)
-v, --verbose show each step; also PF_CLI_VERBOSE=1
-V, --version print the version

.onion.i2p