Alle Pakete

pm-jira

Jira issue sync for pm-cli

Installieren

Mit installiertem pm

pm install npm:pm-jira --project

Mit npm, ohne pm zu installieren

npx -y @unbrained/pm-cli install npm:pm-jira --project

Mit Bun, ohne pm zu installieren

bunx @unbrained/pm-cli install npm:pm-jira --project

README

A pm-cli extension that syncs Jira issues into pm items using the Jira REST API v3.

Cloud imports use the enhanced JQL search API at /rest/api/3/search/jql. All import entrypoints follow opaque nextPageToken cursors until isLast or the requested --max-results limit. Progress reports fetched counts and the configured limit because this API has no exact total. Dry runs show the same initial request without making a network call. The exported buildSearchRequest helper now takes a cursor string or undefined as its third argument, replacing the removed numeric offset.

Malformed pages, repeated issue keys, missing or cycling continuation tokens, oversized responses, and searches exceeding 1,000 pages fail before any tracker write. Empty intermediate pages may advance the cursor; an empty final result succeeds without opening an atomic transaction. Narrow the JQL query if the page ceiling is reached. Jira search is eventually consistent: cursor validation does not guarantee a point-in-time snapshot. This integration targets Jira Cloud; Jira Data Center's separate search contract is not implemented.

Features

  • Pull issues from any Jira project into pm items via pm jira import or pm jira sync
  • Export pm items back out as Jira create payloads via pm jira export (preview, or --push to create issues)
  • Convenience JQL filters — --project, --status, --assignee, --issue-type, --label, --updated-since compose into a single JQL query (or pass full --jql to override)
  • Rich field mapping — Jira status (by name, with a statusCategory fallback for custom workflows), priority, issue type → pm type, labels/fix-versions/components/sprints → tags, assignee → tag; configurable with --status-map and a general --map jiraField=pmField
  • --dry-run everywhere — both import and export print the exact request / mutations they would make and perform no network call (the offline-testable, creds-free path)
  • pm jira validate — report Jira credential/base-URL readiness without leaking any secret (hostname-only preview); --json aware
  • Fail-fast preflight credential gate — a network-mutating pm jira sync / pm jira import (and pm jira export --push) aborts immediately with a clear, actionable message before any pm-store read or Jira call if JIRA_BASE_URL / JIRA_EMAIL / JIRA_API_TOKEN are missing. --dry-run and pm jira validate are exempt (offline). See Preflight gate.
  • Optional, opt-in export-on-write hook (PM_JIRA_PUSH_ON_WRITE) — best-effort, never breaks your pm command, no-op without credentials
  • Jira provenance (key + browse URL) persisted in the item description and declared as jira_key / jira_url schema fields; on export, items that already carry a Jira key become an update (PUT) instead of a duplicate create
  • Works as a pm jira sync/pm jira import command, a pm jira export exporter, and a config-driven jira-sync importer

Which features need live Jira credentials? Everything except the actual network round-trip is usable and testable offline. Building JQL, previewing requests/mutations with --dry-run, field mapping in both directions, and pm jira validate all work with no creds and no network. You only need JIRA_BASE_URL / JIRA_EMAIL / JIRA_API_TOKEN for: a real import fetch, pm jira export --push (creating/updating issues), and live reachability.

Installation

Install with pm from GitHub:

pm install github.com/unbraind/pm-jira

Or build locally from source:

npm ci
npm run build

Jira API Setup

You need a Jira API token to authenticate. To create one:

  1. Go to https://id.atlassian.com/manage-profile/security/api-tokens
  2. Click Create API token
  3. Give it a label (e.g. pm-cli-sync) and copy the token

Required Environment Variables

Variable Description Example
JIRA_BASE_URL Your Jira instance base URL https://company.atlassian.net
JIRA_EMAIL Email address for your Jira account [email protected]
JIRA_API_TOKEN API token generated above <jira-api-token>

Set them in your shell or .env:

export JIRA_BASE_URL=https://company.atlassian.net
export [email protected]
export JIRA_API_TOKEN=<jira-api-token>

Usage

Command: pm jira sync

# Sync all open issues from a project (default JQL: statusCategory != Done)
pm jira sync --project PROJ

# Sync up to 200 issues
pm jira sync --project PROJ --max-results 200

# Use custom JQL
pm jira sync --jql "project = PROJ AND assignee = currentUser() ORDER BY updated DESC"

# Preview without writing (dry run)
pm jira sync --project PROJ --dry-run

# Only sync issues that map to the "wip" pm status
pm jira sync --project PROJ --status wip

# Combine flags
pm jira sync --project PROJ --max-results 100 --status todo --dry-run

Command / Importer: pm jira import

pm jira import is the native importer pipeline equivalent of pm jira sync — it pulls issues via JQL and creates pm items. Both share the same flags and logic.

# Pull all open issues from a project
pm jira import --project PROJ

# Use a custom Jira host (instead of JIRA_BASE_URL) + custom JQL
pm jira import --host https://company.atlassian.net --jql "project = PROJ AND assignee = currentUser()"

# Override status mapping
pm jira import --project PROJ --status-map "QA=blocked,Done=closed"

# Atomic import (pm-cli >= 2026.7.20): one crash-recoverable transaction.
# Ordinary failures request delete compensation; rollback is not guaranteed.
# Interruptions can retain creates; retry the same input to resume.
pm jira import --project PROJ --atomic

Pull flags (pm jira sync / pm jira import)

Flag Type Default Description
--project string — Jira project key (e.g. PROJ). Composed into JQL. Alias: --project-key.
--jql string — Custom JQL query. Used verbatim; overrides all convenience filters below.
--status string — Filter by pm status (open/in_progress/closed/blocked, mapped to a statusCategory clause) or a raw Jira status name. Also filters imported items client-side.
--assignee string — Filter by assignee (accountId, name, or a function like currentUser()).
--issue-type string — Filter by Jira issue type (e.g. Bug).
--label string — Filter by Jira label.
--updated-since string — Filter by updated date, relative (-7d) or absolute (2026-01-01).
--status-map string — Override status mapping, e.g. "In Review=in_progress,QA=blocked".
--map string — Override field mapping, e.g. "issuetype=Task,assignee=skip". Alias: --field-map.
--host string $JIRA_BASE_URL Jira base URL override.
--max-results number 500 Maximum number of issues to pull.
--dry-run boolean false Print the JQL + exact GET request that would run; no network call.
--atomic boolean false Import all creates in ONE crash-recoverable, workspace-writer-locked transaction via the official commitItemMutations SDK helper (pm-cli >= 2026.7.20). Ordinary failures request delete compensation; an exception alone does not confirm rollback. Interrupted runs retain applied work and resume with the same input. A --dry-run + --atomic invocation previews like a normal dry-run and commits no transaction.

When no --jql is given, the convenience filters are AND-combined; if you don't filter on status, a statusCategory != Done clause is appended so an unscoped pull stays focused on active work (the historical default).

--map field overrides

--map accepts a comma list of jiraField=pmTarget pairs. Recognized Jira-side keys: status, statuscategory, priority, issuetype (alias type), labels, fixversions, components, sprint/sprints/customfield_10020, assignee, duedate. Imported tags include component:<name> from Jira components and sprint:<name> from Jira's common Sprint custom field (customfield_10020) when present; set noisy context fields to skip or ignore to suppress them. Examples:

# Pin every imported item's type, and skip the assignee tag
pm jira import --project PROJ --map "issuetype=Task,assignee=skip"

# Keep labels but suppress sprint/component context tags
pm jira import --project PROJ --map "components=ignore,sprint=ignore"

# Force a pm status regardless of the Jira workflow state
pm jira import --project PROJ --map "status=in_progress"

Progress + transparency notes (STDERR)

