pulse-import

module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT

README

Import issues into Pulse from other tools.
v1: Jira CSV → Pulse issues + Main Docs

CI Release Go License GHCR

Install

Release binary (recommended)

Download for your OS from GitHub Releases, then:

chmod +x pulse-import && sudo mv pulse-import /usr/local/bin/
pulse-import --version

Also published: deb / rpm / apk packages on each release.

Go
go install github.com/try-pulse/pulse-import/cmd/pulse-import@latest
Docker
docker pull ghcr.io/try-pulse/pulse-import:latest
docker run --rm -e PULSE_ACCESS_TOKEN -v "$PWD:/data" \
  ghcr.io/try-pulse/pulse-import:latest --help

Authenticate

Variable Purpose
PULSE_ACCESS_TOKEN JWT Bearer token
PULSE_WORKSPACE_ID Optional default workspace
PULSE_API_URL Optional API base · default https://api.trypulse.tech/api/v1

Optional config file: ~/.config/pulse-import/config.yaml. It stores only the API URL and workspace ID; access tokens are never saved.

[!WARNING] Treat the token as a secret. The CLI accepts it only through PULSE_ACCESS_TOKEN; it is never persisted or accepted as a command-line flag.

Quick start

  1. In Jira: Advanced issue searchExport Excel CSV (all fields)
  2. Run:
export PULSE_ACCESS_TOKEN="<jwt>"
pulse-import

Prompts walk a wizard: Source (CSV file picker + Jira URL) → Destination (team / project / assignee) → User mappingReview → import progress.

  • Esc / Ctrl+C on a later step returns to the previous step; declining the final confirm cancels without writing
  • Terminal width drives layout, progress bar width, and truncation
  • TERM=dumb, non-TTY, or NO_COLOR falls back to accessible / plain output

Safe first pass — parses, validates, and prints the full write plan and every mapping decision. Creates nothing:

pulse-import --dry-run

Non-interactive

PULSE_ACCESS_TOKEN=… pulse-import \
  --yes \
  --importer jira-csv \
  --file ./jira-export.csv \
  --workspace <workspace-id> \
  --team <team-id-or-name> \
  --jira-url https://acme.atlassian.net

Undo an import

Every run writes a state journal recording exactly what it created. rollback deletes precisely that and nothing else:

pulse-import rollback --state-file ./jira-export.csv.pulse-import.state.jsonl

Labels are never deleted — they may already be attached to work that was not imported.

Options

Flag Description
--importer Importer id (jira-csv)
--file Path to the export CSV
--workspace Workspace ID
--team Target team (id or name)
--project Pin every issue to one project (id or name); disables epic→project mapping
--jira-url Jira Cloud or on-prem base URL
--assignee mapped (default) · self · none
--self-assign Shorthand for --assignee self
--map-user --map-user "Jane Doe=<pulse-user-id|email|skip>" (repeatable)
--epics project (default) or label
--sprints cycle (default): most recent sprint → Pulse cycle · label: every sprint stays a label
--skip-comments Do not import comments
--skip-labels Do not create or attach labels
--skip-relations Do not import blocks / blocked-by links
--strict-labels Fail instead of dropping labels past Pulse's limit of 10 per issue
--no-migrated-label Do not add the Migrated label
--skip-status Skip issues mapping to these Pulse statuses (comma separated)
--only-status Import only issues mapping to these Pulse statuses
--skip-stale Skip issues not updated within N days (or a duration like 4320h)
--concurrency Parallel Pulse writes (default 4; 1 disables parallelism)
--dry-run Run the complete preflight; create nothing
--continue-on-error Continue after a definitive per-item failure
--yes Non-interactive (no prompts)
--api-url Override the API base URL
--state-file Resume journal path; defaults beside the CSV
--adopt KEY=ID Resolve an unknown create with an existing Pulse entity
--retry-unknown KEY Explicitly retry an unknown create; may duplicate
-v / --version Print version

Field mapping

