pm reads settings from the project tracker root and optional global profile. Use this page for public, user-facing configuration. Use pm config ... list and pm config ... export for the active runtime shape.
- Do not override
PM_PATHfor real repository tracking. - Prefer
--pm-path <repo>/.agents/pmwhen a script must target tracker storage explicitly;--pathremains a compatibility alias and is not a workspace/cwd flag. - Do set
PM_AUTHORfor maintainer and agent mutations. - Use
--jsononly when strict parsing is needed. - Use
pm contractsfor current command/schema metadata.
Tracked documentation work: pm-u9d0.
pm config project list
pm config project export --json
pm config project get item-format --json
pm config project set item-format --format toon
pm config project set test-result-tracking --policy enabledconfig set <key> <value> also accepts the value as a positional argument; pm routes
it to the right typed flag based on the key (so --policy/--format/--criterion
remain optional for single values):
pm config set telemetry-tracking off # off|on|true|false map to disabled|enabled
pm config set item-format toon # same as --format toon
pm config set governance-preset strict # same as --policy strict
pm config set definition-of-done "Tests pass" # same as --criterion "Tests pass"The context key has no single value and still uses --default-depth,
--activity-limit, --stale-threshold-days, and --section-<name> flags. Use
--criterion (repeatable) to set more than one criteria-list value at once.
Scopes:
projectupdates.agents/pm/settings.json.globalupdates the global profile underPM_GLOBAL_PATHor the default global root.
Precedence:
- CLI flags
- environment variables
- project settings
- global settings
- built-in defaults
When settings.json cannot be loaded, pm falls back to built-in defaults and prints a one-time settings_read_invalid_json or settings_read_invalid_schema warning to stderr (stdout output is unchanged); run pm health for remediation.
| Setting | Purpose |
|---|---|
id_prefix |
generated item ID prefix, default pm- |
author_default |
fallback mutation author |
item_format |
item storage format (toon writes; legacy markdown is read/migrate only) |
output.default_format |
default renderer, usually toon |
locks.ttl_seconds |
stale lock threshold |
history.missing_stream |
auto_create or strict_error |
history.compact_policy.enabled |
enable the compaction advisory: pm health warns on over-threshold streams (default false); when disabled, max_entries/trigger are inert |
history.compact_policy.max_entries |
when the policy is enabled, the entry count above which a stream is flagged by pm health and the default pm history-compact --all-over threshold (default 500) |
history.compact_policy.trigger |
policy intent when enabled: health_warn (advisory only) or auto (scheduled sweeps expected) |
testing.record_results_to_items |
persist bounded linked-test summaries |
validation.sprint_release_format |
warn or strict_error |
validation.parent_reference |
warn or strict_error (strict_error is the built-in default; pm create --allow-missing-parent is the explicit escape hatch) |
item_types.definitions[] |
custom item types and type options |
governance.create_default_type |
default --type used by pm create "title" when --type is omitted (defaults to Task); must resolve to a known item type |
governance.workflow_enforcement |
per-type transition enforcement mode for pm update --status (off default, warn, or strict) |
schema.type_workflows[] |
per-type allowed status transitions (see Per-Type Workflows below) |
search.* |
search mode, scoring, providers, embedding timeout, and vector settings |
Runtime item types are context primitives. Use pm schema list to inspect the merged registry and pm schema show <Type> to inspect one type's folder, aliases, defaults, required options, and extension provenance. pm init --type-preset agile|ops|research writes reusable domain types into .agents/pm/schema/types.json; this is equivalent to persisted project schema, not an extension-only runtime overlay.
Each scalar setting above is settable via pm config set <key> <value> (no hand-editing of settings.json). The dotted settings.json path is shown in parentheses where it differs from the CLI key:
pm config project set id_prefix task # (id_prefix) IDs become task-xxxx
pm config project set author_default release-bot # (author_default) default mutation author
pm config project set output_default_format json # (output.default_format) toon | json
pm config project set locks_ttl_seconds 60 # (locks.ttl_seconds) integer >= 1
pm config project set checkpoints_retention_days 30 # (checkpoints.retention_days) integer >= 1; pm gc --scope checkpoints prunes checkpoints older than this many days
pm config project set schema_unknown_field_policy reject # (schema.unknown_field_policy) allow | warn | reject| Variable | Use |
|---|---|
PM_AUTHOR |
explicit mutation author |
PM_PATH |
override project tracker root for tests or sandboxes |
PM_GLOBAL_PATH |
override global profile root for tests or sandboxes |
PM_OLLAMA_MODEL |
choose default Ollama embedding model |
PM_DISABLE_OLLAMA_AUTO_DEFAULTS |
disable implicit Ollama search defaults |
For command-level tracker targeting, prefer --pm-path <tracker-root>. The old
--path flag is still accepted, but it means the pm tracker storage directory
itself, not the repository workspace. During pm init, explicit tracker paths
that look like workspace roots are rejected unless --force is provided.
Tests should set both PM_PATH and PM_GLOBAL_PATH to temporary directories. The wrapper node scripts/run-tests.mjs ... does that automatically.
Telemetry is opt-in via pm config set telemetry-tracking on (see Common Settings). When enabled, these environment variables tune runtime behaviour. All boolean knobs accept 1, true, yes, or on (case-insensitive); any other value leaves the knob off.
| Variable | Values | Use |
|---|---|---|
PM_TELEMETRY_DISABLED |
boolean | Hard-disable all telemetry for this process, ignoring settings. |
PM_NO_TELEMETRY |
boolean | Alias for PM_TELEMETRY_DISABLED (honoured by the same checks). |
PM_TELEMETRY_OTEL_DISABLED |
boolean | Disable only OTLP trace-span export; the event queue still flushes. |
PM_TELEMETRY_INLINE_FLUSH |
boolean | Flush the queue and OTLP spans inline instead of dispatching the detached worker. Mainly for tests; normal use relies on the background worker. |
PM_TELEMETRY_SOURCE_CONTEXT |
user | automation | test | dogfood | audit_smoke |
Override the inferred source context recorded on each event. Any other value is ignored and the context is inferred. |
PM_TELEMETRY_INGEST_KEY |
string | Sent as the x-pm-telemetry-key header on queue flushes; never logged. |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
URL | OTLP/HTTP traces endpoint for command spans. Takes precedence over the base endpoint. |
OTEL_EXPORTER_OTLP_ENDPOINT |
URL | Base OTLP endpoint; the traces endpoint is derived by appending /v1/traces. |
OTEL_SERVICE_NAME |
string | service.name attribute on exported spans (defaults to pm-cli). |
Interaction rules:
PM_TELEMETRY_DISABLED/PM_NO_TELEMETRYshort-circuit everything, including OTLP export, regardless of the other knobs.- OTLP span export only happens when telemetry is enabled,
PM_TELEMETRY_OTEL_DISABLEDis off, and a traces endpoint is configured. By default spans are persisted to a bounded queue and exported by the detached, unref'd flush worker so commands exit promptly even when the traces endpoint is unreachable.PM_TELEMETRY_INLINE_FLUSH=1is the explicit test-oriented exception that performs the flush inline. pm health --check-telemetry --jsonsurfaces flush and OTLP export diagnostics (pending_otel_spans,last_otel_attempt_at,last_otel_success_at,last_otel_failure_at,last_otel_failure_error) and the activeenv_overrides(includingtelemetry_inline_flushandtelemetry_source_context) so agents can self-diagnose a stalled endpoint.PM_AUTHORadds a privacy-preserving agent-identity dimension tocommand_start/command_finishevents so agent-driven invocations can be segmented in dashboards without leaking the raw author string. Atredacted/maxcapture the events carryauthor_context_hash— the same installation-id-keyed one-way SHA-256 used forpm_root_hash/cwd_hash, so the same author hashes consistently within an installation but differently across installations. Atminimalcapture only a booleanhas_author_contextis emitted. The rawPM_AUTHORvalue is never exported.
pm telemetry stats reads the local queue and reports, per command bucket, latency percentiles (duration_p50_ms/duration_p95_ms/duration_max_ms, nearest-rank over command_finish duration_ms), outcome rates (ok_count/error_count/error_rate; a finish event whose ok is missing or not strictly true is counted conservatively as an error), and command_resolution_counts. These are an always-available, zero-network performance and reliability signal for the most recent queued window.
Crash and error diagnostics are reported to Sentry only when telemetry is enabled and the process is not opted out. These environment variables tune the Sentry client.
| Variable | Values | Use |
|---|---|---|
PM_SENTRY_DISABLED |
boolean | Disable Sentry error reporting for this process (the Sentry SDK is not even imported). PM_TELEMETRY_DISABLED / PM_NO_TELEMETRY also disable it. |
SENTRY_DSN |
URL | Override the destination DSN. Defaults to the bundled pm-cli project DSN. |
SENTRY_ENVIRONMENT |
string | Override the reported environment tag. Defaults to test under Vitest, ci when CI is set, otherwise production. |
SENTRY_TRACES_SAMPLE_RATE |
number 0–1 |
Fraction of command spans sampled for performance tracing. Defaults to 0.2 (20%). Set to 1 for full-trace performance debugging or 0 to disable tracing. Non-numeric or out-of-range values fall back to the default. |
Sentry is hard-disabled under Vitest (
VITEST/VITEST_WORKER_ID), so these knobs are no-ops inside the test suite.
TOON is the default:
pm config project set item-format --format toonMarkdown item files are treated as legacy migration input only. Mutations always write TOON files, and history stays JSONL.
Most commands default to sparse TOON:
pm list-open --limit 10Use JSON for strict machine parsing:
pm get <id> --json
pm get <id> --full --json
pm contracts --jsonpm calendar defaults to markdown because date-centric summaries are easier to scan in that format.
pm config project set sprint-release-format-policy --policy warn
pm config project set parent-reference-policy --policy strict_error
pm config project set history-missing-stream-policy --policy auto_create
pm config project set test-result-tracking --policy enabledUse standalone checks when validating a repository:
pm validate --check-resolution --check-history-drift
pm validate --check-files --scan-mode tracked-all
pm health --check-only --summary --jsonRestrict which status transitions are allowed for a given item type. Rules live in
schema/workflows.json alongside the lifecycle workflow block, and enforcement is
gated by governance.workflow_enforcement (default off, so existing projects are
unaffected until you opt in).
pm config project set governance-workflow-enforcement strict # off | warn | strict
pm config project set governance-create-default-type Issue # default create typeSemantics:
- A type with no
type_workflowsentry is unrestricted (every transition allowed). - A type with an entry allows only the listed
[from, to]pairs.from/toare matched case-insensitively and resolved through the status registry's aliases. - A same-status no-op (
from === to) is always allowed. workflow_enforcement: strictrejects a disallowed transition (including transitions toward the close status, gated before close-routing) with the allowed-transition hint.workflow_enforcement: warnstill applies the change but adds aworkflow_transition_not_allowedwarning to the update result.workflow_enforcement: off(default) ignores alltype_workflowsrules.
Keyword search is always available:
pm search "release docs" --mode keyword --limit 10search.tuning controls deterministic lexical weighting in keyword mode and the lexical component of hybrid mode. Default runtime weights:
title_exact_bonus=10title_weight=8,description_weight=5,tags_weight=6,status_weight=2body_weight=1,comments_weight=1,notes_weight=1,learnings_weight=1reminders_weight=2,events_weight=2dependencies_weight=3,linked_content_weight=1
Semantic and hybrid search can use built-in OpenAI-compatible or Ollama providers plus vector stores such as Qdrant or LanceDB. If local Ollama is available and semantic settings are unset, pm can resolve local defaults automatically.
For air-gapped, CI, or zero-setup environments there is a built-in BM25 lexical ranker that powers --semantic/--hybrid search entirely in-process — no embedding service or vector store required. Select it via search.provider:
pm config project set search_provider bm25 # always use BM25 for semantic/hybrid
pm config project set search_provider auto # use BM25 only when no embedding provider is configuredbm25always uses the offline ranker for semantic/hybrid queries, even when an embedding provider is set.autofalls back to BM25 only when no embedding provider (and no extension search provider) is available; the search result then carries asearch_<mode>_offline_bm25:no_embedding_provider:using_lexical_bm25warning.- Any other value (e.g.
openai,ollama, or an extension provider name) keeps the existing embedding/vector path.
Tune BM25 ranking with two persisted, validated knobs:
search.bm25.k1(number>= 0, default1.2) — term-frequency saturation; higher values let repeated terms keep accruing weight, lower values saturate sooner.search.bm25.b(number0..1, default0.75) — document-length normalization;0disables it,1fully normalizes by length.
pm config project set search_bm25_k1 1.5
pm config project set search_bm25_b 0.5BM25 quality is lexical — a strong baseline, but below true dense retrieval; configure an embedding provider when semantic similarity matters. Measure either path against a golden-query set with pm eval (see COMMANDS.md).
search.hybrid_semantic_weight (number 0..1, default 0.7) is the persistent default blend used in hybrid mode: it weights the semantic (vector) score against the lexical score during score fusion — higher favours semantic retrieval, lower favours keyword matching. A per-query pm search --semantic-weight <value> overrides it for a single query; when the flag is omitted the persistent setting applies. Set it once with:
pm config set search_hybrid_semantic_weight 0.85search.score_threshold (number, default 0) drops hits scoring below the floor (after hybrid fusion, before limit truncation). A per-query pm search --min-score <float> overrides it for a single query. search.max_results (default 50) caps returned hits across all modes when --limit is omitted, so broad keyword queries no longer return every match.
For local Ollama or slower embedding providers, tune search.embedding_batch_size, search.embedding_timeout_ms, and search.scanner_max_batch_retries in project config before assuming semantic search is broken. Keyword search remains the fast baseline while semantic indexing catches up.
search.embedding_corpus_max_characters optionally overrides provider defaults for corpus truncation (8000 for OpenAI-compatible providers, 3200 for Ollama). Invalid values fall back to the provider default and emit search_embedding_corpus_max_characters_invalid:using_provider_default warnings in semantic indexing workflows.
search.corpus_fields is an optional string array that controls which item fields are embedded into the semantic search corpus. When unset or empty, the full default field set is embedded (backward compatible). When set, only the named fields are embedded — letting teams opt structured signals in/out for token efficiency so queries like "high priority bugs blocking release" can match on those fields. The default (and full set of valid) field names are:
title, description, tags, status, type, priority, assignee, parent,
goal, value, why_now, risk, confidence, estimated_minutes,
acceptance_criteria, resolution, expected_result, actual_result,
body, comments, notes, learnings, reminders, events, dependencies, plan
Unknown names in the list are ignored. Optional structured fields are only embedded when present/non-empty on an item (absent fields add no tokens). Because this key is an array, it is not settable via pm config set (which handles scalar keys) — hand-edit .agents/pm/settings.json:
{
"search": {
// embed only the high-signal fields for compact, focused vectors
"corpus_fields": ["title", "description", "tags", "type", "priority", "status", "body"]
}
}Re-embed note: changing corpus_fields (or upgrading to a build that embeds new fields) changes the embedding input, so existing items are flagged stale and re-embedded on the next semantic refresh / pm reindex. This is expected and self-healing.
Advanced relevance tuning is opt-in:
search.query_expansion.enabledenables pre-embedding query expansion for semantic/hybrid runs.search.query_expansion.providerselects the expansion provider name. Built-in names areopenaiandollama; extension providers can exposequeryExpansion/query_expansionhooks under the same provider name.search.rerank.enabledenables post-retrieval reranking for hybrid mode.search.rerank.modeloverrides the model used by built-in rerank execution.search.rerank.top_kcontrols how many top hybrid candidates are reranked.
When query expansion or rerank providers fail, pm search degrades gracefully and keeps the query runnable; warning codes are emitted in the result payload.
pm search filter metadata now reports query_expansion_* and rerank_* fields so automation can detect when advanced tuning is active.
Mutation commands invalidate keyword search caches immediately. Semantic vector refresh is controlled by search.mutation_refresh_policy:
| Policy | Behavior |
|---|---|
semantic_configured |
default; refresh vectors during mutations only when semantic provider/store settings are explicitly configured |
cache_only |
fastest writes; invalidate keyword caches and leave vector refresh to pm reindex or pm health --refresh-vectors |
semantic_auto |
also apply implicit local Ollama/LanceDB defaults during mutations |
Useful commands:
pm config project set search_mutation_refresh_policy cache_only
pm config project set search_embedding_corpus_max_characters 12000
pm config project set search_query_expansion_enabled true
pm config project set search_query_expansion_provider openai
pm config project set search_rerank_enabled true
pm config project set search_rerank_model text-embedding-3-small
pm config project set search_rerank_top_k 20
pm search "calendar reminders" --mode hybrid --semantic-weight 0.35 --limit 10
pm reindex --mode hybrid --progress
pm health --check-onlypm supports any OpenAI-compatible API or Ollama-style provider via configuration. No code changes needed. Each leaf below can be set with pm config <scope> set <key> <value> (the corresponding settings.json dotted path is shown in parentheses).
Ollama (local, default for offline embedding):
pm config project set ollama_base_url http://localhost:11434
pm config project set ollama_model nomic-embed-text
pm config project set search_provider ollamaOpenAI:
pm config project set openai_base_url https://api.openai.com/v1
pm config project set openai_api_key '<OPENAI_API_KEY>'
pm config project set openai_model text-embedding-3-small
pm config project set search_provider openaiThe base URL defaults to empty, so it must be set explicitly even for the canonical OpenAI endpoint.
LM Studio (OpenAI-compatible) — point pm at LM Studio's local OpenAI-compatible endpoint:
pm config project set openai_base_url http://localhost:1234/v1
pm config project set openai_model nomic-embed-text-v1.5
pm config project set search_provider openaiSet openai_api_key to any non-empty value if LM Studio is configured to require one.
vLLM (OpenAI-compatible) — point pm at a vLLM server serving an embedding model:
pm config project set openai_base_url http://localhost:8000/v1
pm config project set openai_model BAAI/bge-large-en-v1.5
pm config project set search_provider openaiVector store — choose where vector embeddings live. LanceDB is file-backed and zero-setup; Qdrant runs as a service:
pm config project set vector_store_adapter lancedb
pm config project set vector_store_collection_name pm_items
pm config project set lancedb_path .agents/pm/search/lancedb
# or, with Qdrant:
pm config project set vector_store_adapter qdrant
pm config project set vector_store_collection_name workspace_items
pm config project set qdrant_url http://localhost:6333
pm config project set qdrant_api_key '<QDRANT_API_KEY>' # omit on unauthenticated dev serversAfter changing any of these, run pm reindex --mode hybrid so the vector index reflects the new provider/store. pm search ... --mode semantic|hybrid emits a vector_index_stale warning when items have been modified since the last reindex.
Refresh contract (important for agents): by default a mutation (pm create/pm update) does not synchronously re-embed the changed item, so a brand-new item is absent from --mode semantic|hybrid results until the next pm reindex. This keeps writes fast. Control the tradeoff with search.mutation_refresh_policy: cache_only (never refresh on write — fastest), semantic_auto (refresh when implicit Ollama/LanceDB defaults are active), or semantic_configured (refresh only when semantic search is explicitly configured). Keyword mode (--mode keyword, the default) always reflects writes immediately because it reads items directly.
Custom item types can be defined in settings and by extensions. Runtime type resolution affects create/update validation, list/search/calendar filters, completions, and storage folders.
Use runtime contracts for exact active types:
pm contracts --json
pm create --help --type TaskCustom runtime fields add typed flags to pm create/pm update (and optionally list/search/context) and persist their values into item metadata. They are the most powerful customization primitive: a field defined here becomes a first-class --flag with validation, completions, and --help text, with no code changes.
The preferred way to add a field is the CLI —
pm schema add-fieldwrites a well-formed entry for you and validates collisions. Hand-authoringschema/fields.json(below) is equivalent and useful for bulk edits or version-controlled review.
Fields live in .agents/pm/schema/fields.json with the shape { "fields": [RuntimeFieldDefinition...] }:
// .agents/pm/schema/fields.json
{
"fields": [
{
"key": "component", // required: canonical field name (snake_case)
"type": "string", // string | number | boolean | string_array (default string)
"metadata_key": "component", // item metadata key (defaults to `key`)
"cli_flag": "component", // long flag, used as --component (defaults to key)
"cli_aliases": ["comp"], // extra accepted flag spellings (--comp)
"commands": ["create", "update", "list"], // create | update | update_many | list | search | calendar | context
"description": "Owning component", // shown in --help
"required": false, // required on every mutation when true
"required_on_create": false, // required only on create
"allow_unset": true, // allow clearing via --component "" / --no-component
"required_types": ["Task"] // only required for these item types (subset of required*)
}
]
}RuntimeFieldDefinition properties:
key(required) — canonical field name. Empty/invalid keys are dropped.type—string(default),number,boolean, orstring_array. Astring_arrayfield is repeatable (accepts the flag multiple times).metadata_key— the item metadata key written/read. Defaults tokey.cli_flag— the long flag name (rendered as--<cli_flag>). Defaults to a normalized form ofkey.cli_aliases— additional flag spellings; duplicates ofcli_flagare ignored.commands— which commands expose the flag. Defaults to["create", "update"].required— value must be present on every accepted mutation.required_on_create— value must be present onpm create(but not on later updates).allow_unset— whentrue(default), the field can be cleared; whenfalse, an explicit unset is rejected.required_types— narrowsrequired/required_on_createto only the listed item types.description— surfaced in--help.
End-to-end example — add a required-on-create component string field on Task items:
// .agents/pm/schema/fields.json
{
"fields": [
{
"key": "component",
"type": "string",
"cli_flag": "component",
"commands": ["create", "update", "list"],
"description": "Owning component (auth, billing, ...).",
"required_on_create": true,
"required_types": ["Task"]
}
]
}pm create Task "Fix token refresh" --component auth # stored in item metadata
pm list --component auth # filter by the custom fieldGovernance note: how item I/O treats metadata keys that are not in the field registry is controlled by schema.unknown_field_policy — allow (default, keeps unknown keys), warn (keeps them but emits a warning), or reject. Set reject to catch field-name typos once your field registry is stable:
pm config project set schema_unknown_field_policy reject # allow | warn | rejectPublic docs should describe supported user configuration only. Ignored local operations material, unpublished evidence logs, credentials, hostnames, and private service details must stay outside tracked docs and package output.