For large paginated imports the importer prints Fetched N (limit M)... progress to STDERR after each page, so a multi-page pull surfaces feedback instead of looking hung. The enhanced JQL cursor search returns no total, so N counts the issues fetched so far and M is the configured --max-results ceiling (default 500), never a denominator. This is additive and never touches the stdout / --json output.

If any fetched issue carries attachments or comments, the importer logs a one-line note to STDERR that those are not imported (pm-jira imports title / body / status / priority / labels / due-date only). This prevents a silent expectation that attachment or comment data came across.

Exporter: pm jira export

Local selection uses the public in-process SDK listAllComplete with bodies, all lifecycle states, strict source reads, no extensions, and unbounded output. There is no 10,000-item ceiling or CLI JSON buffer. Both export previews and pushes certify the whole corpus before constructing payloads. Import and sync also certify it before local writes and skip issues whose browse URL already exists, including closed and canceled items. Equal keys on different Jira hosts remain distinct. Import matching preserves existing items; it does not update or reopen them. Re-import reports zero newly imported items when all issues match.

An absent/malformed items array, incomplete source scan, pagination, counts that disagree, compact projection, missing or contradictory receipts, output compaction/omission, duplicate or missing IDs, or malformed payload fields causes a CommandError before writes. The error includes a strict, all-status, unbounded inspection command and a tracker/SDK recovery hint. A certified empty tracker succeeds; a missing or uninitialized tracker fails. See complete-read verification for the refusal matrix and regression evidence.

Render pm items as Jira create payloads. Prints JSON by default; with --push (and credentials + --project) it POSTs each payload to Jira's create-issue API.

# Preview the Jira create payloads for all pm items (no network, no creds needed)
pm jira export --project PROJ

# Print the exact mutations that WOULD run (create vs update), no network/creds
pm jira export --project PROJ --dry-run

# Derive Jira issuetype + priority from each pm item's type/priority
pm jira export --project PROJ --rich --dry-run

# Actually create the issues in Jira (requires creds + --project)
pm jira export --push --project PROJ

# Also PUT changed fields back to issues that already carry a Jira key
pm jira export --push --project PROJ --update-existing

# Preview exactly what --update-existing would do (no network)
pm jira export --project PROJ --update-existing --dry-run
Flag Type Default Description
--project string — Target Jira project key for created issues (required for --push). Alias: --project-key.
--map string — Override field mapping, e.g. "issuetype=Story". Alias: --field-map.
--rich boolean false Derive Jira issuetype + priority from the pm item type/priority.
--update-existing boolean false PUT changed fields to issues that already carry a Jira key. Without it, those items are skipped (no duplicate, no mutation).
--dry-run boolean false Print the Jira POST/PUT mutations that would run; no network call.
--host string $JIRA_BASE_URL Jira base URL override.
--push boolean false POST payloads to Jira (requires credentials + --project).

Items whose description carries a Jira <KEY>: <url> provenance marker (added on import) are matched back to their upstream issue and become an update (PUT):

  • By default (--push alone) those items are skipped so a re-export never duplicates or unexpectedly mutates an existing Jira issue — only items without a key are created. --dry-run shows them as SKIP.
  • With --update-existing each matched item is PUT to Jira's edit-issue endpoint (/rest/api/3/issue/<KEY>) with its changed fields (the immutable project field is stripped, as Jira's edit API rejects it). --dry-run shows them as UPDATE PUT ... so you can review the exact mutations offline first.

Command: pm jira validate

Report whether pm-jira has the credentials/base URL it needs — without making a network call and without leaking any secret value (it prints a hostname-only preview, never the token or email):

pm jira validate            # human-readable readiness summary
pm jira validate --json     # structured object: { ready, baseUrlPresent, ... }
pm jira validate --host https://company.atlassian.net

Preflight gate

pm-jira registers a preflight credential gate (pm-cli preflight capability / registerPreflight). Before a network-mutating command runs, it checks that the required credentials are present and, if not, aborts immediately with a clear, actionable error and a non-zero exit code — before any pm-store read or Jira REST call. This turns a deep, late failure into a fast, obvious one.