Jira Pulse
Summary Issue title
Description Main Doc (Jira wiki → Markdown → editor JSON)
Issue type Pulse type when mappable, plus a Type: … label
Epic Project, with its issues filed into it (--epics label for a label instead)
Parent / Sub-task Parent / sub-issue (Pulse allows one level; deeper nesting is flattened onto the top-most ancestor)
Status Pulse workflow status, best effort
Resolution Forces done when a resolution is set but the status does not say so
Priority urgent · high · medium · low · no_priority
Assignee Matched against the target team's members by email, then name
Labels Labels
Component/s Label Component: …
Fix Version/s Label Release: …
Affects Version/s Label Affects: …
Sprint Cycle — the most recent sprint becomes the issue's cycle, created as planned when missing; earlier sprints stay Sprint: … labels (--sprints label for labels only)
Comments Comments, prefixed with the original author and date
Due date Due date
Story points / Original estimate Estimate, snapped to the team's estimate scale
Blocks / is blocked by Blocking relations, applied after every issue exists
Attachments Links to the original files, in the Main Doc
Issue key, Reporter, Creator, Created, Updated, Resolved, Environment, Time spent Recorded in the Main Doc
Migrated label on everything the import created

Issue description in Pulse stays empty (it is a short UI suffix, not a body). The write-up is the Main Doc.

Anything the export carries that Pulse has no field for is listed in the plan under Not imported, so nothing is dropped silently.

What Pulse cannot store

Reporter, Creator and the original Created date cannot be set through the API — Pulse stamps them from the access token and the clock. They are written into the Main Doc instead, so the information survives the migration even though the fields cannot.

Matching Jira identifiers

Pulse allocates issue codes sequentially per team and the API accepts no explicit code, so identifiers can only line up when the target team starts empty. The importer creates issues in ascending source-key order to make that work, and the plan warns when the team already holds issues.

Safe resume

[!IMPORTANT] Pulse does not provide a server-side import id. pulse-import therefore writes a crash-safe JSONL journal beside the CSV before every create request. Re-running with the same CSV and target resumes completed work instead of creating it again — including after Jira users have joined your Pulse workspace, which is the supported way to improve user matching.

If a connection fails while creating an issue, Pulse may have accepted the request even though the CLI received no response. The item is marked unknown and automatic retry stops:

# After checking the issue in Pulse:
pulse-import ... --adopt ENG-123=64f... --yes

# Only when you confirmed no issue exists:
pulse-import ... --retry-unknown ENG-123 --yes

Changing the source file or the target workspace/team/project requires a different state file.

  • Export the CSV with all fields from Jira so mapping has what it needs.
  • Jira issue keys must be present and unique.
  • Pulse allows at most 10 labels per issue; extra labels are dropped least-first with a warning (--strict-labels fails instead).
  • Creating labels needs the labels:create permission — by default team managers and workspace owners/admins; workspaces with custom roles can differ. Use --skip-labels if you do not have it.
  • Cycles need cycles:create (same default roles) and are only possible on leaf teams; Jira's CSV export carries no sprint dates, so created cycles are planned with dates approximated from the issues' timestamps — complete finished ones in Pulse afterwards. A sprint whose name matches a completed cycle stays a label (Pulse cannot add issues to a completed cycle). Like labels, cycles are reused by name and never deleted by rollback.
  • Pulse sends notifications for every issue and comment an import creates, and Loop automations can trigger on them; there is no server-side way to suppress this for a bulk import. The plan warns about it on large imports.
  • Blocking links that would close a dependency cycle (which Pulse rejects) are dropped in preflight with a warning naming the dropped link.
  • A Main Doc over Pulse's 10 MiB upload cap fails preflight with the offending row, instead of a late 413 during the import.
  • Main Doc, comment and link failures are reported and produce a non-zero exit.

Help

Product trypulse.tech
Issues GitHub Issues
Security SECURITY.md
License MIT

Directories

Path Synopsis
cmd
pulse-import command
internal
cli
cli/tui
Package tui holds shared terminal layout helpers for interactive prompts.
Package tui holds shared terminal layout helpers for interactive prompts.
jira2md
Package jira2md converts Jira wiki markup to the GitHub-flavored Markdown subset that internal/platemd can turn into Plate JSON.
Package jira2md converts Jira wiki markup to the GitHub-flavored Markdown subset that internal/platemd can turn into Plate JSON.
platemd
Package platemd converts Plate.js JSON ↔ Markdown for the node set Pulse uses.
Package platemd converts Plate.js JSON ↔ Markdown for the node set Pulse uses.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL