PakFu extensions are manifest-driven external commands. PakFu discovers JSON
manifests, negotiates command capabilities, launches the declared process, and
passes a structured JSON payload on stdin.
The extension layer currently supports:
- manifest discovery
- capability negotiation
- read-only archive context
- selected-entry materialization to temporary files/directories
- extension-generated imports/write-back through a host-validated manifest
- GUI launch from the
Extensionsmenu - CLI launch with
--list-pluginsand--run-plugin - CLI diagnostics with
--plugin-report - a source-first examples pack under
examples/extensions
There is no binary plugin ABI yet. Extensions are ordinary external processes.
PakFu searches these directories for manifests:
<pakfu-dir>/plugins./pluginsfrom the current working directory- the platform app-data
pluginsdirectory for PakFu
CLI callers can add more search roots with:
--plugin-dir <dir>
For a full discovery and capability report, run:
pakfu --cli --plugin-report
pakfu --cli --plugin-report --plugin-dir path/to/extension-root
The report lists search roots, manifest warnings, resolved commands, executable paths, trust scope (default extension directory vs custom/external directory), selection rules, and capability notes.
PakFu looks for either:
plugin.json*.pakfu-plugin.json
Schema version: 1
Minimal example:
{
"schema_version": 1,
"id": "example",
"name": "Example Tools",
"description": "Sample extension commands.",
"commands": [
{
"id": "inspect-cfg",
"name": "Inspect CFG",
"description": "Run an external checker on one selected CFG file.",
"command": ["python", "inspect_cfg.py"],
"working_directory": ".",
"capabilities": ["archive.read", "entries.read"],
"requires_entries": true,
"allow_multiple": false,
"extensions": ["cfg"]
},
{
"id": "generate-cfg",
"name": "Generate CFG",
"description": "Generate a CFG file and import it into the archive.",
"command": ["python", "generate_cfg.py"],
"working_directory": ".",
"capabilities": ["archive.read", "entries.import"]
}
]
}Top-level fields:
schema_version: currently1id: plugin identifier (A-Z,a-z,0-9,_,.,-)name: display labeldescription: optional descriptioncommands: array of command objects
Command fields:
id: command identifiername: display labeldescription: optional descriptioncommand: argv array; first item is the executableworking_directory: optional; relative paths resolve from the manifest directorycapabilities: optional list of requested capabilitiesrequires_entries: optional boolean, defaultfalse; requiresentries.readallow_multiple: optional boolean, defaulttrue; requiresentries.readwhen selection constraints are meaningfulextensions: optional list of allowed selected-file extensions without dots; requiresentries.read
If capabilities is omitted, PakFu treats the command as a legacy read command
with:
["archive.read", "entries.read"]Known capabilities:
archive.read: receive archive metadata in the JSON payloadentries.read: receive selected archive entries materialized to temporary local pathsentries.import: receive a host-owned import directory and manifest path for generated archive entries
Commands with unknown capabilities are skipped with a manifest warning.
Extensions are ordinary external processes. Before the GUI launches a command,
PakFu shows a confirmation dialog with the command ref, manifest path, working
directory, argv, selection rules, and requested capabilities. This makes the
same trust model visible in the desktop workflow that --plugin-report exposes
for CLI and CI users.
Capability implications:
archive.readexposes archive metadata and filesystem paths.entries.readexposes selected archive entries as temporary local files.entries.importlets the command request generated files for host-validated import into an editable archive.
Prefer the narrowest capability set that matches the command. Commands that only need archive metadata should not request selected entries or import access.
PakFu launches the external process with:
- JSON payload on
stdin - separate
stdoutandstderrcapture - environment variables:
PAKFU_EXTENSION_SCHEMAPAKFU_EXTENSION_HOST_CAPABILITIESPAKFU_EXTENSION_COMMAND_CAPABILITIESPAKFU_EXTENSION_PLUGIN_IDPAKFU_EXTENSION_COMMAND_IDPAKFU_EXTENSION_IMPORT_ROOTfor commands withentries.importPAKFU_EXTENSION_IMPORTS_PATHfor commands withentries.import
Relative executable paths are resolved from the command's working directory.
The payload schema id is:
pakfu-extension/v1
Example payload:
{
"schema": "pakfu-extension/v1",
"host": {
"schema": "pakfu-extension/v1",
"capabilities": ["archive.read", "entries.read", "entries.import"],
"import_root": "/absolute/path/to/temp/extension-import-1",
"import_manifest_path": "/absolute/path/to/temp/extension-import-1/pakfu-imports.json"
},
"command": {
"plugin_id": "example",
"plugin_name": "Example Tools",
"command_id": "inspect-cfg",
"command_name": "Inspect CFG",
"description": "Run an external checker on one selected CFG file.",
"manifest_path": "/absolute/path/to/plugin.json",
"capabilities": ["archive.read", "entries.read"]
},
"archive": {
"path": "/absolute/path/to/archive.pk3",
"readable_path": "/absolute/path/to/archive.pk3",
"format": "zip",
"mounted_entry": "",
"current_prefix": "scripts/",
"quakelive_encrypted_pk3": false,
"wad3": false,
"doom_wad": false
},
"entries": [
{
"archive_name": "scripts/autoexec.cfg",
"local_path": "/absolute/path/to/temp/scripts/autoexec.cfg",
"is_dir": false,
"size": 128,
"mtime_utc_secs": 1714156800
}
]
}The entries array is empty when the command does not request entries.read.
The import_root and import_manifest_path host fields are present only for
commands that request entries.import.
Commands with entries.import can create files below import_root and then
write an import manifest to import_manifest_path.
Import manifest schema:
{
"schema": "pakfu-extension-imports/v1",
"imports": [
{
"archive_name": "scripts/generated.cfg",
"local_path": "generated/generated.cfg",
"mode": "add_or_replace",
"mtime_utc_secs": 1714156800
}
]
}Import fields:
archive_name: target archive path; must be a safe relative archive entry pathlocal_path: file path underimport_root; relative paths are resolved fromimport_rootmode: currently onlyadd_or_replacemtime_utc_secs: optional UTC timestamp for the pending archive entry
PakFu validates import manifests before applying them:
- the manifest must use
pakfu-extension-imports/v1 - every import source must be a file under
import_root - target archive paths must be safe relative entry names
- unsupported modes are rejected
GUI behavior:
- import outputs are added as pending archive changes
- imported entries can be undone with the normal undo stack
- the archive is not saved until the user chooses Save or Save As
- import-capable commands are disabled for read-only views, mounted archive views, and Pure PAK-protected official archives
CLI behavior:
- PakFu validates and reports requested imports
- CLI write-back is not applied automatically
- import files are retained in the reported temporary directory when imports are requested
- scripts can consume the reported import paths or run the command through the GUI workflow when interactive write-back is desired
Open an archive tab, select entries if the command requires them, then use:
Extensions -> <command>
PakFu validates the current selection and editable state before launch. If a command is disabled in the menu, its tooltip explains why.
List discovered commands:
pakfu --cli --list-plugins
Inspect discovery, trust scope, and capability details:
pakfu --cli --plugin-report
pakfu --cli --plugin-report --plugin-dir examples/extensions/metadata-dump
Run a command against an archive:
pakfu --cli --run-plugin example:inspect-cfg path/to/archive.pk3
Pass selected files with existing archive selectors:
pakfu --cli --run-plugin example:inspect-cfg --entry scripts/autoexec.cfg path/to/archive.pk3
pakfu --cli --run-plugin example:inspect-cfg --prefix scripts path/to/archive.pk3
For commands with entries.read, --entry and --prefix pass matching files
as materialized temp files to the extension command. Commands without
entries.read receive an empty entries array.
examples/extensions/metadata-dump contains a minimal C++ extension command and
manifest. It demonstrates explicit capabilities, relative command resolution,
stdin payload handling, and host-captured stdout/stderr without requesting
write-back access.
Build and inspect it locally:
cd examples/extensions/metadata-dump
c++ -std=c++20 metadata_dump.cpp -o metadata-dump
pakfu --cli --plugin-report --plugin-dir .