It fires only for these invocations:

Invocation Gated?
pm jira sync (no --dry-run) yes — pulls over the network
pm jira import (no --dry-run) yes — pulls over the network
pm jira export --push (no --dry-run) yes — POSTs/PUTs to Jira
pm jira sync|import --dry-run no — offline preview, no creds needed
pm jira export (no --push) no — prints payloads offline
pm jira validate no — diagnostics, must run without creds
any other / non-pm-jira command no

Example (no credentials set):

$ pm jira sync --project PROJ
pm-jira preflight: cannot run "pm jira sync" — missing Jira credentials:
JIRA_BASE_URL (or --host), JIRA_EMAIL, JIRA_API_TOKEN. Set JIRA_BASE_URL
(or pass --host), JIRA_EMAIL, and JIRA_API_TOKEN before a mutating command.
Create a token at https://id.atlassian.com/manage-profile/security/api-tokens .
Run "pm jira validate" to diagnose, or add --dry-run to preview offline.
# exit code 2

The message names only the missing variable names — never the token or email value. When credentials are present the gate is a silent pass-through.

Export-on-write hook (opt-in)

Setting PM_JIRA_PUSH_ON_WRITE=1 activates a best-effort onWrite hook. It is a strict no-op unless both the env flag is truthy and Jira credentials are present, and it can never fail your pm command (the pm hook runtime swallows any error). It intentionally does not auto-POST on every write; use the explicit, reviewable pm jira export --push to mirror items upstream.

Importer: jira-sync (config-driven)

Use the jira-sync importer in your pm-cli config for automated syncing:

{
  "importers": {
    "jira-sync": {
      "project": "PROJ",
      "maxResults": 300
    }
  }
}

Credentials are read from environment variables or from the importer config:

{
  "importers": {
    "jira-sync": {
      "JIRA_BASE_URL": "https://company.atlassian.net",
      "JIRA_EMAIL": "[email protected]",
      "JIRA_API_TOKEN": "<jira-api-token>",
      "project": "PROJ"
    }
  }
}

Status Mapping

The default mapping (override per-status with --status-map):

Jira Status pm Status
To Do, Open, Backlog, (any other) open
In Progress, In Review, In Development, Code Review in_progress
Done, Resolved, Closed, Complete, Completed closed
Blocked blocked

When the Jira status name is unrecognized (custom workflows), pm-jira falls back to the issue's statusCategory bucket: new → open, indeterminate → in_progress, done → closed.

Issue type mapping

Jira Issue Type pm Type
Bug, Defect Bug
Story, Epic Feature
Task, Sub-task Task
(any other) Issue

Override with --map issuetype=<pmType> (import) or --map issuetype=<jiraType> (export, with --rich).

Priority Mapping

Jira Priority pm Priority
Highest, Critical 1 (highest)
High 2
Medium, (any other) 3
Low, Lowest 4 (lowest)

Item Structure

Each imported item includes:

  • title: [PROJ-123] Issue summary
  • body: Issue description (converted from Atlassian Document Format to plain text)
  • description: A provenance marker Jira PROJ-123: https://…/browse/PROJ-123 so the Jira key + URL survive round-trips and power pm jira export. The extension also declares jira_key and jira_url as optional item schema fields (capability schema).
  • status: Mapped from Jira status (see table above)
  • priority: Mapped from Jira priority (see table above)
  • tags: Jira labels + fix version names
  • deadline: Jira due date (YYYY-MM-DD), if set

Note: pm's create has no generic custom-field setter for a standalone extension, so provenance is stored in the description marker rather than as structured metadata.

JQL Examples

# All open issues in a project, by priority
project = PROJ AND statusCategory != Done ORDER BY priority ASC

# Only issues assigned to you
project = PROJ AND assignee = currentUser()

# Issues updated in the last 7 days
project = PROJ AND updated >= -7d ORDER BY updated DESC

