Skip to content

Pack Config and Operation Log API

Reference the stable Python APIs for resolving pack directories, loading configuration, and recording operations.


Three primitives give pack scripts a stable, cross-catalogue interface:

PrimitiveWhat it does
pack_dir(pack_name)Resolve (and create) the user-scope directory for a pack
load_pack_config(pack_name)Read the merged two-layer configuration dict
write_entry(pack_name, action, src)Append a structured entry to the pack’s operation log

CLI equivalents are available via agentbundle pack-config and agentbundle oplog.


from agentbundle.config import pack_dir
from pathlib import Path
# Resolve and create ~/.agentbundle/atlassian/ (mode 0o700)
d: Path = pack_dir("atlassian")

Signature:

def pack_dir(
pack_name: str,
*,
state: State | None = None,
home: Path | None = None,
) -> Path

Resolution order:

  1. If state rows for the pack exist, read user-root from them. All rows must agree; PackRootConflict raised if they disagree.
  2. If no rows exist, fall back to ~/.agentbundle (or home / ".agentbundle" when home= is given).

Errors:

  • ValueErrorpack_name fails slug grammar (^[a-z0-9][a-z0-9-]*$) or is reserved.
  • PackRootConflict(ValueError) — multiple adapter rows disagree on user-root.
  • OSError — the target path is a symlink or outside home.

Reserved slugs: bin, state, credentials, state.toml.


from agentbundle.config import load_pack_config
cfg: dict = load_pack_config("atlassian")
url = cfg.get("url", "https://jira.example.com/")

Signature:

def load_pack_config(
pack_name: str,
*,
path: Path | None = None,
state: State | None = None,
home: Path | None = None,
) -> dict

Two-layer cascade:

LayerSourcePrecedence
1 Baked_data/install-defaults.toml [pack-defaults.<pack>]Lower
2 User<pack_dir>/config.tomlHigher

Shallow merge: user wins on key collision. Malformed user config.tomlRuntimeWarning + baked-only result.

Returns {} when both layers are absent.

path= override: reads the user layer from path instead of the default location.


from agentbundle.oplog import write_entry
write_entry("atlassian", "install", src="git+https://example.com/catalogue")
write_entry("atlassian", "upgrade", src="...", dst="/path/to/file")

Signature:

def write_entry(
pack_name: str,
action: str,
src: str,
dst: str | None = None,
*,
extra: dict | None = None,
state: object = None,
home: Path | None = None,
) -> None

Emitted JSON object shape:

{"action": "install", "src": "git+https://example.com/", "ts": "2026-07-28T00:00:00+00:00"}

ts (ISO-8601 UTC) is always the last field. dst is omitted when None.

Errors:

  • ValueErrorextra contains a reserved key (action, src, dst, ts).
  • EntryTooLargeError — base fields exceed 4096 bytes.

Extra fields and truncation: when extra is provided and the full entry would exceed 4096 bytes, extra fields are dropped and "_truncated": true is added.

Atomicity: on POSIX, a single os.write() to O_CREAT|O_APPEND guarantees non-interleaved lines on local filesystems. NFS is not supported — set AGENTBUNDLE_NFS_OPLOG=1 to suppress warnings.


Declare per-pack defaults and a custom user directory in catalogue.toml:

[catalogue]
user-dir = "~/custom-dir" # optional; defaults to ~/.agentbundle
[pack-defaults.atlassian]
url = "https://jira.yourorg.com/"
[pack-defaults.github]
org = "example-org"

Run agentbundle catalogue sync-defaults --write after editing to bake the values into _data/install-defaults.toml.

user-dir must start with ~/. Absolute paths outside $HOME are rejected.


agentbundle pack-config show <pack>
agentbundle pack-config get <pack> <key>
agentbundle pack-config set <pack> <key> <value>
agentbundle pack-config unset <pack> <key>

show prints all keys labeled (baked default) or (user override). get exits 1 when the key is absent.


agentbundle oplog show <pack> [--since=<ISO>]
agentbundle oplog clear <pack> --yes

show prints the last 50 entries (or all when fewer). --since filters to entries at or after the given ISO-8601 timestamp.

clear truncates ops.jsonl. --yes is always required — there is no TTY exception.