# Issues in a specific sprint
project = PROJ AND sprint in openSprints()

# Issues with a specific label
project = PROJ AND labels = "backend"

# Issues by type
project = PROJ AND issuetype = Bug AND statusCategory != Done

Development

# Install dev dependencies
npm install

# Build TypeScript
npm run build

# Watch mode
npm run dev

Multi-agent merge safety

This repo tracks its own project management in .agents/pm/ and ships a committed .gitattributes that maps those tracker artifacts to pm-cli's field-aware Git merge drivers, so concurrent-branch tracker edits merge cleanly instead of hard-conflicting.

The driver definitions live in per-clone Git config, so each clone must wire them once. npm install / npm ci does this automatically via the prepare script, scripts/prepare-merge-driver.ts: the launcher template pm-ops ships, copied unchanged, which a test compares byte for byte with the pinned template. It runs pm-ops's installer, which calls pm merge install when the pm CLI is on PATH and skips with a notice when it is not. A production install of a clone (npm ci --omit=dev) has no pm-ops, so the launcher skips with one notice, while a stale or broken pm-ops fails the install. Registry installs of this package never run prepare. Being Node-based, it behaves identically on POSIX shells and Windows cmd.exe; to (re)run it manually:

npm run merge:install    # or: pm merge install

After merging a branch that touched .agents/pm/, reconcile any residual history-hash drift with pm merge reconcile (pm-cli ≥ 2026.7.22): preview with pm merge reconcile --dry-run, apply with pm merge reconcile --message "post-merge reconcile", then confirm with pm validate, which scans the whole tracker and flags remaining history drift across every affected item (pm merge reconcile itself lists each affected stream in its output; pm history --verify <id> spot-checks one item). The field-aware driver already unions every author's content, so reconcile only re-greens the hash chain (no data loss) — see the authoritative pm-cli merge-safety guide. The older blunt pm history-repair --all remains available as a lower-level primitive.

Requirements

  • Node.js >=22.18.0 (uses native https module and Buffer)
  • pm-cli >=2026.10.4
  • TypeScript 6.x (dev dependency)

License

MIT

Release Automation

This package is release-ready for GitHub, npm, and Bun-compatible installs. CI runs type checking, build, production dependency audit, package packing, Bun install verification, and pm-changelog validation. The daily release workflow publishes only when commits exist after the latest release tag and uses pm-changelog to generate CHANGELOG.md and GitHub release notes.

Plan a lifecycle transition

pm jira plan-transition discovers the issue's current status and the transitions permitted for the authenticated Jira user, including required screen fields:

pm jira plan-transition --issue DEMO-123 --to closed --json
pm jira plan-transition --issue DEMO-123 --to closed --transition 31 \
  --fields '{"resolution":{"id":"1"}}'

Set JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN as for sync. --host overrides the base URL. Targets accept the existing pm status aliases; use --status-map 'QA=blocked,Shipped=closed' for custom workflow meanings. Explicit mappings win, then Blocked, then Jira's status category. Existing import mappings are unchanged. The host renders compact TOON by default or JSON with --json.

The result is ready, noop, or refused, always with writes: 0. Refusals include a stable code, remediation, compact permitted choices and the host-owned exit_code: 2 contract; the CLI exits 2. Required fields need explicit nonempty values, even if Jira advertises a default. Supplied fields must support set on the selected screen. For schema.type: array fields, supply a JSON array: each member must match an advertised allowed value exactly. An optional empty array clears the selection; required arrays must be nonempty. Other fields match their entire supplied value against an allowed value. Values are never echoed. This conservative check can refuse equivalent values with a different JSON representation. It does not replace Jira's execution-time schema checks, validators or permission checks.

Only a direct currently permitted transition can be ready. Multiple matching transitions require --transition. An indirect route returns no_permitted_transition with available next steps: have an authorized actor perform an approved intermediate transition separately, then rediscover. The API cannot certify future-state permissions. Rediscover immediately before any external execution; the plan is advisory, has no execution token, and performs neither Jira nor local tracker mutations. Authentication, permission and network failures return a redacted discovery_failed refusal.

The typed SDK exports JiraTransitionIntent, JiraTransitionPlan, discoverJiraTransitionPlan(intent, options?), and the offline planJiraTransition(intent, issueResponse, transitionsResponse) planner. See the SDK example for an intermediate-state refusal.

Without git

ADF evidence capture/replay, create-field completion previews, JQL selection comparison, transition planning, credential diagnostics and Jira import/export previews work in plain directories and non-git trackers. Import/sync and explicit export push also use the tracker and Jira APIs without requiring git. Ordinary imports create items through the pm SDK in process, so they do not need a separate pm executable on PATH. Atomic imports continue to use commitItemMutations. Changelog generation from release tags requires a git repository. Transition planning invokes no git subprocess and never writes a tracker. Selection comparison also invokes no git subprocess; it reads only the two explicit JSON files and never reads or writes local items or calls Jira.

Explain JQL selection changes

Compare two explicit snapshots of a synchronized selection without removing omitted local work:

pm jira explain-selection --before before.json --after after.json --limit 32 --json
pm jira explain-selection --before before.json --after after.json --limit 32 --offset 32

The typed SDK exports explainJqlSelectionChanges(before, after, options?), JqlSelectionSnapshot, JqlVisibilityEvidence, JqlSelectionOptions, JqlSelectionResult, and the related page, change, explanation and refusal types. See the complete JSON/CLI/SDK example for the snapshot contract and evidence requirements, and the public tracker validation for measured data.

The default output is compact TOON; --json uses the same typed result. kind: explained includes exact aggregate counts, a stable key-sorted page of entries/exits, queryChanged, and an omission/next-offset receipt. Only change keys and classifications are returned; queries, principals, tokens, issue bodies and summaries are not echoed. Defaults are 32 changes per page, maximum 256; offset is a nonnegative safe integer. Host output budgets also apply. If the host reports additional omissions, retry the same offset with --output-budget unbounded --output-limit unbounded before advancing.

Exits are query_excluded only with explicit independently established nonmembership evidence, permission_lost only with explicit permission-loss evidence, and otherwise unresolved. Visibility alone or a 404 is insufficient. Entries mean selected now and absent before; they do not imply a newly created Jira issue. All rows carry localAction: preserve. Every result, including a refusal, has writes: 0 and deletionProposals: 0.

Both snapshots must have the same canonical site origin, principal and scope, and must be chronological. A changed principal refuses rather than guessing permission loss. The entire initial-to-terminal cursor chain must be present; empty intermediate pages may advance tokens. Missing, disconnected, repeated, cycling or premature-terminal pages and duplicate keys fail closed. Incomplete page chains produce no change rows or deletion proposals. A capped import is not a complete selection snapshot. No local tracker or Jira mutation is offered.

Limits are 8 MiB per CLI file, 1,000 pages, 100 keys per page, 10,000 unique keys and 10,000 visibility observations per snapshot. Identifiers are bounded; JQL and opaque tokens have an 8,192-character limit. SDK callers supply parsed objects and receive the same structural limits. Narrow the entire synchronized scope when a limit is reached; never truncate a selection.

Refusals have kind: refused, an empty changes array, stable code, actionable remediation, and exit_code: 2 (the host exits 2):

Code Remedy
invalid_snapshot Supply valid v1 metadata, unique keys and valid observations.
incomplete_chain Recapture every page through the terminal response.
snapshot_limit Narrow the complete scope within file/page/key/evidence limits.
boundary_mismatch Compare the same site, principal and scope.
time_order Put the earlier snapshot first.
conflicting_evidence Recapture consistent visibility observations.
invalid_options Supply both files and valid limit/offset integers.
input_read / input_json Supply readable regular files containing valid JSON.

These are explanations of supplied membership and evidence. Jira's eventual consistency and caller assertions remain explicit limits; a complete chain does not prove atomic capture, live permissions, or why a query stopped matching. Core SDK complete reads/snapshots retain ownership of tracker capture and restore; pm-graph compares relationships rather than Jira query membership.

SDK adoption verification

Expected failures use the SDK createPmCliExpectedError contract. Error messages and semantic exit codes are retained; the error name is now the SDK's PmCliError. SDK callers should use isPmCliExpectedError rather than the removed package-specific CommandError class.

Run node scripts/sdk-adoption-differential.ts after building to compare the current build with an origin/main build against copies of the public tracker. Additional public package checkouts may be passed as arguments. Override the baseline with PM_JIRA_BASELINE_REF. The harness leaves the source checkouts and snapshots unchanged, exercises writes only in disposable non-git copies, and compares create fields, counts, repeat imports, diagnostics, command previews, and expected failures. Generated item IDs and timestamps are excluded from field comparison. The expected error name is an intentional SDK difference.

Certified complete local reads remain mandatory before import matching and export planning. Neither SDK replacement substitutes the cached event index for verified history. The regression suite also primes the event cache, corrupts authoritative SDK-created history, and checks that verifyHistoryEntries still detects the corruption. No Jira create, link or comment call is introduced by these replacements; imports only read Jira and create local items in one commit per item.

Create-field completion previews

Preview the required bindings and missing answers for an explicitly selected Jira project/type, using revisioned create metadata:

pm jira preview-create --contract docs/examples/create-contract.json --json
# Add the required Team answer, then protect the complete create batch:
pm jira export --project DEMO --create-contract create.json --push --json

The SDK exports previewJiraCreate and typed JiraCreate* contracts. Custom required fields and exact allowed values are checked before any guarded POST. Diagnostics fit a declared JSON byte budget and expose page.nextOffset. See the contract, limits and export guarantees and the example fixture. The preview is read-only and validates supplied metadata; it does not establish live Jira permissions.

All Jira commands reject supplied blank option values with invalid_option (exit 2) before I/O. Leave unused options omitted, and supply nonblank values for contracts and other guards. The shared boundary checks the whole bag, including secondary aliases and legacy importer credential options. See the option contract and table-driven validation.

Payload completeness follows each Jira metadata required flag. Optional empty values (including native labels: [] for untagged items) are omitted from guarded POST bodies. Required empty values stop the complete batch. The CLI blank-option rule applies to option values, while JSON payload fields follow their schemas; see the refuse/allow corpora.

Create previews and guarded exports share one final-body builder: explicit contract answers win over mapped item fields, which win over generated --rich and default values. previewJiraExport(plan, contract) reports each exact POST bodyDigest; runExportPush validates and sends that same JSON representation. Optional empty fields and native mappings absent from the captured create screen are omitted. Option references preserve schema-supported id/name/value forms; Jira Cloud users use accountId. See the agreement example and schema projection rules.

Project and issue type stay fixed outside field precedence. Every native and contract source must agree with the selected target before merging; matching contract overrides cannot conceal a plan for another project/type. Scope refusals remain typed in SDK, installed JSON/TOON and packed Node/Bun consumers.

Selected ADF block evidence

pm jira capture-adf --contract capture.json --json preserves selected description blocks with original source coordinates, including table cells, code and lists. Unselected siblings and media/card content are excluded; unsupported nodes are counted. Preview first, then approve the observed source revision and digest. Changed sources refuse capture. Output is a bounded portable packet; native items and Jira remain untouched. The SDK exports captureAdfEvidence, replayAdfEvidence and their typed contracts. See the contract and refusal guide and the runnable example. Snapshot approval requires a fresh caller-supplied source read; it does not attest live Jira freshness.

Programmatic callers can reuse a bound filesystem tracker via bindJiraPersistence. See persistence scope and remaining backend roadmap for its guarantees, recovery diagnostics and conformance limits.

Aus der README des Pakets auf GitHub. Auf GitHub ansehen